
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLJPanel;
import javax.swing.JFrame;

import com.sun.opengl.utils.Animator;

public class Gasket implements GLEventListener {
	
	float v[][]={{0.0f, 0.0f, 1.0f}, {0.0f, 0.942809f, -0.33333f},
			{-0.816497f, -0.471405f, -0.333333f}, {0.816497f, -0.471405f, -0.333333f}};
	
	float colors[][] = {{1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f},
			{0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}};
	
	static int n;
	
	void triangle(float va[], float vb[], float vc[], GL gl) {
		gl.glVertex3fv(va, 0);
		gl.glVertex3fv(vb, 0);
		gl.glVertex3fv(vc, 0);
	}
	
	void tetra(float a[], float b[], float c[], float d[], GL gl) {
		gl.glColor3fv(colors[0], 0);
		triangle(a, b, c, gl);
		gl.glColor3fv(colors[1], 0);
		triangle(a, c, d, gl);
		gl.glColor3fv(colors[2], 0);
		triangle(a, d, b, gl);
		gl.glColor3fv(colors[3], 0);
		triangle(b, d, c, gl);
	}
	
	void divide_tetra(float a[], float b[], float c[], float d[], int m, GL gl) {
		
		float mid[][] = new float[6][3];
		int j;
		if(m > 0) {
			/* compute six midpoints */
			
			for(j=0; j<3; j++) mid[0][j]=(a[j]+b[j])/2;
			for(j=0; j<3; j++) mid[1][j]=(a[j]+c[j])/2;
			for(j=0; j<3; j++) mid[2][j]=(a[j]+d[j])/2;
			for(j=0; j<3; j++) mid[3][j]=(b[j]+c[j])/2;
			for(j=0; j<3; j++) mid[4][j]=(c[j]+d[j])/2;
			for(j=0; j<3; j++) mid[5][j]=(b[j]+d[j])/2;
			
			/* create 4 tetrahedrons by subdivision */
			
			divide_tetra(a, mid[0], mid[1], mid[2], m-1, gl);
			divide_tetra(mid[0], b, mid[3], mid[5], m-1, gl);
			divide_tetra(mid[1], mid[3], c, mid[4], m-1, gl);
			divide_tetra(mid[2], mid[5], mid[4], d, m-1, gl);
			
		}
		else tetra(a, b, c, d, gl); /* draw tetrahedron at end of recursion */
	}
	
	public void init(GLAutoDrawable drawable) {
		
		GL gl = drawable.getGL();
		
		gl.glEnable(GL.GL_DEPTH_TEST);
		gl.glClearColor (1.0f, 1.0f, 1.0f, 1.0f);
	}
	
	public void reshape(GLAutoDrawable drawable, int x, int y, int w, int h) {
		GL gl = drawable.getGL();
		
		gl.glViewport(0, 0, w, h);
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		if (w <= h)
			gl.glOrtho(-2.0, 2.0, -2.0 * h / w,
					2.0 * h / w, -10.0, 10.0);
		else
			gl.glOrtho(-2.0 * w / h,
					2.0 * w / h, -2.0, 2.0, -10.0, 10.0);
		gl.glMatrixMode(GL.GL_MODELVIEW);
	}
	
	public void display(GLAutoDrawable drawable) {
		
		GL gl = drawable.getGL();
		
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		gl.glBegin(GL.GL_TRIANGLES);
		divide_tetra(v[0], v[1], v[2], v[3], n, gl);
		gl.glEnd();
		gl.glFlush();
	}
	
	public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {}
	
	public static void main(String[] args) {
		/* enter number of subdivision steps here */
		if (args.length > 0)
			n = Integer.parseInt(args[0]);
		else n = 3;
		
		JFrame frame = new JFrame("Sierpinski Tetrahedron");
		GLJPanel panel = new GLJPanel();
		panel.addGLEventListener(new Gasket());
		frame.setContentPane(panel);
		frame.setSize(700, 700);
		final Animator mainloop = new Animator(panel);
		frame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				// Run this on another thread than the AWT event queue to
				// make sure the call to Animator.stop() completes before
				// exiting
				new Thread(new Runnable() {
					public void run() {
						mainloop.stop();
						System.exit(0);
					}
				}).start();
			}
		});
		frame.setVisible(true);
		mainloop.start();
	}
}