package vgp.tutor.sizeVertex;

import java.awt.Color;

import jv.geom.PgPointSet;
import jv.number.PuDouble;
import jv.number.PuInteger;
import jv.object.PsConfig;
import jv.project.PjProject;
import jv.project.PvCameraIf;
import jv.project.PvDisplayIf;
import jv.vecmath.PdVector;

/**
 * Tutorial project demonstrates how to adjust the individual thickness
 * of vertices.
 * 
 * @author		Konrad Polthier
 * @version		04.11.06, 2.00 revised (kp) Moved to vgp.tutor.<br>
 *					07.02.04, 1.00 created (kp) 
 */
public class PjSizeVertex extends PjProject implements Runnable {
	/** Point set whose vertices are displayed with varying vertex sizes. */
	protected		PgPointSet						m_geom;
	/** Save number of vertices to be able to detect outside changes on geometry. */
	protected		int								m_numVert;
	/** Rate of change of vertex thickness. */
	protected		PdVector							m_delSize;
	/** Thread created in auto-rotate mode. */
	protected		transient Thread				m_thread;
	/** Global speed of movement. */
	protected		PuDouble							m_speed;
	/** Number of horizontal (and vertical) faces. */
	protected		PuInteger						m_discr;

	public PjSizeVertex() {
		super("Size of Vertices");
		m_geom		= new PgPointSet(2);
		m_geom.setParent(this);

		m_delSize	= new PdVector();
		m_discr		= new PuInteger(PsConfig.getMessage(true, 84000, "Discretization"), this);
		m_speed		= new PuDouble(PsConfig.getMessage(true, 84000, "Speed"), this);
		
		if (getClass() == PjSizeVertex.class) {
			init();
		}
	}
	public void init() {
		super.init();

		m_geom.setGlobalVertexSize(10.);
		m_geom.showVertexColors(true);
		m_geom.showVertices(true);
		m_geom.showVertexSizes(true);
		// m_geom.showVertexOutline(false);
		
		m_discr.setDefBounds(2, 50, 1, 5);
		m_discr.setDefValue(10);
		m_discr.init();
		
		m_speed.setDefBounds(0., 1., 0.01, 0.1);
		m_speed.setDefValue(0.2);
		m_speed.init();
		
		compute();
	}
	public void start() {
		addGeometry(m_geom);
		selectGeometry(m_geom);
		m_geom.update(m_geom);
		PvDisplayIf disp = getDisplay();
		disp.selectCamera(PvCameraIf.CAMERA_ORTHO_XY);
		super.start();
	}
	/**
	 * Initialize individual vertex sizes and the rate of chance.
	 */
	public void compute() {
		int discr = m_discr.getValue();
		m_geom.computePlane(discr, discr, -10., -10., 10., 10.);
	
		int nov				= m_geom.getNumVertices();
		m_delSize.setSize(nov);
		for (int i=0; i<nov; i++) {
			if (Math.random() > .5)
				m_delSize.setEntry(i, 1.);
			else
				m_delSize.setEntry(i, -1.);
		}
		
		for (int i=0; i<nov; i++) {
			m_geom.setVertexSize(i, Math.random());
		}
		m_numVert	= nov;
		computeColorsFromSizeVertex();
	}
	private void computeColorsFromSizeVertex() {
		int nov				= m_geom.getNumVertices();
		m_geom.assureVertexColors();
		Color [] color			= m_geom.getVertexColors();
		for (int i=0; i<nov; i++) {
			double size = m_geom.getVertexSize(i);
			float col	= (float)(size);
			color[i]		= new Color(Color.HSBtoRGB(0.83333f*(1.f-col), 1.f, 1.f));
		}
	}
	/**
	 * Update the class whenever a child has changed.
	 * Method is usually invoked from the children.
	 */
	public boolean update(Object event) {
		if (event == this) {
			m_geom.update(m_geom);
		} else if (event == m_geom) {
			if (m_numVert != m_geom.getNumVertices()) {
				compute();
			}
			return true;
		} else if (event == m_discr) {
			compute();
			m_geom.update(m_geom);
			return true;
		} else if (event == m_speed) {
			return true;
		}
		return super.update(event);
	}
	/**
	 * Change size of each vertex slightly along its element vector.
	 * If an element reaches the boundary then do a reflection like billard.
	 */
	public void changeVertexSize(double speed) {
		int nov				= m_geom.getNumVertices();
		if (nov != m_numVert)
			return;
		for (int i=0; i<nov; i++) {
			double size = m_geom.getVertexSize(i);
			double incr = m_delSize.getEntry(i)*speed;
			if (incr>0 && size+incr > 1. ||
				 incr<0 && size+incr < 0) {
				incr = -incr;
				m_delSize.setEntry(i, -m_delSize.getEntry(i));
			}
			m_geom.setVertexSize(i, size+incr);
		}
		computeColorsFromSizeVertex();
	}
	/** Start animation by creating a new thread */
	public void startAnim() {
		if (m_thread != null)
			return;
		m_thread = new Thread(this, PsConfig.getProgram()+": "+"SizeVertex");
		m_thread.setPriority(Thread.NORM_PRIORITY);
		m_thread.start();
	}
	
	/** Stop animation by removing the thread. */
	public void stopAnim() {
		m_thread = null;
	}
	/** Do the animation by incrementing the vertex size. */
	public void run() {
		while(m_thread != null) {
			changeVertexSize(m_speed.getValue());
			m_geom.update(m_geom);
			try {
				Thread.sleep(30);
			} catch(InterruptedException e) {}
		}
	}
}
