Some work done on charts

This commit is contained in:
Ziver Koc 2018-03-01 15:59:08 +01:00
parent 3df20257b0
commit eb30472a76
5 changed files with 221 additions and 182 deletions

View file

@ -33,7 +33,6 @@ import java.util.logging.Logger;
public abstract class AbstractChart extends JPanel{ public abstract class AbstractChart extends JPanel{
private static final Logger logger = LogUtil.getLogger(); private static final Logger logger = LogUtil.getLogger();
private static final long serialVersionUID = 1L;
/** The offset from the borders of the panel in pixels */ /** The offset from the borders of the panel in pixels */
public static final int PADDING = 20; public static final int PADDING = 20;
@ -46,72 +45,24 @@ public abstract class AbstractChart extends JPanel{
protected void paintComponent(Graphics g){ protected void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D)g; Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
Rectangle bound = drawScale( g2 ); Rectangle bound = drawAxis( g2, new Rectangle(0, 0, getWidth(), getHeight()));
drawChart( g2, bound ); drawChart( g2, bound );
} }
protected Rectangle drawScale(Graphics2D g2){
if( data == null )
return null;
// update values /**
width = this.getWidth(); * This method will draw the axis of the chart
height = this.getHeight(); *
Rectangle bound = new Rectangle(); * @param g2 is the Graphics object that will paint the chart
* @param bound is the bounds of the axis, the drawing should not exceed this bound
// Values */
int stepLength = 7; protected abstract Rectangle drawAxis(Graphics2D g2, Rectangle bound);
/////// Temp values
// Calculate Font sizes
FontMetrics metric = g2.getFontMetrics();
int fontHeight = metric.getHeight();
int fontXWidth = 0;
int fontYWidth = 0;
for( Point p : data.getPoints() ){
int length = 0;
String tmp = data.getXString( p.x );
if( tmp != null ) length = metric.stringWidth( tmp );
else length = metric.stringWidth( ""+p.x );
fontXWidth = Math.max(length, fontXWidth);
tmp = data.getXString( p.y );
if( tmp != null ) length = metric.stringWidth( tmp );
else length = metric.stringWidth( ""+p.y );
fontYWidth = Math.max(length, fontYWidth);
}
// Calculate origo
Point origo = new Point( PADDING+fontYWidth+stepLength, height-PADDING-fontHeight-stepLength );
bound.x = (int) (origo.getX()+1);
bound.y = PADDING;
bound.width = width-bound.x-PADDING;
bound.height = (int) (origo.getY()-PADDING-1);
// Calculate Axis scales
double xScale = (double)(Math.abs(data.getMaxX())+Math.abs(data.getMinX()))/bound.width;
double yScale = (double)(Math.abs(data.getMaxY())+Math.abs(data.getMinY()))/bound.height;
/////// Draw
// Y Axis
g2.draw( new Line2D.Double( origo.getX(), PADDING, origo.getX(), origo.getY() ));
// X Axis
g2.draw( new Line2D.Double( origo.getX(), origo.getY(), width-PADDING, origo.getY() ));
// Y Axis steps and labels
g2.draw( new Line2D.Double( origo.getX(), origo.getY(), origo.getX()-stepLength, origo.getY() ));
g2.draw( new Line2D.Double( origo.getX(), PADDING, origo.getX()-stepLength, PADDING ));
// X Axis steps and labels
g2.draw( new Line2D.Double( width-PADDING, origo.getY(), width-PADDING, origo.getY()+stepLength ));
// DEBUG
/*
g2.setColor(Color.red);
g2.drawRect(bound.x, bound.y, bound.width, bound.height);
*/
return bound;
}
/** /**
* This method is called after the chart scale has been drawn. * This method is called after the chart scale has been drawn.
@ -133,22 +84,34 @@ public abstract class AbstractChart extends JPanel{
} }
/** /**
* Converts a x value to ax pixel coordinate * Converts a x value to a x pixel coordinate
* *
* @param x is the x data value * @param x is the x data value
* @return pixel coordinate, or 0 if the chart have not been drawn yet. * @param scale is the data scale
* @param bound is the drawing boundds
* @return a x pixel coordinate
*/ */
protected int getXCoordinate(int x){ static protected double getXCoordinate(double x, double scale, Rectangle bound){
return 0; return bound.x + x * scale;
} }
/** /**
* Converts a y value to a y pixel coordinate * Converts a y value to a y pixel coordinate
* *
* @param y is the y data value * @param y is the y data value
* @return pixel coordinate, or 0 if the chart have not been drawn yet. * @param scale is the data scale
* @param bound is the drawing boundds
* @return a y pixel coordinate
*/ */
protected int getYCoordinate(int y){ static protected double getYCoordinate(double y, double scale, Rectangle bound){
return 0; return bound.y + bound.height - ( y * scale );
}
static protected double getXScale(ChartData data, Rectangle bound){
return (double) bound.width / (Math.abs(data.getMaxX()) + Math.abs(data.getMinX()));
}
static protected double getYScale(ChartData data, Rectangle bound){
return (double) bound.height / (Math.abs(data.getMaxY()) + Math.abs(data.getMinY()));
} }
} }

View file

@ -31,76 +31,76 @@ import java.util.List;
public class ChartData { public class ChartData {
private HashMap<Integer,String> xStrings; private HashMap<Integer,String> xStrings;
private HashMap<Integer,String> yStrings; private HashMap<Integer,String> yStrings;
private int maxx; private int maxX;
private int minx; private int minX;
private int maxy; private int maxY;
private int miny; private int minY;
private ArrayList<Point> points; private ArrayList<Point> points;
public ChartData(){ public ChartData(){
xStrings = new HashMap<Integer,String>(); xStrings = new HashMap<Integer,String>();
yStrings = new HashMap<Integer,String>(); yStrings = new HashMap<Integer,String>();
points = new ArrayList<Point>(); points = new ArrayList<Point>();
} }
public void setXValueString(int x, String name){ public void setXValueString(int x, String name){
xStrings.put(x, name); xStrings.put(x, name);
} }
public void setYValueString(int y, String name){ public void setYValueString(int y, String name){
yStrings.put(y, name); yStrings.put(y, name);
} }
public void addPoint(int x, int y){ public void addPoint(int x, int y){
points.add( new Point( x, y)); points.add( new Point( x, y));
setMaxMin(x, y); setMaxMin(x, y);
} }
public void addPoint(int y){ public void addPoint(int y){
points.add( new Point( maxx, y)); points.add( new Point(maxX, y));
maxx++; maxX++;
setMaxMin(maxx, y); setMaxMin(maxX, y);
} }
public void addPoint(String x, int y){ public void addPoint(String x, int y){
xStrings.put(maxx, x); xStrings.put(maxX, x);
points.add( new Point( maxx, y)); points.add( new Point(maxX, y));
maxx++; maxX++;
setMaxMin(maxx, y); setMaxMin(maxX, y);
} }
private void setMaxMin(int x, int y){ private void setMaxMin(int x, int y){
if( x > maxx ) maxx = x; if( x > maxX) maxX = x;
else if( x < minx ) minx = x; if( x < minX) minX = x;
if( y > maxy ) maxx = y; if( y > maxY) maxY = y;
else if( y < miny ) minx = y; if( y < minY) minY = y;
} }
public int getMaxX(){ public int getMaxX(){
return maxx; return maxX;
} }
public int getMinX(){ public int getMinX(){
return minx; return minX;
} }
public int getMaxY(){ public int getMaxY(){
return maxy; return maxY;
} }
public int getMinY(){ public int getMinY(){
return miny; return minY;
} }
public String getXString(int x){ public String getXString(int x){
return xStrings.get(x); return xStrings.get(x);
} }
public String getYString(int y){ public String getYString(int y){
return yStrings.get(y); return yStrings.get(y);
} }
protected List<Point> getPoints(){ protected List<Point> getPoints(){
return points; return points;
} }
} }

75
src/zutil/chart/LineAxis.java Executable file
View file

@ -0,0 +1,75 @@
package zutil.chart;
import java.awt.*;
import java.awt.geom.Line2D;
/**
*
*/
public abstract class LineAxis extends AbstractChart{
@Override
protected Rectangle drawAxis(Graphics2D g2, Rectangle bound){
if( data == null )
return null;
width = bound.width;
height = bound.height;
chartBound = new Rectangle();
int stepLength = 7;
// **********************************
// Calculate Font sizes
// **********************************
FontMetrics metric = g2.getFontMetrics();
int fontHeight = metric.getHeight();
int fontXWidth = 0;
int fontYWidth = 0;
for( Point p : data.getPoints() ){
int length;
String tmp = data.getXString( p.x );
if( tmp != null ) length = metric.stringWidth( tmp );
else length = metric.stringWidth( ""+p.x );
fontXWidth = Math.max(length, fontXWidth);
tmp = data.getXString( p.y );
if( tmp != null ) length = metric.stringWidth( tmp );
else length = metric.stringWidth( ""+p.y );
fontYWidth = Math.max(length, fontYWidth);
}
// Calculate origo
Point origo = new Point(
PADDING + fontYWidth + stepLength,
height - PADDING - fontHeight - stepLength );
chartBound.x = (int) (origo.getX() + 1);
chartBound.y = PADDING;
chartBound.width = width - chartBound.x - PADDING;
chartBound.height = (int) (origo.getY() - PADDING - 1);
// **********************************
// Draw
// **********************************
g2.setColor(Color.LIGHT_GRAY);
g2.setStroke(new BasicStroke(1.5f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
// Y Axis
g2.draw( new Line2D.Double( origo.getX(), PADDING, origo.getX(), origo.getY() ));
// X Axis
g2.draw( new Line2D.Double( origo.getX(), origo.getY(), width-PADDING, origo.getY() ));
// Y Axis steps and labels
// X Axis steps and labels
// DEBUG
/*
g2.setColor(Color.red);
g2.drawRect(bound.x, bound.y, bound.width, bound.height);
*/
return chartBound;
}
}

View file

@ -27,36 +27,36 @@ package zutil.chart;
import java.awt.*; import java.awt.*;
import java.awt.geom.Line2D; import java.awt.geom.Line2D;
public class LineChart extends AbstractChart{ public class LineChart extends LineAxis{
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
protected void drawChart(Graphics2D g2, Rectangle bound) { protected void drawChart(Graphics2D g2, Rectangle bound) {
// TODO Auto-generated method stub g2.setPaint(new Color(237,28,36));
drawLine(g2, 50, 50,100,150); g2.setStroke(new BasicStroke(2.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
drawLine(g2, 100,150,150,100);
drawLine(g2, 150,100,200,300);
drawLine(g2, 200,300,250,40);
}
private void drawLine(Graphics2D g2, float x1, float y1, float x2, float y2){ // Calculate position
// Shadow
g2.setPaint(new Color(220,220,220));
g2.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.draw( new Line2D.Float(x1, y1+2, x2, y2+2));
// Smoth shadow
g2.setPaint(new Color(230,230,230));
g2.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.draw( new Line2D.Float(x1+1, y1+3, x2-1, y2+3));
// Line border double xScale = getXScale(data, bound);
g2.setPaint(new Color(255,187,187)); double yScale = getYScale(data, bound);
g2.setStroke(new BasicStroke(4.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.draw( new Line2D.Float(x1, y1, x2, y2));
// Line // Draw lines
g2.setPaint(new Color(237,28,36));
g2.setStroke(new BasicStroke(2.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); Point prevP = null;
g2.draw( new Line2D.Float(x1, y1, x2, y2)); for(Point p : data.getPoints()){
} if (prevP != null)
drawLine(g2, bound, xScale, yScale, prevP.x, prevP.y, p.x, p.y);
prevP = p;
}
}
private void drawLine(Graphics2D g2, Rectangle bound, double xScale, double yScale,
double x1, double y1, double x2, double y2){
// Line
g2.draw(new Line2D.Double(
getXCoordinate(x1, xScale, bound),
getYCoordinate(y1, yScale, bound),
getXCoordinate(x2, xScale, bound),
getYCoordinate(y2, yScale, bound)));
}
} }

View file

@ -41,21 +41,22 @@ public class ChartTest extends JFrame{
public ChartTest(){ public ChartTest(){
ChartData data = new ChartData(); ChartData data = new ChartData();
data.addPoint(0,0);
data.addPoint(1,1); data.addPoint(1,1);
data.addPoint(2,1); data.addPoint(2,2);
data.addPoint(3,1); data.addPoint(3,3);
data.addPoint(4,1); data.addPoint(4,4);
data.addPoint(5,1); data.addPoint(5,5);
data.addPoint(6,1); data.addPoint(6,6);
data.addPoint(7,1); data.addPoint(7,7);
data.addPoint(8,1); data.addPoint(8,8);
LineChart chart = new LineChart(); LineChart chart = new LineChart();
chart.setChartData( data ); chart.setChartData( data );
this.add( chart ); add( chart );
this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
this.setSize(600, 400); setSize(600, 400);
} }
} }