/*       SplineCurves.java 
 *
 *  This displays three multisegment bezier curves.
 *  The first uses two segments to make a curly bracket, drawn in black.
 *
 *  The second uses a small set of control points to make a closed
 *  curve that looks like a circle, which is drawn in red.
 *
 *  The third plots 3N+1 control points exactly on a circle to
 *  allow for N Bezier segments.  This is drawn in green.
 *
 *  All of these are drawn in the y-z plane, with the x-axis coming
 *  out at the viewer.  The viewer is at (10, 0, 0) looking at the origin.
 */

	
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.media.opengl.*;
import javax.media.opengl.glu.*;
import javax.media.opengl.awt.GLCanvas;
import com.jogamp.opengl.util.*;
import java.awt.*;
import javax.swing.*;

import java.awt.event.*;
import java.nio.FloatBuffer;

import javax.swing.event.*;



public class SplineCurves implements GLEventListener, ActionListener, ChangeListener {
	
	public static void main(String[] args) {
		new SplineCurves();
	}
	
	private int INITIAL_WIDTH=800;
	private int INITIAL_HEIGHT=800;
	
	JButton quitButton;
	private GLCanvas canvas;
	private GL2 gl;
	private GLU glu;
	
	private GLUquadric quad;
	
	/* 7 control points gives 2 Bezier segments */
	private float [][] data1= { 
			  {0, -3, 0},  {0, -2, 4}, {0, -1, 0}, {0, 0, 3},
			  {0, 1, 2}, {0, 2, 3}, {0, 3, 4}
			  }; 
	
	/* The following makes a closed curve; if you inscribe this in a box
	 * it will be tangent to each of the sizes of the box.  This makes
	 * 4 Bezier segments.  The figure is drawn in red.
	 */
	private float [][] data2 = { {0, -2, 0},{0, -2, 1},{0, -1, 2},
				{0, 0, 2}, {0, 1, 2}, {0, 2, 1}, {0, 2, 0}, 
				{0, 2, -1}, {0, 1, -2}, {0, 0, -2}, {0, -1, -2}, 
				{0, -2, -1}, {0, -2, 0}};
	
	private float [][] data3;
	
	private float [] buffer1;
	private float [] buffer2;
	private float [] buffer3;
	
	private static final int N = 20;
	private static final int SIZE = 3*N+1;
	
	public SplineCurves() {
			GLProfile glp=GLProfile.getDefault();
			GLCapabilities caps = new GLCapabilities(glp);
			canvas = new GLCanvas(caps);
			canvas.addGLEventListener(this); 

			JFrame frame = new JFrame("SPLINE CURVES");

			frame.setSize(INITIAL_WIDTH, INITIAL_HEIGHT);
			frame.setLayout(new BorderLayout());
			JPanel north = new JPanel( new FlowLayout());
			
			quitButton = new JButton( "Quit");
			quitButton.addActionListener(this);
			north.add(quitButton);;
			
			frame.add(north, BorderLayout.NORTH);
			JPanel myCanvas = new JPanel(new GridLayout(1,1));
			myCanvas.add(canvas);
			frame.add(myCanvas, BorderLayout.CENTER);
			
			frame.setVisible(true);

			FPSAnimator animator = new FPSAnimator(canvas, 60);
			animator.start(); 
	}
	

	public void actionPerformed(ActionEvent event) {
			if (event.getSource() == quitButton)
				System.exit(0);

	}
	
	public void stateChanged(ChangeEvent e) {
	}


		/* This fills in the data2 array with enough control points
		 * to ake N Bezier segments, with the control points lying on a
		 * circle of radius r.
		 */
	public void buildCircle(float r) {
		data3 = new float[SIZE][3];
		
		float angle = (float)(2*Math.PI/(SIZE-1));
		for (int i = 0; i < SIZE; i++) {
			data3[i][0] = 0;
			data3[i][1] = (float)(r*Math.cos(i*angle));
			data3[i][2] = (float)(r*Math.sin(i*angle));
		}
	}
	
	public void display(GLAutoDrawable drawable) {
		update();
		render();
	}

	private void update() {		
	}

	
	private void render() {

		gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
		gl.glColor3f( 0, 0, 0);

		  /* FIRST BLACK SEGMENT: */
		  gl.glMap1f(GL2.GL_MAP1_VERTEX_3, 0, 1, 3, 4, FloatBuffer.wrap(buffer1, 0, 12));
		  gl.glMapGrid1f(10, 0, 1);
		  gl.glEvalMesh1(GL2.GL_LINE, 0, 10);
		  /* SECOND BLACK SEGMENT */
		  gl.glMap1f(GL2.GL_MAP1_VERTEX_3, 0, 1, 3, 4, FloatBuffer.wrap(buffer1, 9, 12));
		  gl.glMapGrid1f(10, 0, 1);
		  gl.glEvalMesh1(GL2.GL_LINE, 0, 10);

		  /* RED CIRCLE with 4 segments */
		  gl.glPushMatrix();
		  gl.glTranslatef(0, -3, -3);
		  
		  gl.glPushMatrix();
		  gl.glRotatef(90, 0, 1, 0);
		  gl.glColor3f(0.7f,0.7f,0.7f);
		  glu.gluDisk(quad, 0.0, 1.9, 360, 16);
		  gl.glPopMatrix();  

		  gl.glColor3f( 1, 0, 0);
		  for (int i = 0; i < 4; i++ ) {
		    gl.glMap1f(GL2.GL_MAP1_VERTEX_3, 0, 1, 3, 4, FloatBuffer.wrap(buffer2, 9*i, 12));
		    gl.glMapGrid1f(10, 0, 1);
		    gl.glEvalMesh1(GL2.GL_LINE, 0, 10);
		  }
		  gl.glPopMatrix();

		/*   GREEN CIRCLE, WITH N=20 SEGMENTS */
		  gl.glPushMatrix();
		  gl.glTranslatef(0, 3, -3);
		  
		  gl.glColor3f(0.7f,0.7f,0.7f);
		  gl.glPushMatrix();
		  gl.glRotatef(90, 0, 1, 0);
		  gl.glColor3f(0.7f,0.7f,0.7f);
		  glu.gluDisk(quad, 0.0, 1.9, 360, 16);
		  gl.glPopMatrix();  

		  gl.glColor3f( 0, 1, 0);
		  for (int i = 0; i < N; i++ ) {
		    gl.glMap1f(GL2.GL_MAP1_VERTEX_3, 0, 1, 3, 4, FloatBuffer.wrap(buffer3, 9*i, 12));
		    gl.glMapGrid1f(10, 0, 1);
		    gl.glEvalMesh1(GL2.GL_LINE, 0, 10);
		  }
		  gl.glPopMatrix();
	}
	

	public void dispose(GLAutoDrawable drawable) {
		// put the cleanup code here
		
	}

	public float [] buildBuffer(float[][] data) {
		/* This copies points from our data arrays into the buffers in a form
		 * that glMap1 is happy with.
		 */
		float [] buffer = new float[data.length*3];
		for (int i = 0; i < data.length; i++)
			for (int j = 0; j < 3; j++)
				buffer[i*3+j] = data[i][j];
		return buffer;
	}
	
	public void init(GLAutoDrawable drawable) {
		gl = drawable.getGL().getGL2();
		glu = new GLU();
		gl.glEnable(GL2.GL_DEPTH_TEST);
		gl.glEnable(GL2.GL_MAP1_VERTEX_3);
		
		gl.glMatrixMode(GL2.GL_PROJECTION);
		gl.glLoadIdentity();
		glu.gluPerspective(60f, 1, 0.5f, 20); 
		gl.glMatrixMode(GL2.GL_MODELVIEW);
		gl.glLoadIdentity();
		glu.gluLookAt(10,0,0, 0,0,0, 0,0,1);
		gl.glClearColor(1f,  1f, 1f, 1f);
		gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
		
		buildCircle(2);
		buffer1 = buildBuffer(data1);
		buffer2 = buildBuffer(data2);
		buffer3 = buildBuffer(data3);
		
		quad = glu.gluNewQuadric();
	}
		

	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
		// this is called when the window is resized
		gl.glViewport(0, 0, width, height);
		float aspect = width*1.0f/height;
		gl.glMatrixMode(GL2.GL_PROJECTION);
		gl.glLoadIdentity();
		glu.gluPerspective(60f, aspect, 0.5f, 200f); 
		gl.glMatrixMode(GL2.GL_MODELVIEW);
		
		
	}
	
}
