/****************************************************************************/
/*                                                                          */
/*  VolVis is a volume visualization system for investigating, manipulating */
/*  and rendering geometric and volumetric data.                            */
/*                                                                          */
/*  Copyright (C) 1993 by the Research Foundation of the State University   */
/*                            of New York                                   */
/*                                                                          */
/*  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 1, or (at your option)     */
/*  any later version.                                                      */
/*                                                                          */
/*  This program is distributed in the hope that it will be useful,         */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of          */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           */
/*  GNU General Public License for more details.                            */
/*                                                                          */
/*  You should have received a copy of the GNU General Public License       */
/*  along with this program; if not, write to the Free Software             */
/*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               */
/*                                                                          */
/*  For information on VolVis, contact us at:                               */
/*                                                                          */
/*                volvis@cs.sunysb.edu                         (email)      */
/*                                                                          */
/*                Lisa Sobierajski & Ricardo Avila             (US Mail)    */
/*                Department of Computer Science                            */
/*                State University of New York at Stony Brook               */
/*                Stony Brook, New York  11794-4400                         */
/*                                                                          */
/****************************************************************************/



/*
 *                  File: project.c
 *                Author: Lisa Sobierajski & Rick Avila
 *                  Date:
 *           Description:
 *  Modification History:
 *
 *         who?         when?           why?
 *    -----------------------------------------------------------
 *
 */

# include <stdio.h>
# include <math.h>

# include "C_volvis.h"
# include "C_raytrace.h"

extern C_parc_cast_ray();

extern C_transform_fposition(); 
extern C_transform_fvector(); 
extern C_trace_ray();
extern C_setup_supersample();


extern C_IndirectLightInfo	indirect_light_info;

C_ReturnStatus C_cast_rays( world, view, image, parc_flag )
C_World			*world;
C_View			*view; 
C_Image			*image;
int			parc_flag;
{
	extern C_ImageTable	image_table;

	C_Time_Val	start_time,
			end_time;
	double		interval;
	extern void	C_get_time();
	extern double	C_diff_sec();
	extern int	C_delete_image();

	int		num_processors;
	void 		C_cast_rays_process();
	void		C_Single_Processor();
	void		image_expose_callback();
	C_ReturnStatus	status[50]; /* should be C_MAX_PROCESSORS? */
	C_ReturnStatus  final_status;
	int		loop;
	int		image_num;



	C_get_time( &start_time );

	num_processors = C_Parallel_Get_Num_Processors();

	C_Parallel_Set_Num_Processors( num_processors );

	C_Parallel_Process((C_cast_rays_process, world, view,
			    image, parc_flag, status ));
	C_Parallel_Kill_Processes();

	final_status = C_NORMAL_STATUS;

	for ( loop = 0; loop < num_processors; loop++ )
	{
	 	if ( status[loop] == C_CANCEL_STATUS  ||
		     status[loop] == C_ERROR_STATUS )
		{
			final_status = status[loop];
			break;
		}
	}

	/*** Find out image number  ***/
	for( image_num = 0; image_num < image_table.num_images; image_num++ )
	{
		if( image == image_table.image_entry[image_num]->image )
			break;
	}

	switch( final_status )
	{
	    case C_NORMAL_STATUS:
		C_get_time( &end_time );
		interval = C_diff_sec( &start_time, &end_time );
		image->image_info.compute_time = interval;

		/* Redisplay Image */
		if ( view->display_during_projection )
		{
		    image_expose_callback( 
			image_table.image_entry[image_num]->ui_win, 
			image_num,
			NULL );
		}
		break;

	    case C_CANCEL_STATUS:
	    case C_ERROR_STATUS:
		if ( image_num < image_table.num_images )
		{
            		if ( image_table.image_entry[ image_num]->parent_win )
                           C_Destroy_UIWin( 
				image_table.image_entry[image_num]->parent_win);

			C_delete_image( &image_table, image_num );
		}


		break;
	}

	return final_status;

}

void C_Single_Processor( func, world, view, image, parc_flag, status )
void			*func;
C_World			*world;
C_View			*view; 
C_Image			*image;
int			parc_flag;
C_ReturnStatus		status[50];
{
	C_cast_rays_process( world, view, image, parc_flag, status );
}


void C_cast_rays_process( world, view, image, parc_flag, status )
C_World			*world;
C_View			*view; 
C_Image			*image;
int			parc_flag;
C_ReturnStatus		status[50];
{
/***
	extern void	C_rt_sample_indirect_light();
***/
	extern void	C_parc_polygon_projection();
	extern int	C_process_stop_event();

	extern C_ImageTable	image_table;

	int		image_x, 
			image_y,
			difference,
			num_samples,
			sample_loop,
			total_color_red,
			total_color_green,
			total_color_blue;
	C_Byte		*rptr,
			*gptr,
			*bptr,
			*red_row[2],
			*green_row[2],
			*blue_row[2],
			*changed_row[2],
			*top_red,
			*bottom_red,
			*top_green,
			*bottom_green,
			*top_blue,
			*bottom_blue,
			*top_changed,
			*bottom_changed,
			*top_ptr_red,
			*bottom_ptr_red,
			*top_ptr_green,
			*bottom_ptr_green,
			*top_ptr_blue,
			*bottom_ptr_blue,
			*top_ptr_changed,
			*bottom_ptr_changed,
			*temp_ptr,
			change_bottom;
	C_FPosition	ray_origin,
			ss_ray_origin,
			eye_point,
			upper_left_corner;
	C_FVector	ray_direction,
			ss_ray_direction,
			sample_directions[C_MAX_SAMPLES];
	C_Color		color,
			extra_samples[C_MAX_SAMPLES];
	float		x_spacing,
			y_spacing,
	     		temp_float,
			distance;
	C_RayTraceInfo	rt_info;
	int		volume;
	C_IntersectInfo intersect_info;
	int		processor_num;
	int		num_processors;
	int		image_num;

	/*** Find out image number  ***/
	for ( image_num = 0; image_num < image_table.num_images; image_num++ )
	{
		if ( image == image_table.image_entry[image_num]->image )
			break;
	}

	if ( image_num >= image_table.num_images )
		image_num = -1;


	processor_num = C_Parallel_Get_Processor_Id();
	num_processors = C_Parallel_Get_Num_Processors(); 

	C_Parallel_Sync();

	if ( view->max_indirect_light_level > 0 )
	{
/***
		C_rt_sample_indirect_light( world, view );
***/
	}

	
	if (view->fov != 0.0)
	{
		view->height_units = 2.0*tan((double)(view->fov/2.0));
		view->width_units = view->height_units *
		   ((float)(view->width_pixels)/((float)view->height_pixels));	
	}

	upper_left_corner.x = view->c_sys.origin.x - 
		view->width_units/2.0 * view->c_sys.x_axis.x +
		view->height_units/2.0 * view->c_sys.y_axis.x; 
		
	upper_left_corner.y = view->c_sys.origin.y - 
		view->width_units/2.0 * view->c_sys.x_axis.y +
		view->height_units/2.0 * view->c_sys.y_axis.y; 
		
	upper_left_corner.z = view->c_sys.origin.z - 
		view->width_units/2.0 * view->c_sys.x_axis.z +
		view->height_units/2.0 * view->c_sys.y_axis.z; 



	x_spacing = view->width_units /
		    (float)(view->width_pixels-1);

	y_spacing = view->height_units /
		    (float)(view->height_pixels-1);

	rptr = image->red;
	gptr = image->green;
	bptr = image->blue;

	rt_info.ray_type = C_PRIMARY_RAY;
	rt_info.ray_level = 0;
	rt_info.max_distance = C_BIG_FLOAT;

	/* Project PARC Polygons If Necessary */
	if( parc_flag )
		C_parc_polygon_projection();

	if (view->fov == 0.0)
	{
	      ray_direction.x = view->c_sys.z_axis.x;
	      ray_direction.y = view->c_sys.z_axis.y;
	      ray_direction.z = view->c_sys.z_axis.z;

	      for (image_y = image->height - 1; image_y >= 0; image_y--)
	      {
	       if ( (image_y % num_processors) == processor_num )
	       {
		    if ( C_process_stop_event() )
		    {
			status[processor_num] = C_CANCEL_STATUS;
			C_Parallel_Sync();
			return;
		    }

		    if ( (view->display_during_projection > 0 ) &&
			 (image_y%view->display_during_projection) <=
				(num_processors-1) && 
			   C_MASTER_PROCESS )
		    {
			C_Parallel_Sync();

			if( processor_num == 0 )
				ivi_display_image( image_num );
		    }

		    if ( (view->display_during_projection > 0 ) &&
			  (image_y%view->display_during_projection) == 0 && 
			   C_MASTER_PROCESS )
		    {
 	    		printf("image_y = %d\n",image_y);
		    }

		    ray_origin.x = upper_left_corner.x - 
			       (image->height - image_y) * 
			       view->c_sys.y_axis.x * y_spacing;

		    ray_origin.y = upper_left_corner.y - 
			       (image->height - image_y) * 
			       view->c_sys.y_axis.y * y_spacing;
				
		    ray_origin.z = upper_left_corner.z - 
			       (image->height - image_y) * 
			       view->c_sys.y_axis.z * y_spacing;
				
	    	    for (image_x = 0; image_x < image->width; image_x++)	
	    	    {
			switch ( view->projection_type )
			{
			  case C_RAY_TRACE:
			  case C_VOLRAD:
				rt_info.ray_origin.x = ray_origin.x;
				rt_info.ray_origin.y = ray_origin.y;
				rt_info.ray_origin.z = ray_origin.z;
				rt_info.ray_direction.x = ray_direction.x;
				rt_info.ray_direction.y = ray_direction.y;
				rt_info.ray_direction.z = ray_direction.z;
				C_trace_ray( &rt_info, world, view,
					     &intersect_info );
				color.red   = 
					intersect_info.intersect_color.red;
				color.green = 
					intersect_info.intersect_color.green;
				color.blue  = 
					intersect_info.intersect_color.blue;
				break;
			  case C_PARC:
				C_parc_cast_ray(image_x, image_y,
						ray_origin, ray_direction, 
					        world, &color);
				break;
			  default:
				fprintf(stderr,"Unknow projection type!\n");
				break;
			}

			*rptr++ = color.red;
			*gptr++ = color.green;
			*bptr++ = color.blue;

			ray_origin.x += view->c_sys.x_axis.x * x_spacing;
			ray_origin.y += view->c_sys.x_axis.y * x_spacing;
			ray_origin.z += view->c_sys.x_axis.z * x_spacing;
		    }
	       }
	       else
	       {
			rptr += image->width;
			gptr += image->width;
			bptr += image->width;
	       }
  	      }
	}
	else
	{
	      eye_point.x = view->c_sys.origin.x - view->c_sys.z_axis.x;
	      eye_point.y = view->c_sys.origin.y - view->c_sys.z_axis.y;
	      eye_point.z = view->c_sys.origin.z - view->c_sys.z_axis.z;

	      for (image_y = image->height - 1; image_y >= 0; image_y--)
	      {
	       if ( (image_y % num_processors) == processor_num )
	       {
		    if ( C_process_stop_event() )
		    {
			status[processor_num] = C_CANCEL_STATUS;
			C_Parallel_Sync();
			return;
		    }

		    if ( (view->display_during_projection > 0 ) &&
			  (image_y%view->display_during_projection) <=
				(num_processors-1) && 
			   C_MASTER_PROCESS )
		    {
			C_Parallel_Sync();
			if( processor_num == 0 )
				ivi_display_image( image_num );
		    }

		    ray_origin.x = upper_left_corner.x - 
			       (image->height - image_y) * 
			       view->c_sys.y_axis.x * y_spacing;

		    ray_origin.y = upper_left_corner.y - 
			       (image->height - image_y) * 
			       view->c_sys.y_axis.y * y_spacing;
				
		    ray_origin.z = upper_left_corner.z - 
			       (image->height - image_y) * 
			       view->c_sys.y_axis.z * y_spacing;
				

	    	    for (image_x = 0; image_x < image->width; image_x++)	
	    	    {
			ray_direction.x = ray_origin.x - eye_point.x;
			ray_direction.y = ray_origin.y - eye_point.y;
			ray_direction.z = ray_origin.z - eye_point.z;


			C_Normalize( ray_direction.x, ray_direction.y,
				     ray_direction.z, temp_float );

			if ( view->image_accuracy == C_LOW_IMAGE_ACCURACY ) 
			{
			  if ( (image->height-image_y-1)%2 )
			  {
			    *rptr++ = *(rptr-image->width);
			    *gptr++ = *(gptr-image->width);
			    *bptr++ = *(bptr-image->width);
			  }
			  else if ( image_x%2 )
			  {
			    *rptr++ = *(rptr-1);
			    *gptr++ = *(gptr-1);
			    *bptr++ = *(bptr-1);
			  }
			  else
			  {
			    switch ( view->projection_type )
			    {
			      case C_RAY_TRACE:
			      case C_VOLRAD:
				rt_info.ray_origin.x = view->c_sys.origin.x;
				rt_info.ray_origin.y = view->c_sys.origin.y;
				rt_info.ray_origin.z = view->c_sys.origin.z;
				rt_info.ray_direction.x = ray_direction.x;
				rt_info.ray_direction.y = ray_direction.y;
				rt_info.ray_direction.z = ray_direction.z;
				C_trace_ray( &rt_info, world, view,
					     &intersect_info );
				color.red   = 
					intersect_info.intersect_color.red;
				color.green = 
					intersect_info.intersect_color.green;
				color.blue  = 
					intersect_info.intersect_color.blue;
				break;
			      case C_PARC:
				C_parc_cast_ray(image_x, image_y,
					      	view->c_sys.origin, 
						ray_direction, 
					      	world, &color);
				break;
			      default:
				fprintf(stderr,"Unknow projection type!\n");
				break;
			    }


			    *rptr++ = color.red;
			    *gptr++ = color.green;
			    *bptr++ = color.blue;

			  }
			}
			else
			{
			  switch ( view->projection_type )
			  {
			    case C_RAY_TRACE:
			    case C_VOLRAD:
				rt_info.ray_origin.x = view->c_sys.origin.x;
				rt_info.ray_origin.y = view->c_sys.origin.y;
				rt_info.ray_origin.z = view->c_sys.origin.z;
				rt_info.ray_direction.x = ray_direction.x;
				rt_info.ray_direction.y = ray_direction.y;
				rt_info.ray_direction.z = ray_direction.z;
				C_trace_ray( &rt_info, world, view,
					     &intersect_info );
				color.red   = 
					intersect_info.intersect_color.red;
				color.green = 
					intersect_info.intersect_color.green;
				color.blue  = 
					intersect_info.intersect_color.blue;
				break;
			    case C_PARC:
				C_parc_cast_ray(image_x, image_y,
					      	view->c_sys.origin, 
						ray_direction, 
					      	world, &color);
				break;
			    default:
				fprintf(stderr,"Unknow projection type!\n");
				break;
			  }


			  *rptr++ = color.red;
			  *gptr++ = color.green;
			  *bptr++ = color.blue;
			}

			ray_origin.x += view->c_sys.x_axis.x * x_spacing;
			ray_origin.y += view->c_sys.x_axis.y * x_spacing;
			ray_origin.z += view->c_sys.x_axis.z * x_spacing;

		    }
	       }	
	       else
	       {
			rptr += image->width;
			gptr += image->width;
			bptr += image->width;
	       }
  	      }
	}

    if ( processor_num == 0 )
    {
	switch ( view->supersample_type )
	{
	  case C_NO_SUPER_SAMPLE:
			break;

	  case C_LOW_SUPER_SAMPLE:
	  case C_MEDIUM_SUPER_SAMPLE:
	  case C_HIGH_SUPER_SAMPLE:
			
		C_setup_supersample( view->supersample_type,
				     &num_samples, 
				     sample_directions,
				     view );

		if ( view->fov == C_PARALLEL_FOV )
		{
			ss_ray_direction.x = ray_direction.x;
			ss_ray_direction.y = ray_direction.y;
			ss_ray_direction.z = ray_direction.z;
		}

		top_ptr_red	  =
		top_red		  =
		red_row[0]        = (C_Byte *) malloc( image->width *
						 sizeof( C_Byte ) ); 
		bottom_ptr_red	  =
		bottom_red	  =
		red_row[1]        = (C_Byte *) malloc( image->width *
						 sizeof( C_Byte ) ); 
		top_ptr_green	  =
		top_green	  =
		green_row[0]      = (C_Byte *) malloc( image->width *
						 sizeof( C_Byte ) ); 
		bottom_ptr_green  =
		bottom_green	  =
		green_row[1]      = (C_Byte *) malloc( image->width *
						 sizeof( C_Byte ) ); 
		top_ptr_blue	  =
		top_blue	  =
		blue_row[0]       = (C_Byte *) malloc( image->width *
						 sizeof( C_Byte ) ); 
		bottom_ptr_blue	  =
		bottom_blue	  =
		blue_row[1]       = (C_Byte *) malloc( image->width *
						 sizeof( C_Byte ) ); 
		top_ptr_changed	  =
		top_changed	  =
		changed_row[0]    = (C_Byte *) malloc( image->width *
						 sizeof( C_Byte ) ); 
		bottom_ptr_changed=
		bottom_changed	  =
		changed_row[1]    = (C_Byte *) malloc( image->width *
						 sizeof( C_Byte ) ); 

		rptr = image->red;
		gptr = image->green;
		bptr = image->blue;

		for (image_x = 0; image_x < image->width; image_x++)
		{
			*(top_ptr_red++)   = *(rptr++);	
			*(top_ptr_green++) = *(gptr++);	
			*(top_ptr_blue++)  = *(bptr++);	
		}
		
		for (image_x = 0; image_x < image->width; image_x++)
		{
			*(bottom_ptr_red++)   = *(rptr++);	
			*(bottom_ptr_green++) = *(gptr++);	
			*(bottom_ptr_blue++)  = *(bptr++);	
		}

		for (image_x = 0; image_x < image->width; image_x++)
		{
			*(top_ptr_changed++)    = FALSE;
			*(bottom_ptr_changed++) = FALSE;
		}

		top_ptr_red        = top_red;
		top_ptr_green      = top_green; 
		top_ptr_blue       = top_blue;
    
		bottom_ptr_red     = top_red;
		bottom_ptr_green   = top_green; 
		bottom_ptr_blue    = top_blue;

		top_ptr_changed    = top_changed;
		bottom_ptr_changed = bottom_changed;


		for (image_y = image->height - 2; image_y > 0; image_y--) 
		{
		    top_ptr_red++;
		    top_ptr_green++;
		    top_ptr_blue++;
		    bottom_ptr_red++;
		    bottom_ptr_green++;
		    bottom_ptr_blue++;
		    top_ptr_changed++;
		    bottom_ptr_changed++;

		    ray_origin.x = upper_left_corner.x - 
			       (image->height - image_y) * 
			       view->c_sys.y_axis.x * y_spacing;

		    ray_origin.y = upper_left_corner.y - 
			       (image->height - image_y) * 
			       view->c_sys.y_axis.y * y_spacing;
				
		    ray_origin.z = upper_left_corner.z - 
			       (image->height - image_y) * 
			       view->c_sys.y_axis.z * y_spacing;
				
		    ray_origin.x += view->c_sys.x_axis.x * x_spacing;
		    ray_origin.y += view->c_sys.x_axis.y * x_spacing;
		    ray_origin.z += view->c_sys.x_axis.z * x_spacing;

		    for (image_x=1; image_x<image->width-1; image_x++)
		    {
			change_bottom = FALSE;

			difference = (int)(*(bottom_ptr_red)) - 
				     (int)(*(bottom_ptr_red-1)) +
				     (int)(*(bottom_ptr_green)) - 
				     (int)(*(bottom_ptr_green-1)) +
				     (int)(*(bottom_ptr_blue)) - 
				     (int)(*(bottom_ptr_blue-1));

			if ( difference > C_MAX_PIXEL_DIFF || 
			     difference < -C_MAX_PIXEL_DIFF)
			{
			  change_bottom = TRUE;

			  if ( *(bottom_ptr_changed-1) == FALSE )
			  {
				*(bottom_ptr_changed-1) = TRUE;

				total_color_red   = *(bottom_ptr_red-1);
				total_color_green = *(bottom_ptr_green-1);
				total_color_blue  = *(bottom_ptr_blue-1);
				
				for (sample_loop = 0; 
				     sample_loop < num_samples; 
				     sample_loop++ )
				{
				  ss_ray_origin.x = ray_origin.x -
					view->c_sys.x_axis.x * x_spacing +
					sample_directions[sample_loop].x;
				  ss_ray_origin.y = ray_origin.y -
					view->c_sys.x_axis.y * x_spacing +
					sample_directions[sample_loop].y;
				  ss_ray_origin.z = ray_origin.z -
					view->c_sys.x_axis.z * x_spacing +
					sample_directions[sample_loop].z;


				  if ( view->fov != C_PARALLEL_FOV )
				  {
				    ss_ray_direction.x = ss_ray_origin.x -
						         eye_point.x ;
				    ss_ray_direction.y = ss_ray_origin.y -
						         eye_point.y;
				    ss_ray_direction.z = ss_ray_origin.z -
							 eye_point.z;

				    C_Normalize( ss_ray_direction.x, 
					         ss_ray_direction.y,
				     	         ss_ray_direction.z, 
					         temp_float );
				  }

				  switch ( view->projection_type )
				  {
			  	    case C_RAY_TRACE:
					  rt_info.ray_origin.x = 
							ss_ray_origin.x;
					  rt_info.ray_origin.y = 
							ss_ray_origin.y;
					  rt_info.ray_origin.z = 
							ss_ray_origin.z;
					  rt_info.ray_direction.x = 
							ss_ray_direction.x;
					  rt_info.ray_direction.y = 
							ss_ray_direction.y;
					  rt_info.ray_direction.z = 
							ss_ray_direction.z;
					  C_trace_ray( &rt_info, 
						       world, view, 
						       &(intersect_info) );
					  color.red   = 
					   intersect_info.intersect_color.red;
					  color.green = 
					   intersect_info.intersect_color.green;
					  color.blue  = 
					   intersect_info.intersect_color.blue;
					  break;
			  	    case C_PARC:
					  C_parc_cast_ray(image_x, image_y,
							ss_ray_origin, 
						        ss_ray_direction, 
					       		world, &color);
					  break;
			  	    default:
					  fprintf(stderr,
						"Unknow projection type!\n");
					  break;
				  }

				  total_color_red   += color.red;
				  total_color_green += color.green;
				  total_color_blue  += color.blue;

				}

				*(image->red + image_x - 1 + 
					(image->height - image_y - 1) *
					image->width) = total_color_red /
						        (num_samples + 1);
				*(image->green + image_x - 1 + 
					(image->height - image_y - 1) *
					image->width) = total_color_green /
						        (num_samples + 1);
				*(image->blue + image_x - 1 + 
					(image->height - image_y - 1) *
					image->width) = total_color_blue /
						        (num_samples + 1);
			  }

			}

			difference = (int)(*(bottom_ptr_red)) - 
				     (int)(*(top_ptr_red-1)) +
				     (int)(*(bottom_ptr_green)) - 
				     (int)(*(top_ptr_green-1)) +
				     (int)(*(bottom_ptr_blue)) - 
				     (int)(*(top_ptr_blue-1));

			if ( difference > C_MAX_PIXEL_DIFF || 
			     difference < -C_MAX_PIXEL_DIFF)
			{
			  change_bottom = TRUE;

			  if ( *(top_ptr_changed-1) == FALSE )
			  {
				*(top_ptr_changed-1) = TRUE;

				total_color_red   = *(top_ptr_red-1);
				total_color_green = *(top_ptr_green-1);
				total_color_blue  = *(top_ptr_blue-1);

				for (sample_loop = 0; 
				     sample_loop < num_samples; 
				     sample_loop++ )
				{
				  ss_ray_origin.x = ray_origin.x -
					view->c_sys.x_axis.x * x_spacing +
					view->c_sys.y_axis.x * y_spacing +
					sample_directions[sample_loop].x;
				  ss_ray_origin.y = ray_origin.y -
					view->c_sys.x_axis.y * x_spacing +
					view->c_sys.y_axis.y * y_spacing +
					sample_directions[sample_loop].y;
				  ss_ray_origin.z = ray_origin.z -
					view->c_sys.x_axis.z * x_spacing +
					view->c_sys.y_axis.z * y_spacing +
					sample_directions[sample_loop].z;

				  if ( view->fov != C_PARALLEL_FOV )
				  {
				    ss_ray_direction.x = ss_ray_origin.x -
						         eye_point.x ;
				    ss_ray_direction.y = ss_ray_origin.y -
						         eye_point.y;
				    ss_ray_direction.z = ss_ray_origin.z -
							 eye_point.z;

				    C_Normalize( ss_ray_direction.x, 
					         ss_ray_direction.y,
				     	         ss_ray_direction.z, 
					         temp_float );
				  }

				  switch ( view->projection_type )
				  {
			  	    case C_RAY_TRACE:
					  rt_info.ray_origin.x = 
							ss_ray_origin.x;
					  rt_info.ray_origin.y = 
							ss_ray_origin.y;
					  rt_info.ray_origin.z = 
							ss_ray_origin.z;
					  rt_info.ray_direction.x = 
							ss_ray_direction.x;
					  rt_info.ray_direction.y = 
							ss_ray_direction.y;
					  rt_info.ray_direction.z = 
							ss_ray_direction.z;
					  C_trace_ray( &rt_info, 
						       world, view, 
						       &(intersect_info) );
					  color.red   = 
					   intersect_info.intersect_color.red;
					  color.green = 
					   intersect_info.intersect_color.green;
					  color.blue  = 
					   intersect_info.intersect_color.blue;
					  break;
			  	    case C_PARC:
					  C_parc_cast_ray(image_x, image_y,
							ss_ray_origin, 
						        ss_ray_direction, 
					       		world, &color);
					  break;
			  	    default:
					  fprintf(stderr,
						"Unknow projection type!\n");
					  break;
				  }

				  total_color_red   += color.red;
				  total_color_green += color.green;
				  total_color_blue  += color.blue;
				}

				*(image->red + image_x - 1 + 
					(image->height - image_y - 2) *
					image->width) = total_color_red /
						        (num_samples + 1);
				*(image->green + image_x - 1 + 
					(image->height - image_y - 2) *
					image->width) = total_color_green /
						        (num_samples + 1);
				*(image->blue + image_x - 1 + 
					(image->height - image_y - 2) *
					image->width) = total_color_blue /
						        (num_samples + 1);
			  }

			}

			difference = (int)(*(bottom_ptr_red)) - 
				     (int)(*(top_ptr_red)) +
				     (int)(*(bottom_ptr_green)) - 
				     (int)(*(top_ptr_green)) +
				     (int)(*(bottom_ptr_blue)) - 
				     (int)(*(top_ptr_blue));

			if ( difference > C_MAX_PIXEL_DIFF || 
			     difference < -C_MAX_PIXEL_DIFF)
			{
			  change_bottom = TRUE;

			  if ( *top_ptr_changed == FALSE )
			  {
				*top_ptr_changed = TRUE;

				total_color_red   = *(top_ptr_red);
				total_color_green = *(top_ptr_green);
				total_color_blue  = *(top_ptr_blue);

				for (sample_loop = 0; 
				     sample_loop < num_samples; 
				     sample_loop++ )
				{
				  ss_ray_origin.x = ray_origin.x +
					view->c_sys.y_axis.x * y_spacing +
					sample_directions[sample_loop].x;
				  ss_ray_origin.y = ray_origin.y +
					view->c_sys.y_axis.y * y_spacing +
					sample_directions[sample_loop].y;
				  ss_ray_origin.z = ray_origin.z +
					view->c_sys.y_axis.z * y_spacing +
					sample_directions[sample_loop].z;

				  if ( view->fov != C_PARALLEL_FOV )
				  {
				    ss_ray_direction.x = ss_ray_origin.x -
						         eye_point.x ;
				    ss_ray_direction.y = ss_ray_origin.y -
						         eye_point.y;
				    ss_ray_direction.z = ss_ray_origin.z -
							 eye_point.z;

				    C_Normalize( ss_ray_direction.x, 
					         ss_ray_direction.y,
				     	         ss_ray_direction.z, 
					         temp_float );
				  }

				  switch ( view->projection_type )
				  {
			  	    case C_RAY_TRACE:
					  rt_info.ray_origin.x = 
							ss_ray_origin.x;
					  rt_info.ray_origin.y = 
							ss_ray_origin.y;
					  rt_info.ray_origin.z = 
							ss_ray_origin.z;
					  rt_info.ray_direction.x = 
							ss_ray_direction.x;
					  rt_info.ray_direction.y = 
							ss_ray_direction.y;
					  rt_info.ray_direction.z = 
							ss_ray_direction.z;
					  C_trace_ray( &rt_info, 
						       world, view, 
						       &(intersect_info) );
					  color.red   = 
					   intersect_info.intersect_color.red;
					  color.green = 
					   intersect_info.intersect_color.green;
					  color.blue  = 
					   intersect_info.intersect_color.blue;
					  break;
			  	    case C_PARC:
					  C_parc_cast_ray(image_x, image_y,
							ss_ray_origin, 
						        ss_ray_direction, 
					       		world, &color);
					  break;
			  	    default:
					  fprintf(stderr,
						"Unknow projection type!\n");
					  break;
				  }

				  total_color_red   += color.red;
				  total_color_green += color.green;
				  total_color_blue  += color.blue;
				}

				*(image->red + image_x + 
					(image->height - image_y - 2) *
					image->width) = total_color_red /
						        (num_samples + 1);
				*(image->green + image_x + 
					(image->height - image_y - 2) *
					image->width) = total_color_green /
						        (num_samples + 1);
				*(image->blue + image_x + 
					(image->height - image_y - 2) *
					image->width) = total_color_blue /
						        (num_samples + 1);
			  }

			}

			difference = (int)(*(bottom_ptr_red)) - 
				     (int)(*(top_ptr_red+1)) +
				     (int)(*(bottom_ptr_green)) - 
				     (int)(*(top_ptr_green+1)) +
				     (int)(*(bottom_ptr_blue)) - 
				     (int)(*(top_ptr_blue+1));

			if ( difference > C_MAX_PIXEL_DIFF || 
			     difference < -C_MAX_PIXEL_DIFF)
			{
			  change_bottom = TRUE;

			  if ( *(top_ptr_changed+1) == FALSE )
			  {
				*(top_ptr_changed+1) = TRUE;

				total_color_red   = *(top_ptr_red+1);
				total_color_green = *(top_ptr_green+1);
				total_color_blue  = *(top_ptr_blue+1);

				for (sample_loop = 0; 
				     sample_loop < num_samples; 
				     sample_loop++ )
				{
				  ss_ray_origin.x = ray_origin.x +
					view->c_sys.x_axis.x * x_spacing +
					view->c_sys.y_axis.x * y_spacing +
					sample_directions[sample_loop].x;
				  ss_ray_origin.y = ray_origin.y +
					view->c_sys.x_axis.y * x_spacing +
					view->c_sys.y_axis.y * y_spacing +
					sample_directions[sample_loop].y;
				  ss_ray_origin.z = ray_origin.z +
					view->c_sys.x_axis.z * x_spacing +
					view->c_sys.y_axis.z * y_spacing +
					sample_directions[sample_loop].z;

				  if ( view->fov != C_PARALLEL_FOV )
				  {
				    ss_ray_direction.x = ss_ray_origin.x -
						         eye_point.x ;
				    ss_ray_direction.y = ss_ray_origin.y -
						         eye_point.y;
				    ss_ray_direction.z = ss_ray_origin.z -
							 eye_point.z;

				    C_Normalize( ss_ray_direction.x, 
					         ss_ray_direction.y,
				     	         ss_ray_direction.z, 
					         temp_float );
				  }

				  switch ( view->projection_type )
				  {
			  	    case C_RAY_TRACE:
					  rt_info.ray_origin.x = 
							ss_ray_origin.x;
					  rt_info.ray_origin.y = 
							ss_ray_origin.y;
					  rt_info.ray_origin.z = 
							ss_ray_origin.z;
					  rt_info.ray_direction.x = 
							ss_ray_direction.x;
					  rt_info.ray_direction.y = 
							ss_ray_direction.y;
					  rt_info.ray_direction.z = 
							ss_ray_direction.z;
					  C_trace_ray( &rt_info, 
						       world, view, 
						       &(intersect_info) );
					  color.red   = 
					   intersect_info.intersect_color.red;
					  color.green = 
					   intersect_info.intersect_color.green;
					  color.blue  = 
					   intersect_info.intersect_color.blue;
					  break;
			  	    case C_PARC:
					  C_parc_cast_ray(image_x, image_y,
							ss_ray_origin, 
						        ss_ray_direction, 
					       		world, &color);
					  break;
			  	    default:
					  fprintf(stderr,
						"Unknow projection type!\n");
					  break;
				  }

				  total_color_red   += color.red;
				  total_color_green += color.green;
				  total_color_blue  += color.blue;
				}

				*(image->red + image_x + 1 + 
					(image->height - image_y - 2) *
					image->width) = total_color_red /
						        (num_samples + 1);
				*(image->green + image_x + 1 + 
					(image->height - image_y - 2) *
					image->width) = total_color_green /
						        (num_samples + 1);
				*(image->blue + image_x + 1 + 
					(image->height - image_y - 2) *
					image->width) = total_color_blue /
						        (num_samples + 1);
			  }

			}

			if (change_bottom)
			{
			  if ( *bottom_ptr_changed == FALSE )
			  {
				*bottom_ptr_changed = TRUE;

				total_color_red   = *(bottom_ptr_red);
				total_color_green = *(bottom_ptr_green);
				total_color_blue  = *(bottom_ptr_blue);

				for (sample_loop = 0; 
				     sample_loop < num_samples; 
				     sample_loop++ )
				{
				  ss_ray_origin.x = ray_origin.x +
					sample_directions[sample_loop].x;
				  ss_ray_origin.y = ray_origin.y +
					sample_directions[sample_loop].y;
				  ss_ray_origin.z = ray_origin.z +
					sample_directions[sample_loop].z;
				  if ( view->fov != C_PARALLEL_FOV )
				  {
				    ss_ray_direction.x = ss_ray_origin.x -
						         eye_point.x ;
				    ss_ray_direction.y = ss_ray_origin.y -
						         eye_point.y;
				    ss_ray_direction.z = ss_ray_origin.z -
							 eye_point.z;

				    C_Normalize( ss_ray_direction.x, 
					         ss_ray_direction.y,
				     	         ss_ray_direction.z, 
					         temp_float );
				  }

				  switch ( view->projection_type )
				  {
			  	    case C_RAY_TRACE:
					  rt_info.ray_origin.x = 
							ss_ray_origin.x;
					  rt_info.ray_origin.y = 
							ss_ray_origin.y;
					  rt_info.ray_origin.z = 
							ss_ray_origin.z;
					  rt_info.ray_direction.x = 
							ss_ray_direction.x;
					  rt_info.ray_direction.y = 
							ss_ray_direction.y;
					  rt_info.ray_direction.z = 
							ss_ray_direction.z;
					  C_trace_ray( &rt_info, 
						       world, view, 
						       &(intersect_info) );
					  color.red   = 
					   intersect_info.intersect_color.red;
					  color.green = 
					   intersect_info.intersect_color.green;
					  color.blue  = 
					   intersect_info.intersect_color.blue;
					  break;
			  	    case C_PARC:
					  C_parc_cast_ray(image_x, image_y,
							ss_ray_origin, 
						        ss_ray_direction, 
					       		world, &color);
					  break;
			  	    default:
					  fprintf(stderr,
						"Unknow projection type!\n");
					  break;
				  }

				  total_color_red   += color.red;
				  total_color_green += color.green;
				  total_color_blue  += color.blue;
				}

				*(image->red + image_x + 
					(image->height - image_y - 1) *
					image->width) = total_color_red /
						        (num_samples + 1);
				*(image->green + image_x + 
					(image->height - image_y - 1) *
					image->width) = total_color_green /
						        (num_samples + 1);
				*(image->blue + image_x + 
					(image->height - image_y - 1) *
					image->width) = total_color_blue /
						        (num_samples + 1);
			  }
			}

		        top_ptr_red++;
		        top_ptr_green++;
		        top_ptr_blue++;
		        bottom_ptr_red++;
		        bottom_ptr_green++;
		        bottom_ptr_blue++;
		        top_ptr_changed++;
		        bottom_ptr_changed++;
			
		        ray_origin.x += view->c_sys.x_axis.x * x_spacing;
		        ray_origin.y += view->c_sys.x_axis.y * x_spacing;
		        ray_origin.z += view->c_sys.x_axis.z * x_spacing;
		    }


	   	    temp_ptr       = top_red;
	    	    top_red        = bottom_red;
	   	    bottom_red     = temp_ptr;

	    	    temp_ptr       = top_green;
	    	    top_green      = bottom_green;
	    	    bottom_green   = temp_ptr;
    
	    	    temp_ptr       = top_blue;
	    	    top_blue       = bottom_blue;
	    	    bottom_blue    = temp_ptr;
    
	    	    temp_ptr       = top_changed;
	    	    top_changed    = bottom_changed;
	    	    bottom_changed = temp_ptr;
    
	    	    top_ptr_red        = top_red;
	    	    top_ptr_green      = top_green; 
	    	    top_ptr_blue       = top_blue;
        
	    	    bottom_ptr_red     = bottom_red;
	    	    bottom_ptr_green   = bottom_green; 
	    	    bottom_ptr_blue    = bottom_blue;
    
	    	    top_ptr_changed    = top_changed;
	    	    bottom_ptr_changed = bottom_changed;

	    	    for (image_x=0; image_x < image->width; image_x++)
	    	    {
			*(bottom_ptr_red++)   = *(rptr++);	
			*(bottom_ptr_green++) = *(gptr++);	
			*(bottom_ptr_blue++)  = *(bptr++);	
	    	    }

	    	    bottom_ptr_red     = bottom_red;
	    	    bottom_ptr_green   = bottom_green; 
	    	    bottom_ptr_blue    = bottom_blue;

	    	    for (image_x=0; image_x < image->width; image_x++)
	    	    {
			*(bottom_ptr_changed++) = FALSE;
	    	    }

	    	    bottom_ptr_changed = bottom_changed;
		} 

		break;
	}
    }

    /* Set The Status Of The Image Rendered To Normal */
    status[processor_num] = C_NORMAL_STATUS;

    /* Free PARC Memory If Necessary */
    if( parc_flag && processor_num == 0 )
	C_parc_free_memory();


}

