*/
Check out and contribute to CodePedia, the wiki for developers.
*/

View \PLOT3D.CPP

Display mazes in three dimensions on your VGA monitor. C++ src.

Submitted By: Unknown
Rating: (Not rated) (Rate It)


#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <iostream.h>
#include "titillat.h"
#include "plot3d.h"

#ifndef TRUE
#define TRUE -1
#endif
#ifndef FALSE
#define FALSE 0
#endif

typedef struct
          {
            int x;
            int y;
          } box_rec;

plot3d::plot3d()
  {
    prime_array_allocated=FALSE;
    plot_prepared=FALSE;
  }

plot3d::~plot3d()
  {
    if (prime_array_allocated)
      delete prime_array;
  }

int plot3d::prepare_plot(
  double (*f)(double,double),
  double x_min,
  double x_max,
  double y_min,
  double y_max,
  int    (*external_to_plot)(double,double),
  int    (*red)(double,double),
  int    x_division_count,
  int    y_division_count,
  double rotation_in_degrees,
  double tilt_in_degrees,
  double light_x,
  double light_y,
  double light_z)
//      This function prepares a plot for generation.  If returns TRUE if
// and only if it is successful.  If it is successful, "plot" may be called
// to actually generate the plot.  Its parameters are as follow:
//
//           f -- z=f(x,y), the function to be plotted.  Before the plot is
//      tilted or rotated, the z-axis runs from the bottom to the top of the
//      display, the y-axis runs from the left to the right of the display,
//      and the x-axis runs out of the display.
//
//           x_min -- the minimum value of x to be plotted.
//
//           x_max -- the maximum value of x to be plotted.
//
//           y_min -- the minimum value of y to be plotted.
//
//           y_max -- the maximum value of y to be plotted.
//
//           external_to_plot -- a function that returns TRUE if and only if a
//      point should be omitted from the plot.
//
//           red -- a function that returns TRUE if and only if a point should
//      be flagged for highlighting.  A point should be so flagged only if it
//      can be seen in the final plot.
//
//           x_division_count -- the number of x divisions to be used in
//      constructing the plot.  At least two must be specified.
//
//           y_division_count -- the number of y divisions to be used in
//      constructing the plot.  At least two must be specified.
//
//           rotation_in_degrees -- rotation (degrees) about an axis parallel to
//      the z-axis and through the center of the surface.
//
//           tilt_in_degrees -- tilt (degrees) about an axis through the center
//      of the surface and parallel to a line from the lower left hand corner of
//      the display to the lower right hand corner of the display.  The plot is
//      tilted after it is rotated.
//
//           (light_x,light_y,light_z) -- a vector pointing to the light source
//      (at infinity).  The light source remains fixed while the plot is rotated
//      or tilted.
    {
      int       display_ready;
      prime_rec initialized_prime_rec;
      int       result;
      int       x_byte_num;

      plot_prepared=FALSE;
      if (prime_array_allocated)
        {
          delete prime_array;
          prime_array_allocated=FALSE;
        }
      num_x_divisions=x_division_count;
      num_y_divisions=y_division_count;
      num_primes=(long) num_x_divisions;
      num_primes*=((long) num_y_divisions);
        // Number of quadrilaterals composing the plot.
      initialized_prime_rec.base_z=(unsigned char) '\0';
      initialized_prime_rec.color=(unsigned char) '\0';
      initialized_prime_rec.x=(float) 0.0;
      initialized_prime_rec.y=(float) 0.0;
      initialized_prime_rec.z=(float) 0.0;
      initialized_prime_rec.x_division_index=0;
      initialized_prime_rec.y_division_index=0;
      prime_array=new varray<prime_rec>(initialized_prime_rec,num_primes,5);
        // Virtual array of quadrilaterals composing the plot.
      if (prime_array_allocated=(prime_array->allocated()))
        {
          titillator_ptr=new titillator;
          rotation=rotation_in_degrees;
          tilt=tilt_in_degrees;
          light.x=light_x;
          light.y=light_y;
          light.z=light_z;
          evaluate_and_transform(f,x_min,x_max,y_min,y_max,
           num_x_divisions,num_y_divisions,rotation,tilt,
           external_to_plot,red);
            // Compute the vertices, etc. of the quadrilaterals composing the
            // plot.
          shade();
            // Compute the shade of gray for each quadrilateral composing the
            // plot.
          adjust_perspective();
            // Force parallel lines running away from the viewer to converge at
            // the horizon.
          sort_back_to_front();
          plot_prepared=TRUE;
          delete titillator_ptr;
          display_ready=display_initialized();
            // Do whatever is necessary to prepare the display.
        }
      return (prime_array_allocated & display_ready);
    }

void plot3d::evaluate_and_transform(
  double (*f)(double,double),
  double x_min,
  double x_max,
  double y_min,
  double y_max,
  int    num_x_divisions,
  int    num_y_divisions,
  double rotation,
  double tilt,
  int    (*external_to_plot)(double,double),
  int    (*red)(double,double))
// Compute the vertices, etc. for each quadrilateral composing the plot.
    {
      double    cos_rotation;
      double    cos_tilt;
      double    degrees_per_radian;
      double    magnitude;
      prime_rec *prime;
      long      prime_num;
      double    radians;
      double    sin_rotation;
      double    sin_tilt;
      double    tem_x;
      double    tem_y;
      double    tem_z;
      double    x;
      double    x_delta;
      int       x_division_num;
      double    x_rotated;
      double    y;
      double    y_delta;
      int       y_division_num;
      double    z;

      degrees_per_radian=45.0/atan(1.0);
      radians=tilt/degrees_per_radian;
      cos_tilt=cos(radians);
      sin_tilt=sin(radians);
      radians=rotation/degrees_per_radian;
      cos_rotation=cos(radians);
      sin_rotation=sin(radians);
      z=f(x_min,y_min);
      x_rotated=x_min*cos_rotation+y_min*sin_rotation;
      y_prime_min=-x_min*sin_rotation+y_min*cos_rotation;
      z_prime_min=-x_rotated*sin_tilt+z*cos_tilt;
      y_prime_max=y_prime_min;
      z_prime_max=z_prime_min;
      x_prime_max=x_rotated*cos_tilt+z*sin_tilt;
      x_delta=(double) (num_x_divisions-1);
      x_delta=(x_max-x_min)/x_delta;
      y_delta=(double) (num_y_divisions-1);
      y_delta=(y_max-y_min)/y_delta;
      x=x_min;
      prime_num=(long) 0;
      for (x_division_num=0; x_division_num < num_x_divisions; x_division_num++)
        {
          titillator_ptr->titillate();
          y=y_min;
          for (y_division_num=0; y_division_num < num_y_divisions;
           y_division_num++)
            {
              z=f(x,y);
              prime=prime_array->vm_ptr(prime_num);
              if (external_to_plot(x,y))
                prime->base_z=(unsigned char) 3;
              else
                if (red(x,y))
                  prime->base_z=(unsigned char) 2;
                else
                  prime->base_z=(unsigned char) 1;
              prime->x_division_index=x_division_num;
              prime->y_division_index=y_division_num;
              x_rotated=x*cos_rotation+y*sin_rotation;
              tem_y=(-x*sin_rotation+y*cos_rotation);
              prime->y=(float) tem_y;
              tem_x=(x_rotated*cos_tilt+z*sin_tilt);
              prime->x=(float) tem_x;
              tem_z=(-x_rotated*sin_tilt+z*cos_tilt);
              prime->z=(float) tem_z;
              if (tem_x > x_prime_max)
                x_prime_max=tem_x;
              if (tem_y < y_prime_min)
                y_prime_min=tem_y;
              if (tem_y > y_prime_max)
                y_prime_max=tem_y;
              if (tem_z < z_prime_min)
                z_prime_min=tem_z;
              if (tem_z > z_prime_max)
                z_prime_max=tem_z;
              y+=y_delta;
              prime_num++;
            }
          x+=x_delta;
        }
      magnitude=light.x*light.x+light.y*light.y+light.z*light.z;
      magnitude=sqrt(magnitude);
      light.x=light.x/magnitude;
      light.y=light.y/magnitude;
      light.z=light.z/magnitude;
      return;
    }

void plot3d::shade()
// Compute the shade of gray for each quadrilateral composing the plot.
    {
      double     magnitude;
      vertex_rec normal;
      prime_rec  *prime;
      long       prime_num;
      vertex_rec vertex [4];
      int        x_division_num;
      int        y_division_num;

      color_min=(unsigned char) (NUM_COLORS-1);
      color_max=(unsigned char) '\0';
      prime_num=num_primes;
      for (x_division_num=num_x_divisions-1; x_division_num >= 0;
       --x_division_num)
        {
          titillator_ptr->titillate();
          for (y_division_num=num_y_divisions-1; y_division_num >= 0;
           --y_division_num)
            {
              prime_num--;
              prime=prime_array->vm_ptr(prime_num);
              vertex[0].x=(double) (prime->x);
              vertex[0].y=(double) (prime->y);
              vertex[0].z=(double) (prime->z);
              if (x_division_num < (num_x_divisions-1))
                if (y_division_num < (num_y_divisions-1))
                  {
                    prime_num+=((long) num_y_divisions);
                    prime=prime_array->vm_ptr(prime_num);
                    vertex[1].x=(double) (prime->x);
                    vertex[1].y=(double) (prime->y);
                    vertex[1].z=(double) (prime->z);
                    prime_num++;
                    prime=prime_array->vm_ptr(prime_num);
                    vertex[2].x=(double) (prime->x);
                    vertex[2].y=(double) (prime->y);
                    vertex[2].z=(double) (prime->z);
                    prime_num-=((long) num_y_divisions);
                    prime=prime_array->vm_ptr(prime_num);
                    vertex[3].x=(double) (prime->x);
                    vertex[3].y=(double) (prime->y);
                    vertex[3].z=(double) (prime->z);
                    prime_num--;
                  }
                else
                  {
                    prime_num--;
                    prime=prime_array->vm_ptr(prime_num);
                    vertex[1].x=(double) (prime->x);
                    vertex[1].y=(double) (prime->y);
                    vertex[1].z=(double) (prime->z);
                    prime_num+=((long) num_y_divisions);
                    prime=prime_array->vm_ptr(prime_num);
                    vertex[2].x=(double) (prime->x);
                    vertex[2].y=(double) (prime->y);
                    vertex[2].z=(double) (prime->z);
                    prime_num++;
                    prime=prime_array->vm_ptr(prime_num);
                    vertex[3].x=(double) (prime->x);
                    vertex[3].y=(double) (prime->y);
                    vertex[3].z=(double) (prime->z);
                    prime_num-=((long) num_y_divisions);
                  }
              else
                if (y_division_num < (num_y_divisions-1))
                  {
                    prime_num++;
                    prime=prime_array->vm_ptr(prime_num);
                    vertex[1].x=(double) (prime->x);
                    vertex[1].y=(double) (prime->y);
                    vertex[1].z=(double) (prime->z);
                    prime_num-=((long) num_y_divisions);
                    prime=prime_array->vm_ptr(prime_num);
                    vertex[2].x=(double) (prime->x);
                    vertex[2].y=(double) (prime->y);
                    vertex[2].z=(double) (prime->z);
                    prime_num--;
                    prime=prime_array->vm_ptr(prime_num);
                    vertex[3].x=(double) (prime->x);
                    vertex[3].y=(double) (prime->y);
                    vertex[3].z=(double) (prime->z);
                    prime_num+=((long) num_y_divisions);
                  }
                else
                  {
                    prime_num-=((long) num_y_divisions);
                    prime=prime_array->vm_ptr(prime_num);
                    vertex[1].x=(double) (prime->x);
                    vertex[1].y=(double) (prime->y);
                    vertex[1].z=(double) (prime->z);
                    prime_num--;
                    prime=prime_array->vm_ptr(prime_num);
                    vertex[2].x=(double) (prime->x);
                    vertex[2].y=(double) (prime->y);
                    vertex[2].z=(double) (prime->z);
                    prime_num+=((long) num_y_divisions);
                    prime=prime_array->vm_ptr(prime_num);
                    vertex[3].x=(double) (prime->x);
                    vertex[3].y=(double) (prime->y);
                    vertex[3].z=(double) (prime->z);
                    prime_num++;
                  }
              // Compute the normal to a quadrilateral by averaging the
              // normals to each vertex of the quadrilateral.
              normal.x
               =(vertex[1].y-vertex[0].y)*(vertex[3].z-vertex[0].z)
               -(vertex[3].y-vertex[0].y)*(vertex[1].z-vertex[0].z)
               +(vertex[2].y-vertex[1].y)*(vertex[0].z-vertex[1].z)
               -(vertex[0].y-vertex[1].y)*(vertex[2].z-vertex[1].z)
               +(vertex[3].y-vertex[2].y)*(vertex[1].z-vertex[2].z)
               -(vertex[1].y-vertex[2].y)*(vertex[3].z-vertex[2].z)
               +(vertex[0].y-vertex[3].y)*(vertex[2].z-vertex[3].z)
               -(vertex[2].y-vertex[3].y)*(vertex[0].z-vertex[3].z);
              normal.y
               =(vertex[3].x-vertex[0].x)*(vertex[1].z-vertex[0].z)
               -(vertex[1].x-vertex[0].x)*(vertex[3].z-vertex[0].z)
               +(vertex[0].x-vertex[1].x)*(vertex[2].z-vertex[1].z)
               -(vertex[2].x-vertex[1].x)*(vertex[0].z-vertex[1].z)
               +(vertex[1].x-vertex[2].x)*(vertex[3].z-vertex[2].z)
               -(vertex[3].x-vertex[2].x)*(vertex[1].z-vertex[2].z)
               +(vertex[2].x-vertex[3].x)*(vertex[0].z-vertex[3].z)
               -(vertex[0].x-vertex[3].x)*(vertex[2].z-vertex[3].z);
              normal.z
               =(vertex[1].x-vertex[0].x)*(vertex[3].y-vertex[0].y)
               -(vertex[3].x-vertex[0].x)*(vertex[1].y-vertex[0].y)
               +(vertex[2].x-vertex[1].x)*(vertex[0].y-vertex[1].y)
               -(vertex[0].x-vertex[1].x)*(vertex[2].y-vertex[1].y)
               +(vertex[3].x-vertex[2].x)*(vertex[1].y-vertex[2].y)
               -(vertex[1].x-vertex[2].x)*(vertex[3].y-vertex[2].y)
               +(vertex[0].x-vertex[3].x)*(vertex[2].y-vertex[3].y)
               -(vertex[2].x-vertex[3].x)*(vertex[0].y-vertex[3].y);
              prime=prime_array->vm_ptr(prime_num);
              magnitude
               =sqrt(normal.x*normal.x+normal.y*normal.y+normal.z*normal.z);
              if (magnitude == 0.0)
                {
                  color_min=(unsigned char) '\0';
                  prime->color=(unsigned char) '\0';
                }
              else
                {
                  prime->color
                   =(unsigned char) int((double(NUM_COLORS)/2.0)
                   *(1.0+(light.x*normal.x+light.y*normal.y
                   +light.z*normal.z)/magnitude)); // shadows not absolute
                  if (prime->color
                   >= (unsigned char) (NUM_COLORS))
                    prime->color=(unsigned char) (NUM_COLORS-1);
                  if ((prime->color) < color_min)
                    color_min=(prime->color);
                  if ((prime->color) > color_max)
                    color_max=(prime->color);
                }
            }
        }
      return;
    }

void plot3d::adjust_perspective()
// Make parallel lines running away from the viewer appear to converge at the
// horizon. 
    {
      prime_rec  *prime;
      long       prime_num;
      double     tem;
      vertex_rec vertex [4];
      int        x_division_num;
      double     x_eye;
      double     y_center;
      int        y_division_num;
      double     z_center;

      if ((y_prime_max-y_prime_min) > (z_prime_max-z_prime_min))
        x_eye=1.1*(y_prime_max-y_prime_min)+x_prime_max;
      else
        x_eye=1.1*(z_prime_max-z_prime_min)+x_prime_max;
      if (((y_prime_max-y_prime_min) > (z_prime_max-z_prime_min))
      ||  (z_prime_max != z_prime_min))
        {
          y_center=(y_prime_max+y_prime_min)/2.0;
          z_center=(z_prime_max+z_prime_min)/2.0;
          prime_num=(long) 0;
          for (x_division_num=0; x_division_num < num_x_divisions;
           x_division_num++)
            {
              titillator_ptr->titillate();
              for (y_division_num=0; y_division_num < num_y_divisions;
               y_division_num++)
                {
                  prime=prime_array->vm_ptr(prime_num);
                  vertex[0].x=(double) (prime->x);
                  vertex[0].y=(double) (prime->y);
                  vertex[0].z=(double) (prime->z);
                  if (x_division_num < (num_x_divisions-1))
                    if (y_division_num < (num_y_divisions-1))
                      {
                        prime_num+=((long) num_y_divisions);
                        prime=prime_array->vm_ptr(prime_num);
                        vertex[1].x=(double) (prime->x);
                        vertex[1].y=(double) (prime->y);
                        vertex[1].z=(double) (prime->z);
                        prime_num++;
                        prime=prime_array->vm_ptr(prime_num);
                        vertex[2].x=(double) (prime->x);
                        vertex[2].y=(double) (prime->y);
                        vertex[2].z=(double) (prime->z);
                        prime_num-=((long) num_y_divisions);
                        prime=prime_array->vm_ptr(prime_num);
                        vertex[3].x=(double) (prime->x);
                        vertex[3].y=(double) (prime->y);
                        vertex[3].z=(double) (prime->z);
                        prime_num--;
                      }
                    else
                      {
                        prime_num--;
                        prime=prime_array->vm_ptr(prime_num);
                        vertex[1].x=(double) (prime->x);
                        vertex[1].y=(double) (prime->y);
                        vertex[1].z=(double) (prime->z);
                        prime_num+=((long) num_y_divisions);
                        prime=prime_array->vm_ptr(prime_num);
                        vertex[2].x=(double) (prime->x);
                        vertex[2].y=(double) (prime->y);
                        vertex[2].z=(double) (prime->z);
                        prime_num++;
                        prime=prime_array->vm_ptr(prime_num);
                        vertex[3].x=(double) (prime->x);
                        vertex[3].y=(double) (prime->y);
                        vertex[3].z=(double) (prime->z);
                        prime_num-=((long) num_y_divisions);
                      }
                  else
                    if (y_division_num < (num_y_divisions-1))
                      {
                        prime_num++;
                        prime=prime_array->vm_ptr(prime_num);
                        vertex[1].x=(double) (prime->x);
                        vertex[1].y=(double) (prime->y);
                        vertex[1].z=(double) (prime->z);
                        prime_num-=((long) num_y_divisions);
                        prime=prime_array->vm_ptr(prime_num);
                        vertex[2].x=(double) (prime->x);
                        vertex[2].y=(double) (prime->y);
                        vertex[2].z=(double) (prime->z);
                        prime_num--;
                        prime=prime_array->vm_ptr(prime_num);
                        vertex[3].x=(double) (prime->x);
                        vertex[3].y=(double) (prime->y);
                        vertex[3].z=(double) (prime->z);
                        prime_num+=((long) num_y_divisions);
                      }
                    else
                      {
                        prime_num-=((long) num_y_divisions);
                        prime=prime_array->vm_ptr(prime_num);
                        vertex[1].x=(double) (prime->x);
                        vertex[1].y=(double) (prime->y);
                        vertex[1].z=(double) (prime->z);
                        prime_num--;
                        prime=prime_array->vm_ptr(prime_num&