/*
 *   surf - visualizing algebraic curves and algebraic surfaces
 *   Copyright (C) 1996-1997 Friedrich-Alexander-Universitaet
 *                           Erlangen-Nuernberg
 *                 1997-2000 Johannes Gutenberg-Universitaet Mainz
 *   Authors: Stephan Endrass, Hans Huelf, Ruediger Oertel,
 *            Kai Schneider, Ralf Schmitt, Johannes Beigel
 *
 *   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 2 of the License, 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.
 *
 */


#include <math.h>
#include <string.h>

#include "debug.h"

#include "SurfaceCalc.h"
#include "RgbBuffer.h"
#include "degree.h"

#include "color.h"
#include "roots.h"
#include "gui_enum.h"

#include "TSDrawingArea.h"
#include "compfn.h"

#include "gui_config.h"
#include "Thread.h"
#include "float_buffer.h"
#include "antialiasing_factor.h"

// FIXME

extern double Y_AXIS_LR_ROTATE;

#define SETNULL(x) memset(x, 0, sizeof(x))

static inline void set_null_root_n (int roots[MAIN_SURFACE_AMOUNT_NUM])
{
	memset (roots, 0, sizeof(roots));
}

static inline void set_null_root_n_with_curve (int roots[MAIN_SURFACE_AMOUNT_NUM+1])
{
	memset (roots, 0, sizeof(roots));
}

void SurfaceCalc::packLights()
{
        numLights = 0;

	for( int i=0; i<LIGHT_SOURCE_MAX_VALUE; i++ ) {
		if( licht[i].red>0 || licht[i].green>0 || licht[i].blue>0 ) {
			if (numLights != i) {
				LPOS [numLights]=LPOS [i];
				licht[numLights]=licht[i];
			}
			numLights++;
		}
	}
}

void  SurfaceCalc::surface_refine_pixel( int px_refine,int py_refine,
					 colorrgb& pixel_color_refine )
{
	// -------------------
	//  pixel coordinates 
	// -------------------
	double  px;
	double  py;
	double  px_start;
	double  py_start;
	double  p_inc;
	
	int     i,j,k,root_n;
	int     px_count;
	int     py_count;
	
	// ------------------
	//  user coordinates 
	// ------------------
	double  ux;
	double  uy;

	double  zmin;
	double  zmax;
	double  stepwidth;
	
	
	colorrgb pixel_tmp;      				
	pixel_tmp.setBlack();
	
	pixel_color_refine.setBlack();
	

	double  root[MAIN_SURFACE_AMOUNT_NUM][MAX_DEGREE];       	
	double  root_last_pixel[MAIN_SURFACE_AMOUNT_NUM][MAX_DEGREE];
	int     root_last_pixel_n[MAIN_SURFACE_AMOUNT_NUM];	
	
	float refine_radius=display_ref_radius_data;     
	
	// sk3:get refine px_start & py_start & p_inc & stepwidth with jump, refine_radius
	p_inc = ( refine_radius*jump )/display_ref_level_data;  
	px_start = px_refine - ((refine_radius/2.0)*jump) + 0.5*p_inc;  
	py_start = py_refine - ((refine_radius/2.0)*jump) + 0.5*p_inc;  
	stepwidth = fabs( pixel_to_user_x(px_start)-pixel_to_user_x (px_start + p_inc));
	
	// --------------------------------------------------------------------
	//  Cycle through all lines of the pixel 
	// --------------------------------------------------------------------
	for( py=py_start, py_count=0; py_count<display_ref_level_data && !Thread::shouldStop(); py_count++, py+=p_inc ) {

		uy = pixel_to_user_y( py );
		
		if (clipper->clip_user_y(uy)) {
			// -----------------
			//  Prepare formula
			// -----------------
			for(i=0;i<sf_ds.surf_n;i++) {  
				sf_ds.formulas[i].hpxyz->setRow (uy);  
				sf_ds.formulas[i].hpDxyz->setRow (uy);  
				sf_ds.formulas[i].hpxDyz->setRow (uy);  
			} 
			// ----------------------------------------------------------------
			// At first point of line assume that no root has been found before
			// ----------------------------------------------------------------
			set_null_root_n(root_last_pixel_n);
			
			// ----------------------------------------------------------------
			//  Cycle through all points of a line 
			// ----------------------------------------------------------------
			for( px = px_start, px_count = 0;
			     px_count < display_ref_level_data;
			     px_count++, px += p_inc ) {
				// -------------------------
				//  Get screen x-coordinate
				// -------------------------
				ux = pixel_to_user_x( px );
				
				if (clipper->clip_user_xy(ux,uy,zmin,zmax)) {
					// -----------------
					//  Prepare formula
					// -----------------

					for(i=0;i<sf_ds.surf_n;i++) {          
						sf_ds.formulas[i].hpxyz->setColumn (ux);
					}  
					// ----------------------------------------------------
					//  Look for roots on the z-axis in the interval
					//  [zmin,zmax] with estimates 
					//  root_last_pixel[0] 
					//  ...
					//  root_last_pixel[root_last_pixel_n-1]
					// ----------------------------------------------------
					sf_ds.root_n_all=0;				

					for(i=0;i<sf_ds.surf_n;i++) {  
						sf_ds.surfaces[i].root_n = 	
							zero_of_polyx( &sf_ds.formulas[i].hpxyz->pZ,
								       zmax,zmin,root[i],			
								       root_last_pixel[i],root_last_pixel_n[i],
                                                                       clipper );
						sf_ds.root_n_all += sf_ds.surfaces[i].root_n;
						root_last_pixel_n[i] = sf_ds.surfaces[i].root_n;     
					} 
					if (sf_ds.root_n_all) {
						
						// -----------------
						//  Prepare formula
						// -----------------

						for(i=0;i<sf_ds.surf_n;i++) {  
							sf_ds.formulas[i].hpDxyz->setColumn (ux);
							sf_ds.formulas[i].hpxDyz->setColumn (ux);
						} 

						
						illumination_of_surface(ux, uy, root, root_last_pixel,
									stepwidth,
									pixel_tmp );

						pixel_tmp *= antialiasing_factor(px_count, py_count, display_ref_level_data);	
						
						pixel_color_refine += pixel_tmp;
						
					} else {
						// ---------------------------------
						//  No surface point is background, 
						// ---------------------------------
						set_null_root_n(root_last_pixel_n);
						
						if( !color_do_background_data ) {
							pixel_tmp=backgr;
							pixel_tmp *= antialiasing_factor(px_count, py_count, display_ref_level_data);
							pixel_color_refine += pixel_tmp;
						}
					}
				} else {
					// --------------------------
					//  Invisible point is background, 
					// --------------------------
					set_null_root_n(root_last_pixel_n);
					
					if ( !color_do_background_data ) {
						pixel_tmp=backgr;
						pixel_tmp *= antialiasing_factor(px_count, py_count, display_ref_level_data);
						pixel_color_refine+=pixel_tmp;
					}
					
				}
				
			} // end , foreach Pixel
			
		} else {
			// -------------------------------------
			//  Invisible line is background, don't 
			// -------------------------------------
			if( !color_do_background_data ) {
				for( px=px_start, px_count=0;
				     px_count<display_ref_level_data;
				     px_count++, px += p_inc ) {
					pixel_tmp=backgr;
					pixel_tmp *= antialiasing_factor(px_count, py_count, display_ref_level_data);	  
					pixel_color_refine += pixel_tmp;
				}
			}
		}		
	} 
}


SurfaceCalc::SurfaceCalc()
{
	// memset (&sf_ds, 0, sizeof(sf_ds));
	vdisplay=0;

	main_factor_set();
 	cb = -10.0;
 	cf = 10.0;
	
	// clipper = new Clipper();
	clipper = NewClip::createSimpleClip (position_perspective_data, clip_data);
	clipper->init();

	sf_ds.init();
}

SurfaceCalc::~SurfaceCalc()
{
}

inline int SurfaceCalc::needsRefining (const colorrgb &i1, const colorrgb &i2, const colorrgb &i3,
				       const colorrgb &i4, const colorrgb &i5, const colorrgb &i6,
				       const colorrgb &i7, const colorrgb &i8, const colorrgb &i9)
{
	
	return     fabs( i5.red  - i1.red  ) > display_ref_treshold_data 
		|| fabs( i5.green- i1.green) > display_ref_treshold_data 
		|| fabs( i5.blue - i1.blue ) > display_ref_treshold_data 
		|| fabs( i5.red  - i2.red  ) > display_ref_treshold_data 
		|| fabs( i5.green- i2.green) > display_ref_treshold_data 
		|| fabs( i5.blue - i2.blue ) > display_ref_treshold_data 
		|| fabs( i5.red  - i3.red  ) > display_ref_treshold_data 
		|| fabs( i5.green- i3.green) > display_ref_treshold_data 
		|| fabs( i5.blue - i3.blue ) > display_ref_treshold_data 
		|| fabs( i5.red  - i4.red  ) > display_ref_treshold_data 
		|| fabs( i5.green- i4.green) > display_ref_treshold_data 
		|| fabs( i5.blue - i4.blue ) > display_ref_treshold_data 
		|| fabs( i5.red  - i6.red  ) > display_ref_treshold_data 
		|| fabs( i5.green- i6.green) > display_ref_treshold_data 
		|| fabs( i5.blue - i6.blue ) > display_ref_treshold_data 
		|| fabs( i5.red  - i7.red  ) > display_ref_treshold_data 
		|| fabs( i5.green- i7.green) > display_ref_treshold_data 
		|| fabs( i5.blue - i7.blue ) > display_ref_treshold_data 
		|| fabs( i5.red  - i8.red  ) > display_ref_treshold_data 
		|| fabs( i5.green- i8.green) > display_ref_treshold_data 
		|| fabs( i5.blue - i8.blue ) > display_ref_treshold_data 
		|| fabs( i5.red  - i9.red  ) > display_ref_treshold_data 
		|| fabs( i5.green- i9.green) > display_ref_treshold_data 
		|| fabs( i5.blue - i9.blue ) > display_ref_treshold_data;
}


void    SurfaceCalc::surface_calculate( int xmin, int ymin, int xmax, int ymax,
					RgbBuffer 	 &intensity )
{
	BEGIN("SurfaceCalc::surface_calculate");
	Thread::setDoing ("drawing surface...");

	int     i,j,k,root_n;
    
	// ------------------------
	//  for  calc_this  matrix                         
	// ------------------------

	int     jump_row_index = 0, jump_col_index = 0;
	bool    calc_this[3][3]={ {true,true,true},
				  {true,true,true},
				  {true,true,true}};
	// -------------------
	//  pixel coordinates
	// -------------------
	int     px;
	int     py;
	
	// ------------------
	//  user coordinates
	// ------------------
	double  ux;
	double  uy;

	// -------------------
	//  min/max on z-axis    
	// -------------------
	double  zmin;
	double  zmax;
	double  stepwidth;
	
	// ----------------------
	//  save one Pixel-Color
	// ----------------------
	colorrgb  pixel_color;     					  
	
	pixel_max_i = 0.0;  
	
	// ----------------
	//  root variables 
	// ----------------
	double  root[MAIN_SURFACE_AMOUNT_NUM][MAX_DEGREE];       	  
	double  root_last_pixel[MAIN_SURFACE_AMOUNT_NUM][MAX_DEGREE]; 
	int     root_last_pixel_n[MAIN_SURFACE_AMOUNT_NUM];		  
	
	initVars();
	
	stepwidth = pixel_to_user_x( 1 ) - pixel_to_user_x( 2 );
	
	
	// -------------------------
	//  render big blocks first		  	
	// -------------------------
	int ju = MAIN_MEM_LAST_MOSAIC_INDEX - 1;
	int startjump = 27;	
	for( jump=startjump; jump>0; jump=jump/3,ju-- ) {
		if (main_mosaic_choice_data[ju]) {
			jump_memory = jump;
			halfjump = ( jump - 1 )/2;

			// ----------------------------------------
			//  cycle through all lines of the picture
			// ----------------------------------------
			
			for( py = ymin + halfjump; py < ymax && !Thread::shouldStop() ; py += jump ) {
				// ------------
				//  clear line
				// ------------
#if 0
				XSetForeground( sci->display,sci->gc,sci->bg );
				XFillRectangle( sci->display,pixmap,sci->gc,xmin,
						py - halfjump,xmax - xmin,jump );
#endif
				// ---------------------------------------------
				//  calculate row position in matrix  calc_this
				// ---------------------------------------------
				jump_row_index = ((py-halfjump)/jump)%3;
				
				uy = pixel_to_user_y( py );
				
				if (clipper->clip_user_y(uy)) {
					// ------------------
					//  prepare surfaces
					// ------------------
					for( i = 0; i < sf_ds.surf_n; i++ ) { 
						sf_ds.formulas[i].hpxyz->setRow (uy);
						sf_ds.formulas[i].hpDxyz->setRow (uy);
						sf_ds.formulas[i].hpxDyz->setRow (uy);
					}

					// --------------------------------------------
					//  At first point of line assume that no root
					//  has been found before
					// --------------------------------------------
					set_null_root_n(root_last_pixel_n);
					
					// ------------------------------------
					//  cycle through all points of a line
					// ------------------------------------
					for( px = xmin + halfjump; px < xmax; px += jump ) {
						// -----------------------------------------------
						//  calculate column position in matrix calc_this
						// -----------------------------------------------
						jump_col_index=((px-halfjump)/jump)%3;         
						
						ux = pixel_to_user_x( px );
		    
						if (clipper->clip_user_xy(ux,uy,zmin,zmax)) {
							if( calc_this[jump_row_index][jump_col_index] ) {
								// -----------------
								//  prepare formula
								// -----------------
								for( i = 0; i < sf_ds.surf_n; i++ ) {
									sf_ds.formulas[i].hpxyz->setColumn( ux ); 
								}
								// ----------------------------------------------
								//  look for roots on the z-axis in the interval
								//  [zmin,zmax] with estimates 
								//  root_last_pixel[0] 
								//  ...
								//  root_last_pixel[root_last_pixel[n-1]
								// -----------------------------------------------
								sf_ds.root_n_all=0;			
								for( i=0; i < sf_ds.surf_n; i++ ) {  
									sf_ds.surfaces[i].root_n = zero_of_polyx(
										&sf_ds.formulas[i].hpxyz->pZ,
										zmax,zmin,root[i],
										root_last_pixel[i],
										root_last_pixel_n[i],clipper );
								    sf_ds.root_n_all+=sf_ds.surfaces[i].root_n;
								    root_last_pixel_n[i] = sf_ds.surfaces[i].root_n;
								} 
								if( sf_ds.root_n_all ) {
									// -----------------
									//  prepare formula
									// -----------------
									for( i = 0;i < sf_ds.surf_n; i++ ) {
										sf_ds.formulas[i].hpDxyz->setColumn (ux);
										sf_ds.formulas[i].hpxDyz->setColumn (ux);
									}

									illumination_of_surface(ux,uy,root,
												root_last_pixel,stepwidth, // sf_d,
												pixel_color );

									surface_set_pixel (px, py, pixel_color);

									intensity.SetPixelColor (px, py, pixel_color);
									intensity.SetTag( px,py,SURFBIT | DATABIT);
								} else {
									// -----------------------------------------
									//  no surface point is background, 
									// -----------------------------------------
									set_null_root_n(root_last_pixel_n);
									
									if( !color_do_background_data ) {
										pixel_color=backgr;
										
										surface_set_pixel( px, py, pixel_color);
										intensity.SetPixelColor(px,py, pixel_color);
									}
								}
							} else { 
								// ------------------------------------------
								//  color calculated before: Get from memory
								// ------------------------------------------ 
								intensity.GetPixelColor (px, py, pixel_color);
								surface_set_pixel( px, py, pixel_color);
								
							}
						} else {
							// --------------------------
							//  Invisible point is background, 
							// --------------------------
							set_null_root_n(root_last_pixel_n);
							
							if( !color_do_background_data ) {
								pixel_color=backgr;
								
								surface_set_pixel( px, py, pixel_color );

								intensity.Set( px, py,
									       (int)(rint(pixel_color.red  *255.0)),
									       (int)(rint(pixel_color.green*255.0)),
									       (int)(rint(pixel_color.blue *255.0)) );
							}
						}
					}
				} else {
					// -----------------------------------------
					//  invisible line is background, 
					// -----------------------------------------
					
					if ( !color_do_background_data )  {
						for( px = xmin+halfjump; px<xmax; px=px+jump ) {
							pixel_color = backgr;
							
							surface_set_pixel( px, py, pixel_color );
							
							intensity.Set( px,py,
								       (int)(rint(pixel_color.red  *255.0)),
								       (int)(rint(pixel_color.green*255.0)),
								       (int)(rint(pixel_color.blue *255.0)) );
						}
					}
				}
				
				// ----------------------
				//  map line onto screen
				// ----------------------
				if (vdisplay) {
					vdisplay->displayRectangle(xmin,py-halfjump,xmax-xmin,jump);
				}
			}
			
			// -----------------------------------------------
			//  delete central entry of calc_this for gext go
			// -----------------------------------------------
			calc_this[1][1]=false;
		}
	}
	
	if( display_normalize_data == 1 ) {
		// ---------------------------
		//  suggest a normalize value 
		// ---------------------------
		//         display_normalize_set_value( 1.0/pixel_max_i );
	}
    
	jump = jump_memory;
	halfjump=(jump-1)/2;    
	
	// ----------------
	//  refine picture
	// ----------------
	int   refine_count = 0;
	

	if( display_ref_level_data>1 ) {
                int parity;
		colorrgb  i1,i2,i3,i4,i5,i6,i7,i8,i9;  		
		Thread::setDoing ("antialiasing...");
		for( py = ymin + halfjump; py < ymax  && !Thread::shouldStop() ; py += jump ) {
			// --------------------------------------
			//  read intensities of neighbour pixels
			// --------------------------------------
			
			intensity.GetPixelColor(-jump,    py-jump, i1);
			intensity.GetPixelColor(halfjump, py-jump, i2);
			intensity.GetPixelColor( jump,    py-jump, i3);

			intensity.GetPixelColor(-jump,    py,      i4);
			intensity.GetPixelColor(halfjump, py,      i5);
			intensity.GetPixelColor(jump,     py,      i6);

			intensity.GetPixelColor(-jump,    py+jump, i7);
			intensity.GetPixelColor(halfjump, py+jump, i8);
			intensity.GetPixelColor(jump,     py+jump, i9);
			
			parity = py%2;
			for( px= ( parity ? xmin+halfjump : xmax-halfjump-1 );
                                 ( parity ? px<xmax       : px>xmin-1 );
                             px+=( parity ? jump : -jump ) ) {
				if (needsRefining(i1,i2,i3,i4,i5,i6,i7,i8,i9)) {
					
					surface_refine_pixel   ( px,py,i5 );
					surface_set_pixel      ( px,py,i5 );              
					intensity.SetPixelColor( px,py,i5 );
					
					refine_count++;
				}
                                if( parity ){
				        i1=i2;
				        i2=i3;
				        intensity.GetPixelColor (px+jump, py-jump, i3);
				        i4=i5;
				        i5=i6;
				        intensity.GetPixelColor (px+jump, py,      i6);
				        i7=i8;
				        i8=i9;
				        intensity.GetPixelColor (px+jump, py+jump, i9);
				}
                                else {
				        i3=i2;
				        i2=i1;
				        intensity.GetPixelColor (px-jump, py-jump, i1);
				        i6=i5;
				        i5=i4;
				        intensity.GetPixelColor (px-jump, py,      i4);
				        i9=i8;
				        i8=i7;
				        intensity.GetPixelColor (px-jump, py+jump, i7);
				}
			}

			if (vdisplay)
				vdisplay->displayRectangle(0, py-halfjump, main_width_data, jump);
			Thread::setDone (((double)py)/((double)main_height_data));
		}
	}
}




void  SurfaceCalc::color_of_single_point_old( Vector &P, Vector &N, colorrgb &I, int obj_color )
{

	Vector  V;
	Vector  L;
	Vector  H;

	register  float   fade    = 1.0;
	register  float   fadecol = 1.0, fadecoln = 0.0;
	
	register  int     i;

	float  cm = ( cb + cf )/2.0; 	      // midpoint of clipping area
	float  cd = ( cf - cb );                  // diameter of clipping area

	// -----------------------------------------
	//  color-gradient-fading
	//  choose gradient outside, inside or both
	// -----------------------------------------
	
	if( ( obj_color == color_gradient_data - 1 &&
	      color_gradient_data ) || color_gradient_data==3 ) {
		fadecol = ( P.z - cm )/cd + 0.5;
		fadecol = max( fadecol,0.0 );
		fadecol = min( fadecol,1.0 );
	} else {
		fadecol = 1.0;
	}
	fadecoln = 1.0 - fadecol;

	// ------------------------------
	//  begin with color calculation
	// ------------------------------
    
	if( N.Length() > 1e-8 ) {
		V = POS-P;		// VECTOR_SUBTRACT( V,POS,P );
		V.Unify(); // vector_self_unit( &V );
 
		// -------------------
		//  set ambient light
		// -------------------

		if( flag_ambient ) {
			I.set( ambient[RED]  [obj_color]*fadecol + tocolor[RED]  *fadecoln,
			       ambient[GREEN][obj_color]*fadecol + tocolor[GREEN]*fadecoln,
			       ambient[BLUE] [obj_color]*fadecol + tocolor[BLUE] *fadecoln );
		} else {
			I.setBlack(); // I.Set ( 0.0,0.0,0.0 );
		}

		// ---------------------------
		//  cycle through all sources
		// --------------------------- 

		for( i = 0; i<numLights; i++) {
			
			L = LPOS[i]-P; // VECTOR_SUBTRACT( L, LPOS[i], P );
 
			L.Unify(); // vector_self_unit( &L );
			H = L+V; // VECTOR_ADD( H,L,V );   				
				
			H.Unify(); // vector_self_unit( &H );
			// VECTOR_NEGATE( Hd,H );
			
			double N_dot_L  = DotProduct (N,L); // VECTOR_DOT_PRODUCT( N,L );
			double H_dot_L  = DotProduct (H,N); // VECTOR_DOT_PRODUCT( H,N );  		
			// double Hd_dot_L = VECTOR_DOT_PRODUCT( Hd,N ); 		
 
			double H_dot_L_pow  = pow( H_dot_L,smoothness );
			// double Hd_dot_L_pow = pow( Hd_dot_L,smoothness );
 
			int gets_diffuse     = (   N_dot_L  > 0.0 );
			int gets_reflected   = ( ( H_dot_L  > 0.0 ) && gets_diffuse );
			// int gets_transmitted = ( ( Hd_dot_L > 0.0 ) && gets_diffuse );

			double factor=part_D*fadecoln;
			double my_reflected = flag_reflected && gets_reflected ? reflected*H_dot_L_pow : 0.0;
			if (flag_diffuse && gets_diffuse) {
				I.add( licht[i].red  *(( (diffuse[RED  ][obj_color]*fadecol+tocolor[RED  ]*factor)*N_dot_L) + my_reflected),
				       licht[i].green*(( (diffuse[GREEN][obj_color]*fadecol+tocolor[GREEN]*factor)*N_dot_L) + my_reflected),
				       licht[i].blue *(( (diffuse[BLUE ][obj_color]*fadecol+tocolor[BLUE ]*factor)*N_dot_L) + my_reflected));
			} else {
				I.add( licht[i].red  * my_reflected,
				       licht[i].green  * my_reflected,
				       licht[i].red  * my_reflected);
			}
		}
        
		// --------------
		//  depth cueing
		// --------------

		if( !display_depth_cue_data ) {
			fade = 1.3*( P.z - display_depth_value_data )/cd;
			fade = max( fade,0.0 );
			fade = min( fade,1.0 );
			
			I *= fade;
		}
	} else {
		// ------------------------
		//  Probably a singularity
		// ------------------------
		
		I.setBlack();
     }
}

// ---------------------------------------------
//  new variant of that very function
//  P = point on the surface
//  N = unit normal vector of the surface at P
//  obj_color = one of {SURFS,INSIDE}
//  
//  returns I = color value of the surface at P
// ---------------------------------------------

void  SurfaceCalc::color_of_single_point( Vector &P, Vector &N, colorrgb &I, int obj_color )
{
        // --------------
	//  depth cueing
	// --------------
        float  fade = 1.0;

	if( !display_depth_cue_data ) {
	        fade = 1.3*( P.z - display_depth_value_data )/( cf - cb );

                if( fade <= 0.0 ) {
 			I.setBlack( );
                        return;
		}
                else {
			fade = min( fade,1.0 );
		}
	}


	// ------------------------------
	//  begin with color calculation
	// ------------------------------
    	Vector  V,L,H;
        double N_dot_L, H_dot_N_pow;

        // -------------------
	//  set ambient light
	// -------------------
        if( flag_ambient ) {
		I.set( ambient[RED]  [obj_color],
                       ambient[GREEN][obj_color],
                       ambient[BLUE] [obj_color] );
        } else {
		I.setBlack( );
        }

        // ----------------------------------------------------------
	//  if N.length( )<=1e-18 we have probably hit a singularity
	// ----------------------------------------------------------
	if( N.Length( ) > 1e-18 ) {
		// -----------------------
                //  compute viewer vector
		// -----------------------
		V = POS - P;
		V.Unify( );

		// ---------------------------------
		//  cycle through all light sources
		// --------------------------------- 
		for( int i=0; i<numLights; i++ ) {
		        // ----------------------
                        //  compute light vector
		        // ----------------------
			L = LPOS[i] - P;
			L.Unify( );
 
          		// -------------------
		        //  set diffuse light
		        // -------------------
			N_dot_L = DotProduct( N,L );

			if( flag_diffuse && ( N_dot_L > 0.0 ) ) {
				I.add( licht[i].red  *diffuse[RED  ][obj_color]*N_dot_L,
				       licht[i].green*diffuse[GREEN][obj_color]*N_dot_L,
				       licht[i].blue *diffuse[BLUE ][obj_color]*N_dot_L );
			}

          		// -------------------
		        //  set reflected light
		        // -------------------
			H = L + V;
			H.Unify( );
			H_dot_N_pow = pow( DotProduct( H,N ),smoothness );

			if( flag_reflected && ( N_dot_L > 0.0  ) && ( H_dot_N_pow > 0.0 ) ) {
				I.add( licht[i].red  *reflected*H_dot_N_pow,
                                       licht[i].green*reflected*H_dot_N_pow,
                                       licht[i].blue *reflected*H_dot_N_pow );
			}
		}
	}

        I *= fade;
}
 
// ===========================================================================
// (sk :)Compute the intensity at wx,wy     
//  sk1: neuer struct für surface-datas
//  sk2: unterscheide ob 'N' zum oder weg vom Betrachter ...
//       zeigt, mit Flächenfarbe
//  sk3: multisurfaces     
// =========================================================================== 

void SurfaceCalc::illumination_of_surface( double wx,double wy,           
					   double (*root)[MAX_DEGREE],
					   double (*root_old)[MAX_DEGREE],    
					   double stepwidth,
					   colorrgb& color_intens ) 		
{
	// FIXME
	static    int     inside;         // true if normal points into monitor

	Vector  P;              // our point on the surface
	Vector  N;              // the surface normal in P


    
	register  float   norm_it = display_max_i_data;
	double    transfak,solidfak;

	transmission = 1.0;
    
	color_intens.setBlack();
	tmpcolor.setBlack();
   
	int k;
	int i         = 0;
	int root_count= 0;
	int best_i    =-1;
	int root_level[MAIN_SURFACE_AMOUNT_NUM];
	
	for( i = 0; i <= sf_ds.surf_n; i++ ) {
		root_level[i]=0;
	}

	P.x = wx;
	P.y = wy;

	// -------------------------------------
	//  go through all roots of all surface
	// -------------------------------------
	do {
		i = (best_i+1)%(sf_ds.surf_n);
		sf_ds.prev_root=-10000.0;
        
		for( k = 0; k < sf_ds.surf_n; k++ ) {        
			if ( root_level[i] < sf_ds.surfaces[i].root_n 	
			     && root[i][ root_level[i] ] >= sf_ds.prev_root ) {  
				sf_ds.prev_root=root[i][ root_level[i] ];
				best_i=i;
			}
			i =(i+1)%(sf_ds.surf_n);
		}          

		//  best_i = number of surface
		//  root_level[best_i] = number of root of surface best_i

		// --------------------------
		//  compute point of surface
		// --------------------------
		P.z = root[best_i][root_level[best_i]];

		// ---------------------------
		//  compute normal of surface
		// ---------------------------
		N.x = sf_ds.formulas[best_i].hpDxyz->pZ.horner  ( root[best_i][root_level[best_i]] );
		N.y = sf_ds.formulas[best_i].hpxDyz->pZ.horner  ( root[best_i][root_level[best_i]] );
		N.z = sf_ds.formulas[best_i].hpxyz->pZ.dx_horner( root[best_i][root_level[best_i]] );
		N.Unify( );

		root_old[best_i][root_level[best_i]] =
			root[best_i][root_level[best_i]] - 0.001;
        
		if( N.z != 0.0 ) {
			root_old[best_i][root_level[best_i]] += stepwidth*N.x/N.z;
		}

		root_level[best_i]++;
		root_count++;

		// -------------------------------
		//  make normal vector look to me
		// -------------------------------
		if( N.z < 0.0 ) {
			inside = 1;
			N.Negate( );
		} else {
     		        inside = 0;
		}

 
		// ------------------------------------
		//  set properties for current surface
		// ------------------------------------
                //  surface transparence
                transparence = ( flag_transmitted ? sf_ds.surfaces[best_i].transp : 0.0 );

		if( light_settings[best_i].thickness > 0 ) {
		        if( N.z > 0 ) {
				double tmp = light_settings[best_i].thickness/(30.0*N.z);
			     	transfak = pow( transparence,min( tmp,4.0 ) );
			} else {
				transfak = pow( transparence,4.0 );
			}
	        } else {
			transfak = transparence;
		}

		solidfak = 1.0 - transfak;

                //  reflected, transmitted, smoothness
		reflected    = ( solidfak==0.0 ? 0.0 : sf_ds.surfaces[best_i].reflect );
		transmitted  = ( flag_transmitted ? sf_ds.surfaces[best_i].transmit : 0.0 );
		smoothness   = sf_ds.surfaces[best_i].smooth; 
	        part_D = sf_ds.surfaces[best_i].diffuse;

		// --------------------------------------------
		//  if we are on a curve take curve properties
		// --------------------------------------------
		//  better would be: take the nearest point on the surface ...
		if( sf_ds.curve.root_n && ( fabs( root[sf_ds.surf_n][0] - P.z ) < 0.01 ) ) {
		        //  the curve os not opaque
                        transfak      = 0.0;
                        solidfak      = 1.0;

		        ambient[RED]  [SURFS] = ambient[RED]  [INSIDE] = part_A*sf_ds.curve.red;
		        ambient[GREEN][SURFS] = ambient[GREEN][INSIDE] = part_A*sf_ds.curve.green;
		        ambient[BLUE] [SURFS] = ambient[BLUE] [INSIDE] = part_A*sf_ds.curve.blue;

		        diffuse[RED]  [SURFS] = diffuse[RED]  [INSIDE] = part_D*sf_ds.curve.red;
		        diffuse[GREEN][SURFS] = diffuse[GREEN][INSIDE] = part_D*sf_ds.curve.green;
		        diffuse[BLUE] [SURFS] = diffuse[BLUE] [INSIDE] = part_D*sf_ds.curve.blue;
		} else {
			ambient[RED]  [SURFS]  = sf_ds.surfaces[best_i].red      *part_A*solidfak;
			ambient[GREEN][SURFS]  = sf_ds.surfaces[best_i].green    *part_A*solidfak;
			ambient[BLUE] [SURFS]  = sf_ds.surfaces[best_i].blue     *part_A*solidfak;
			ambient[RED]  [INSIDE] = sf_ds.surfaces[best_i].ins_red  *part_A*solidfak;
			ambient[GREEN][INSIDE] = sf_ds.surfaces[best_i].ins_green*part_A*solidfak;
			ambient[BLUE] [INSIDE] = sf_ds.surfaces[best_i].ins_blue *part_A*solidfak;
			
			diffuse[RED]  [SURFS]  = sf_ds.surfaces[best_i].red      *part_D*solidfak;
			diffuse[GREEN][SURFS]  = sf_ds.surfaces[best_i].green    *part_D*solidfak;
			diffuse[BLUE] [SURFS]  = sf_ds.surfaces[best_i].blue     *part_D*solidfak;
			diffuse[RED]  [INSIDE] = sf_ds.surfaces[best_i].ins_red  *part_D*solidfak;
			diffuse[GREEN][INSIDE] = sf_ds.surfaces[best_i].ins_green*part_D*solidfak;
			diffuse[BLUE] [INSIDE] = sf_ds.surfaces[best_i].ins_blue *part_D*solidfak;
		}
         
		// ---------------
		//  Get intensity
		// ---------------
		color_of_single_point( P,N,tmpcolor,SURFS+inside ); 
        
		tmpcolor     *= transmission;
		color_intens += tmpcolor;
		transmission *= transfak;

	} while( root_count < sf_ds.root_n_all && flag_transmitted && transmission>0.0 );

	if( flag_transmitted && !color_do_background_data && transmission>0.0 ) {
		tmpcolor     =  backgr;
		tmpcolor     *= transmission;
		color_intens += tmpcolor;
	}

	// ---------------------
	//  normalize intensity
	// ---------------------
	if( display_normalize_data == 0 ) { 
		color_intens *= norm_it;
	}

	// --------------------------------    
	//  get max of all calculated pixels,
	//  to set it after all in the display-menu      
	// --------------------------------
	if( color_intens.red   > pixel_max_i ) 
		pixel_max_i = color_intens.red;
	if( color_intens.green > pixel_max_i ) 
		pixel_max_i = color_intens.green;
	if( color_intens.blue  > pixel_max_i ) 
		pixel_max_i = color_intens.blue;

	// ----------------------------
	//  cut all intensities at 1.0  
	// ----------------------------
	color_intens.cutAtMax( );
}

// ----------------------------------------------------------------------------
// sk : CalculateCurveOnSurface, ...
//      berechnet die Fläche an den Stellen, die im layer2 != 0 sind , ...
//      also dort wo die Kurve auf der Fläche liegt . 
// ----------------------------------------------------------------------------

void SurfaceCalc::CalculateCurveOnSurface( int xmin, int ymin, int xmax, int ymax,
					   RgbBuffer 	        &intensity,
					   float_buffer         &z_buffer)
{
	Thread::setDoing("drawing curve on surface ...");
	sf_ds.initCurveProperty();

	jump=1;
	int NULL_ROOT_N_WITH_CURVE[MAIN_SURFACE_AMOUNT_NUM+1]={0,0,0,0,0,0,0,0,0,0};	/* sk69 neu*/

	int     i,j,k,root_n;
    
	// -------------------
	//  Pixel coordinates
	int     px;
	int     py;
	// ------------------
	//  User coordinates
	double  ux;
	double  uy;
	// ------------------
	//  Min / Max Z-Axis    
	double  zmin;
	double  zmax;
	double  stepwidth;
	
	// -----------------------
	//  Save one Pixel-Color  
	colorrgb  pixel_color, pixel_old;     					
	
	pixel_max_i = 0.0;  
	
	// --------------------------------------------------------------
	// Root - Variablen 
	double  root[MAIN_SURFACE_AMOUNT_NUM+1][MAX_DEGREE];       	   
	double  root_last_pixel[MAIN_SURFACE_AMOUNT_NUM+1][MAX_DEGREE];
	int     root_last_pixel_n[MAIN_SURFACE_AMOUNT_NUM+1];

        double  tmp;
        float   distf = display_numeric.stereo_z*display_numeric.stereo_eye/
			position_numeric.spectator_z;
	int     dist  = (int)(distf*((float)(min(main_width_data,main_height_data)))/20.0);
        int     pxdist;

	initVars();
	
// 	XFlush( sci->display );

	i = MAIN_SURFACE_AMOUNT_NUM;
	// Does this make sense ??? FIXME
	// sk: get thickness of all surfaces
	thickness    =          light_settings[i].thickness;  
	thickness    = (thickness) ?  LIGHT_SETTINGS_SECOND_MAX_VALUE[LIGHT_THICKNESS]+1 - thickness  : 0; 
      
	//intensity.DelTag(0,0,ALREADYDITHERED);

	stepwidth = pixel_to_user_x( 1 ) - pixel_to_user_x( 2 );

	// ----------------------------------------
	//  Cycle through all lines of the picture
	// ----------------------------------------
	for( py = ymin; py < ymax && !Thread::shouldStop(); py=py+1 ) {

		// -------------------------
		//  Get screen y-coordinate
		// -------------------------
		uy = pixel_to_user_y( py );

		// ----------------------------------------------
		//  Visibility test if row contains curve points
		// ----------------------------------------------

		if (clipper->clip_user_y(uy) && intensity.GetTag(0,py,CURVELINEBIT)) {
			// -----------------
			//  Prepare formula
			// -----------------
			for( i=0; i<sf_ds.surf_n; i++ ) {  
				sf_ds.formulas[i].hpxyz->setRow (uy);
				sf_ds.formulas[i].hpDxyz->setRow (uy);
				sf_ds.formulas[i].hpxDyz->setRow (uy);
			} 
			// ------------------------------------
			//  At first point of line assume that
			//  no root has been found before
			// ------------------------------------
			set_null_root_n_with_curve(root_last_pixel_n);
			

			// ------------------------------------
			//  Cycle through all points of a line
			// ------------------------------------

			for( px = xmin; px < xmax; px = px + 1 ) { 
				// -------------------------
				//  Get screen x-coordinate
				// -------------------------
				ux = pixel_to_user_x( px );

				// --------------------------------------
				//  Visibility test if its a curve point
				// --------------------------------------

				if (clipper->clip_user_xy(ux,uy, zmin, zmax) 
				    && intensity.GetTag( px,py,CURVEBIT )) {
					// ------------------
					//  Prepare formulas
					// ------------------
					
					for( i = 0; i < sf_ds.surf_n; i++ ) {  
						sf_ds.formulas[i].hpxyz->setColumn (ux); 
					} 

					// --------------------------------------------------
					//  Look for roots on the z-axis in the interval
					//  [zmin,zmax] with estimates 
					//  root_last_pixel[0] 
					//  ...
					//  root_last_pixel[root_last_pixel_n-1]
					// --------------------------------------------------
					sf_ds.root_n_all=0;
					
					for( i = 0; i < sf_ds.surf_n; i++ ) {  
						sf_ds.surfaces[i].root_n = zero_of_polyx(
							&sf_ds.formulas[i].hpxyz->pZ,	
							zmax,zmin,root[i],			
							root_last_pixel[i],
							root_last_pixel_n[i],clipper );
						sf_ds.root_n_all += sf_ds.surfaces[i].root_n; 	
						root_last_pixel_n[i] = sf_ds.surfaces[i].root_n;
					}

					root_last_pixel_n[sf_ds.surf_n]=sf_ds.curve.root_n=1;
					root[sf_ds.surf_n][0]=z_buffer.Get(px,py);
					
					if( sf_ds.root_n_all) {
						// -----------------
						//  Prepare formula
						// -----------------
						for( i=0; i<sf_ds.surf_n; i++ ) {  
							sf_ds.formulas[i].hpDxyz->setColumn (ux);
							sf_ds.formulas[i].hpxDyz->setColumn (ux);
						}

					        // ---------------------------------
						//  Mix surface & curve intensities
						// ---------------------------------
						illumination_of_surface( ux,uy,root,root_last_pixel,stepwidth,
                                                                         pixel_color );
                        
                                                double lambda = (double)intensity.GetLayerTwo(px,py)/255.0;

                                                if( !display_numeric.stereo_eye )
						{
                                                        pixel_color *= lambda;
                                                        intensity.GetPixelColor( px,py,pixel_old );
                                                        pixel_old   *= 1.0 - (double)intensity.GetLayerTwo(px,py)/255.0;
                                                        pixel_color += pixel_old;

						        // ------------------
						        //  Draw onto screen
						        // ------------------
						        surface_set_pixel (px, py, pixel_color);

						        // ------------------
						        //  Draw into memory set pixel to memory 
						        // ------------------
						        intensity.SetPixelColor (px, py, pixel_color);
							intensity.SetTag        (px, py, DATABIT | SURFBIT );
						}
                                                else if( Y_AXIS_LR_ROTATE != 0.0 )
						{
                                                        pxdist = px+dist;

						        if( pxdist >= 0 && pxdist < xmax )
						        {
                                                                intensity.GetPixelColor( pxdist,py,pixel_old );

                                                                tmp               = pixel_color.getBW( )*lambda;
                                                                pixel_color.red   = intensity.GetPixelColorR( pxdist,py );
                                                                pixel_color.green = pixel_old.green*( 1.0 -lambda ) +
                                                                                    tmp*display_numeric.stereo_green;
                                                                pixel_color.blue  = pixel_old.blue*( 1.0 -lambda ) +
                                                                                    tmp*display_numeric.stereo_blue;

                                                                surface_set_pixel      (pxdist, py, pixel_color);
							        intensity.SetPixelColor(pxdist, py, pixel_color);
							        intensity.SetTag       (pxdist, py, DATABIT3D | SURFBIT3D );
						        }
						}
                                                else
						{
                                                        pxdist = px-dist;

						        if( pxdist >= 0 && pxdist < xmax )
						        {
                                                                intensity.GetPixelColor( pxdist,py,pixel_old );

                                                                pixel_color.red   = pixel_color.getBW( )*lambda
                                                                                    *display_numeric.stereo_red +
                                                                                    pixel_old.red*(1.0 -lambda );
                                                                pixel_color.green = intensity.GetPixelColorG( pxdist,py );
                                                                pixel_color.blue  = intensity.GetPixelColorB( pxdist,py );

                                                                surface_set_pixel      (pxdist, py, pixel_color);
							        intensity.SetPixelColor(pxdist, py, pixel_color);
							        intensity.SetTag       (pxdist, py, DATABIT | SURFBIT );
						        }
						}
						intensity.DelTag( 0,py,CURVELINEBIT); 
						intensity.DelTag(px,py,CURVEBIT    );

					} else { // end of 'Find root'

						// ---------------------------
						//  No surface point is black   OR ... 
						// ---------------------------
						
						set_null_root_n_with_curve(root_last_pixel_n);
					}
				} else { //end of 'Inside xy-Clip'
					// --------------------------
					//  Invisible point is black   OR  ...
					// --------------------------
					set_null_root_n_with_curve(root_last_pixel_n);
				}

			}// end of 'For each point in line'
			
		}// end of 'Inside y-clip'

		// ----------------------
		//  Map line onto screen
		// ----------------------
		if (vdisplay) 
			vdisplay->displayRectangle (xmin, py, xmax-xmin,1);
		//XCopyArea( sci->display,pixmap,win,sci->gc,
		//           xmin,py,           
		//           xmax - xmin,1,           
		//           xmin,py);           
		
        
	}// end of 'Foreach line' 

	if (vdisplay)
		vdisplay->displayRectangle (xmin, ymin, xmax-xmin, ymax-ymin);
// 	else
// 		cout << "no vdisplay..." << endl;
#if 0
	XCopyArea( sci->display,pixmap,win,sci->gc,
		   xmin,ymin,           
		   xmax - xmin,ymax - ymin,           
		   xmin,ymin );           
#endif

	// ----------------
	//  Small notifier
	// ----------------
	//XFlush( sci->display );
#if 0
	if ( display_normalize_data==1) { 
		main_frame_set_left_footer( "normalizing ..." );
		display_normalize_set_value( 1.0/pixel_max_i );
	}
#endif
    
}

void SurfaceCalc::initVars( )
{
	// --------------------
	//  spectator position
	// --------------------
	POS.x = 0.0;
	POS.y = 0.0;
	POS.z = position_numeric.spectator_z;
	
	// ---------------
	//  light sources
	// ---------------
	int i;

	for( i=0; i<LIGHT_SOURCE_MAX_VALUE; i++ ) {
		//  position
		LPOS[i].x = light_data[i].position[LIGHT_POSITION_X];
		LPOS[i].y = light_data[i].position[LIGHT_POSITION_Y];
		LPOS[i].z = light_data[i].position[LIGHT_POSITION_Z];
		
		
		if( display_numeric.stereo_eye ) {
			//  change position for 3D image
			LPOS[i].x = cos(Y_AXIS_LR_ROTATE)*LPOS[i].x - sin(Y_AXIS_LR_ROTATE)*LPOS[i].z;
			LPOS[i].z = sin(Y_AXIS_LR_ROTATE)*LPOS[i].x + cos(Y_AXIS_LR_ROTATE)*LPOS[i].z;
		}

 		//  color
 		licht[i].red   = light_data[i].getColorValue( 0 );
 		licht[i].green = light_data[i].getColorValue( 1 );
 		licht[i].blue  = light_data[i].getColorValue( 2 );
	}
	
	packLights( ); 
	
	// ------------------------
	//  illumiation components
	// ------------------------
	flag_ambient      = light_illumination_data & light_illumination_ambient_data;
	flag_diffuse      = light_illumination_data & light_illumination_diffuse_data;
	flag_reflected    = light_illumination_data & light_illumination_reflected_data;
	flag_transmitted  = light_illumination_data & light_illumination_transmitted_data;
	
	numeric_multi_root = flag_transmitted;
	
	part_A=((float)light_settings[0].ambient)/100.0;
		
	// fade color
	tocolor[RED]   =(((double)color_gradient_end_data[RED]  )/255.0)*part_A;
	tocolor[GREEN] =(((double)color_gradient_end_data[GREEN])/255.0)*part_A;
	tocolor[BLUE]  =(((double)color_gradient_end_data[BLUE] )/255.0)*part_A;
	
	//  set background color     
	backgr.set( color_background_data[RED]  /255.0,
		    color_background_data[GREEN]/255.0,
		    color_background_data[BLUE] /255.0 );
}

