// This code example is created for educational purpose
// by Thorsten Thormaehlen (contact: www.thormae.de).
// It is distributed without any warranty.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import com.jogamp.opengl.*;
import com.jogamp.opengl.awt.GLCanvas;
import com.jogamp.opengl.util.FPSAnimator;
import com.jogamp.opengl.GL.*;  
import com.jogamp.opengl.GL2.*; 
import com.jogamp.opengl.glu.GLU;

class Renderer {

  private GLU glu = new GLU();
  private double alpha = 0.0;
  private double beta = 0.0;
  
  public int mode = 1;

  public void init(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2(); // get the OpenGL 2 graphics context
    gl.glEnable(GL2.GL_DEPTH_TEST);
  }
  
  public void resize(GLAutoDrawable d, int w, int h) {
    GL2 gl = d.getGL().getGL2(); // get the OpenGL 2 graphics context
    double aspect = ((double)w)/((double)h);
    gl.glViewport(0, 0, w, h);
    gl.glMatrixMode(GL2.GL_PROJECTION);
    gl.glLoadIdentity();
    gl.glOrtho(-2.0*aspect, 2.0*aspect, -2.0, 2.0, -5.0, 5.0);
  }
  
  public void setAngles() {
    if(mode == 1) { // isometric
      alpha = 45.0;
      beta = 180.0 /  Math.PI * Math.atan(1.0/Math.sqrt(2.0));
    }
    if(mode == 2) { // dimetric
      alpha= 180.0 /  Math.PI * Math.atan(1.0/Math.sqrt(7.0));
      beta = 180.0 /  Math.PI * Math.atan(1.0/(2.0*Math.sqrt(2.0)));
    }
    if(mode == 3) { // trimetric
      alpha= 30.0;
      beta = 35.0;
    }
  }
  
  public void display(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2();  // get the OpenGL 2 graphics context
    gl.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
    gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
    
    gl.glMatrixMode(GL2.GL_MODELVIEW);
    gl.glLoadIdentity();
    
    setAngles();
    
    if(mode < 4) {
      gl.glRotated(beta, 1.0, 0.0, 0.0);
      gl.glRotated(-alpha, 0.0, 1.0, 0.0);
    }
    
    if(mode == 4) { // isometric
      //alternativ implementation
      glu.gluLookAt(1.0, 1.0, 1.0, // eye
                    0.0, 0.0, 0.0, // look at
                    0.0, 1.0, 0.0); //up
    }
    
    if(mode == 5) { // dimetric
      //alternativ implementation
      glu.gluLookAt(1.0, 1.0, Math.sqrt(7.0),
                    0.0, 0.0, 0.0,
                    0.0, 1.0, 0.0);
    }
    
    
    gl.glColor3f(0.0f, 0.0f, 1.0f);
    drawCoordinateAxisZ(d);
    gl.glColor3f(0.0f, 1.0f, 0.0f);
    drawCoordinateAxisY(d);
    gl.glColor3f(1.0f, 0.0f, 0.0f);
    drawCoordinateAxisX(d);
    
    drawUnitCube(d);
    
  }

  private void drawCoordinateAxisZ(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2();  // get the OpenGL 2 graphics context
    gl.glLineWidth(2);
    gl.glBegin(GL2.GL_LINES);
    gl.glVertex3f(0.0f, 0.0f, 0.0f); // z-axis
    gl.glVertex3f(0.0f, 0.0f, 2.0f);
    gl.glEnd();
    gl.glLineWidth(1);

    // z-axis tip
    gl.glBegin(GL2.GL_TRIANGLES);
    gl.glVertex3f( 0.0f, 0.0f, 2.0f);
    gl.glVertex3f(-0.05f, 0.05f, 1.9f);
    gl.glVertex3f( 0.05f, 0.05f, 1.9f);
    gl.glVertex3f( 0.0f,  0.0f, 2.0f);
    gl.glVertex3f( 0.05f, -0.05f, 1.9f);
    gl.glVertex3f(-0.05f, -0.05f, 1.9f);
    gl.glVertex3f( 0.0f,  0.0f, 2.0f);
    gl.glVertex3f( 0.05f,  0.05f, 1.9f);
    gl.glVertex3f( 0.05f, -0.05f, 1.9f);
    gl.glVertex3f( 0.0f,  0.0f, 2.0f);
    gl.glVertex3f(-0.05f, -0.05f, 1.9f);
    gl.glVertex3f(-0.05f,  0.05f, 1.9f);
    gl.glEnd();
    gl.glBegin(GL2.GL_POLYGON);
    gl.glVertex3f( 0.05f, -0.05f, 1.9f);
    gl.glVertex3f( 0.05f,  0.05f, 1.9f);
    gl.glVertex3f(-0.05f,  0.05f, 1.9f);
    gl.glVertex3f(-0.05f, -0.05f, 1.9f);
    gl.glEnd();
  }

  private void drawCoordinateAxisX(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2();  // get the OpenGL 2 graphics context
    gl.glPushMatrix();
    gl.glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
    drawCoordinateAxisZ(d);
    gl.glPopMatrix();
  }

  private void drawCoordinateAxisY(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2();  // get the OpenGL 2 graphics context
    gl.glPushMatrix();
    gl.glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
    drawCoordinateAxisZ(d);
    gl.glPopMatrix();
  }

  void drawUnitCube(GLAutoDrawable d) {
    GL2 gl = d.getGL().getGL2();  // get the OpenGL 2 graphics context
    gl.glColor3f(0.0f,0.0f,0.0f);
    gl.glBegin(GL2.GL_LINE_LOOP);
    gl.glVertex3f(0.0f, 1.0f, 1.0f);
    gl.glVertex3f(0.0f,0.0f, 1.0f);
    gl.glVertex3f( 1.0f,0.0f, 1.0f);
    gl.glVertex3f( 1.0f, 1.0f, 1.0f);
    gl.glEnd();
    gl.glBegin(GL2.GL_LINE_LOOP);
    gl.glVertex3f( 1.0f, 1.0f,0.0f);
    gl.glVertex3f( 1.0f,0.0f,0.0f);
    gl.glVertex3f(0.0f,0.0f,0.0f);
    gl.glVertex3f(0.0f, 1.0f,0.0f);
    gl.glEnd();
    gl.glBegin(GL2.GL_LINE_LOOP);
    gl.glVertex3f( 1.0f, 1.0f, 1.0f);
    gl.glVertex3f( 1.0f,0.0f, 1.0f);
    gl.glVertex3f( 1.0f,0.0f,0.0f);
    gl.glVertex3f( 1.0f, 1.0f,0.0f);
    gl.glEnd();
    gl.glBegin(GL2.GL_LINE_LOOP);
    gl.glVertex3f(0.0f, 1.0f,0.0f);
    gl.glVertex3f(0.0f,0.0f,0.0f);
    gl.glVertex3f(0.0f,0.0f, 1.0f);
    gl.glVertex3f(0.0f, 1.0f, 1.0f);
    gl.glEnd();
    gl.glBegin(GL2.GL_LINE_LOOP);
    gl.glVertex3f( 1.0f, 1.0f,0.0f);
    gl.glVertex3f(0.0f, 1.0f,0.0f);
    gl.glVertex3f(0.0f, 1.0f, 1.0f);
    gl.glVertex3f( 1.0f, 1.0f, 1.0f);
    gl.glEnd();
    gl.glBegin(GL2.GL_LINE_LOOP);
    gl.glVertex3f( 1.0f, 0.0f, 1.0f);
    gl.glVertex3f(0.0f, 0.0f, 1.0f);
    gl.glVertex3f(0.0f, 0.0f,0.0f);
    gl.glVertex3f( 1.0f, 0.0f,0.0f);
    gl.glEnd();
  }
  
  public void getAbsLengthXYZ(double[] l) {
        double cb =  Math.cos(beta/180.0*Math.PI);
        double ca =  Math.cos(alpha/180.0*Math.PI);
        double sb =  Math.sin(beta/180.0*Math.PI);
        double sa =  Math.sin(alpha/180.0*Math.PI);
        l[0] = Math.sqrt(ca*ca+sb*sb*sa*sa);
        l[1] = Math.sqrt(cb*cb);
        l[2] = Math.sqrt(sa*sa+sb*sb*ca*ca);
    }
  
}

class MyGui extends JFrame implements GLEventListener {

  private Renderer renderer;
  
  public void createGUI() {
    setTitle("Use 1, 2, and 3 keys to change axonometry class");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   
    GLProfile glp = GLProfile.getDefault();
    GLCapabilities caps = new GLCapabilities(glp);
    GLCanvas canvas = new GLCanvas(caps);
    setSize(320, 320);
    
    getContentPane().add(canvas);
    final FPSAnimator ani = new FPSAnimator(canvas, 60, true);
    canvas.addGLEventListener(this);
    setVisible(true);
    renderer = new Renderer();
    
    canvas.addKeyListener(new KeyAdapter() {
      public void keyPressed(KeyEvent event) {
        boolean redraw = false;
        String modeStr = "";
        switch(event.getKeyCode()) {
        case '1':
          renderer.mode = 1;
          redraw = true;
          modeStr = "isometric";
          break;
        case '2':
          renderer.mode = 2;
          redraw = true;
          modeStr = "dimetric";
          break;
        case '3':
          renderer.mode = 3;
          redraw = true;
          modeStr = "trimetric";
          break;
        case '4':
          renderer.mode = 4;
          redraw = true;
          modeStr = "isometric alternative";
          break;
        case '5':
          renderer.mode = 5;
          redraw = true;
          modeStr = "dimetric alternative";
          break;
        }
        if(redraw) {
          if(renderer.mode < 4) {
            renderer.setAngles();
            double [] l = new double[3];
            renderer.getAbsLengthXYZ(l);
            modeStr += "    lx:" + l[0] + " ly:" + l[1] + " lz:" + l[2];
          }
          setTitle(modeStr);
        }
      }
    });
    
    ani.start();
  }

  @Override
  public void init(GLAutoDrawable d) { 
    renderer.init(d); 
  }

  @Override
  public void reshape(GLAutoDrawable d, int x, int y, int width, int height) {
    renderer.resize(d, width, height);
  }

  @Override
  public void display(GLAutoDrawable d) {
    renderer.display(d);
  }

  @Override
  public void dispose(GLAutoDrawable d) { 
  }

}

public class Axonometric {
  public static void main(String[] args) {
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        MyGui myGUI = new MyGui();
        myGUI.createGUI();
      }
    });
  }
}
