/**
 * @(#) steam.java
 * @(#) author: Troy Robinette (converted to Java by Ron Cemer)
 */

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.*;
import java.util.*;
import java.io.*;
import java.util.*;
import gl4java.GLContext;
import gl4java.awt.GLAnimCanvas;
import gl4java.applet.SimpleGLAnimApplet1;

/**
  Description: Interactive 3D graphics, Assignment #1
               Miniature Steam Engine Simulation.
  Author:      Troy Robinette
  Date:        29/9/95
  Email:       troyr@yallara.cs.rmit.edu.au
  Notes:       - Transparence doesn't quite work. The color of the
                 underlying object doesn't show through. 
	       - Also only the front side of the transparent objects are
		 transparent.
**/

public class steam extends SimpleGLAnimApplet1
{
    private static final float boxnormals[][] =
    {
        {-1.0f, 0.0f, 0.0f},
        {0.0f, 1.0f, 0.0f},
        {1.0f, 0.0f, 0.0f},
        {0.0f, -1.0f, 0.0f},
        {0.0f, 0.0f, 1.0f},
        {0.0f, 0.0f, -1.0f}
    };
    private static final int boxfaces[][] =
    {
        {0, 1, 2, 3},
        {3, 2, 6, 7},
        {7, 6, 5, 4},
        {4, 5, 1, 0},
        {5, 6, 2, 1},
        {7, 4, 0, 3}
    };

        /* Initialize the applet */


	public void init()
	{
	super.init();
        Dimension d = getSize();
        canvas = new steamCanvas(d.width, d.height);
	canvas.requestFocus();
        add("Center", canvas);
	}


        /* Local GLAnimCanvas extension class */


    private class steamCanvas extends GLAnimCanvas
        implements MouseListener, ActionListener, KeyListener
    {
            /* Dimensions of texture image. */
        private final int IMAGE_WIDTH  = 64;
        private final int IMAGE_HEIGHT = 64;
            /* Step to be taken for each rotation. */
        private final int ANGLE_STEP = 10;
            /* Magic numbers for relationship b/w cylinder head and crankshaft. */
        private final float MAGNITUDE  = 120.0f;
        private final float PHASE      = 270.112f;
        private final float FREQ_DIV   = 58.0f;
        private final float ARC_LENGTH = 2.7f;
        private final float ARC_RADIUS = 0.15f;
            /* Rotation angles */
        private float view_h = 270.0f, view_v = 0.0f;
        private float head_angle = 0.0f;
        private int crank_angle = 0;
            /* Crank rotation step. */
        private float crank_step = 5.0f;
            /* Toggles */
        private boolean filled = true;
        private boolean textured = false, transparent = false;
            /* Storage for the angle look up table and the texture map */
        private float head_look_up_table[] = new float[360];
        private byte image[] = new byte[IMAGE_WIDTH*IMAGE_HEIGHT*3];
            /* Indentifiers for each Display list */
        private int list_piston_filled = 1;
        private int list_piston_texture = 2;
        private int list_flywheel_filled = 4;
        private int list_flywheel_texture = 8;
            /* Variable used in the creaton of glu objects */
        long obj;    // (GLU quadric object)

        private PopupMenu menu = null;
        private boolean menu_showing = false;
        private boolean save_suspended = false;

        private final String MENU_FILLED = "Filled";
        private final String MENU_WIREFRAME = "Wireframe";
        private final String MENU_TEXTURED = "Textured";
        private final String MENU_UNTEXTURED = "Untextured";
        private final String MENU_TRANSPARENT = "Transparent";
        private final String MENU_OPAQUE = "Opaque";
        private final String MENU_RIGHT_LIGHT_ON = "Right light on";
        private final String MENU_RIGHT_LIGHT_OFF = "Right light off";
        private final String MENU_LEFT_LIGHT_ON = "Left light on";
        private final String MENU_LEFT_LIGHT_OFF = "Left light off";
        private final String MENU_PAUSE = "Pause (use a and z to step)";
        private final String MENU_RESUME = "Resume animation";
        private final String MENU_SPEED_UP = "Speed up (+)";
        private final String MENU_SLOW_DOWN = "Slow down (-)";

        public steamCanvas(int w, int h)
        {
            super(w, h);
            GLContext.gljNativeDebug = false;
            GLContext.gljClassDebug = false;
            setAnimateFps(30.0);
        }
    
        public void preInit()
        {
            doubleBuffer = true;
            stereoView = false;
        }
    
        public void init()
        {
            System.out.println("init(): " + this);
            reshape(getSize().width, getSize().height);

            float mat_specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
            float mat_shininess[] = {50.0f};
            float light_position0[] = {1.0f, 1.0f, 1.0f, 0.0f};
            float light_position1[] = {-1.0f, 1.0f, 1.0f, 0.0f};
            float light_diffuse1[] = {1.0f,1.0f,1.0f,1.0f};
            float light_specular1[] = {1.0f,1.0f,1.0f,1.0f};

            gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

            obj = glu.gluNewQuadric();
            make_table();
            make_image();

                /* Set up Texturing */
            gl.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
            gl.glTexImage2D
                (GL_TEXTURE_2D,
                 0,
                 3,
                 IMAGE_WIDTH,
                 IMAGE_HEIGHT,
                 0,
                 GL_RGB,
                 GL_UNSIGNED_BYTE,
                 image);
            gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
            gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
            gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            gl.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            gl.glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

                /* Set up Lighting */
            gl.glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
            gl.glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
            gl.glLightfv(GL_LIGHT0, GL_POSITION, light_position0);
            gl.glLightfv(GL_LIGHT1, GL_POSITION, light_position1);
            gl.glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse1);
            gl.glLightfv(GL_LIGHT1, GL_SPECULAR, light_specular1);

                /* Initial render mode is with full shading and LIGHT 0
                   enabled. */
            gl.glEnable(GL_LIGHTING);
            gl.glEnable(GL_LIGHT0);
            gl.glDisable(GL_LIGHT1);
            gl.glDepthFunc(GL_LEQUAL);
            gl.glEnable(GL_DEPTH_TEST);
            gl.glDisable(GL_ALPHA_TEST);

            gl.glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
            gl.glEnable(GL_COLOR_MATERIAL);
            gl.glShadeModel(GL_SMOOTH);

                /* Initialise display lists */
            gl.glNewList(list_piston_filled, GL_COMPILE);
            draw_piston();
            gl.glEndList();
            gl.glNewList(list_flywheel_filled, GL_COMPILE);
            draw_flywheel();
            gl.glEndList();

            glu.gluQuadricTexture(obj, true);
            gl.glNewList(list_piston_texture, GL_COMPILE);
            draw_piston();
            gl.glEndList();
            gl.glNewList(list_flywheel_texture, GL_COMPILE);
            draw_flywheel();
            gl.glEndList();
            glu.gluQuadricTexture(obj, false);
                
            glj.gljCheckGL();

            menu = new PopupMenu("Options");
            menu.add(MENU_FILLED);
            menu.add(MENU_WIREFRAME);
            menu.add(MENU_TEXTURED);
            menu.add(MENU_UNTEXTURED);
            menu.add(MENU_TRANSPARENT);
            menu.add(MENU_OPAQUE);
            menu.add(MENU_RIGHT_LIGHT_ON);
            menu.add(MENU_RIGHT_LIGHT_OFF);
            menu.add(MENU_LEFT_LIGHT_ON);
            menu.add(MENU_LEFT_LIGHT_OFF);
            menu.add(MENU_PAUSE);
            menu.add(MENU_RESUME);
            menu.add(MENU_SPEED_UP);
            menu.add(MENU_SLOW_DOWN);
            menu.addActionListener(this);
            add(menu);

            addMouseListener(this);
            addKeyListener(this);
        }
    
        public void doCleanup()
        {
            System.out.println("destroy(): " + this);
            removeMouseListener(this);
            menu.removeActionListener(this);
            removeKeyListener(this);
        }
    
        public void reshape(int width, int height)
        {
            gl.glViewport(0,0,width,height);
            gl.glMatrixMode(GL_PROJECTION);
            gl.glLoadIdentity();
            glu.gluPerspective(65.0f, (float)width/(float)height, 1.0f, 20.0f);
            gl.glMatrixMode(GL_MODELVIEW);
            gl.glLoadIdentity();
            gl.glTranslatef(0.0f, 0.0f, -5.0f);     /* viewing transform  */
            gl.glScalef(1.5f, 1.5f, 1.5f);
        }

        public void display()
        {
            if (glj.gljMakeCurrent() == false) return;

            if (!isSuspended())
            {
                crank_angle += crank_step;
                while (crank_angle >= 360) crank_angle -= 360;
                while (crank_angle < 0) crank_angle += 360;
                head_angle = head_look_up_table[crank_angle];
            }

                /* Main display code. Clears the drawing buffer and if
                   transparency is set, displays the model twice, 1st time
                   accepting those fragments with an ALPHA value of 1 only,
                   then with DEPTH_BUFFER writing disabled for those with
                   other values. */

            int pass;
            gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            gl.glPushMatrix();
            if (transparent)
            {
                gl.glEnable(GL_ALPHA_TEST);
                pass = 2;
            }
            else
            {
                gl.glDisable(GL_ALPHA_TEST);
                pass = 0;
            }

                /* Rotate the whole model */
            gl.glRotatef(view_h, 0.0f, 1.0f, 0.0f);
            gl.glRotatef(view_v, 1.0f, 0.0f, 0.0f);

            do
            {
                if (pass == 2)
                {
                    gl.glAlphaFunc(GL_EQUAL, 1);
                    gl.glDepthMask(true);
                    pass = 1;
                }
                else if (pass != 0)
                {
                    gl.glAlphaFunc(GL_NOTEQUAL, 1);
                    gl.glDepthMask(false);
                    pass = 0;
                }

                draw_engine_pole();

                gl.glPushMatrix();
                gl.glTranslatef(0.5f, 1.4f, 0.0f);
                draw_cylinder_head();
                gl.glPopMatrix();

                gl.glPushMatrix();
                gl.glTranslatef(0.0f, -0.8f, 0.0f);
                draw_crank();
                gl.glPopMatrix();
            } while (pass > 0);

            gl.glDepthMask(true);
            gl.glPopMatrix();

            glj.gljSwap();
            glj.gljCheckGL();
            glj.gljFree();
    
            //if (!isSuspended()) repaint();  // Animate at full speed.
        }

        // Methods required for the implementation of MouseListener
        public void mouseEntered( MouseEvent evt )
        {
            Component comp = evt.getComponent();
	    if( comp.equals(this ) )
	    {
		requestFocus();
	    }
        }

        public void mouseClicked( MouseEvent evt )
        { 
            Component comp = evt.getComponent();
	    if( comp.equals(this ) )
	    {
		requestFocus();
	    }
	}
    
        public void mouseExited(MouseEvent evt)
        {
        }
    
        public void mousePressed(MouseEvent evt)
        {
                // If user presses right mouse button within canvas area,
                // suspend animation and pop up menu.
                // If menu was already popped up and user presses either
                // mouse button within canvas area, resume animation
                // because the menu will have been removed automatically.
            if (!menu_showing)
            {
                if ((evt.getModifiers() & evt.BUTTON3_MASK) != 0)
                {
                    menu_showing = true;
                    save_suspended = isSuspended();
                    if (!save_suspended)
                    {
                        setSuspended(true);
                        repaint(100);
                        try
                        {
                            Thread.currentThread().sleep(200);
                        }
                        catch (Exception e)
                        { }
                    }
                    menu.show(this,evt.getX(),evt.getY());
                }
            }
            else
            {
                menu_showing = false;
                setSuspended(save_suspended);
            }
        }
    
        public void mouseReleased(MouseEvent evt)
        {
        }
    
        // Method required for the implementation of ActionListener
        public void actionPerformed(ActionEvent evt)
        {
            if (glj.gljMakeCurrent() == false) return;
            boolean dorepaint = false;
            String c = evt.getActionCommand();
            if (c.equals(MENU_FILLED))
            {
                filled = true;
                gl.glShadeModel(GL_SMOOTH);
                gl.glEnable(GL_LIGHTING);
                gl.glEnable(GL_DEPTH_TEST);
                gl.glEnable(GL_COLOR_MATERIAL);
                glu.gluQuadricNormals(obj, GLU_SMOOTH);
                glu.gluQuadricDrawStyle(obj, GLU_FILL);
                    // Conditionally re-enable texturing.
                if (textured)
                {
                    gl.glEnable(GL_TEXTURE_2D);
                    glu.gluQuadricTexture(obj, true);
                }
                dorepaint = true;
            }
            else if (c.equals(MENU_WIREFRAME))
            {
                filled = false;
                gl.glShadeModel(GL_FLAT);
                gl.glDisable(GL_LIGHTING);
                gl.glDisable(GL_DEPTH_TEST);
                gl.glDisable(GL_COLOR_MATERIAL);
                glu.gluQuadricNormals(obj, GLU_NONE);
                glu.gluQuadricDrawStyle(obj, GLU_LINE);
                glu.gluQuadricTexture(obj, false);
                    // Be sure texturing is disabled.
                gl.glDisable(GL_TEXTURE_2D);
                glu.gluQuadricTexture(obj, false);
                dorepaint = true;
            }
            else if (c.equals(MENU_TEXTURED))
            {
                textured = true;
                if (filled)
                {
                    gl.glEnable(GL_TEXTURE_2D);
                    glu.gluQuadricTexture(obj, true);
                    dorepaint = true;
                }
            }
            else if (c.equals(MENU_UNTEXTURED))
            {
                textured = false;
                gl.glDisable(GL_TEXTURE_2D);
                glu.gluQuadricTexture(obj, false);
                dorepaint = true;
            }
            else if (c.equals(MENU_TRANSPARENT))
            {
                transparent = true;
                dorepaint = true;
            }
            else if (c.equals(MENU_OPAQUE))
            {
                transparent = false;
                dorepaint = true;
            }
            else if (c.equals(MENU_RIGHT_LIGHT_ON))
            {
                gl.glEnable(GL_LIGHT0);
                dorepaint = true;
            }
            else if (c.equals(MENU_RIGHT_LIGHT_OFF))
            {
                gl.glDisable(GL_LIGHT0);
                dorepaint = true;
            }
            else if (c.equals(MENU_LEFT_LIGHT_ON))
            {
                gl.glEnable(GL_LIGHT1);
                dorepaint = true;
            }
            else if (c.equals(MENU_LEFT_LIGHT_OFF))
            {
                gl.glDisable(GL_LIGHT1);
                dorepaint = true;
            }
            else if (c.equals(MENU_PAUSE))
            {
                if (menu_showing)
                    save_suspended = true;
                else
                    setSuspended(true);
            }
            else if (c.equals(MENU_RESUME))
            {
                if (menu_showing)
                    save_suspended = false;
                else
                    setSuspended(false);
            }
            else if (c.equals(MENU_SPEED_UP))
            {
                crank_step++;
                if (crank_step > 45) crank_step = 45;
            }
            else if (c.equals(MENU_SLOW_DOWN))
            {
                crank_step--;
                if (crank_step < 1) crank_step = 1;
            }
            if (menu_showing)
            {
                menu_showing = false;
                setSuspended(save_suspended);
            }
            glj.gljFree();
            if (dorepaint) repaint();
        }

        // Methods required for the implementation of KeyListener
        public void keyPressed(KeyEvent e)
        {
            switch (e.getKeyCode())
            {
            case KeyEvent.VK_LEFT:
                view_h -= ANGLE_STEP;
                while (view_h < 0) view_h += 360;
                repaint();
                break;
            case KeyEvent.VK_RIGHT:
                view_h += ANGLE_STEP;
                while (view_h >= 360) view_h -= 360;
                repaint();
                break;
            case KeyEvent.VK_UP:
                view_v += ANGLE_STEP;
                while (view_v >= 360) view_v -= 360;
                repaint();
                break;
            case KeyEvent.VK_DOWN:
                view_v -= ANGLE_STEP;
                while (view_v < 0) view_v += 360;
                repaint();
                break;
            }
        }

        public void keyReleased(KeyEvent e)
        {
        }

        public void keyTyped(KeyEvent e)
        {
            if (glj.gljMakeCurrent() == false) return;
            boolean dorepaint = false;
            switch ((char)e.getKeyChar())
            {
            case 'a':
            case 'A':
                if (!isSuspended()) break;
                crank_angle += crank_step;
                while (crank_angle >= 360) crank_angle -= 360;
                head_angle = head_look_up_table[crank_angle];
                dorepaint = true;
                break;
            case 'z':
            case 'Z':
                if (!isSuspended()) break;
                crank_angle -= crank_step;
                while (crank_angle < 0) crank_angle += 360;
                head_angle = head_look_up_table[crank_angle];
                dorepaint = true;
                break;
            case '+':
                crank_step++;
                if (crank_step > 45) crank_step = 45;
                break;
            case '-':
                crank_step--;
                if (crank_step < 1) crank_step = 1;
                break;
            }
            glj.gljFree();
            if (dorepaint) repaint();
        }

            /* Draws a box by scaling a glut cube of size 1.
               Also checks the filled toggle to see which rendering
               style to use. NB Texture doesn't work correctly due
               to the cube being scaled. */
        private void myBox(float x, float y, float z)
        {
            gl.glPushMatrix();
            gl.glScalef(x, y, z);
            if (filled)
                glutSolidCube(1);
            else
                glutWireCube(1);
            gl.glPopMatrix();
        }

            /* Draws a cylinder using glu function, drawing flat discs
               at each end, to give the appearence of it being solid. */
        private void myCylinder
            (long object, float outerRadius, float innerRadius, float length)
        {
            gl.glPushMatrix();
            glu.gluCylinder(object, outerRadius, outerRadius, length, 20, 1);
            gl.glPushMatrix();
            gl.glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
            glu.gluDisk(object, innerRadius, outerRadius, 20, 1);
            gl.glPopMatrix();
            gl.glTranslatef(0.0f, 0.0f, length);
            glu.gluDisk(object, innerRadius, outerRadius, 20, 1);
            gl.glPopMatrix();
        }

            /* Draws a piston.  */
        private void draw_piston()
        {
            gl.glPushMatrix();
            gl.glColor4f(0.3f, 0.6f, 0.9f, 1.0f);
            gl.glPushMatrix();
            gl.glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
            gl.glTranslatef(0.0f, 0.0f, -0.07f);
            myCylinder(obj, 0.125f, 0.06f, 0.12f);
            gl.glPopMatrix();
            gl.glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
            gl.glTranslatef(0.0f, 0.0f, 0.05f);
            myCylinder(obj, 0.06f, 0.0f, 0.6f);
            gl.glTranslatef(0.0f, 0.0f, 0.6f);
            myCylinder(obj, 0.2f, 0.0f, 0.5f);
            gl.glPopMatrix();
        }

            /* Draws the engine pole and the
               pivot pole for the cylinder head. */
        private void draw_engine_pole()
        {
            gl.glPushMatrix();
            gl.glColor4f(0.9f, 0.9f, 0.9f, 1.0f);
            myBox(0.5f, 3.0f, 0.5f);
            gl.glColor3f(0.5f, 0.1f, 0.5f);
            gl.glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
            gl.glTranslatef(0.0f, 0.9f, -0.4f);
            myCylinder(obj, 0.1f, 0.0f, 2);
            gl.glPopMatrix();
        }

            /* Draws the cylinder head at the appropreate angle, doing
               the necessary translations for the rotation. */
        private void draw_cylinder_head()
        {
            gl.glPushMatrix();
            gl.glColor4f(0.5f, 1.0f, 0.5f, 0.1f);
            gl.glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
            gl.glTranslatef(0.0f, 0.0f, 0.4f);
            gl.glRotatef(head_angle, 1.0f, 0.0f, 0.0f);
            gl.glTranslatef(0.0f, 0.0f, -0.4f);
            myCylinder(obj, 0.23f, 0.21f, 1.6f);
            gl.glRotatef(180.0f, 1.0f, 0.0f, 0.0f);
            glu.gluDisk(obj, 0.0f, 0.23f, 20, 1);
            gl.glPopMatrix();
        }

            /* Draws the flywheel.  */
        private void draw_flywheel()
        {
            gl.glPushMatrix();
            gl.glColor4f(0.5f, 0.5f, 1.0f, 1.0f);
            gl.glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
            myCylinder(obj, 0.625f, 0.08f, 0.5f);
            gl.glPopMatrix();
        }

/* Draws the crank bell, and the pivot pin for the piston. Also calls the
   appropreate display list of a piston doing the nesacary rotations before
   hand.  */
        private void draw_crankbell()
        {
            gl.glPushMatrix();
            gl.glColor4f(1.0f, 0.5f, 0.5f, 1.0f);
            gl.glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
            myCylinder(obj, 0.3f, 0.08f, 0.12f);

            gl.glColor4f(0.5f, 0.1f, 0.5f, 1.0f);
            gl.glTranslatef(0.0f, 0.2f, 0.0f);
            myCylinder(obj, 0.06f, 0.0f, 0.34f);

            gl.glTranslatef(0.0f, 0.0f, 0.22f);
            gl.glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
            gl.glRotatef(((float)crank_angle)-head_angle, 1.0f, 0.0f, 0.0f);
            if (filled)
            {
                if (textured)
                    gl.glCallList(list_piston_texture);
                else
                    gl.glCallList(list_piston_filled);
            }
            else
                draw_piston();
            gl.glPopMatrix();
        }

            /* Draws the complete crank. Piston also gets drawn through
               the crank bell function. */
        private void draw_crank()
        {
            gl.glPushMatrix();
            gl.glRotatef(crank_angle, 1.0f, 0.0f, 0.0f);

            gl.glPushMatrix();
            gl.glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
            gl.glTranslatef(0.0f, 0.0f, -1.0f);
            myCylinder(obj, 0.08f, 0.0f, 1.4f);
            gl.glPopMatrix();

            gl.glPushMatrix();
            gl.glTranslatef(0.28f, 0.0f, 0.0f);
            draw_crankbell();
            gl.glPopMatrix();

            gl.glPushMatrix();
            gl.glTranslatef(-0.77f, 0.0f, 0.0f);
            if (filled)
            {
                if (textured)
                    gl.glCallList(list_flywheel_texture);
                else
                    gl.glCallList(list_flywheel_filled);
            }
            else
                draw_flywheel();
            gl.glPopMatrix();
            gl.glPopMatrix();
        }

            /* Makes a simple checkered pattern image. (Copied from the
               redbook example "checker.c".) */
        private void make_image()
        {
            int c, idx = 0;
            for (int i = 0; i < IMAGE_WIDTH; i++)
            {
                for (int j = 0; j < IMAGE_HEIGHT; j++)
                {
                    c = ((((i & 0x8) == 0) ^ ((j & 0x8)) == 0)) ? 255 : 0;
                    image[idx++] = (byte)c;
                    image[idx++] = (byte)c;
                    image[idx++] = (byte)c;
                }
            }
        }

            /* Makes the head lookup table for all possible crank angles. */
        private void make_table()
        {
            for (int i = 0; i < 360; i++)
            {
                float rads = PHASE-(((float)i)/FREQ_DIV);
                head_look_up_table[i] =
                    MAGNITUDE *
                    (float)Math.atan
                        ((ARC_RADIUS*Math.sin(rads)) /
                         (ARC_LENGTH-(ARC_RADIUS*Math.cos(rads))));
            }
        }

        // Imported from glut.
        private void glutWireCube(float size)
        {
            drawBox(size, GL_LINE_LOOP);
        }

        // Imported from glut.
        private void glutSolidCube(float size)
        {
            drawBox(size, GL_QUADS);
        }

        // Imported from glut.
        private void drawBox(float size, int type)
        {
            float v[][] = new float[8][3];
            int i;

            v[0][0] = v[1][0] = v[2][0] = v[3][0] = -size / 2;
            v[4][0] = v[5][0] = v[6][0] = v[7][0] = size / 2;
            v[0][1] = v[1][1] = v[4][1] = v[5][1] = -size / 2;
            v[2][1] = v[3][1] = v[6][1] = v[7][1] = size / 2;
            v[0][2] = v[3][2] = v[4][2] = v[7][2] = -size / 2;
            v[1][2] = v[2][2] = v[5][2] = v[6][2] = size / 2;

            for (i = 5; i >= 0; i--)
            {
                gl.glBegin(type);
                gl.glNormal3fv(boxnormals[i]);
                gl.glVertex3fv(v[boxfaces[i][0]]);
                gl.glVertex3fv(v[boxfaces[i][1]]);
                gl.glVertex3fv(v[boxfaces[i][2]]);
                gl.glVertex3fv(v[boxfaces[i][3]]);
                gl.glEnd();
            }
        }
    }
}
