package vgp.tutor.torusknot;

import java.awt.Color;

import jv.geom.PgElementSet;
import jv.geom.PgPolygon;
import jv.number.PdColor;
import jv.number.PuInteger;
import jv.object.PsDebug;
import jv.project.PjProject;
import jv.vecmath.PdVector;
import jvx.geom.PgTube;

/**
 * Demo project for handling multiple geometries in the JavaView viewer.
 * 
 * @author		Konrad Polthier
 * @version		01.06.03, 2.00 revised (kp) Handling of color and update improved.<br>
 *					02.03.01, 1.10 revised (kp) Moved to vgp.tutor from vgp.curve.<br>
 *					07.11.99, 1.01 revised (kp) Lower limit of tube discr changed to 2.<br>
 *					30.01.99, 1.00 created (kp)
 */
public class PjTorusKnot extends PjProject {
	protected	boolean				m_bShowTorus;
	protected	boolean				m_bShowTube;
	protected	boolean				m_bShowKnot;
	protected	PgElementSet		m_torus;						// Torus geometry
	protected	PgTube				m_tube;						// Thickened tube around knot
	protected	PgPolygon			m_knot;						// Knot winding around torus
	protected	double				m_radius				= 2.;	// Radius of torus
	protected	double				m_thick				= 1.;	// Thickness of torus
	protected	int					m_defPolygonDiscr = 120;
	protected	int					m_defNumUWindings = 5;
	protected	int					m_defNumZWindings = 4;
	protected	PuInteger			m_polygonDiscr;
	protected	PuInteger			m_numUWindings;
	protected	PuInteger			m_numZWindings;

	public PjTorusKnot() {
		super("TorusKnot");
		m_torus = new PgElementSet(3);
		m_torus.setName("Torus");
		m_knot = new PgPolygon(3);
		m_knot.setName("TorusKnot");
		m_knot.setGlobalEdgeColor(Color.blue);
		m_tube = new PgTube(3);
		m_tube.setName("Tube");
		m_tube.setPolygon(m_knot);

		m_polygonDiscr = new PuInteger("Knot Discr", this);
		m_numUWindings = new PuInteger("U-Windings", this);
		m_numZWindings = new PuInteger("Z-Windings", this);
		if (getClass() == PjTorusKnot.class)
		  init();
	}
	public void init() {
		super.init();
		m_bShowTorus	= true;
		m_bShowTube		= true;
		m_bShowKnot		= true;

		m_torus.computeTorus(21, 15, m_radius, m_thick);
		m_torus.showElements(false);

		m_polygonDiscr.setDefBounds(2, 300, 1, 5);
		m_polygonDiscr.setDefValue(m_defPolygonDiscr);
		m_polygonDiscr.init();

		m_numUWindings.setDefBounds(0, 20, 1, 2);
		m_numUWindings.setDefValue(m_defNumUWindings);
		m_numUWindings.init();
		m_numZWindings.setDefBounds(0, 20, 1, 2);
		m_numZWindings.setDefValue(m_defNumZWindings);
		m_numZWindings.init();
		setWindings(m_defNumUWindings, m_defNumZWindings);

		m_knot.assureVertexColors();
		m_knot.showVertexColors(true);
		m_knot.showEdgeColors(true);
		m_knot.setGlobalVertexSize(4.);
		m_knot.setGlobalEdgeSize(3.);

		m_tube.setEnabledArrowPanel(false);
		m_tube.setDefThickness(0.2);
		m_tube.setDefDiscr(5);
		m_tube.init();
		m_tube.assureElementColors();
		m_tube.setEnabledInduceColors(true);
	}
	public void start() {
		if (PsDebug.NOTIFY) PsDebug.notify("start: ");
		computeKnot(m_polygonDiscr.getValue(), m_numUWindings.getValue(), m_numZWindings.getValue());
		m_knot.update(m_knot);
		
		addGeometry(m_torus);
		addGeometry(m_knot);
		addGeometry(m_tube);
		selectGeometry(m_torus);
		super.start();
	}
	/**
	 * Update the class whenever a child has changed.
	 * Method is usually invoked from the children.
	 */
	public boolean update(Object event) {
		if (event == this) {
			computeKnot(m_polygonDiscr.getValue(), m_numUWindings.getValue(), m_numZWindings.getValue());
			m_knot.update(m_knot);
			return super.update(this);
		} else if (event == m_numUWindings) {
			computeKnot(m_polygonDiscr.getValue(), m_numUWindings.getValue(), m_numZWindings.getValue());
			m_knot.update(m_knot);
			return true;
		} else if (event == m_numZWindings) {
			computeKnot(m_polygonDiscr.getValue(), m_numUWindings.getValue(), m_numZWindings.getValue());
			m_knot.update(m_knot);
			return true;
		} else if (event == m_polygonDiscr) {
			computeKnot(m_polygonDiscr.getValue(), m_numUWindings.getValue(), m_numZWindings.getValue());
			m_knot.update(m_knot);
			return true;
		}
		return super.update(event);
	}
	/**
	 * Evaluate torus at given parameter values.
	 * Parameter values are in interval [0,2Pi].
	 */
	private void evalTorus(PdVector p, double u, double v) {
		if (p==null || p.getSize()<3)
			return;
		double su = Math.sin(u);
		double cu = Math.cos(u);
		double sv = Math.sin(v);
		double cv = Math.cos(v);
		p.set(cu*(m_thick*cv+m_radius), su*(m_thick*cv+m_radius), m_thick*sv);
	}
	/**
	 * Compute torus knot with given number of windings by evaluating
	 * the torus formulas. Knot is colored as rainbow.
	 */
	private void computeKnot(int numVertices, int numUWindings, int numZWindings) {
		if (numVertices == 1)
			return;
		m_knot.setNumVertices(numVertices);
		double deltaU	= 2.*Math.PI*numUWindings/(numVertices-1.);
		double deltaV	= 2.*Math.PI*numZWindings/(numVertices-1.);
		double u			= 0.;
		double v			= 0.;
		for (int i=0; i<numVertices; i++) {
			evalTorus(m_knot.getVertex(i), u, v);
			u		+= deltaU;
			v		+= deltaV;
			int dist	= 255*i/(numVertices-1);
			m_knot.setVertexColor(i, PdColor.hsv2rgb(dist, 255, 255));
		}
		m_knot.makeEdgeFromVertexColors();
	}
	public void setDefWindings(int defNumUWindings, int defNumZWindings) {
		m_defNumUWindings = defNumUWindings;
		m_defNumZWindings = defNumZWindings;
	}
	public void setWindings(int numUWindings, int numZWindings) {
		m_numUWindings.setValue(numUWindings);
		m_numZWindings.setValue(numZWindings);
	}
}
