package edu.byu.cs.roots.opg.chart.multisheet;
import java.awt.Color;
import java.util.ArrayList;
import edu.byu.cs.roots.opg.chart.cmds.DrawCmdMoveTo;
import edu.byu.cs.roots.opg.chart.cmds.DrawCmdRelFillRect;
import edu.byu.cs.roots.opg.chart.cmds.DrawCmdRelLineTo;
import edu.byu.cs.roots.opg.chart.cmds.DrawCmdRelRoundRect;
import edu.byu.cs.roots.opg.chart.preset.templates.ChartMargins;
import edu.byu.cs.roots.opg.chart.preset.templates.DescBoxParent;
import edu.byu.cs.roots.opg.chart.preset.templates.PresetChartOptions;
import edu.byu.cs.roots.opg.model.Individual;
import edu.byu.cs.roots.opg.model.OpgSession;
public class DescBox extends DescBoxParent
{
private static final long serialVersionUID = 1L;
public DescBox(Individual indi)
{
super(indi);
}
/**
* Recursively calculates the minimum coordinates for this individual
* (and its subtree(s)) based on the minimum font size
*
* @param options - chart options that contain the minimum font size
*/
@Override
public void calcCoords(PresetChartOptions options, int curGen, int[] maxSpouseLineOffset)
{
int maxGen = options.getDescGens();
if (curGen > maxGen)
return;
//calculate the dimensions for this box
//boxHeight = printArray.get(curGen).getBoxHeight(this);
setBoxHeight(styleBox.boxHeight * ((single)? 1.2 : innerBoxes.size()*(styleBox.layout.parallelCouple?1:2)*1.2));
upperSubTreeHeight = getBoxHeight()/2.0;
lowerSubTreeHeight = -upperSubTreeHeight;
//boxWidth = printArray.get(curGen).getBoxWidth();
setUpperBounds(new ArrayList<Double>(maxGen - curGen));
setLowerBounds(new ArrayList<Double>(maxGen - curGen));
//inner spouse box offset calculations
for (int i = 0; i < innerBoxes.size(); ++i) {
DescInnerBox curSpouseBox = innerBoxes.get(i);
curSpouseBox.innerBoxHeight = getBoxHeight() / innerBoxes.size();
curSpouseBox.pieceOffset = getBoxHeight()/2.0 - curSpouseBox.innerBoxHeight/2.0 - (i * curSpouseBox.innerBoxHeight);
}
//recurse on all of the children
int maxGensOfSubTree = 0;
if (curGen < maxGen)
{
for (int i = 0; i < children.size(); ++i)
{
children.get(i).calcCoords(options, curGen+1, maxSpouseLineOffset);
//keep track of the maximum number of generations in each subTree
if (children.get(i).maxGensInTree > maxGensOfSubTree)
maxGensOfSubTree = children.get(i).maxGensInTree;
}
}
//set the maximum number of generations in this tree
maxGensInTree = maxGensOfSubTree + 1;
//calculate the distances between each of the children by intersecting their subtrees
if (children.size() > 0 && curGen < maxGen)
{
//add gap onto bottom of last child to ensure that siblings are spaced closer than siblings to cousins
double lastChildLowerBound = children.get(children.size()-1).getLowerBounds().get(0);
children.get(children.size()-1).getLowerBounds().set(0, new Double(lastChildLowerBound - vertSeperation ));
double childrenHeight = 0;
for (int i = 1; i < children.size(); ++i)
{
int curSubTreeMaxGen = 0;
double tempDist = 0;
int controlSibling = -1;
for (int j = i-1; j >= 0; --j)
{
double dist = children.get(j).getRelSiblingVertOffset() + DescBox.calcIntersectDist( children.get(j) , children.get(i), curSubTreeMaxGen);
if (dist > tempDist)
{
controlSibling = j;
tempDist = dist;
curSubTreeMaxGen = children.get(j).maxGensInTree;
}
}
//set the offset distance of this child to the maximum distance
//children.get(i).relSiblingVertOffset = tempDist;
childrenHeight = tempDist;
children.get(i).setRelSiblingVertOffset(childrenHeight);
//TODO redistribute any children inbetween evenly//fix me
if (controlSibling > -1)
redistrubuteVertically(children, controlSibling, i);
}//end children loop
//calculate the distance from the center of the parent to the center of the first child
// this is half of the distance from the top of the first child box to the bottom of the last child box
//relChildVertOffset = (childrenHeight + (children.get(0).boxHeight/2.0) + (children.get(children.size()-1).boxHeight/2.0) ) / 2.0;
//relChildVertOffset = (childrenHeight + (children.get(0).boxHeight/2.0) ) / 2.0;
relChildVertOffset = (childrenHeight) / 2.0;
}
for (int i = 0; i < innerBoxes.size(); i++){
DescInnerBox curSpouseBox = innerBoxes.get(i);
double childCenter = 0;
if (curSpouseBox.children.size() > 0){
DescBoxParent firstChild = curSpouseBox.children.get(0);
DescBoxParent lastChild = curSpouseBox.children.get(curSpouseBox.children.size()-1);
childCenter = (firstChild.getRelSiblingVertOffset() + lastChild.getRelSiblingVertOffset())/2.0;
double childCenterDiff = relChildVertOffset - (firstChild.getRelSiblingVertOffset() + lastChild.getRelSiblingVertOffset())/2.0 - curSpouseBox.pieceOffset;
if(childCenterDiff > 0){
setBoxHeight(getBoxHeight() + childCenterDiff);
relChildVertOffset -= childCenterDiff/2.0;
for (int j = 0; j<=i; j++)
innerBoxes.get(j).pieceOffset += childCenterDiff/2.0;
}
// curSpouseBox.pieceOffset += childCenterDiff;
//(relChildVertOffset - curChild.relSiblingVertOffset)* scaler
}
//curSpouseBox.pieceOffset += (curSpouseBox.children.size()>0?((relChildVertOffset - childCenter)*scaler):0)/5.0;
}
for (DescBoxParent child: children)
{
upperSubTreeHeight = Math.max(upperSubTreeHeight, relChildVertOffset - child.getRelSiblingVertOffset() + child.upperSubTreeHeight);
lowerSubTreeHeight = Math.min(lowerSubTreeHeight, relChildVertOffset - child.getRelSiblingVertOffset() + child.lowerSubTreeHeight);
}
//calculate the spacing for lines from multiple spouses to their children
int maxOffset = 0;
if (innerBoxes.size() > 1)//if there is more than one spouse
{
//loop through all of the spouses
for (int i = 0; i < innerBoxes.size(); ++i)
{
DescInnerBox curSpouse = innerBoxes.get(i);
if (curSpouse.children.size() > 0)
{
DescBoxParent firstChild = curSpouse.children.get(0);
DescBoxParent lastChild = curSpouse.children.get(curSpouse.children.size()-1);
//find if children center is above or below piece
double childCenterDiff = relChildVertOffset - (firstChild.getRelSiblingVertOffset() + lastChild.getRelSiblingVertOffset())/2.0 - curSpouse.pieceOffset;
//below
if (childCenterDiff < 0)
{
int firstPosIdx = i;
int curIdx = i;
DescInnerBox prevSpouse = innerBoxes.get(curIdx);
while (childCenterDiff < 0)
{
//move to next spouse
++curIdx;
//quit if we've past the last spouse
if (curIdx >= innerBoxes.size())
break;
//skip spouses with no children
if ( innerBoxes.get(curIdx).children.size() == 0)
continue;
//if the spouse has children, then check to see if this spouses children are
// centered below the spouse and that the previous spouse's (with
// children) children's lower edge is below this spouses center
firstChild = innerBoxes.get(curIdx).children.get(0);
lastChild = innerBoxes.get(curIdx).children.get(innerBoxes.get(curIdx).children.size()-1);
childCenterDiff = relChildVertOffset - (firstChild.getRelSiblingVertOffset() + lastChild.getRelSiblingVertOffset())/2.0 - innerBoxes.get(curIdx).pieceOffset;
DescBoxParent prevLastChild = prevSpouse.children.get(prevSpouse.children.size()-1);
if (relChildVertOffset - prevLastChild.getRelSiblingVertOffset() + prevLastChild.getBoxHeight()/2.0 >= innerBoxes.get(curIdx).pieceOffset)
break;
prevSpouse = innerBoxes.get(curIdx);
}
//set the children connecting line offsets for all of the spouses that overlap
int k = 0;
for (int j = firstPosIdx; j <= curIdx - 1; ++j)
{
if (innerBoxes.get(j).children.size() > 0)
++k;
innerBoxes.get(j).spouseChildOffset = k;
}
if (k > maxOffset)
maxOffset = k;
//set the piece counter(i) to the next piece(spouse)
i = curIdx - 1;
}
//above
else if (childCenterDiff > 0)
{
int firstPosIdx = i;
int curIdx = i;
DescInnerBox prevSpouse = innerBoxes.get(curIdx);
while (childCenterDiff > 0)
{
//move to next spouse
++curIdx;
//quit if we've past the last spouse
if (curIdx >= innerBoxes.size())
break;
//skip spouses with no children
if ( innerBoxes.get(curIdx).children.size() == 0)
continue;
//if the spouse has children, then check to see if this spouses children are
// centered below the spouse and that the previous spouse's (with
// children) children's lower edge is below this spouses center
firstChild = innerBoxes.get(curIdx).children.get(0);
lastChild = innerBoxes.get(curIdx).children.get(innerBoxes.get(curIdx).children.size()-1);
childCenterDiff = relChildVertOffset - (firstChild.getRelSiblingVertOffset() + lastChild.getRelSiblingVertOffset())/2.0 - innerBoxes.get(curIdx).pieceOffset;
DescBoxParent prevLastChild = prevSpouse.children.get(prevSpouse.children.size()-1);
if (relChildVertOffset -prevLastChild.getRelSiblingVertOffset() + prevLastChild.getBoxHeight()/2.0 <= innerBoxes.get(curIdx).pieceOffset)
break;
prevSpouse = innerBoxes.get(curIdx);
}
//set the children connecting line offsets for all of the spouses that overlap
int k = 0;
for (int j = curIdx - 1 ; j >= firstPosIdx; --j)
{
if (innerBoxes.get(j).children.size() > 0)
++k;
innerBoxes.get(j).spouseChildOffset = k;
}
if (k > maxOffset)
maxOffset = k;
//set the piece counter(i) to the next piece(spouse)
i = curIdx - 1;
}
}
else
curSpouse.spouseChildOffset = 0;
}
}
maxSpouseChildOffset = maxOffset;
if (maxOffset > maxSpouseLineOffset[curGen])
maxSpouseLineOffset[curGen] = maxOffset;
//
//calculate first element of upper bounds array
getUpperBounds().add(new Double(getBoxHeight()/2.0));
//calculate first element of lower bounds array
getLowerBounds().add(new Double( -(getBoxHeight()/2.0 + vertSeperation) ));
if (curGen < maxGen)
{
//calculate rest of upper bounds array
int curSubTreeMaxGen = 0;
for (int i = 0; i < children.size(); ++i)
{
DescBoxParent curChild = children.get(i);
if (curChild != null && curChild.maxGensInTree > curSubTreeMaxGen)
{
for (int j = curSubTreeMaxGen; j < curChild.maxGensInTree && curChild != null; ++j)
{
getUpperBounds().add(relChildVertOffset - curChild.getRelSiblingVertOffset() + (curChild.getUpperBounds() == null ? 0 : curChild.getUpperBounds().get(j)));
}
curSubTreeMaxGen = curChild.maxGensInTree;
}
}
//calculate rest of lower bounds array
curSubTreeMaxGen = 0;
for (int i = children.size()-1; i >= 0; --i)
{
DescBoxParent curChild = children.get(i);
if (curChild.maxGensInTree > curSubTreeMaxGen && curChild != null)
{
for (int j = curSubTreeMaxGen; j < curChild.maxGensInTree; ++j)
{
getLowerBounds().add(relChildVertOffset - curChild.getRelSiblingVertOffset() + (curChild.getLowerBounds() == null ? 0 : curChild.getLowerBounds().get(j)));
}
curSubTreeMaxGen = curChild.maxGensInTree;
}
}
}
}
//new drawing code
@Override
public void drawDescRootTree(ChartMargins chart, PresetChartOptions options, ArrayList<ArrayList<DescBoxParent>> genPositions, double x, double y, OpgSession session)
{
fillGaps(genPositions, 0, options.getDescGens(), options);
//start recursion on self
int genToStart = options.isIncludeSpouses() ? 1 : 0;
if (options.getDescGens() > 0)
drawDescTreeRec(genToStart, chart, options, x, y, session);
}
@Override
public void drawDescTreeRec(int curGen, ChartMargins chart, PresetChartOptions options, double x, double y, OpgSession session)
{
/* this method recursively draws a container onto a chart
* x,y - origin position where container should be drawn (origin is the right middle of the Individual's box)
* curGen - the current generation number from the root
* maxGen - the maximum number of generations to draw
* chart - the ChartDrawInfo object into which the chart DrawCommands will be placed
* printArray - an ArrayList of Print objects that are to be used in drawing each corresponding generation
* c - the Container to be drawn
*/
//public void drawContainer(double x, double y, int curGen, int maxGen, ChartDrawInfo chart, ArrayList<Print> printArray, Container c, float upperColor, float lowerColor, double heightScalar, double widthScalar) throws IOException
{
//calculate current box width
//c.boxWidth = getpresetWidth(widthScalar);
baseXSepValue = styleBox.getRelativeOffset();//(float)(c.boxWidth * 0.1);
//xsepvalue = baseXSepValue*((xSepScalar + 1)/2.0);//xsepvalue = baseXSepValue + baseXSepValue*((xSepScalar + 1)/2);
xsepvalue = baseXSepValue + baseXSepValue*((xSepScalar + 1)/2);
//draw individual box for container
//calculate lower? left corner
//double boxX = x - boxWidth;
//double boxY = y - (boxHeight/2.0);
//draw outer box for box with multiple spouse inner boxes
if (innerBoxes.size() > 1)
{
chart.addDrawCommand(new DrawCmdMoveTo(chart.xOffset(x),chart.yOffset( y + (((upperRelBoxBound - lowerRelBoxBound)*scaler-(upperRelBoxBound - lowerRelBoxBound))/2.0) - (upperRelBoxBound - lowerRelBoxBound)/2.0)));
if (options.isRoundedCorners())
{
chart.addDrawCommand(new DrawCmdRelRoundRect(styleBox.getBoxWidth(), (upperRelBoxBound - lowerRelBoxBound)*scaler, styleBox.borderlineWidth, styleBox.cornerCurve, Color.black, Color.white, boxInfo));
}
else
chart.addDrawCommand(new DrawCmdRelFillRect(styleBox.getBoxWidth(), (upperRelBoxBound - lowerRelBoxBound)*scaler, styleBox.borderlineWidth, Color.black, Color.white, boxInfo));
//chart.addDrawCommand(new DrawCmdRelFillRect(presetWidth, boxHeight, 1, Color.black, Color.white));
}
//draw "pieces" inside container
for (int i = 0; i < innerBoxes.size(); ++i)
{
DescInnerBox curPiece = innerBoxes.get(i);
boolean oneSpouse = curPiece.husband == null || curPiece.wife == null;
if (single )
{
//single individual
drawSingle(chart, options, chart.xOffset(x), chart.yOffset(y), styleBox.getBoxWidth(), (upperRelBoxBound - lowerRelBoxBound), curPiece, false, session);
//drawSingle(x, y, boxWidth, yXForm(boxHeight),chart, options, curPiece);
}
else
{
//married or has children
//drawMarriage(x,y,curGen, maxGen, chart, printArray, c, i, (MarriageContainerPiece)curPiece, Print.floatToColor(tempColorPos) );
//drawSingle(x, y, boxWidth, yXForm(boxHeight),chart, options, curPiece);
double pieceOffsetAbsolute = 0;
if (innerBoxes.size() == 1)
{
if (oneSpouse)
drawSingle(chart, options, chart.xOffset(x), chart.yOffset(y), styleBox.getBoxWidth(), (upperRelBoxBound - lowerRelBoxBound), curPiece, false, session);
else
drawMarriageBox(chart, options, chart.xOffset(x), chart.yOffset(y), styleBox.getBoxWidth(), (getBoxHeight()), curPiece, true, session);
}
else
{
if(oneSpouse){
System.out.println(curPiece.husband + " , " + curPiece.wife);
System.out.println("null spouse");
drawSingle(chart, options, chart.xOffset(x), chart.yOffset(y), styleBox.getBoxWidth(), (upperRelBoxBound - lowerRelBoxBound), curPiece, true, session);
}
else{
double height = (upperRelBoxBound - lowerRelBoxBound);
//double innerBoxHeight = height / innerBoxes.size();
double childCenter = 0;
if (curPiece.children.size() > 0){
DescBoxParent firstChild = curPiece.children.get(0);
DescBoxParent lastChild = curPiece.children.get(curPiece.children.size()-1);
childCenter = (firstChild.getRelSiblingVertOffset() + lastChild.getRelSiblingVertOffset())/2.0;
}
//pieceOffsetAbsolute = (height/2.0) - innerBoxHeight/2.0 - (i * innerBoxHeight);
double innerBoxYPos = y + curPiece.pieceOffset;
drawInnerMarriageBox(chart, options, chart.xOffset(x), chart.yOffset(innerBoxYPos), styleBox.getBoxWidth(), curPiece.innerBoxHeight, curPiece, session);
}
}
if (curGen < options.getDescGens())
{
int numChildren = curPiece.children.size();
Color lineColor = (curPiece.spouseChildOffset % 2 == 1)? Color.black : Color.red ;
if (numChildren > 0)
{
DescBoxParent firstChild = curPiece.children.get(0);
DescBoxParent lastChild = curPiece.children.get(curPiece.children.size()-1);
double childCenter = (firstChild.getRelSiblingVertOffset() + lastChild.getRelSiblingVertOffset())/2.0;
//print lines to children (the ones shared by all children of this marriage
if (innerBoxes.size() > 1)
{
chart.addDrawCommand(new DrawCmdMoveTo(chart.xOffset(x - xsepvalue + baseXSepValue/2.0), chart.yOffset(y + (relChildVertOffset - curPiece.children.get(0).getRelSiblingVertOffset())*scaler )) );
chart.addDrawCommand(new DrawCmdRelLineTo(0, -(curPiece.children.get(curPiece.children.size()-1).getRelSiblingVertOffset() - curPiece.children.get(0).getRelSiblingVertOffset())* scaler, 1, lineColor));
double lineSegmentWidth = (xsepvalue - baseXSepValue/2.0)/(maxSpouseChildOffset + 1);
chart.addDrawCommand(new DrawCmdMoveTo(chart.xOffset(x + framesepvalue), chart.yOffset(y + curPiece.pieceOffset)));
chart.addDrawCommand(new DrawCmdRelLineTo(-(lineSegmentWidth * (maxSpouseChildOffset + 1 - curPiece.spouseChildOffset)) - framesepvalue, 0, 1, lineColor));
chart.addDrawCommand(new DrawCmdRelLineTo(0, (relChildVertOffset - childCenter)*scaler - curPiece.pieceOffset, 1, lineColor));
chart.addDrawCommand(new DrawCmdRelLineTo(-lineSegmentWidth * curPiece.spouseChildOffset, 0, 1, lineColor));
}
else
{
lineColor = Color.black;
chart.addDrawCommand(new DrawCmdMoveTo(chart.xOffset(x - xsepvalue + baseXSepValue/2.0), chart.yOffset( (y + relChildVertOffset*scaler) )));
chart.addDrawCommand(new DrawCmdRelLineTo(0, -curPiece.children.get(curPiece.children.size()-1).getRelSiblingVertOffset() * scaler, 1, Color.black));
chart.addDrawCommand(new DrawCmdMoveTo(chart.xOffset(x), chart.yOffset(y)) );
chart.addDrawCommand(new DrawCmdRelLineTo(-xsepvalue + baseXSepValue/2.0 , 0, 1, Color.black));
}
}
//Draws connection lines and recursively calls again
for (DescBoxParent curChild: curPiece.children)
{
//TODO:print line to child
chart.addDrawCommand(new DrawCmdMoveTo(chart.xOffset(x - xsepvalue), chart.yOffset((y + (relChildVertOffset - curChild.getRelSiblingVertOffset())* scaler))) );
chart.addDrawCommand(new DrawCmdRelLineTo(baseXSepValue/2.0, 0, 1, lineColor));
//print child container on chart (recursive)
curChild.drawDescTreeRec(curGen+1, chart, options, x - curChild.styleBox.getBoxWidth() - xsepvalue, y + yXForm(relChildVertOffset - curChild.getRelSiblingVertOffset()), session);
//curChild.drawContainers(x - c.boxWidth - xsepvalue, y + c.relChildVertOffset - curPiece.children.get(j).relSiblingVertOffset, curGen+1, maxGen, chart, printArray, curColorPos, curColorPos + childColorGap, heightScalar, widthScalar);
}
}
}
}//end pieces loop
}
}
private void drawInnerMarriageBox(ChartMargins chart,
PresetChartOptions options, double x, double y,
double width, double height, DescInnerBox innerBox, OpgSession session) {
drawMarriageBox(chart, options, x+2, y, width-4, height-4, innerBox, false, session);
}
private void drawMarriageBox(ChartMargins chart,
PresetChartOptions options, double x, double y,
double width, double height, DescInnerBox innerBox, boolean drawBorder, OpgSession session) {
Individual wife = innerBox.wife;
Individual husband = innerBox.husband;
BoxLayout b = new BoxLayout();
styleBox.layout.convertToSpecificLayout(b);
b.drawDescMarried(chart, styleBox, options, x, y, width, height,
getIndi(), husband, wife, drawBorder, maleDescendant,
innerBoxes.size() > 1?null:boxInfo, innerBoxes.indexOf(innerBox), session);
}
protected void drawSingle(ChartMargins chart,
PresetChartOptions options, double x, double y,
double width, double height, DescInnerBox piece, boolean innerBox, OpgSession session) {
//pick out the person we're drawing
Individual indi = (maleDescendant)? piece.husband : piece.wife;
Individual spouse = (!maleDescendant)? piece.husband : piece.wife;
if(indi==null && spouse != null)
indi = spouse;
height = getBoxHeight();
double X = innerBox ? x+2 : x;
double WIDTH = innerBox ? width - 4 : width;
BoxLayout b = new BoxLayout();
styleBox.layout.convertToSpecificLayout(b);
b.drawDescSingle(chart, styleBox, options, X, y, WIDTH, height, indi, innerBoxes.size() > 1?null:boxInfo, session);
}
@Override
public DescBoxParent addSpecificBox(Individual indi){return new DescBox(indi);}
}