package edu.byu.cs.roots.opg.chart.selectvertical;
import java.awt.Color;
import java.awt.Font;
import java.awt.font.FontRenderContext;
import edu.byu.cs.roots.opg.chart.cmds.DrawCmdFillRect;
import edu.byu.cs.roots.opg.chart.cmds.DrawCmdMoveTo;
import edu.byu.cs.roots.opg.chart.cmds.DrawCmdSetFont;
import edu.byu.cs.roots.opg.chart.cmds.DrawCmdText;
import edu.byu.cs.roots.opg.model.Individual;
import edu.byu.cs.roots.opg.util.NameAbbreviator;
/**
* This contains the information needed to draw this Node on
* a tree in the chart.
* @author thedqs
* @version 0.0.1
* @category TreeCreation
*/
public class AncesTreeNode {
// Constants
/** The Width of the box */
static double boxWidth = 144;
/** Width of each column */
static final double columnWidth = 11 / 10 * boxWidth;
/** Width of the space between boxes */
static final double spaceWidth = columnWidth - boxWidth;
// Global variables set the AncesTree
/** The height of the chart used in FillUp and FillDown */
static double chartHeight = 0;
/** The width of the chart used in FillUp and FillDown */
static double chartWidth = 0;
/** The individual of this node */
Individual nodeIndi;
/** The father of this node */
AncesTreeNode father;
/** The mother of this node */
AncesTreeNode mother;
/** This is the height of the father and his subtree */
double fatherHeight;
/** This is the bottom portion of the father from the midpoint.
* This is used for positioning the mother right under the father */
double fatherBottom;
/** This is the bottom portion of the father from the midpoint.
* This is used for positioning the father right above the mother */
double motherTop;
/** This is the height of the mother and her subtree */
double motherHeight;
/** This is the height of our tree and subtrees */
double treeHeight;
/** This is the middle point of the tree's height or in
* other words where this node should be placed in
* respect with the 0 height.
*/
double treeMiddle;
/**
* Create a node based on the individual passed to us
* @param indi - The individual to make this node for.
* @param curGen - The current number of generations
* we have already traversed.
* @param maxGen - The maximum number of generations
* we will traverse.
*/
public AncesTreeNode(Individual indi, int curGen, int maxGen) {
if (indi.father != null && curGen < maxGen)
father = new AncesTreeNode(indi.father, curGen + 1, maxGen);
else
father = null;
if (indi.mother != null && curGen < maxGen)
mother = new AncesTreeNode(indi.mother, curGen + 1, maxGen);
else
mother = null;
motherHeight = (mother == null) ? 0 : mother.GetHeight();
fatherHeight = (father == null) ? 0 : father.GetHeight();
motherTop = motherHeight - (mother == null ? 0 : mother.GetMiddle());
fatherBottom = father == null ? 0 : father.GetMiddle();
double upHeight = (fatherHeight + motherHeight) / 2;
double downHeight = upHeight;
// TO DO: Calculate the Max Height we need here and use it instead of just 2/9ths of an inch
treeHeight = upHeight + downHeight;
treeHeight = Math.max(treeHeight, 16);
treeMiddle = Math.max(motherHeight, 8);
nodeIndi = indi;
}
/**
* Retreive the height of the node and its subtrees
* @return The height of this subtree
*/
public double GetHeight() {
return treeHeight;
}
/**
* Retrieve the middle of the tree based on 0 being
* the bottom of the height
* @return The middle of the tree, or where this node
* should be placed.
*/
public double GetMiddle() {
return treeMiddle;
}
/**
* Draw this box onto the chart relative to the Y
* and curGen we receive.
* @param chart - The chart to draw to
* @param curGen - The current generation we are on
* @param y - The Y coordinate that we are building on
*/
public void DrawBox(ChartMargins chart, int curGen, double y)
{
// Margin from the top of the chart
double verticalMargin = 150;
// Margin of space for the text in each box
double textMargin = 10;
// The height to the base of our box
double baseHeight = -(verticalMargin + y + treeHeight - treeMiddle);
// The height of our box
double boxHeight = Math.min(treeHeight, boxWidth);
// Get the name to display in the box
String indiName = nodeIndi.givenName.trim() + " " +nodeIndi.surname.trim();
// Set up the height of the font for this box based on the name
int fontMin = 4;
int fontMax = (int)(boxHeight / 4);
int fontHeight = Math.min(Math.max(fontMin, (int)(treeHeight / 4)), fontMax);
// The font conext uses the identity matrix transformation, is not anti-aliasing and uses
// fractional matrices.
FontRenderContext fontContext = NameAbbreviator.frc;
Font min = new Font("Times New Roman", Font.ITALIC, fontHeight);
// Get the size down until we can fit the name into the box
while (min.getStringBounds(indiName, fontContext).getWidth() > (boxWidth - 2 * textMargin)&&
fontHeight >= fontMin)
{
min = new Font("Times New Roman", Font.ITALIC, --fontHeight);
}
// Draw the parents first and build the tree from the end to the beginning.
if (father != null)
father.DrawBox(chart, curGen + 1,
(mother != null) ? y - (motherHeight + father.GetMiddle()) : y);
if (mother != null)
mother.DrawBox(chart, curGen + 1,
(father != null) ? y + (fatherHeight + (motherHeight - mother.GetMiddle())) : y);
// Draw the box and the text inside the box
chart.addDrawCommand(new DrawCmdFillRect(curGen * columnWidth, baseHeight,
boxWidth, boxHeight , 1, Color.BLACK, Color.LIGHT_GRAY, null));
chart.addDrawCommand(new DrawCmdMoveTo(curGen * columnWidth + textMargin, baseHeight + boxHeight - fontHeight));
chart.addDrawCommand(new DrawCmdSetFont(min, Color.BLACK));
chart.addDrawCommand(new DrawCmdText(indiName));
}
/**
* Squishes all the boxes to the top since their current height
* is the min height then we can see how much room we have to squish
* and then squish them all up there. Uses chartHeight
* @param curGen - The current generation we are
* @param arySpace - The current filled array of space.
*/
public void FillUp(int curGen, double[] arySpace) {
}
}