package vgp.iterate.pythagoreanTree;
import java.awt.Color;
import jv.geom.PgElementSet;
import jv.number.PdColor;
import jv.number.PuInteger;
import jv.project.PgGeometryIf;
import jv.project.PjProject;
import jv.project.PvCameraIf;
import jv.project.PvDisplayIf;
import jv.vecmath.PdMatrix;
import jv.vecmath.PdVector;
import jv.vecmath.PuMath;
import jv.vecmath.PuReflect;
/**
* Computes a Pythagorean tree which is recursively generated from a simple trunk.
* @author Andreas Haferburg, Konrad Polthier
* @version 04.11.06, 2.00 revised (kp) Full revision (new functionality and sync with JavaView).
* 29.03.06, 1.00 created (ah)
* @since JavaView 3.95
*/
public class PjPythagoreanTree extends PjProject {
/** Age of tree determines number of iterations and size of tree. */
protected PuInteger m_age;
/** Flag if triangle above trunk is a right triangle, or can be modified. */
private boolean m_bRightTriangle;
/** Flag if trunk of tree is a square, or can be modified. */
private boolean m_bSquareTrunk;
/** Vector from the mid point of the triangle base to the triangle tip */
private PdVector m_diff;
/** Map m_trunk to the left side. */
private PdMatrix m_left;
/** Map m_trunk to the right side. */
private PdMatrix m_right;
/** Lower part of the tree */
protected PgElementSet m_trunk;
/** Upper part of the tree */
protected PgElementSet m_tree;
/** Color of trunk. */
protected PdColor m_colTrunk;
/** Color of leafs. */
protected PdColor m_colLeaf;
/** Creates a new instance of PjPythagoreanTree */
public PjPythagoreanTree() {
super("Pythagorean Tree");
m_trunk = new PgElementSet(2);
m_trunk.addUpdateListener(this);
m_tree = new PgElementSet(2);
m_diff = new PdVector(2);
m_left = new PdMatrix(3);
m_right = new PdMatrix(3);
m_colTrunk = new PdColor("Color of Trunk", this);
m_colLeaf = new PdColor("Color of Leafs", this);
m_age = new PuInteger("Age of Tree", this);
if (getClass() == PjPythagoreanTree.class)
init();
}
/** Initialization. */
public void init() {
super.init();
m_bRightTriangle = true;
m_bSquareTrunk = true;
m_colTrunk.setColor(new Color(159.f/256.f, 19.f/256.f, 5.f/256.f));
m_colLeaf.setColor(new Color(7.f/256.f, 147.f/256.f, 36.f/256.f));
m_trunk.setName("Pythagorean Tree Trunk");
m_trunk.showVertices(true);
m_trunk.setGlobalElementColor(m_colTrunk.getColor());
m_trunk.setNumVertices(5);
m_trunk.setNumElements(2);
m_trunk.setVertex(0, 0.,0.);
m_trunk.setVertex(1, 2.57921,1.93016);
m_trunk.setVertex(2, 4.,0.);
m_trunk.setVertex(3, 0.,-4.);
m_trunk.setVertex(4, 4.,-4.);
m_trunk.setElement(0,0,1,2);
m_trunk.setElement(1,0,2,4,3);
m_tree.setName("Pythagorean Tree");
m_tree.showElementColors(true);
m_age.setDefBounds(1, 15, 1, 2);
m_age.setDefValue(3);
m_age.init();
setAge(m_age.getDefValue());
}
/** Initializes display, geometries. */
public void start() {
super.start();
getDisplay().selectCamera(PvCameraIf.CAMERA_ORTHO_XY);
getDisplay().setMajorMode(PvDisplayIf.MODE_PICK);
m_trunk.update(m_trunk);
addGeometry(m_tree);
addGeometry(m_trunk);
selectGeometry(m_trunk);
}
/**
* Reset project to initial state by calling project.init().
* Must call project.update(project) after calling reset().
*/
public void reset() {
init();
m_trunk.update(m_trunk);
}
/**
* Update the class whenever a child has changed.
* Method is usually invoked from the children.
*/
public boolean update(Object event) {
if (event == this) {
return super.update(this);
} else if (event == m_trunk) {
computeMatrices();
computeTreeVertices(m_age.getValue());
m_tree.update(m_tree);
return update(this);
} else if (event == m_age) {
setAge(m_age.getValue());
m_tree.update(m_tree);
return update(this);
} else if (event == m_colTrunk) {
computeTreeConnectivity(m_age.getValue());
m_trunk.setGlobalElementColor(m_colTrunk.getColor());
m_trunk.update(m_trunk);
return true;
} else if (event == m_colLeaf) {
computeTreeConnectivity(m_age.getValue());
m_tree.update(m_tree);
return true;
}
return super.update(event);
}
/**
* Computes the matrices m_left and m_right that map the m_trunk to the left
* and right side of the tree. Matrices must be updated when m_trunk has changed.
*/
private void computeMatrices() {
PdVector [] vertex = m_trunk.getVertices();
double a = vertex[4].dist(vertex[3]); // triangle base
double c = vertex[1].dist(vertex[2]); // triangle right
double d = vertex[1].dist(vertex[0]); // triangle left
double alpha = PdVector.angle(vertex[2], vertex[0], vertex[1])/180.*Math.PI;
if (vertex[1].m_data[1] > vertex[0].m_data[1])
alpha *= -1.;
computeMatrix(m_right, vertex[4], c/a, alpha, vertex[2]);
double beta = PdVector.angle(vertex[0], vertex[1], vertex[2])/180.*Math.PI;
if (vertex[1].m_data[1] < vertex[0].m_data[1])
beta *= -1.;
computeMatrix(m_left, vertex[3], d/a, beta, vertex[0]);
}
/**
* Computes the 3x3-matrix that moves to the pivot, then scales, then rotates
* by alpha, and finally moves to the origin.
* @param matrix Output matrix.
* @param pivot Pivot for scale and rotation.
* @param scale Scale factor.
* @param alpha Angle of the rotation.
* @param origin New origin.
*/
private void computeMatrix(PdMatrix matrix, PdVector pivot, double scale, double alpha, PdVector origin) {
// set the pivot
PdVector translation = PdVector.copyNew(pivot);
translation.multScalar(-1);
matrix.copy(PuReflect.translate(translation));
// scale
matrix.leftMult(PuReflect.scale(scale, 2));
// rotate
matrix.leftMult(PuReflect.rotateXY(alpha, 2));
// set origin
matrix.leftMult(PuReflect.translate(origin));
}
/**
* Computes the position of the vertices of the tree of the specified age.
* Method assumes that trunk has been copied into the tree,
* and that number of vertices has been allocated elsewhere.
*
* @param age Number of years of this tree, ie. number of iterations.
*/
private void computeTreeVertices(int age) {
m_tree.setNumVertices(3*((1<true if the triangle is kept right.
*/
public boolean isEnabledRightTriangle() {
return m_bRightTriangle;
}
/**
* Determines if the triangle above trunk is a right triangle.
* @param bRightTriangle use right triangle above trunk
*/
public void setEnabledRightTriangle(boolean bRightTriangle) {
m_bRightTriangle = bRightTriangle;
if (bRightTriangle) {
assureRightTriangle();
}
}
/**
* Returns true if the trunk is kept square.
* @return true
if the rectangle is kept square.
*/
public boolean isEnabledSquareTrunk() {
return m_bSquareTrunk;
}
/**
* Determines if the trunk should be a square.
* @param bSquareTrunk use square trunk
*/
public void setEnabledSquareTrunk(boolean bSquareTrunk) {
m_bSquareTrunk = bSquareTrunk;
if (bSquareTrunk) {
pickVertex(m_trunk, 0, null);
}
}
}