/*****************************************************/
/* Doughnut 3D                                       */
/* - Double buffering                                */
/* - Lambert flat shading                            */
/* - Z-Sorting                                       */
/*                                                   */
/* I have learned most of the 3d-algorithms from     */
/* 3DICA-document                                    */
/*                                                   */
/* Copyright 2001-2004 Tommi Laukkanen               */
/*****************************************************/

import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;

public class doughnut extends Applet implements Runnable
{
    private MemoryImageSource src;
    private ColorModel src_cm=ColorModel.getRGBdefault();
    private Image trg_img;
    
    private boolean initialized=false;
    
    private Graphics g2;
    public  Thread lanka;
    private int dy=0;
    private int pix[], tpix[], kpix[];
  
	//Runtime structures
	
    final static int cc=0xff000000;
    final static int RED=0xff0000;
    final static int GREEN=0xff00;
    final static int BLUE=0xff;
    final static int MASK1=0xfefeff;

    int kerta=0;
    int a,b;
    int w=256;
    int h=256;
    float a_x=0,a_y=0,a_z=0,moro;
    int x[][]= new int[400][3];
    int y[][]= new int[400][3];
    float z[][]= new float[400][4];
    float dotx[] = new float[400];
    float doty[] = new float[400];
    float dotz[] = new float[400];
    int poly_color[]= new int[400];
    //3D objektin arvot
    double vertex[][]= new double[][]{{3.0294,20.0119,-1.5163},{-7.485,18.1851,2.5833},{-4.1873,8.5368,2.1058},{-0.5215,9.4467,-1.48},{2.3378,14.8296,-7.2572},{20.813,3.8461,-3.2375},{18.6823,9.8558,-3.6606},{14.3994,4.9655,5.7593},{16.9808,5.1518,5.5758},{10.2895,1.7429,-1.98},{10.2691,2.3797,1.2916},{14.3801,1.6772,-7.4116},{14.369,-14.558,2.3539},{2.5211,-15.1689,7.0444},{5.1991,-8.3465,-0.9058},{-0.2814,-10.6949,0.602},{10.8105,-12.1023,-4.8369},{5.4284,-16.1452,-5.0729},{4.4748,-13.8843,-5.3784},{-18.4035,-17.4579,0.63},{-26.2795,-16.0727,-1.1492},{-19.6195,-12.6194,-5.0904},{-23.5732,-11.996,-3.5742},{-24.7257,-12.7049,-10.2625},{-33.9179,-4.6858,-1.2843},{-33.1837,-2.3309,-1.8928},{-27.8093,-5.7797,3.2741},{-25.7138,-6.0042,-1.459},{-25.4263,-4.4953,-2.7436},{-29.4293,-2.6454,-6.5279},{-27.9272,2.1359,-4.4627},{5.3396,9.6349,2.6374},{8.3997,8.3105,3.0273},{8.9225,10.2847,4.5314},{4.1943,11.4368,4.5298},{7.2769,7.8335,-4.4271},{9.8146,-3.7618,4.0353},{9.9438,-5.6799,6.1365},{9.8085,-5.0604,-2.7342},{10.7734,-3.0268,-2.966},{-10.9728,-15.8263,2.3524},{-10.9898,-13.1808,-5.0613},{-26.5071,-11.1046,-1.7439},{-27.1015,-8.8924,-0.4802},{-28.272,-10.2935,0.2318},{-26.3886,-12.8843,-1.1216},{-27.4772,-7.6167,-5.8679},{-26.4902,-9.7138,-6.9738},{-31.7774,-12.0507,-9.3802},{-33.1141,-5.9918,-7.5623},{-15.8462,11.9166,6.1549},{-17.8312,5.5344,-3.3386},{18.7001,9.0403,1.6548},{14.3054,15.323,-4.3566},{-4.4769,17.002,6.259},{0.5866,18.2503,5.2127},{8.0277,14.7738,5.1359},{12.4138,3.8499,4.6945},{-0.4102,11.6758,5.4356},{6.7907,7.4791,-1.6466},{11.4303,1.6097,-4.5536},{10.9443,10.8393,-9.094},{19.9247,2.8083,-5.4022},{17.7167,5.9723,-7.3072},{7.4153,-17.2782,6.0559},{11.3022,-13.4603,7.7626},{19.2145,-8.5993,1.7215},{13.4703,-4.5321,8.1197},{9.9745,-2.3038,-0.4535},{8.975,-4.0082,1.3789},{2.5511,-11.1073,-3.4356},{5.9501,-9.5914,-3.1337},{14.805,-4.7642,-6.1193},{11.8633,-16.0225,-1.0511},{-27.0388,-18.3998,-4.548},{-10.9705,-22.1852,-2.8036},{-5.3358,-18.0306,4.6771},{-23.2622,-14.807,-1.0677},{-19.7233,-14.5527,-1.0643},{-10.9991,-12.9679,-1.3095},{-11.1785,-15.8954,-8.5587},{-27.1169,-14.9938,-10.7542},{-32.1821,-4.4622,1.7734},{-31.335,-5.0462,2.4744},{-33.8495,-12.2144,-7.1573},{-29.8066,-13.4705,-0.7157},{-23.7201,-3.2202,2.4503},{-31.5035,-6.6659,-8.9636},{-29.7449,-8.849,-9.6549},{-32.6637,-1.5398,-3.65},{-21.7262,9.4338,4.4622},{-18.8467,5.0559,7.942},{-12.4437,4.8956,0.4643},{-19.3616,8.4331,-3.3092},{-14.2272,10.0261,-3.8741}};
    int planes[][]= new int[][]{{58,34,56},{10,33,32},{7,56,33},{3,35,59},{6,63,53},{14,36,69},{16,71,72},{11,62,72},{13,76,64},{20,74,19},{19,76,40},{20,77,45},{20,45,85},{22,42,77},{27,26,43},{22,23,47},{23,48,88},{25,89,49},{29,87,49},{1,50,54},{55,1,54},{0,1,55},{54,50,91},{58,55,54},{58,56,55},{54,91,58},{2,3,31},{2,92,3},{4,94,0},{6,53,52},{5,6,52},{5,52,66},{5,66,62},{6,5,62},{6,62,63},{7,33,57},{7,57,67},{8,7,67},{8,56,7},{59,10,32},{9,59,35},{9,35,60},{68,9,60},{68,60,39},{10,68,36},{59,9,10},{9,68,10},{11,63,62},{11,61,63},{12,66,65},{64,12,65},{12,73,66},{12,64,73},{64,75,73},{65,13,64},{13,67,37},{13,37,40},{67,13,65},{14,37,36},{14,69,38},{14,38,71},{15,14,71},{15,71,70},{37,15,40},{14,15,37},{16,18,71},{16,72,73},{17,16,73},{17,18,16},{17,80,18},{81,74,84},{81,84,48},{75,81,80},{81,75,74},{20,19,77},{20,85,74},{78,22,77},{21,78,79},{21,79,41},{22,21,23},{22,47,42},{22,78,21},{23,81,48},{23,80,81},{82,24,84},{25,90,89},{24,25,49},{24,49,84},{82,90,25},{25,24,82},{26,85,44},{91,26,86},{26,91,83},{83,85,26},{27,86,26},{46,27,43},{86,28,92},{27,46,28},{28,86,27},{30,49,89},{29,93,51},{49,30,29},{30,93,29},{52,56,8},{0,55,56},{56,53,0},{56,52,53},{31,34,58},{32,31,3},{32,3,59},{34,33,56},{33,34,32},{34,31,32},{4,35,3},{4,11,35},{61,4,53},{4,0,53},{53,63,61},{8,67,66},{67,65,66},{66,52,8},{36,57,10},{36,68,69},{67,57,36},{36,37,67},{39,38,68},{72,38,39},{72,71,38},{39,60,72},{60,11,72},{66,73,72},{72,62,66},{76,19,75},{19,74,75},{75,64,76},{79,40,15},{40,79,78},{40,78,19},{40,76,13},{70,41,15},{41,70,80},{70,18,80},{80,21,41},{75,80,17},{82,85,83},{84,74,85},{85,82,84},{42,45,77},{44,43,26},{45,44,85},{44,45,42},{42,43,44},{88,47,23},{46,29,28},{88,29,46},{46,47,88},{48,49,87},{48,87,88},{49,48,84},{91,50,90},{90,82,91},{91,2,58},{91,86,92},{92,2,91},{94,51,93},{92,94,4},{92,28,51},{28,29,51},{51,94,92},{90,30,89},{93,90,1},{93,30,90},{1,94,93},{1,0,94},{33,10,57},{58,2,31},{35,11,60},{3,92,4},{61,11,4},{68,38,69},{18,70,71},{75,17,73},{19,78,77},{15,41,79},{23,21,80},{83,91,82},{47,46,43},{43,42,47},{87,29,88},{50,1,90}};
    float maailma[][]= new float[400][3]; 
    int maxv=95;
    int maxp=190;
    float lightx=2;
    float lighty=2;
    float lightz=2;
    int z_sort[] = new int[400];
    int thing=35;

    public void init()
    {
    }

  public void initialize()
  {
    tpix = new int[256*256];

	for (int i=w*h-1;i>=0;i--)
	{
		tpix[i]=cc;
	}

    //Ladataan taustakuva
    Image rk = getImage(getCodeBase(),"doughnut.jpg");
    kpix = new int[256*256];
    PixelGrabber pg = new PixelGrabber(rk, 0, 0, 256, 256, kpix, 0, 256);
    try { pg.grabPixels(); } catch (InterruptedException e) {}


    //Alustetaan memory image source
    src=new MemoryImageSource(256,256,src_cm,tpix,0,256);
    src.setAnimated(true);
    src.setFullBufferUpdates(true);
    trg_img=createImage(src);

    //Done
    System.gc();
    initialized=true;
  }

	public void paint(Graphics gx)
	{
		gx.setColor(Color.black);
		gx.fillRect(0,0,this.size().width,this.size().height);
		gx.setColor(Color.white);
		gx.drawString("Not.So.Doughnut v1.0",10,20);
		gx.drawString("©2000-2004 by Tommi Laukkanen",10,32);
		gx.drawString("http://koti.mbnet.fi/surface",10,44);
		gx.drawString("Initializing...",10,68);	
	}

	public void update(Graphics g)
	{
		if (initialized)
	    	{
			clear_buffer(100,100,200);
			render();
			src.newPixels(0,0,256,256);
			g.drawImage(trg_img, 0, 0, this);
		}
		else
		{
			paint(g);
			initialize();
		}
	}

  public void start(){
    if (lanka == null)
    {
      lanka = new Thread(this);
      lanka.start();
    }
  }

  public void stop(){
    if (lanka != null)
    {
      lanka.stop();
      lanka = null;
    }
  }

  public void run(){
    while (true)
    {
	repaint();
    try
    {
	lanka.sleep(20);
    }
    catch (InterruptedException e)
    {
      System.out.println("interrupted");
    }
  }
}

private void clear_buffer(int red, int green, int blue)
{
	for (int i=w*h-1;i>=0;i--)
	{
		tpix[i]=kpix[i];
	}
}

private void render()
{
      int i,j;
	rotate(a_x,a_y,a_z);
	a_x+=0.01;
	a_y+=0.01;
	a_z+=0.01;

	if (a_x>6.28) 
	{a_x=0;
	 a_y=0;
	 a_z=0;}

	for(i=0;i<maxp;i++)
	{
		for(j=0;j<3;j++)
		{
        	z[i][j]=maailma[planes[i][j]][2]-100;
	        if(z[i][j]==0)
			z[i][j]=(float)(0.001);
	        x[i][j]=(int)((maailma[planes[i][j]][0])/z[i][j]*256+125);
  	        y[i][j]=(int)((maailma[planes[i][j]][1])/z[i][j]*256+125);
          	}
            z_sort[i]=i;
		z[i][3]=(z[i][0]+z[i][1]+z[i][2])/3;
	}
	z_sorttaus();
	count_normals();

     for(i=0;i<maxp;i++){
  	  if (backfase(x[z_sort[i]][0], y[z_sort[i]][0], x[z_sort[i]][1], y[z_sort[i]][1], x[z_sort[i]][2], y[z_sort[i]][2]))
	  {
          triangle_filler(x[z_sort[i]][0], y[z_sort[i]][0], x[z_sort[i]][1], y[z_sort[i]][1], x[z_sort[i]][2], y[z_sort[i]][2], poly_color[z_sort[i]]);
        }
    }
          line(x[thing][2]-5, y[thing][2], x[thing][2]+5, y[thing][2]);
          line(x[thing][2]-5, y[thing][2]+10, x[thing][2]+5, y[thing][2]+10);
          line(x[thing][2]-5, y[thing][2], x[thing][2]-5, y[thing][2]+10);
          line(x[thing][2]+5, y[thing][2], x[thing][2]+5, y[thing][2]+10);

          line(x[thing][2], y[thing][2], x[thing][2], 13);
          line(x[thing][2], 13, 1, 13);

}

//Viivanpiirto rutiini
void line (int x1, int y1, int x2, int y2)
{
//	float hl=Math.abs(x2-x1), vl=Math.abs(y2-y1), length=(hl>vl)?hl:vl;
	float hl=Math.abs(x2-x1), vl=Math.abs(y2-y1), length=(float)(Math.sqrt(hl*hl+vl*vl));
	float deltax=(x2-x1)/(float)length, deltay=(y2-y1)/(float)length;
	for (int i=0; i<(int)length; i++)
      {
//		int x=(int)(x1+=deltax), y=(int)(y1+=deltay);
		int x=(int)(x1+(i*deltax)), y=(int)(y1+(i*deltay));
//		int x=(int)(i*deltax), y=(int)(i*deltay);
		tpix[x+y*256]=(255<<24)|(255<<16)|(255<<8)|255;
	}
} 

//Lasketaan polygonien normaalit
void count_normals()
{
float temp_x1, temp_x2;
float temp_y1, temp_y2;
float temp_z1, temp_z2;
float a,b;
  for (int loop = 0; loop < maxp ; loop++ )
  {
    temp_x1 = x[loop][0]-x[loop][1];
    temp_y1 = y[loop][0]-y[loop][1];
    temp_z1 = z[loop][0]-z[loop][1];
    temp_x2 = x[loop][1]-x[loop][2];
    temp_y2 = y[loop][1]-y[loop][2];
    temp_z2 = z[loop][1]-z[loop][2];
    dotx[loop]=temp_y1*temp_z2-temp_y2*temp_z1;
    doty[loop]=temp_z1*temp_x2-temp_z2*temp_x1;
    dotz[loop]=temp_x1*temp_y2-temp_x2*temp_y1;
    a=(float)(Math.sqrt(dotx[loop]*dotx[loop]+doty[loop]*doty[loop]+dotz[loop]*dotz[loop]));
    b=(float)(Math.sqrt(lightx*lightx+lighty*lighty+lightz+lightz));
    poly_color[loop]=(int)(255*(lightx*dotx[loop]+lighty*doty[loop]+lightz*dotz[loop])/(a*b));
    if(poly_color[loop]<0) {poly_color[loop]=0;}
  }
}

void z_sorttaus()
{
int sorted;
int temp_zsort;
float temp_polygon;
do
        {
        sorted = 1;
        for (int loop = 0; loop < maxp - 1; loop++ )
                {
                if ( z[ loop + 1 ][3] < z[ loop ][3])
                        {
                        temp_polygon = z[ loop ][3];
                        z[ loop ][3] = z[ loop + 1 ][3];
                        z[ loop + 1 ][3] = temp_polygon;
				temp_zsort = z_sort[ loop ];
				z_sort[ loop ] = z_sort[ loop +1 ];
				z_sort[ loop + 1 ] = temp_zsort;
                        sorted = 0;
                        }
                }
        }while ( sorted == 0 );
}

private void triangle_filler(int x1, int y1, int x2, int y2, int x3, int y3, int vari)
{ 
int x, y, //looping variables 
height; // the heights of the triangles 
float dx_right, // the dx/dy ratio of the right edge of the line 
dx_left, // the dx/dy ratio of the left edge of the line 
xs, xe; // the starting and ending points of the edges 
int right_x, 
left_x; 
// make sure points are in order 
int temp_x, temp_y; 

if (y2 < y1) 
{ 
temp_y = y1; 
temp_x = x1; 
y1 = y2; 
x1 = x2; 
y2 = temp_y; 
x2 = temp_x; 
} 
if (y3 < y1) 
{ 
temp_y = y1; 
temp_x = x1; 
y1 = y3; 
x1 = x3; 
y3 = temp_y; 
x3 = temp_x; 
} 
if (y3 < y2) 
{ 
temp_y = y3; 
temp_x = x3; 
y3 = y2; 
x3 = x2; 
y2 = temp_y; 
x2 = temp_x; 
} 

left_x = x2; 
right_x = x1 + (int)((float)(y2-y1)*(float)(x3-x1)/(float)(y3-y1)); 
if (right_x < left_x) // messes up if right is on left 
{ 
temp_x = right_x; 
right_x = left_x; 
left_x = temp_x; 
} 

//draw the triangle top 
if (y1 != y2) 
{ 

// compute height of subtriangle 
height = y2 - y1; 

// set starting points 
xs = (float)x1; 
xe = (float)x1; 

// compute edge ratios 
dx_left = (float)(left_x - x1) / (float)height; 
dx_right = (float)(right_x - x1) / (float)height; 

// draw
for (y = y1; y <= y2; y++) 
	{ 
	for (x = (int)xs; x <= xe; x++) 
		{ 
			tpix[x+y*256]=(255<<24)|(50<<16)|(50<<8)|vari;
		} 
	//adjust starting and ending point 
	xs += dx_left; 
	xe += dx_right; 
	} 
} 
if (y2 != y3) 
	{ 
	//now recompute slope of shorter edge to finish triangle bottom 
	// recompute slopes 
		height = y3 - y2; 
		dx_right = (float)(x3 - right_x) / (float)(height); 
		dx_left = (float)(x3 - left_x) / (float)(height); 
		xs = (float)left_x; 
		xe = (float)right_x; 
	// draw the rest of the triangle 
	for (y = y2; y <= y3; y++) 
		{ 
		for (x = (int)xs; x <= xe; x++) 
			{ 
			tpix[x+y*256]=(255<<24)|(50<<16)|(50<<8)|vari;
		} 
	//adjust starting and ending point 
	xs += dx_left; 
	xe += dx_right; 
	} //end for 
} 
} 

	public void rotate(double angle_x, double angle_y, double angle_z)
	{
	int i;
	for(i=0;i<maxv;i++)
		{
		// x-koordinaattien rotaatiot
		maailma[i][0]=(float)(vertex[i][0]*(Math.sin(angle_x)*Math.sin(angle_y)*Math.sin(angle_z)+Math.cos(angle_x)*Math.cos(angle_z))+vertex[i][1]* (Math.cos(angle_y)*Math.sin(angle_z))+vertex[i][2]*(Math.cos(angle_x)*Math.sin(angle_y)*Math.sin(angle_z)-Math.cos(angle_z)*Math.sin(angle_x)));
		// y-koordinaattien rotaatiot
		maailma[i][1]=(float)(vertex[i][0]*(Math.cos(angle_z)*Math.sin(angle_x)*Math.sin(angle_y)-Math.cos(angle_x)*Math.sin(angle_z))+vertex[i][1]*(Math.cos(angle_y)*Math.cos(angle_z))+vertex[i][2]*(Math.cos(angle_x)*Math.cos(angle_z)*Math.sin(angle_y)+Math.sin(angle_x)*Math.sin(angle_z)));
		// z-koordinaattien rotaatiot
		maailma[i][2]=(float)(vertex[i][0]*(Math.cos(angle_x)*Math.sin(angle_y)*Math.sin(angle_z)-Math.cos(angle_z)*Math.sin(angle_x))+vertex[i][1]*(-Math.sin(angle_y))+vertex[i][2]*(Math.cos(angle_x)*Math.cos(angle_y)));
		}
	}

	// Katsotaan onko face ruutuunpäin kääntyneenä pistetulolla
	public boolean backfase(int x1, int y1, int x2, int y2, int x3, int y3)
	{
	float dx1 = x3 - x1;
	float dy1 = y3 - y1;
	float dx2 = x3 - x2;
	float dy2 = y3 - y2;
	if ( ( dx1*(dy2-dy1)-(dx2-dx1)*dy1 ) > 0 )
		return true;
	else
		return false;
	}

}


