package vgp.tutor.height;

import java.awt.Color;

import jv.geom.PgElementSet;
import jv.number.PdColor;
import jv.object.PsDebug;
import jv.project.PgGeometryIf;
import jv.project.PjProject;
import jv.vecmath.PdVector;
import jv.vecmath.PiVector;
import jvx.geom.PwCurvature;

/**
 * Demo project for displaying scalar field on a surfaces. Vertices and elements
 * are colored according to different criteria.
 * 
 * @author		Konrad Polthier
 * @version		11.07.00, 1.20 revised (kp) Further colors added for test purpose.<br>
 *					23.10.99, 1.10 revised (kp) Argument of constructor removed.<br>
 *					00.00.98, 1.00 created (kp)
 */
public class PjHeight extends PjProject {
	/** Type of scalar field is height along coordinate axis. */
	protected	int				HEIGHT				= 10;
	/** Type of scalar field is discrete Gauss curvature. */
	protected	int				GAUSS					= 11;
	/** Type of color is similar to Mathematica graphics. */
	protected	int				MATH					= 12;
	/** Type of color is similar to Maple graphics. */
	protected	int				MAPLE					= 13;
	/** Shown surface. */
	protected	PgElementSet	m_geom;
	/** Flag to determine whether current project has issued an update on m_geom. */
	protected	boolean			m_bSender;
	/** Different color types. */
	protected	String []		m_scalarFields		= {"Gauss Curvature", "x-Height",
																	"y-Height", "z-Height", "Math", "Maple"};
	/** Default color type. */
	protected	String			m_defScalarName	= m_scalarFields[0];
	/** Current color type. */
	protected	String			m_scalarName;

	public PjHeight() {
		super("Scalar Field");
		m_scalarName = m_defScalarName;
		m_geom = new PgElementSet(3);
		m_geom.setParent(this);
		if (getClass() == PjHeight.class)
		  init();
	}
	public void init() {
		super.init();
		m_geom.setName("Colored Surface");
		int size = 20;
		m_geom.computeTorus(size, size, 2., 1.);
		// m_geom.computePlane(size, size, 0., 0., 1., 1.);
		m_geom.makeQuadrBnd(size, size);
		m_geom.close();
		// PgElementSet.triangulate(m_geom);
		m_geom.assureElementColors();
		m_geom.showElementColors(true);
		m_geom.assureVertexColors();
		m_geom.showVertexColors(true);
		addGeometry(m_geom);
		selectGeometry(m_geom);
	}
	public void start() {
		if (PsDebug.NOTIFY) PsDebug.notify("PjHeight.start: scalar field = "+m_scalarName);
		computeColor(m_geom);
		super.start();
	}
	/**
	 * Register new geometry in project and adjust geometry to show colors.
	 * @return		<code>true</code> if geometry was added to list of geometries.
	 * @author		Konrad Polthier
	 * @version		11.07.00, 1.00 revised (kp)<br>
	 *					11.07.00, 1.00 created (kp)
	 */
	public boolean addGeometry(PgGeometryIf aGeometry) {
		if (!super.addGeometry(aGeometry))
			return false;
		if (aGeometry instanceof PgElementSet) {
			PgElementSet elemSet = (PgElementSet)aGeometry;
			elemSet.assureElementColors();
			elemSet.showElementColors(true);
			elemSet.assureVertexColors();
			elemSet.showVertexColors(true);		
		}
		return true;
	}
	public boolean update(Object event) {
		if (event == m_geom) {
			if (m_bSender) {
				return true;
			}
			computeColor(m_geom);
			return true;
		}
		return super.update(event);
	}
	/**
	 * Get name of current color criteria.
	 */
	public String getScalarName()		{ return m_scalarName; }
	/**
	 * Set name of current color criteria.
	 */
	public void setScalarName(String scalarName) {
		m_scalarName = scalarName.trim();
		if (getGeometry() != null)
			computeColor(getGeometry());
	}
	/**
	 * Compute color of surface and vertices depending on current color criteria.
	 */
	public boolean computeColor(PgGeometryIf geomIf) {
		if (m_scalarName==null) {
			if (PsDebug.WARNING) PsDebug.warning("missing scalarName");
			return false;
		}
		if (!(geomIf instanceof PgElementSet)) {
			if (PsDebug.WARNING) PsDebug.warning("geometry has wrong class.");
		}
		PgElementSet geom = (PgElementSet)geomIf;

		if (PsDebug.NOTIFY) PsDebug.notify("loading = "+m_scalarName);
		int nDir = 0;
		PdVector dir = null;
		int type = HEIGHT;
		String scalarName = m_scalarName.toLowerCase();
		if (scalarName.startsWith("gauss")) {
			type = GAUSS;
		} else if (scalarName.startsWith("x")) {
			nDir = 0;
			dir = new PdVector(1., 0., 0.);
		} else if (scalarName.startsWith("y")) {
			nDir = 1;
			dir = new PdVector(0., 1., 0.);
		} else if (scalarName.startsWith("z")) {
			nDir = 2;
			dir = new PdVector(0., 0., 1.);
		} else if (scalarName.startsWith("math")) {
			type = MATH;
		} else if (scalarName.startsWith("maple")) {
			type = MAPLE;
		} else {
			if (PsDebug.WARNING) PsDebug.warning("unknown scalar field = "+m_scalarName);
			return false;
		}
		if (type == HEIGHT) {
			PdVector [] bndBox = geom.getBounds();
			if (bndBox == null) {
				if (PsDebug.WARNING) PsDebug.warning("missing bounding box");
				return false;
			}
			double min = bndBox[0].m_data[nDir];
			double max = bndBox[1].m_data[nDir];

			double height;
			for (int i=geom.getNumElements()-1; i>=0; i--) {
				PiVector elem = geom.getElement(i);
				height = 0.;
				int size = elem.getSize();
				for (int j=0; j<size; j++)
					height += PdVector.dot(dir, geom.getVertex(elem.getEntry(j)));
				height /= size;
				geom.setElementColor(i, PdColor.hsv2rgb((int)((height-min)*255./(max-min)), 255, 255));
			}
			for (int i=geom.getNumVertices()-1; i>=0; i--) {
				height = PdVector.dot(dir, geom.getVertex(i));
				geom.setVertexColor(i, PdColor.hsv2rgb((int)((height-min)*255./(max-min)), 255, 255));
			}
		} else if (type == GAUSS) {
			PdVector gauss = new PdVector(geom.getNumVertices());
			PwCurvature.getGaussCurvature(geom, gauss);
			double minG = gauss.min();
			double maxG = gauss.max();
			for (int i=geom.getNumElements()-1; i>=0; i--) {
				PiVector elem = geom.getElement(i);
				double elemGauss = 0.;
				for (int j=0; j<elem.getSize(); j++)
					elemGauss += gauss.getEntry(elem.m_data[j]);
				elemGauss /= elem.getSize();
				geom.setElementColor(i, PdColor.hsv2rgb((int)((elemGauss-minG)*255./(maxG-minG)), 255, 255));
			}
			for (int i=geom.getNumVertices()-1; i>=0; i--) {
				geom.setVertexColor(i, PdColor.hsv2rgb((int)((gauss.getEntry(i)-minG)*255./(maxG-minG)), 255, 255));
			}
		} else if (type == MATH) {
			if (!geom.hasElementNormals())
				geom.makeElementNormals();
			PdVector [] normal = geom.getElementNormals();
			for (int i=geom.getNumElements()-1; i>=0; i--) {
				double [] nor = normal[i].m_data;
				Color hsvCol;
				if (nor[1] > 0)
					hsvCol = PdColor.hsv2rgb((int)(127*Math.acos(nor[0])/Math.PI),
													 (int)(255*(1.-Math.abs(nor[2]))), 255);
				else
					hsvCol = PdColor.hsv2rgb((int)(127+128*Math.acos(-nor[0])/Math.PI),
													 (int)(255*(1.-Math.abs(nor[2]))), 255);
				geom.setElementColor(i, hsvCol);
			}
		} else if (type == MAPLE) {
			if (!geom.hasElementNormals())
				geom.makeElementNormals();
			PdVector [] normal = geom.getElementNormals();
			for (int i=geom.getNumElements()-1; i>=0; i--) {
				double [] nor = normal[i].m_data;
				Color col = new Color((float)Math.abs(nor[0]), (float)Math.abs(nor[1]), (float)Math.abs(nor[2]));
				geom.setElementColor(i, col);
			}
		}
		m_bSender = true;
		geom.update(geom);
		m_bSender = false;
		return true;
	}
}

