/**
 * @(#) simpleshadow.java
 * @(#) author: Øyvind Liland (HIØ 2002)
 */

/**
 * How to control triangle and light:
 *
 * Arrow up:    Rotate triangle around x-axis in negative z-direction
 * Arrow down:  Rotate triangle around x-axis in positive z-direction
 * Arrow left:  Rotate triangle around z-axis in negative x-direction
 * Arrow right: Rotate triangle around z-axis in positive x-direction
 * D or d key:  Move triangle down
 * U or u key:  Move triangle up
 * L or l key:  Move triangle left
 * R or r key:  Move triangle right
 * A or a key:  Move triangle away (negative z-axis)
 * T or t key:  Move triangle towards (positive z-axis)
 * F1:          Move light left (negative x-axis)
 * F2:          Move light right (positive x-axis)
 * F3:          Move light away (negative z-axis)
 * F4:          Move light towards (positive z-axis)
 */

/**
 *This code is mostly commented in norwegian
 */

//Imports
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.lang.*;
import java.util.*;
import java.io.*;

import gl4java.GLContext;
import gl4java.awt.GLCanvas;
import gl4java.utils.glut.*;
import gl4java.utils.textures.*;

public class simpleshadow extends JFrame {
    JPanel panel;
    JLabel light;
    
    myGLCanvas canvas = null;
       
    //Windows events
    WindowHandler wHandler;
    KeyHandler kHandler;

    //Degrees the triangle should rotate for each key-press
    private double step_rot = 0.0;

    //Boolean variables to determine whether to rotate round x-axis or z-axis
    private boolean rotate_x = false;
    private boolean rotate_z = false;

    //final int som brukes for å avgjøre hvilken akse det skal transeleres på 
    //i canvas.translate(int axis,double step)
    private final int axis_x = 0;
    private final int axis_y = 1;
    private final int axis_z = 2;

    //Step on y-axis, x-axis or z-axis
    private double step_trans = 0.0;

    //Lightposition on x-axis and y-axis
    private float light_x = 1.0f;
    private float light_y = 2.0f;

    //Sjekker om trekanten befinner seg i "lovlig" område
    private boolean triangleIsInside = true;

    //Constructor
    public simpleshadow () {
	
	panel = (JPanel) this.getContentPane();	
	panel.setLayout(new BorderLayout());
	this.setSize(new Dimension(600,600));
	this.setTitle("Simple Shadow");
	
	light = new JLabel("Positional light at: ");
	panel.add("North",light);

	//gl4java initiering
	Dimension d = getSize();
	canvas = new myGLCanvas(d.width, d.height);
	panel.add("Center", canvas);

	wHandler = new WindowHandler();
	kHandler = new KeyHandler();
	this.addWindowListener(wHandler);
	this.addKeyListener(kHandler);

	pack();
	show();
    }

    //Main method
    public static void main(String[]args){
	new simpleshadow();
    }

    //---------------------------------------------------------    
    //Inner class for window events, interface WindowListener
    class WindowHandler implements WindowListener {
	public void windowOpened(WindowEvent e){}
	public void windowClosing(WindowEvent e){
	    System.exit(0);
	}
	public void windowClosed(WindowEvent e){}
	public void windowIconified(WindowEvent e){}
	public void windowDeiconified(WindowEvent e){}
	public void windowActivated(WindowEvent e){}
	public void windowDeactivated(WindowEvent e){}
    }
    //-------------------------------------------------------------------
    //Inner class for key events, interface KeyListener
    class KeyHandler implements KeyListener {
	public void keyPressed(KeyEvent e){

	    switch (e.getKeyCode())
		{
		//Roterer trekant mot venstre
		case KeyEvent.VK_LEFT: 
		    rotate_x = false;
		    rotate_z = true;

		    step_rot = -2.0;
		    canvas.rotate(step_rot);

		    //Metode som sjekker at alle punktene i trekanten befinner
		    //seg innenfor "lovlig" område. Trekanten kan ikke gå gjennom
		    //gulv eller vegger.
		    if (canvas.isInside(canvas.triangle)) 
			canvas.repaint();
		    else
			canvas.rotate(-step_rot);
		    break;
		//Roterer trekant mot høyre
		case KeyEvent.VK_RIGHT:
		    rotate_x = false;
		    rotate_z = true;

		    step_rot = 2.0;
		    canvas.rotate(step_rot);

		    if (canvas.isInside(canvas.triangle)) 
			canvas.repaint();
		    else
			canvas.rotate(-step_rot);
		    break;
		//Roterer trekant "inn i skjerm" 
		case KeyEvent.VK_UP:
		    rotate_x = true;
		    rotate_z = false;

		    step_rot = -2.0;
		    canvas.rotate(step_rot);

		    if (canvas.isInside(canvas.triangle)) 
			canvas.repaint();
		    else
			canvas.rotate(-step_rot);
		    break;
		//Roterer trekant "ut av skjerm"
		case KeyEvent.VK_DOWN:
		    rotate_x = true;
		    rotate_z = false;

		    step_rot = 2.0;
		    canvas.rotate(step_rot);

		    if (canvas.isInside(canvas.triangle)) 
			canvas.repaint();
		    else
			canvas.rotate(-step_rot);
		    break;
		//Flytter lyskilden mot høyre (positiv x)
		case KeyEvent.VK_F1:
		    light_x += 0.25;
		    
		    canvas.light_position[0] = light_x;

		    canvas.repaint();
		    break;
		//Flytter lyskilden mot venstre (negativ x)
		case KeyEvent.VK_F2:
		    light_x -= 0.25;

		    canvas.light_position[0] = light_x;

		    canvas.repaint();
		    break;
		//Flytter lyskilden ned (negativ y)
		case KeyEvent.VK_F3:
		    light_y -= 0.25;

		    canvas.light_position[1] = light_y;

		    canvas.repaint();
		    break;
		//Flytter lyskilden opp (positiv y)
		case KeyEvent.VK_F4:
		    light_y += 0.25;

		    canvas.light_position[1] = light_y;

		    canvas.repaint();
		    break;
		}
	   
	    switch ((char)e.getKeyChar())
		{
		//Beveger trekanten nedover (negativ y)
		case 'D':
		case 'd':
		    step_trans = -0.025;
		    canvas.translate(axis_y,step_trans);
		    
		    if (canvas.isInside(canvas.triangle))
			canvas.repaint();
		    else
			canvas.translate(axis_y,-step_trans);

		    break;
		//Beveger trekanten oppover (positiv y)
		case 'U':
		case 'u':
		    step_trans = 0.025;
		    canvas.translate(axis_y,step_trans);

		    if (canvas.isInside(canvas.triangle)) 
			canvas.repaint();
		    else
			canvas.translate(axis_y,-step_trans);
		    break;
		//Beveger trekanten mot skjerm (positiv z)
		case 'T':
		case 't':
		    step_trans = 0.025;
		    canvas.translate(axis_z,step_trans);

		    if (canvas.isInside(canvas.triangle)) 
			canvas.repaint();
		    else
			canvas.translate(axis_z,-step_trans);
		    break;
		//Beveger trekanten "inn i" skjerm (negativ z)
		case 'A':
		case 'a':
		    step_trans = -0.025;
		    canvas.translate(axis_z,step_trans);

		    if (canvas.isInside(canvas.triangle)) 
			canvas.repaint();
		    else
			canvas.translate(axis_z,-step_trans);
		    break;
		//Beveger trekant mot venstre (negativ x)
		case 'L':		    
		case 'l':
		    step_trans = -0.025;
		    canvas.translate(axis_x,step_trans);

		    if (canvas.isInside(canvas.triangle)) 
			canvas.repaint();
		    else
			canvas.translate(axis_x,-step_trans);
		    break;
		//Beveger trekant mot høyre (positiv x)
		case 'R':
		case 'r':
		    step_trans = 0.025;
		    canvas.translate(axis_x,step_trans);

		    if (canvas.isInside(canvas.triangle)) 
			canvas.repaint();
		    else
			canvas.translate(axis_x,-step_trans);
		    break;
		}	    
	}
	public void keyReleased(KeyEvent e){}	
	public void keyTyped(KeyEvent e){}
    }
    //-----------------------------------------------------------------------
    //Inner class myGLCanvas
    private class myGLCanvas extends GLCanvas {
	GLUTFunc glut = null;
	
	//Global variabel for bruk i posisjonering av lys og utregning av projeksjon
	//for skyggekasting
	float light_position[] = { light_x, light_y, 2.0f, 0.0f };

	//Setter koordinatene for gulv og vegger.
	float floor[][] = 
	  {  
	      {-2.0f,-0.5f,-3.0f},
	      {-2.0f,-0.5f,4.0f},
	      {2.0f,-0.5f,4.0f},
	      {2.0f,-0.5f,-3.0f}
	  };

	float back_wall[][] = 
	  {
	      {-2.0f, -0.5f, -3.0f},
	      {-2.0f, 2.5f, -3.0f},
	      {2.0f, 2.5f, -3.0f},
	      {2.0f, -0.5f,-3.0f}
	  };

	float left_wall[][] = 
	  {
	      {-2.0f,-0.5f,4.0f},
	      {-2.0f,2.5f,4.0f},
	      {-2.0f,2.5f,-3.0f},
	      {-2.0f,-0.5f,-3.0f}
	  };

	float right_wall[][] = 
	  {
	      {2.0f,-0.5f,4.0f},
	      {2.0f,2.5f,4.0f},
	      {2.0f,2.5f,-3.0f},
	      {2.0f,-0.5f,-3.0f}
	  };

	//Normalene til gulv og vegger
	float normals[][] =
	  {
	      {1.0f,0.0f,0.0f},  //Left wall
	      {-1.0f,0.0f,0.0f}, //Right wall
	      {0.0f,0.0f,1.0f},  //Back wall
	      {0.0f,1.0f,0.0f}   //Floor
	  };

	//Koordinater for trekanten som skal projisere skygge
	float triangle[][] = 
	  {
	      {-1.0f,0.0f,1.0f},
	      {1.0f,0.0f,1.0f},
	      {0.0f,0.0f,-1.0f}
	  };


	//Storage for one texture
	int texture[] = new int[1];

	//Name on the file to be loaded as a texture
	String fileName = "data/marblefloor.png";


	//Constructor
	public myGLCanvas(int w, int h) {
	    super(w, h);
	}
	
	//Kalles før myGLCanvas blir skapt
	public void preInit() {
	    doubleBuffer = true;
	    stereoView = false;
	    accumSize = 8;
	}
    
	//Kalles når programmet starter og tar seg av engans initiering.
	//Typisk lyssetting, glattemodus, bakgrunnsfarge
	public void init() {
	    reshape (getSize().width, getSize().height);
	    glut = new GLUTFuncLightImpl(gl,glu);  

	    //Turns on light in the scene
	    setLigth(); 

	    //Laster tekstur for bruk på gulv
	    loadTexture();

	    // Enables Clearing Of The Depth Buffer
	    gl.glClearDepth(1.0);                                 
	    // Enables Depth Testing
	    gl.glEnable(GL_DEPTH_TEST);
	    // Normalize
	    gl.glEnable(GL_NORMALIZE);
	    //Clear the background color to black
	    gl.glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
	    //Smooth
	    gl.glShadeModel (GL_SMOOTH);
	}
	
	//Textureloader. Legger teksturer inn i array texture for senere bruk
	public void loadTexture(){
	    TextureLoader texLoader = new PngTextureLoader(gl,glu);
	    texLoader.readTexture(fileName);

	    if (texLoader.isOk()) {
		gl.glGenTextures(1,texture);
		gl.glBindTexture(GL_TEXTURE_2D,texture[0]);

		gl.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		gl.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		
		gl.glTexImage2D(GL_TEXTURE_2D, 0, 3, texLoader.getImageWidth(),
				texLoader.getImageHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, texLoader.getTexture());	
	    }
	}
	//Metode for å sette lyset i scenen
	public void setLigth() {
	    float light_ambient[] = { 0.1f, 0.1f, 0.1f, 1.0f };
	    float light_diffuse[] = { 0.2f, 0.2f, 0.2f, 1.0f };

	    gl.glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
	    gl.glLightfv(GL_LIGHT0, GL_POSITION, light_position);

	    gl.glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);

  	    gl.glEnable(GL_LIGHTING);
 	    gl.glEnable(GL_LIGHT0);
	}

	//Metode for å sette materialegenskaper til de forskjellige objektene i scenen
	public void setMaterial(int choice) {	    
	    if (choice == 1) {
		//Blå (Vegger)
		float mat_ambient[] = { 0.1f, 0.1f, 0.6f, 1.0f };
		float mat_diffuse[] = { 0.2f, 0.2f, 1.0f, 1.0f };
		float shininess[] = {0.0f};

		//Setter material properties
		gl.glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
		gl.glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
		gl.glMaterialfv(GL_FRONT, GL_SHININESS, shininess);
	    }else if (choice == 2) {
		//Rød (Trekant)
		float mat_ambient[] = { 0.5f, 0.0f, 0.0f, 1.0f };
		float mat_diffuse[] = { 0.8f, 0.0f, 0.0f, 1.0f };
		float shininess[] = {100.0f};

		//Setter material properties
		gl.glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
		gl.glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
		gl.glMaterialfv(GL_FRONT, GL_SHININESS, shininess);
	    }else if (choice == 3) {
		//Sort/grå (Skygge)
		float mat_ambient[] = { 0.5f, 0.5f, 0.5f, 1.0f };
		float mat_diffuse[] = { 0.0f, 0.0f, 0.0f, 1.0f };
		float shininess[] = {0.0f};

		//Setter material properties
		gl.glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
		gl.glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
		gl.glMaterialfv(GL_FRONT, GL_SHININESS, shininess);
	    }else{
		//Hvit (Default)
		float mat_ambient[] = { 1.0f, 1.0f, 1.0f, 1.0f };
		float mat_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
		float shininess[] = {0.0f};

		//Setter material properties
		gl.glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
		gl.glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
		gl.glMaterialfv(GL_FRONT, GL_SHININESS, shininess);
	    }
	}
		
	//Kalles før myGLCanvas blir skapt
	public void display() {
	    if (glj.gljMakeCurrent() == false)
		return;

	    gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	    gl.glClear(GL_ACCUM_BUFFER_BIT);

	    gl.glLoadIdentity();	       
	    
	    glu.gluLookAt(0.0f,2.0f,7.0f,// eye
			  0.0f,0.0f,0.0f, // looking at
			  0.0f,1.0f,0.0f);// is up	    	    


	    //Gir informasjon om hvor lyset befinner seg og vises i label øverst i JFrame.
	    light.setText("Positional light at: (" + light_position[0] + ", " + 
	    light_position[1] + ", " + light_position[2] + ")");

	    //Moves the light
	    gl.glPushMatrix();
	    gl.glLightfv(GL_LIGHT0,GL_POSITION,light_position);
	    gl.glPushMatrix();

	    gl.glPushMatrix();
	    //Må tegne gulv og vegger med polygon offset for å forhindre "stiching" når
	    //skyggene tegnes på planene
	    gl.glEnable(GL_POLYGON_OFFSET_FILL);
	    gl.glPolygonOffset(1.0f,1.0f);
	    drawWall(right_wall, normals[1]);
	    drawWall(left_wall, normals[0]);
	    drawWall(back_wall, normals[2]);
	    drawFloor(floor, normals[3]);
	    gl.glDisable(GL_POLYGON_OFFSET_FILL);
	    gl.glPopMatrix();

	    //Vil "blande" skygge med bakgrunn. Dette gjelder bare for skyggen
	    gl.glEnable(GL_BLEND);
	    gl.glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);

	    //Tegner skyggen på gulvet med PolygonOffset disablet
	    gl.glPushMatrix();
	    drawShadow(triangle,right_wall);
	    drawShadow(triangle,left_wall);
	    drawShadow(triangle,back_wall);
	    drawShadow(triangle,floor);
	    gl.glPopMatrix();

	    //Skur av blending
	    gl.glDisable(GL_BLEND);

	    //Tegner så triangelet
	    gl.glPushMatrix();
	    drawTriangle();
	    gl.glPopMatrix();
	    
	    glj.gljSwap();
	    glj.gljFree();
	}
	
	//Metoden tegner ut skyggen. Tar et object (trekanten) og et plan (gulv og vegger) som argument
	public void drawShadow(float object[][],float plane[][]) {

	    //Regner ut gulvets normal ved hjelp av tre kjente punkter i planet
	    float normal[] = calculateNormal(plane[0],plane[1],plane[3]);

	    //Finner koordinatene til projeksjonene
	    float p1[] = calculateProjection(plane[0], object[0], normal, light_position);
	    float p2[] = calculateProjection(plane[0], object[1], normal, light_position);
	    float p3[] = calculateProjection(plane[0], object[2], normal, light_position);

	    //Sort
	    setMaterial(3);
	    
	    //Tegner skyggen (projeksjonen)
	    gl.glBegin(GL_POLYGON);
	      gl.glNormal3f(normal[0],normal[1],normal[2]);
	      gl.glVertex3f(p1[0],p1[1],p1[2]);
	      gl.glVertex3f(p2[0],p2[1],p2[2]);
	      gl.glVertex3f(p3[0],p3[1],p3[2]);
	    gl.glEnd();	
	}

	//Metode for å tegne gulvet
	public void drawFloor(float coo[][],float normal[]){
	    //Array som inneholder hjørnene til texturen. Hjørnene skal mappes slik at 
	    //de ligger på korensponderende hjørner på gulvet
	    float corner[][] = 
		{
		    {0.0f,1.0f},//Top-left corner
		    {0.0f,0.0f},//Bottom-left corner
		    {1.0f,0.0f},//Bottom-right corner
		    {1.0f,1.0f}//Top-right corner
		};
	    
	    //Aktiviserer tekstur og henter denne fra texture-array.
	    gl.glEnable(GL_TEXTURE_2D);
	    gl.glBindTexture(GL_TEXTURE_2D, texture[0]);
	    //Tekstur skal overskygge alle materialegenskaper som gulvet eventuelt må ha
	    gl.glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

	    gl.glBegin(GL_QUADS);
	      //Setter normalen (Peker opp langs positiv y)
	      gl.glNormal3f(normal[0],normal[1],normal[2]);
	      for (int i = 0; i < coo.length; i++) {
		  //Binder tekstur til gulvet og tegner
		  gl.glTexCoord2f(corner[i][0],corner[i][1]);
		  gl.glVertex3f(coo[i][0],coo[i][1],coo[i][2]);	    
	      }
	    gl.glEnd();
	    
	    //Skrur av teksture slik at ingen andre objekter i scenen skal bli tegnet med tekstur
	    gl.glDisable(GL_TEXTURE_2D);
	}

	//Metode for å tegne vegger. Tar en array av hjørnekoordinater og planets normal som argument	
	public void drawWall(float coo[][], float normal[]){
	    //Blå
	    setMaterial(1);

	    gl.glBegin(GL_QUADS);
	      //Setter normal
	      gl.glNormal3f(normal[0],normal[1],normal[2]);
	      for (int i = 0; i < coo.length; i++) {
		  //Tegner vegg
		  gl.glVertex3f(coo[i][0],coo[i][1],coo[i][2]);	    
	      }
	    gl.glEnd();
	}

	//Metode for å tegne trekant
	public void drawTriangle() {
	    //Rød
	    setMaterial(2);

		gl.glBegin(GL_POLYGON);
		//Tegner trekant
		for (int i = 0; i < triangle.length; i++) 
		    gl.glVertex3f(triangle[i][0],triangle[i][1],triangle[i][2]);
		gl.glEnd();	    
	}

	//Metode for å sjekke om et objekt (trekanten) ligger innenfor begrenset område
	public boolean isInside(float object[][]){
	    //Offset slik at ikke trekanten skal gå helt inn i planet, denne er den samme som
	    //step_trans, men setter den også her.
	    float off = 0.025f;

	    float x_min = -2.0f + off;
	    float x_max = 2.0f - off;
	    float y_min = -0.5f + off;
	    float y_max = 3.0f - off;
	    float z_min = -3.0f + off;
	    float z_max = 4.0f - off;


	    //Bruker en variabel check for å avgjøre om alle koordinatene i alle punktene
	    //(3 punkter for triangel) ligger innenfor det "lovlige" definerte området.
	    //Hvis ALLE koordinatene for hvert punkt i object-arrayen oppfyller kravet 
	    //vil check økes med èn. 
	    int check = 0;

	    for (int i = 0; i < object.length; i ++) {
		if ((x_min <= object[i][0]) && (object[i][0] <= x_max) && 
		    (y_min <= object[i][1]) && (object[i][1] <= y_max) &&
		    (z_min <= object[i][2]) && (object[i][2] <= z_max))
		    
		    check++;
	    }

	    //Hvis alle koordinatene i object-arrayen ligger innefor "lovlig" område vil
	    //check = 3 (altså antall punkter som oppfyller kravene) og metoden kan returnere true
	    if (check == 3)
		return true;

	    return false;
	}
	    
	//Metode for å rotere
	public void rotate(double rot){
	    //Må translere alle punktene i trekanten tilbake til utgangspunkt for å få ønsket rotasjon. 
	    //Det vil si at trekanten IKKE skal rotere om origo den "globale" origo, men den "lokale". Dette
	    //gjør at vi får en lik rotasjon uansett hvor i det "globale" koordinatsystemet trekanten befinner
	    //seg.

	    //Tar vare på alle y-koordinatene i temporær array
	    float tmp_y[] = new float[3];
	    float tmp_x[] = new float[3];
	    float tmp_z[] = new float[3];

	    for (int k = 0; k < 3; k++){
		tmp_y[k] = triangle[k][1];
		tmp_x[k] = triangle[k][0];
		tmp_z[k] = triangle[k][2];
		//Uansett hvor trekanten befinner seg skal den flyttes tilbake til utgangspunkt
		translate(axis_y, -(double)tmp_y[k]);
		translate(axis_x, -(double)tmp_x[k]);
		translate(axis_z, -(double)tmp_z[k]);
	    }
	    //Vi kan nå rotere trekanten
	    if (rotate_x) {				
		//Trekantens koordinater multiplisert med rotasjonsmatrisen for rotasjon om x-aksen
		for (int j = 0; j < 3; j++){
		    float x = triangle[j][0];
		    float y = (triangle[j][1] * (float)Math.cos(Math.toRadians(rot))) - (triangle[j][2] * 
			      (float)Math.sin(Math.toRadians(rot)));
		    float z = ((float)Math.sin(Math.toRadians(rot) * triangle[j][1])) + (triangle[j][2] * 
			      (float)Math.cos(Math.toRadians(rot)));

		    triangle[j][0] = x;
		    triangle[j][1] = y;
		    triangle[j][2] = z;	
		}
	    }else if (rotate_z) {
		//Trekantens koordinater multiplisert med rotasjonsmatrisen for rotasjon om z-aksen
		for (int j = 0; j < 3; j++){
		    float x = (triangle[j][0] * (float)Math.cos(Math.toRadians(rot))) + (triangle[j][1] * 
			      (float)Math.sin(Math.toRadians(rot)));
		    float y = -((float)Math.sin(Math.toRadians(rot) * triangle[j][0])) + (triangle[j][1] *
			      (float)Math.cos(Math.toRadians(rot)));
   	 	    float z = triangle[j][2];

		    triangle[j][0] = x;
		    triangle[j][1] = y;
		    triangle[j][2] = z;			    
		}
	    }else{
		System.err.println("Error in method: void rotate()");
		System.exit(-1);
	    }

	    //Når rotasjonen er utført kan vi translere trekanten tilbake igjen hvor den befant seg før
	    //rotasjon.
	    for (int k = 0; k < 3; k++){
		translate(axis_y, (double)tmp_y[k]);
		translate(axis_x, (double)tmp_x[k]);
		translate(axis_z, (double)tmp_z[k]);
	    }
	}
	
	//Metode for å transelere et objekt (trekanten)
	public void translate(int axis, double step) {
	    for (int i = 0; i < 3; i++){
		float tmp = triangle[i][axis] + (float)step;

		triangle[i][axis] = tmp;
	    }
	}
	
	//Finner normalen til et plan basert på tre kjente punkter i planet
	public float [] calculateNormal(float p1[], float p2[], float p3[]) {
	    float normal[] = new float[3];

	    //Calculate the normal vector for the plane
	    //Gitt to vektorer (tre punkter) i planet kan normalen regnes ut
	    normal[0] = (((p2[1]-p1[1])*(p3[2]-p1[2]))-((p2[2]-p1[2])*(p3[1]-p1[1])));
	    normal[1] = (((p2[2]-p1[2])*(p3[0]-p1[0]))-((p2[0]-p1[0])*(p3[2]-p1[2])));
	    normal[2] = (((p2[0]-p1[0])*(p3[1]-p1[1]))-((p2[1]-p1[1])*(p3[0]-p1[0])));

	    return normal;
	}

	//Metode som regner ut parameter t ut  fra et gitt punkt på en gitt flate og med retningsvektor
	//lik lysets retning. r er et gitt punkt i planet, p er punktet som skal projiseres, n er planets
	//normal og a er lysets retningsvektor. 
	public float[] calculateProjection(float r[], float p[], float n[], float a[]){
	    float projection [] = new float[3];
	    
	    /*TEORI:
	      En linje L gjennom et punkt p = (p1,p2,p3) og med samme retning som a = [a1,a2,a3] er samlingen
	      av alle punkter x på formen: x = p + t*a. Dette gir oss for x1,x2 og x3:
	      
	      (1)
	        x1 = p1 + t*a1
	        x2 = p2 + t*a2
	        x3 = p3 + t*a3
		
	      Likning for et plan er gitt ved: 
	      
	      (2)
	        n1*r1 + n2*r2 + n3*r3 + d = 0 
		hvor planets normal n = [n1,n2,n3] og et vilkårlig punkt i planet r = (r1,r2,r3).

	      For å finne punktet på linje L som ligger i planet har vi sammenhengen:

	      (3)
	        n1(p1 + t*a1) + n2(p2 + t*a2) + n3(p3 + t*a3) = n1*r1 + n2*r2 + n3*r3

	      Vi finner t:

	      (4)
	        n1*p1 + n1*t*a1 + n2*p2 + n2*t*a2 + n3*p3 + n3*t*a3 = n1*r1 + n2*r2 + n3*r3
		n1*t*a1 + n2*t*a2 + n3*t*a3 = n1*r1 + n2*r2 + n3*r3 - n1*p1 - n2*p2 - n3*p3
		t(n1*a1 + n2*a2 + n3*a3) = n1(r1 - p1) + n2(r2 - p2) + n3(r3 - p3)
		t = (n1(r1 - p1) + n2(r2 - p2) + n3(r3 - p3))/(n1*a1 + n2*a2 + n3*a3) 

	      Vi kan nå sette inn t i likningsettet (1) for å finne x1,x2 og x3. Dette er altså koordinatene
	      for et punkt i planet på rett linje L som gjennomløper punktet p (objektet som skal kaste skygge)
	      og som har en retningsvektor a (lysets retning)	       
	    */
	    
	    //Regner ut t	    
	    float t = 
		(n[0]*(r[0] - p[0]) + n[1]*(r[1] - p[1]) + n[2]*(r[2] - p[2]))/
		(n[0]*a[0] + n[1]*a[1] + n[2]*a[2]);
	    
	    //Setter t inn i likningsettet (1)
	    float x1 = p[0] + (t * a[0]);
	    float x2 = p[1] + (t * a[1]);
	    float x3 = p[2] + (t * a[2]);

	    projection[0] = x1;
	    projection[1] = x2;
	    projection[2] = x3;

	    return projection;	   
	}

	//Kalles når vindu reskaleres
	public void reshape(int w, int h){
	    gl.glViewport(0,0,w,h);
	    gl.glMatrixMode(GL_PROJECTION);
	    gl.glLoadIdentity();
	    glu.gluPerspective(35.0, (float) w/h, 1.0, 500.0);
	    gl.glMatrixMode(GL_MODELVIEW);
	}
    }
}