package edu.byu.cs.roots.opg.io;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.apache.fop.svg.PDFDocumentGraphics2D;
import org.apache.log4j.Logger;
import edu.byu.cs.roots.opg.chart.ChartConversion;
import edu.byu.cs.roots.opg.chart.ChartDrawInfo;
import edu.byu.cs.roots.opg.chart.cmds.DrawCommand;
import edu.byu.cs.roots.opg.chart.cmds.DrawState;
import edu.byu.cs.roots.opg.model.OpgPage;
import edu.byu.cs.roots.opg.model.OpgSession;
public class AffineOnScreenChartWriter implements ChartWriter {
public static Logger log = Logger.getLogger(AffineOnScreenChartWriter.class);
//the position of the upper left corner of the page in relation to the upperleft corner of the panel
// private int xOffset, yOffset;
// private double scaleFactor;
// private boolean firstTime = true;
// private boolean inDrag = false;
// private int dragStartX, dragStartY, dragStartXOffset, dragStartYOffset;
private DrawState state = new DrawState();
//private double fontSize;
//private Font font;
public void createChart(ChartDrawInfo chartInfo, String fileName) throws IOException {
}
private static final double rulerWidth = 72; // In points for paper
private static final double specialRulerWidth = 20; // In pixels for screen
private static final Color darkShade3D = new Color(100, 100, 100);
private static final Color normalColor3D = new Color(180, 180, 180);
private static final Color lightShade3D = new Color(230, 230, 230);
private static final Color rulerBackground = Color.WHITE;
private static final Color rulerFontColor = Color.RED;
private static final Color rulerMarkColor = Color.BLACK;
/**
* Binary Search that quickly (logn) will get which commands should be removed from the lists of
* draw commands because they are not in the viewing space.
* @param cutOff - The X or Y that should be the last thing to include
* @param dir - Which direction we are cutting off from
* @param SortedCmds - The list of commands to chop.
* @return A list containting the elements of the original list that should be removed.
*/
/*private List<DrawCommand> getElementsToRemove(double cutOff, SortDirection dir, List<DrawCommand> SortedCmds)
{
int lowerbound = 0;
int upperbound = SortedCmds.size();
ArrayList<DrawCommand> retValue = new ArrayList<DrawCommand>(SortedCmds);
//System.err.println(retValue.get(0).toString());
while (lowerbound != upperbound)
{
int index = (lowerbound + upperbound) / 2;
Rectangle spBox = retValue.get(index).getShapeBox();
double curPos = 0;
switch(dir)
{
case BOTTOM:
curPos = spBox.y;
break;
case LEFT:
curPos = spBox.x + spBox.width;
break;
case RIGHT:
curPos = spBox.x;
break;
case TOP:
curPos = spBox.y + spBox.height;
break;
}
if (curPos >= cutOff)
{
switch(dir)
{
case BOTTOM:
upperbound = index;
break;
case LEFT:
upperbound = index;
break;
case RIGHT:
upperbound = index;
break;
case TOP:
lowerbound = index;
break;
}
}
else
{
switch(dir)
{
case BOTTOM:
lowerbound = index;
break;
case LEFT:
lowerbound = index;
break;
case RIGHT:
lowerbound = index;
break;
case TOP:
upperbound = index;
break;
}
}
if (Math.abs(upperbound - lowerbound) <= 1)
{
if (dir == SortDirection.BOTTOM || dir == SortDirection.LEFT)
{
upperbound = lowerbound;
}
else
{
lowerbound = upperbound;
}
}
}
return retValue.subList((dir == SortDirection.BOTTOM || dir == SortDirection.LEFT) ? 0 : lowerbound,
(dir == SortDirection.BOTTOM || dir == SortDirection.LEFT) ? upperbound : SortedCmds.size());
}
*/
public void createChart(OpgPage page, Graphics2D g, int width, int height, double zoom, int xOffset, int yOffset, Graphics screenG, boolean ruler, OpgSession session) {
ArrayList<ChartDrawInfo> charts = session.getCharts();
if(charts == null || charts.size() == 0 || charts.get(0) == null){ return; }
state.reset();
state.chartLeftToDisplay = xOffset;
state.chartTopToDisplay = yOffset;
state.session = session;
double screenRulerHeight = ChartConversion.convertToScreenSize(rulerWidth, zoom);
//turn on fractional font metrics (eliminate font rounding) and antialiasing
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//This part is where the back gets printed white
double chartHeight = 0;
for (ChartDrawInfo chart : charts){
chartHeight += chart.getYExtent();
}
double chartWidth = charts.get(0).getXExtent();
boolean leftRuler = false;
boolean topRuler = false;
g.setColor(Color.white);
Point upperLeft = ChartConversion.convertToScreenCoord(new Point(0,0), zoom, state);
Point lowerRight = ChartConversion.convertToScreenCoord(new Point((int)chartWidth, (int)chartHeight), zoom, state);
double chartLeft = 0;
double chartTop = 0;
if (upperLeft.y < screenRulerHeight)
{
if (upperLeft.y < 0)
{
chartTop = ChartConversion.convertDoubleToChartDouble(0, zoom, yOffset);
chartHeight -= chartTop;
}
topRuler = true;
}
if (lowerRight.y > height)
{
chartHeight = ChartConversion.convertToChartSize(height, zoom) + 10;
}
if (upperLeft.x < screenRulerHeight)
{
if (upperLeft.x < 0)
{
chartLeft = ChartConversion.convertDoubleToChartDouble(0, zoom, xOffset);
chartWidth -= chartLeft;
}
leftRuler = true;
}
if (lowerRight.x > width)
{
chartWidth = ChartConversion.convertToChartSize(width, zoom) + 10;
}
g.fillRect((int)chartLeft, (int)chartTop, (int)chartWidth, (int)chartHeight);
double chartHeightIncrease = 0;
for (ChartDrawInfo chartInfo : charts){
//g.translate(0d, chartInfo.getYOffset());
state.xExtent = chartInfo.getXExtent();
state.yExtent = chartInfo.getYExtent();
//if (chartInfo.getUpSortedDrawCmds() == null)
// chartInfo.updateSortedCharts();
List<DrawCommand> sortCmds = new LinkedList<DrawCommand>(chartInfo.getDrawCommands());//new LinkedList<DrawCommand>(chartInfo.getUpSortedDrawCmds());
// Sort the commands only if the chart changed
//sortCmds.removeAll(getElementsToRemove(-xOffset, SortDirection.LEFT, chartInfo.getLeftSortedDrawCmds()));
//sortCmds.removeAll(getElementsToRemove(-xOffset + width + 117, SortDirection.RIGHT, chartInfo.getRightSortedDrawCmds()));
//sortCmds.removeAll(getElementsToRemove(yOffset, SortDirection.TOP, chartInfo.getUpSortedDrawCmds()));
//sortCmds.removeAll(getElementsToRemove(yOffset + height, SortDirection.BOTTOM, chartInfo.getDownSortedDrawCmds()));
// Sort the drawing commands to be the last things written
//sortCmds = chartInfo.sortTextCmds(sortCmds);
// Execute the commands for drawing
boolean useAbsolute = false;
for(DrawCommand cmd : (useAbsolute ? sortCmds : chartInfo.getDrawCommands())){
if (useAbsolute)
cmd.executeAbsolute(g, state, width, height, zoom);
else
cmd.execute(g, state, width, height, zoom, new Point(0, (int)chartHeightIncrease));
}
chartHeightIncrease+=chartInfo.getYExtent();
g.translate(0d, chartInfo.getYExtent());
}
// Draw the rulers
if (ruler)
{
if (!topRuler)
{
double rulerWidth = 36 * 72;
//rulerWidth = chartInfo.getXExtent();
/* Draw the normal top ruler*/
g.setColor(new Color(217, 180,94));
g.fill3DRect(0, -80-(int)(page.getPageHeight()), (int)rulerWidth, 72, true);
//System.out.println("Draw Normal Top Ruler");
double currentXPos = 2;
int startRuler = 0;
int divider = 8;
double minMarkSpacing = 72.0 / divider;
while (currentXPos < rulerWidth - 4)
{
int initialHeight = 24;
int markLength = 14;
int temp = 2;
// Inch mark
while (startRuler % temp == 0 && temp <= divider)
{
// 8
markLength = initialHeight;
initialHeight += 6;
temp *= 2;
}
// Draw the mark
g.setColor(rulerMarkColor);
g.drawLine((int)currentXPos, -10-(int)(page.getPageHeight()), (int)currentXPos, -8 - (3 + markLength)-(int)(page.getPageHeight()));
if (startRuler % divider == 0 && startRuler != 0)
{
g.setColor(rulerMarkColor);
// Write inch mark number
g.setFont(new Font("Times New Roman",0, 22 ));
g.drawString("" + startRuler / divider, (int)currentXPos - 5, -60-(int)(page.getPageHeight()));
g.setColor(rulerMarkColor);
}
startRuler++;
if (minMarkSpacing > 0)
currentXPos += minMarkSpacing;
else
currentXPos += 1;
}
}
if (leftRuler)
{
/* Draw the special left ruler*/
double rulerChartWidth = specialRulerWidth + 2;
Point rulerStart = new Point(0,0);
double rulerChartLength = width;
screenG.setColor(darkShade3D);
screenG.fillRect(rulerStart.x, rulerStart.y, (int)Math.ceil(rulerChartWidth), (int)Math.ceil(rulerChartLength) + 2);
screenG.setColor(normalColor3D);
screenG.fillRect(rulerStart.x, rulerStart.y, (int)Math.ceil(rulerChartWidth)-2, (int)Math.ceil(rulerChartLength));
screenG.setColor(lightShade3D);
screenG.fillRect(rulerStart.x, rulerStart.y, (int)Math.ceil(rulerChartWidth) - 2, 1);
//System.out.println("Draw Special Left Ruler");
//System.out.println("Draw Special Top Ruler");
screenG.setColor(darkShade3D);
screenG.fillRect(0, 0, (int)specialRulerWidth+2, (int)specialRulerWidth+2);
screenG.setColor(normalColor3D);
screenG.fillRect(0, 0, (int)specialRulerWidth, (int)specialRulerWidth);
screenG.setColor(lightShade3D);
screenG.fillRect(0, 0, (int)specialRulerWidth, 1);
screenG.fillRect(0, 0, 1, (int)specialRulerWidth);
// Draw the background for the ruler
screenG.setColor(rulerBackground);
screenG.fillRect(2, (int)specialRulerWidth + 4, (int)specialRulerWidth - 4, height - (int)specialRulerWidth - 6);
screenG.setColor(darkShade3D);
screenG.drawLine(2, (int)specialRulerWidth + 4, 2, height - 4);
screenG.drawLine(2, (int)specialRulerWidth + 4, (int)specialRulerWidth - 3, (int)specialRulerWidth + 4);
screenG.setColor(lightShade3D);
screenG.drawLine((int)specialRulerWidth - 3, (int)specialRulerWidth + 4, (int)specialRulerWidth - 3, height - 4);
screenG.drawLine(2, height - 4, (int)specialRulerWidth - 3, height - 4);
//System.out.println("Draw Special Left Ruler");
double pointsPerPixel = 1/zoom;
double pixelsPerInch = 72 / pointsPerPixel;
// Draw the lines.
// Find the initial coordinate for a 1/divider of an inch
int startRuler = 0;
int currentYPos = (int)(specialRulerWidth + 4);
double divider = Math.min(Math.pow(2, (int)Math.floor(Math.log(pixelsPerInch)/Math.log(2))), 32);
boolean drawFootMarks = false;
if (divider <= 8.0)
{
drawFootMarks = true;
divider = Math.min(Math.pow(2, (int)Math.floor(Math.log((pixelsPerInch * 12))/Math.log(2))), 32);
}
double markSpacing = ((drawFootMarks ? pixelsPerInch * 12 : pixelsPerInch) / divider);
int marks = 0;
int minHeight = 3;
int maxHeight = 16;
double heightDivisions = (maxHeight - minHeight)/(Math.log(divider)/Math.log(2));
double yEdge = yOffset;
startRuler = -(int)(((yEdge - currentYPos) / (drawFootMarks ? pixelsPerInch * 12 : pixelsPerInch)) * divider);
/*screenG.setColor(Color.BLUE);
int lineMark = yOffset;
screenG.drawLine(0, lineMark, (int)specialRulerWidth, lineMark);*/
currentYPos += -yEdge - (startRuler / divider * (drawFootMarks ? pixelsPerInch * 12 : pixelsPerInch));
screenG.setColor(rulerMarkColor);
while (currentYPos < height - 4)
{
marks++;
int markLength = minHeight;
double curExtension = heightDivisions;
int temp = 2;
// Inch mark
while (startRuler % temp == 0 && temp <= divider)
{
// 8
markLength = (int)curExtension;
curExtension += heightDivisions;
temp *= 2;
}
// Draw the mark
screenG.drawLine((int)specialRulerWidth - 3, currentYPos, (int)specialRulerWidth - (3 + markLength), currentYPos);
if (startRuler % divider == 0)
{
screenG.setColor(rulerFontColor);
// Write inch or foot mark number
screenG.drawString("" + (int)(startRuler / divider) + (drawFootMarks ? "'" : "\""), 4, currentYPos - 2);
screenG.setColor(rulerMarkColor);
}
currentYPos = (int)(specialRulerWidth + 4 + marks * markSpacing);
startRuler++;
}
}
else
{
/* Draw the normal left ruler*/
double rulerHeight = 36 * 72;
//rulerHeight = chartInfo.getYExtent();
g.setColor(new Color(217, 180,94));
g.fill3DRect(-80, 0-(int)(page.getPageHeight()), 72, (int)rulerHeight, true);
//System.out.println("Draw Normal Left Ruler");
double currentYPos = 2;
int startRuler = 0;
int divider = 8;
double minMarkSpacing = 72.0 / divider;
while (currentYPos < rulerHeight - 1)
{
int initialHeight = 24;
int markLength = 14;
int temp = 2;
// Inch mark
while (startRuler % temp == 0 && temp <= divider)
{
// 8
markLength = initialHeight;
initialHeight += 6;
temp *= 2;
}
// Draw the mark
g.setColor(rulerMarkColor);
g.drawLine(-10, (int)currentYPos-(int)(page.getPageHeight()), -8 - (3 + markLength), (int)currentYPos-(int)(page.getPageHeight()));
if (startRuler % divider == 0 && startRuler != 0)
{
g.setColor(rulerMarkColor);
// Write inch mark number
g.setFont(new Font("Times New Roman",0, 22 ));
g.drawString("" + startRuler / divider, -75, (int)currentYPos + 6-(int)(page.getPageHeight()));
g.setColor(rulerMarkColor);
}
startRuler++;
if (minMarkSpacing > 0)
currentYPos += minMarkSpacing;
else
currentYPos += 1;
}
}
if (topRuler)
{
/* Draw the special top ruler*/
double pointsPerPixel = 1/zoom;
double pixelsPerInch = 72 / pointsPerPixel;
double rulerChartLength = specialRulerWidth;
Point rulerStart = new Point(0,0);
double rulerChartWidth = width;
screenG.setColor(darkShade3D);
screenG.fillRect(rulerStart.x, rulerStart.y, (int)Math.ceil(rulerChartWidth), (int)Math.ceil(rulerChartLength) + 2);
screenG.setColor(normalColor3D);
screenG.fillRect(rulerStart.x, rulerStart.y, (int)Math.ceil(rulerChartWidth)-2, (int)Math.ceil(rulerChartLength));
screenG.setColor(lightShade3D);
screenG.fillRect(rulerStart.x, rulerStart.y, (int)Math.ceil(rulerChartWidth) - 2, 1);
screenG.setColor(darkShade3D);
screenG.fillRect(0, 0, (int)specialRulerWidth+2, (int)specialRulerWidth+2);
screenG.setColor(normalColor3D);
screenG.fillRect(0, 0, (int)specialRulerWidth, (int)specialRulerWidth);
screenG.setColor(lightShade3D);
screenG.fillRect(0, 0, (int)specialRulerWidth, 1);
screenG.fillRect(0, 0, 1, (int)specialRulerWidth);
// Draw the background for the ruler
screenG.setColor(rulerBackground);
screenG.fillRect((int)specialRulerWidth + 4, 2, width - (int)specialRulerWidth - 6, (int)specialRulerWidth - 4);
screenG.setColor(darkShade3D);
screenG.drawLine((int)specialRulerWidth + 4, 2, width - 4, 2);
screenG.drawLine((int)specialRulerWidth + 4, 2, (int)specialRulerWidth + 4, (int)specialRulerWidth - 3);
screenG.setColor(lightShade3D);
screenG.drawLine((int)specialRulerWidth + 4, (int)specialRulerWidth - 3, width - 4, (int)specialRulerWidth - 3);
screenG.drawLine(width - 4, 2, width - 4, (int)specialRulerWidth - 3);
// Find the initial coordinate for a 1/divider of an inch
int startRuler = 0;
int currentXPos = (int)(specialRulerWidth + 4);
double divider = Math.min(Math.pow(2, (int)Math.floor(Math.log(pixelsPerInch)/Math.log(2))), 32);
boolean drawFootMarks = false;
if (divider <= 8.0)
{
drawFootMarks = true;
divider = Math.min(Math.pow(2, (int)Math.floor(Math.log((pixelsPerInch * 12))/Math.log(2))), 32);
}
double markSpacing = ((drawFootMarks ? pixelsPerInch * 12 : pixelsPerInch) / divider);
int marks = 0;
int minHeight = 3;
int maxHeight = 16;
double heightDivisions = (maxHeight - minHeight)/(Math.log(divider)/Math.log(2));
double xEdge = xOffset;
startRuler = -(int)Math.floor((xEdge - currentXPos) / (drawFootMarks ? pixelsPerInch * 12 : pixelsPerInch) * divider);
currentXPos += -xEdge - (startRuler / divider * (drawFootMarks ? pixelsPerInch * 12 : pixelsPerInch));
screenG.setColor(rulerMarkColor);
while (currentXPos < width - 4)
{
marks++;
int markLength = minHeight;
double curExtension = heightDivisions;
int temp = 2;
// Inch mark
while (startRuler % temp == 0 && temp <= divider)
{
// 8
markLength = (int)curExtension;
curExtension += heightDivisions;
temp *= 2;
}
// Draw the mark
screenG.drawLine(currentXPos, (int)specialRulerWidth - 3, currentXPos, (int)specialRulerWidth - (3 + markLength));
if (startRuler % divider == 0)
{
screenG.setColor(rulerFontColor);
// Write inch mark number
screenG.drawString("" + (int)(startRuler / divider) + (drawFootMarks ? "'" : "\""), currentXPos + 2, 13);
screenG.setColor(rulerMarkColor);
}
currentXPos = (int)(specialRulerWidth + 4 + marks * markSpacing);
startRuler++;
}
}
}
}
public void createChart(ChartDrawInfo chartInfo, PDFDocumentGraphics2D g){
if(chartInfo == null){ return; }
state.reset();
//paint the page white
g.setColor(Color.WHITE);
g.fillRect(0, 0, (int)(chartInfo.getXExtent()), (int)(chartInfo.getYExtent()) );
state.xExtent = chartInfo.getXExtent();
state.yExtent = chartInfo.getYExtent();
for(DrawCommand cmd : chartInfo.getDrawCommands()){
cmd.execute(g, state);
}
}
public void createChart(ChartDrawInfo chartInfo, Graphics2D g, double xOffset, double yOffset){
if(chartInfo == null){ return; }
state.reset();
g.translate(xOffset, yOffset);
//paint the page white
g.setColor(Color.white);
g.fillRect(0, 0, (int)(chartInfo.getXExtent()), (int)(chartInfo.getYExtent()) );
state.xExtent = chartInfo.getXExtent();
state.yExtent = chartInfo.getYExtent();
for(DrawCommand cmd : chartInfo.getDrawCommands()){
cmd.execute(g, state);
}
g.translate(-xOffset, -yOffset);
}
}