Line Graph in Java(not a jfreechart) - java

I have 2 sets of array x and y read from a .txt file and I need to put it in a line graph.
so far this is my code:
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.awt.Font;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class DrawGraph extends JPanel {
private int width = 800;
private int heigth = 400;
private int padding = 25;
private int labelPadding = 25;
private Color lineColor = new Color(44, 102, 230, 180);
private Color pointColor = new Color(100, 100, 100, 180);
private Color gridColor = new Color(200, 200, 200, 200);
private static final Stroke GRAPH_STROKE = new BasicStroke(2f);
private int pointWidth = 4;
private int numberYDivisions = 10;
private List<Double> scores;
private int xp;
private int yp;
public DrawGraph(List<Double> scores){
this.scores=scores;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
double xScale = ((double) getWidth() - (2 * padding) - labelPadding) / (scores.size() - 1);
double yScale = ((double) getHeight() - 2 * padding - labelPadding) / (getMaxScore() - getMinScore());
List<Point> graphPoints = new ArrayList<>();
for (int i = 0; i < scores.size(); i++) {
int x1 = (int) (i * xScale + padding + labelPadding);
int y1 = (int) ((getMaxScore() - scores.get(i)) * yScale + padding);
graphPoints.add(new Point(x1, y1));
}
// draw white background
g2.setColor(Color.WHITE);
g2.fillRect(padding + labelPadding, padding, getWidth() - (2 * padding) - labelPadding,
getHeight() - 2 * padding - labelPadding);
g2.setColor(Color.BLACK);
// create hatch marks and grid lines for y axis.
for (int i = 0; i < numberYDivisions + 1; i++) {
int x0 = padding + labelPadding;
int x1 = pointWidth + padding + labelPadding;
int y0 = getHeight()
- ((i * (getHeight() - padding * 2 - labelPadding)) / numberYDivisions + padding + labelPadding);
int y1 = y0;
if (scores.size() > 0) {
g2.setColor(gridColor);
g2.drawLine(padding + labelPadding + 1 + pointWidth, y0, getWidth() - padding, y1);
g2.setColor(Color.BLACK);
String yLabel = ((int) ((getMinScore()
+ (getMaxScore() - getMinScore()) * ((i * 1.0) / numberYDivisions)) * 100)) / 100.0 + "";
FontMetrics metrics = g2.getFontMetrics();
int labelWidth = metrics.stringWidth(yLabel);
g2.drawString(yLabel, x0 - labelWidth - 5, y0 + (metrics.getHeight() / 2) - 3);
}
g2.drawLine(x0, y0, x1, y1);
}
// and for x axis
for (int i = 0; i < scores.size(); i++) {
if (scores.size() > 1) {
int x0 = i * (getWidth() - padding * 2 - labelPadding) / (scores.size() - 1) + padding + labelPadding;
int x1 = x0;
int y0 = getHeight() - padding - labelPadding;
int y1 = y0 - pointWidth;
if ((i % ((int) ((scores.size() / 20.0)) + 1)) == 0) {
g2.setColor(gridColor);
g2.drawLine(x0, getHeight() - padding - labelPadding - 1 - pointWidth, x1, padding);
g2.setColor(Color.BLACK);
String xLabel = i + "";
FontMetrics metrics = g2.getFontMetrics();
int labelWidth = metrics.stringWidth(xLabel);
g2.drawString(xLabel, x0 - labelWidth / 2, y0 + metrics.getHeight() + 3);
}
g2.drawLine(x0, y0, x1, y1);
}
}
// create x and y axes
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, padding + labelPadding, padding);
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, getWidth() - padding,
getHeight() - padding - labelPadding);
Stroke oldStroke = g2.getStroke();
g2.setColor(lineColor);
g2.setStroke(GRAPH_STROKE);
for (int i = 0; i < graphPoints.size() - 1; i++) {
int x1 = graphPoints.get(i).x;
int y1 = graphPoints.get(i).y;
int x2 = graphPoints.get(i + 1).x;
int y2 = graphPoints.get(i + 1).y;
g2.drawLine(x1, y1, x2, y2);
}
g2.setStroke(oldStroke);
g2.setColor(pointColor);
for (int i = 0; i < graphPoints.size(); i++) {
int x = graphPoints.get(i).x - pointWidth / 2;
int y = graphPoints.get(i).y - pointWidth / 2;
int ovalW = pointWidth;
int ovalH = pointWidth;
g2.fillOval(x, y, ovalW, ovalH);
}
}
// #Override
// public Dimension getPreferredSize() {
// return new Dimension(width, heigth);
// }
private double getMinScore() {
double minScore = Double.MAX_VALUE;
for (Double score : scores) {
minScore = Math.min(minScore, score);
}
return minScore;
}
private double getMaxScore() {
double maxScore = Double.MIN_VALUE;
for (Double score : scores) {
maxScore = Math.max(maxScore, xp);
}
return maxScore;
}
public void setScores(List<Double> scores) {
this.scores = scores;
invalidate();
this.repaint();
}
public List<Double> getScores() {
return scores;
}
private static void createAndShowGui() throws FileNotFoundException, IOException {
List<Double> scores = new ArrayList<>();
BufferedReader br = new BufferedReader(new BufferedReader(new FileReader("C:\\Users\\Johanna\\Desktop\\values.txt")));
String line = "";
while((line = br.readLine())!=null){
String[] t = line.split(",");
double x = Double.parseDouble(t[0].trim());
double y = Double.parseDouble(t[1].trim());
}
MainPanel mainPanel = new MainPanel(scores);
mainPanel.setPreferredSize(new Dimension(800, 600));
JFrame frame = new JFrame("DrawGraph");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
createAndShowGui();
} catch (IOException ex) {
Logger.getLogger(DrawGraph.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
//Main changes underneath
static class MainPanel extends JPanel {
public MainPanel(List<Double> scores) {
setLayout(new BorderLayout());
JLabel title = new JLabel("");
title.setFont(new Font("Arial", Font.BOLD, 25));
title.setHorizontalAlignment(JLabel.CENTER);
JPanel DrawGraph = new DrawGraph(scores);
VerticalPanel vertPanel = new VerticalPanel();
HorizontalPanel horiPanel = new HorizontalPanel();
add(title, BorderLayout.NORTH);
add(horiPanel, BorderLayout.SOUTH);
add(vertPanel, BorderLayout.WEST);
add(DrawGraph, BorderLayout.CENTER);
}
class VerticalPanel extends JPanel {
public VerticalPanel() {
setPreferredSize(new Dimension(25, 0));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D gg = (Graphics2D) g;
gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Font font = new Font("Arial", Font.PLAIN, 15);
String string = "Transmittance";
FontMetrics metrics = g.getFontMetrics(font);
int width = metrics.stringWidth(string);
int height = metrics.getHeight();
gg.setFont(font);
drawRotate(gg, getWidth(), (getHeight() + width) / 2, 270, string);
}
public void drawRotate(Graphics2D gg, double x, double y, int angle, String text) {
gg.translate((float) x, (float) y);
gg.rotate(Math.toRadians(angle));
gg.drawString(text, 0, 0);
gg.rotate(-Math.toRadians(angle));
gg.translate(-(float) x, -(float) y);
}
}
class HorizontalPanel extends JPanel {
public HorizontalPanel() {
setPreferredSize(new Dimension(0, 25));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D gg = (Graphics2D) g;
gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Font font = new Font("Arial", Font.PLAIN, 15);
String string = "Wavenumber (cm)";
FontMetrics metrics = g.getFontMetrics(font);
int width = metrics.stringWidth(string);
int height = metrics.getHeight();
gg.setFont(font);
gg.drawString(string, (getWidth() - width) / 2, 11);
}
}
}
}
This is what i tried. It doesn't work :( Why it doesn't work?
..............................................................

Related

how to save a made graph as image instead of showing in jpanel?

By the code below, a graph is made and is added to a JPanel in another class which contains a bar to show the long graph (which usually has 3000 width):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JPanel;
public class Gf extends JPanel {
private static final long serialVersionUID = 1L;
private int padding = 0;
private int labelPadding = 0;
private Color pointColor = new Color(0, 10, 100);
private int pointWidth = 1;
private int numberYDivisions = 2;
private List<Double> scores;
public Gf(List<Double> scores) {
this.scores = scores;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
double xScale = ((double) getWidth() - (2 * padding) - labelPadding)
/ (scores.size() - 1);
double yScale = ((double) getHeight() - 2 * padding - labelPadding) / 60000;
List<Point> graphPoints = new ArrayList<>();
for (int i = 0; i < scores.size(); i++) {
int x1 = (int) (i * xScale + padding + labelPadding);
int y1 = (int) ((getMaxScore() - scores.get(i)) * yScale + padding);
graphPoints.add(new Point(x1, y1));
}
// draw white background
g2.setColor(Color.BLACK);
g2.fillRect(padding + labelPadding, padding,
getWidth() - (2 * padding) - labelPadding,
getHeight() - 2 * padding - labelPadding);
g2.setColor(Color.BLACK);
// create hatch marks and grid lines for y axis.
for (int i = 0; i < numberYDivisions + 1; i++) {
int x0 = padding + labelPadding;
int x1 = pointWidth + padding + labelPadding;
int y0 = getHeight()
- ((i * (getHeight() - padding * 2 - labelPadding)) / numberYDivisions
+ padding + labelPadding);
int y1 = y0;
if (scores.size() > 0) {
String yLabel = ((int) ((getMinScore()
+ (getMaxScore() - getMinScore())
* ((i * 1.0) / numberYDivisions))
* 100))
/ 100.0
+ "";
FontMetrics metrics = g2.getFontMetrics();
int labelWidth = metrics.stringWidth(yLabel);
g2.drawString(
yLabel, x0 - labelWidth - 5, y0 + (metrics.getHeight() / 2) - 3);
}
g2.drawLine(x0, y0, x1, y1);
}
// and for x axis
for (int i = 0; i < scores.size(); i++) {
if (scores.size() > 1) {
int x0 =
i * (getWidth() - padding * 2 - labelPadding) / (scores.size() - 1)
+ padding + labelPadding;
int x1 = x0;
int y0 = getHeight() - padding - labelPadding;
int y1 = y0 - pointWidth;
if ((i % ((int) ((scores.size() / 20.0)) + 1)) == 0) {
String xLabel = i + "";
FontMetrics metrics = g2.getFontMetrics();
int labelWidth = metrics.stringWidth(xLabel);
g2.drawString(
xLabel, x0 - labelWidth / 2, y0 + metrics.getHeight() + 3);
}
g2.drawLine(x0, y0, x1, y1);
}
}
// create x and y axes
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding,
padding + labelPadding, padding);
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding,
getWidth() - padding, getHeight() - padding - labelPadding);
Stroke oldStroke = g2.getStroke();
g2.setStroke(oldStroke);
g2.setColor(pointColor);
for (int i = 0; i < graphPoints.size(); i++) {
int x = graphPoints.get(i).x - pointWidth / 2;
int y = graphPoints.get(i).y - pointWidth / 2;
int ovalW = pointWidth;
int ovalH = pointWidth;
g2.fillOval(x, y, ovalW, ovalH);
}
}
// #Override
// public Dimension getPreferredSize() {
// return new Dimension(width, heigth);
// }
private double getMinScore() {
double minScore = -30000;
return minScore;
}
private double getMaxScore() {
double maxScore = 30000;
return maxScore;
}
public void setScores(List<Double> scores) {
this.scores = scores;
invalidate();
this.repaint();
}
public List<Double> getScores() {
return scores;
}
public static void createAndShowGui() {
List<Double> scores = new ArrayList<>();
short[] x = mainclass.Omega; // getting required data from main class
for (int i = 0; i < x.length; i++) {
scores.add((double) x[i]);
}
Gf mainPanel = new Gf(scores);
mainPanel.setPreferredSize(new Dimension(
mainclass.range, 276)); // range is calculated at main class and usually
// has more than 3k width
mainclass.p.add(mainPanel); // adding to jpanel at main class
mainclass.f.revalidate();
mainclass.f.repaint();
}
}
The problem is that sometimes gets way slow specially on slower devices when scrolling the bar to check the entire diagram.
So wanted to know if there is a way to save the graph as an image instead of showing at JPanel(so that it can be added to the JPanel later in some way that doesn't affect the performance).

Java - paint() called on Windows 10 but not on OSX 10.10.5

So I wrote a program to display Julia sets on my Windows machine, but the paint() method isn't being called when I transferred the exact same code to my MacBook.
The whole class:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class JuliaSet extends JPanel implements MouseWheelListener {
public double width;
public double height;
public double pixToCoord;
public Complex c;
public double iterations = 100;
double xMin = -2;
double xMax = 2;
double centerY = 0;
public JuliaSet(double width, double height) {
this.width = width;
this.height = height + 31;
pixToCoord = (xMax - xMin) / width;
c = new Complex(0.285, 0.01);
}
public void setC(double x, double y) {
c.x = x;
c.y = y;
}
public void paint(Graphics g) {
pixToCoord = (xMax - xMin) / width;
double yMin = centerY - (height * pixToCoord / 2.0);
Graphics2D g2 = (Graphics2D) g;
for (double y = 0; y <= height - 31; y++) {
for (double x = 1; x <= width; x++) {
Complex z = new Complex(x * pixToCoord + xMin, (-y + height) * pixToCoord + yMin);
double count = 0;
for (count = 0; count < iterations && z.magnitude() < 2; count++) {
z = z.multiply(z).add(c);
}
g2.setColor(Color.getHSBColor((float) (count / iterations), (float) 1.0, (float) (1.0 - (count / iterations))));
g2.drawLine((int) x, (int) y - 31, (int) x, (int) y - 31);
}
}
}
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
double x = (double) e.getX();
double y = (double) e.getY() - 31;
x = x * pixToCoord + xMin;
double yMin = centerY - (height * pixToCoord / 2.0);
double yMax = centerY + (height * pixToCoord / 2.0);
y = (-y + height) * pixToCoord + yMin;
if (e.getWheelRotation() < 0) {
xMin += 0.25 * (x - xMin);
xMax -= 0.25 * (xMax - x);
yMin += 0.25 * (y - yMin);
yMax -= 0.25 * (yMax - y);
} else {
xMin -= (x - xMin) / 3.0;
xMax += (xMax - x) / 3.0;
yMin -= (y - yMin) / 3.0;
yMax += (yMax - y) / 3.0;
}
centerY = (yMax + yMin) / 2.0;
this.repaint();
}
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame();
frame.setTitle("Julia Set");
frame.setBounds(0, 0, 600, 631);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JuliaSet p = new JuliaSet((double) frame.getWidth(), (double) frame.getHeight());
frame.add(p);
//frame.addMouseWheelListener(p);
double angle = 0;
long time = System.currentTimeMillis();
while (true) {
{Thread.sleep(16);}
angle += Math.PI / 960.0;
if (angle >= Math.PI * 2.0) angle = 0;
p.setC(0.775 * Math.cos(angle), 0.775 * Math.sin(angle));
p.repaint();
System.out.println((int) (1000.0 / (double) (System.currentTimeMillis() - time)));
time = System.currentTimeMillis();
}
}
}
Is there any reason that this code would work on Windows and not OSX?
Your code ignores Swing threading rules with use of while (true) and Thread.sleep as well as Swing component mutational changes within possible background threads, so the question you should be asking is why it worked at all on any system.
Suggestions:
Use a SwingWorker to create your background thread and to allow you to make mutational changes on Swing components on the event thread.
Draw within paintComponent, not paint, and call the super's painting method within your override, here super.paintComponent(g) if you correctly override this method.
Do complex math or any CPU- or time-intensive processing within the background thread, and avoid doing it within the painting method. These methods should be for painting and painting only.
For example, here's a sample program that uses a SwingWorker and graphics to calculate and draw sections of the Mandelbrot set (sorry, don't have a Julia Set implementation yet):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
#SuppressWarnings("serial")
public class Mandel2 extends JPanel {
private static final int GUI_HEIGHT = 600;
private static final int GUI_WIDTH = 600;
private static final int MAX_ITERS = 50000;
private BufferedImage image = new BufferedImage(GUI_WIDTH, GUI_HEIGHT,
BufferedImage.TYPE_INT_ARGB);
private Rectangle zoomRect;
private double myX0 = -2.5;
private double myY0 = -2.0;
private double myX1 = 1.5;
private double myY1 = 2.0;
private JDialog waitDialog;
public Mandel2() {
final MyMouse myMouse = new MyMouse();
int delayStartingCalc = 2 * 1000; // 2 second delay
Timer timer = new Timer(delayStartingCalc, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
Rectangle myRect = new Rectangle(0, 0, GUI_WIDTH, GUI_HEIGHT);
createMandel(myRect);
}
});
timer.setRepeats(false);
timer.start();
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(GUI_WIDTH, GUI_HEIGHT);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
g.drawImage(image, 0, 0, this);
}
Graphics2D g2 = (Graphics2D) g;
if (zoomRect == null) {
return;
}
g2.setXORMode(Color.gray);
g2.draw(zoomRect);
}
private double screenToLogicalX(double screenX) {
return myX0 + (screenX * (myX1 - myX0)) / GUI_WIDTH;
}
private double screenToLogicalY(double screenY) {
return myY0 + ((GUI_HEIGHT - screenY) * (myY1 - myY0)) / GUI_HEIGHT;
}
private void createMandel(Rectangle myRect) {
double x0 = screenToLogicalX(myRect.x);
double y0 = screenToLogicalY(myRect.y + myRect.height);
double x1 = screenToLogicalX(myRect.x + myRect.width);
double y1 = screenToLogicalY(myRect.y);
myX0 = x0;
myY0 = y0;
myX1 = x1;
myY1 = y1;
MandelWorker mandelWorker = new MandelWorker(MAX_ITERS, x0, y0, x1, y1);
mandelWorker.addPropertyChangeListener(new MandelWorkerListener());
mandelWorker.execute();
if (waitDialog == null) {
Window win = SwingUtilities.getWindowAncestor(Mandel2.this);
JProgressBar jProgressBar = new JProgressBar();
jProgressBar.setIndeterminate(true);
waitDialog = new JDialog(win, "Please Wait", ModalityType.APPLICATION_MODAL);
waitDialog.add(jProgressBar);
waitDialog.pack();
waitDialog.setLocationRelativeTo(win);
}
waitDialog.setVisible(true);
}
private class MyMouse extends MouseAdapter {
private Point p;
#Override
public void mousePressed(MouseEvent e) {
p = e.getPoint();
}
public void mouseDragged(MouseEvent e) {
zoomRect = createRect(e);
repaint();
};
#Override
public void mouseReleased(MouseEvent e) {
zoomRect = createRect(e);
repaint();
createMandel(zoomRect);
}
private Rectangle createRect(MouseEvent e) {
int x = Math.min(p.x, e.getX());
int y = Math.min(p.y, e.getY());
int width = Math.abs(p.x - e.getX());
int height = Math.abs(p.y - e.getY());
return new Rectangle(x, y, width, height);
}
}
private class MandelWorkerListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
waitDialog.setVisible(false);
waitDialog.dispose();
MandelWorker worker = (MandelWorker) evt.getSource();
try {
image = worker.get();
zoomRect = null;
repaint();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
private class MandelWorker extends SwingWorker<BufferedImage, Void> {
private int maxIters;
private double x1;
private double y1;
private double x2;
private double y2;
public MandelWorker(int maxIters, double x1, double y1, double x2, double y2) {
this.maxIters = maxIters;
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
#Override
protected BufferedImage doInBackground() throws Exception {
int[][] iterGrid = new int[GUI_HEIGHT][GUI_WIDTH];
for (int i = 0; i < GUI_HEIGHT; i++) {
double y = y1 + i * (y2 - y1) / GUI_HEIGHT;
for (int j = 0; j < GUI_WIDTH; j++) {
double x = x1 + j * (x2 - x1) / GUI_WIDTH;
int iIndex = GUI_HEIGHT - i - 1;
iterGrid[iIndex][j] = calcMandel(x, y);
}
}
return render(iterGrid);
}
private BufferedImage render(int[][] iterGrid) {
int w = GUI_WIDTH;
int h = GUI_HEIGHT;
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
if (iterGrid[i][j] < maxIters) {
String hexCode = String.format("#%06x", (0xFFFFFF & (32 * iterGrid[i][j])));
g2.setColor(Color.decode(hexCode));
} else {
g2.setColor(Color.CYAN);
}
g2.drawLine(j, i, j, i);
}
}
g2.dispose();
return img;
}
private int calcMandel(double x, double y) {
Complex c = new Complex(x, y);
Complex z = new Complex();
int iters = 0;
while (z.getMagnitude() < 2 && iters <= maxIters) {
z = z.multiply(z).add(c);
iters++;
}
return iters;
}
}
private class Complex {
private double real, imag;
// Constructors
public Complex() {
real = 0.0;
imag = 0.0;
}
public Complex(double real, double imag) {
this.real = real;
this.imag = imag;
}
// add given complex number to this one, returning the Complex result
public Complex add(Complex other) {
return new Complex(this.real + other.real, this.imag + other.imag);
}
// multiply given complex number by this one, returning the Complex
// result
public Complex multiply(Complex other) {
return new Complex((this.real * other.real) - (this.imag * other.imag),
(this.imag * other.real) + (this.real * other.imag));
}
// get the magnitude of this complex number
public double getMagnitude() {
return Math.sqrt((real * real) + (imag * imag));
}
}
private static void createAndShowGui() {
Mandel2 mainPanel = new Mandel2();
JFrame frame = new JFrame("Mandel2");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.setResizable(false);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Adding axis labels and titles to a simple line graph/chart

I'm new to Java programming. I have a graph created in java but I want to add a title (e.g. "Variation of Distance with time")and also label the x-axis (e.g. "Time (s)") and y-axis (e.g. "Distance (m)"), with the y-axis rotated through 90 degrees.
How would I achieve that?
The code is given below.
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class GraphPanel extends JPanel {
private int width = 800;
private int heigth = 400;
private int padding = 25;
private int labelPadding = 25;
private Color lineColor = new Color(44, 102, 230, 180);
private Color pointColor = new Color(100, 100, 100, 180);
private Color gridColor = new Color(200, 200, 200, 200);
private static final Stroke GRAPH_STROKE = new BasicStroke(2f);
private int pointWidth = 4;
private int numberYDivisions = 10;
private List<Double> scores;
public GraphPanel(List<Double> scores) {
this.scores = scores;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
double xScale = ((double) getWidth() - (2 * padding) - labelPadding) / (scores.size() - 1);
double yScale = ((double) getHeight() - 2 * padding - labelPadding) / (getMaxScore() - getMinScore());
List<Point> graphPoints = new ArrayList<>();
for (int i = 0; i < scores.size(); i++) {
int x1 = (int) (i * xScale + padding + labelPadding);
int y1 = (int) ((getMaxScore() - scores.get(i)) * yScale + padding);
graphPoints.add(new Point(x1, y1));
}
// draw white background
g2.setColor(Color.WHITE);
g2.fillRect(padding + labelPadding, padding, getWidth() - (2 * padding) - labelPadding, getHeight() - 2 * padding - labelPadding);
g2.setColor(Color.BLACK);
// create hatch marks and grid lines for y axis.
for (int i = 0; i < numberYDivisions + 1; i++) {
int x0 = padding + labelPadding;
int x1 = pointWidth + padding + labelPadding;
int y0 = getHeight() - ((i * (getHeight() - padding * 2 - labelPadding)) / numberYDivisions + padding + labelPadding);
int y1 = y0;
if (scores.size() > 0) {
g2.setColor(gridColor);
g2.drawLine(padding + labelPadding + 1 + pointWidth, y0, getWidth() - padding, y1);
g2.setColor(Color.BLACK);
String yLabel = ((int) ((getMinScore() + (getMaxScore() - getMinScore()) * ((i * 1.0) / numberYDivisions)) * 100)) / 100.0 + "";
FontMetrics metrics = g2.getFontMetrics();
int labelWidth = metrics.stringWidth(yLabel);
g2.drawString(yLabel, x0 - labelWidth - 5, y0 + (metrics.getHeight() / 2) - 3);
}
g2.drawLine(x0, y0, x1, y1);
}
// and for x axis
for (int i = 0; i < scores.size(); i++) {
if (scores.size() > 1) {
int x0 = i * (getWidth() - padding * 2 - labelPadding) / (scores.size() - 1) + padding + labelPadding;
int x1 = x0;
int y0 = getHeight() - padding - labelPadding;
int y1 = y0 - pointWidth;
if ((i % ((int) ((scores.size() / 20.0)) + 1)) == 0) {
g2.setColor(gridColor);
g2.drawLine(x0, getHeight() - padding - labelPadding - 1 - pointWidth, x1, padding);
g2.setColor(Color.BLACK);
String xLabel = i + "";
FontMetrics metrics = g2.getFontMetrics();
int labelWidth = metrics.stringWidth(xLabel);
g2.drawString(xLabel, x0 - labelWidth / 2, y0 + metrics.getHeight() + 3);
}
g2.drawLine(x0, y0, x1, y1);
}
}
// create x and y axes
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, padding + labelPadding, padding);
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, getWidth() - padding, getHeight() - padding - labelPadding);
Stroke oldStroke = g2.getStroke();
g2.setColor(lineColor);
g2.setStroke(GRAPH_STROKE);
for (int i = 0; i < graphPoints.size() - 1; i++) {
int x1 = graphPoints.get(i).x;
int y1 = graphPoints.get(i).y;
int x2 = graphPoints.get(i + 1).x;
int y2 = graphPoints.get(i + 1).y;
g2.drawLine(x1, y1, x2, y2);
}
g2.setStroke(oldStroke);
g2.setColor(pointColor);
for (int i = 0; i < graphPoints.size(); i++) {
int x = graphPoints.get(i).x - pointWidth / 2;
int y = graphPoints.get(i).y - pointWidth / 2;
int ovalW = pointWidth;
int ovalH = pointWidth;
g2.fillOval(x, y, ovalW, ovalH);
}
}
// #Override
// public Dimension getPreferredSize() {
// return new Dimension(width, heigth);
// }
private double getMinScore() {
double minScore = Double.MAX_VALUE;
for (Double score : scores) {
minScore = Math.min(minScore, score);
}
return minScore;
}
private double getMaxScore() {
double maxScore = Double.MIN_VALUE;
for (Double score : scores) {
maxScore = Math.max(maxScore, score);
}
return maxScore;
}
public void setScores(List<Double> scores) {
this.scores = scores;
invalidate();
this.repaint();
}
public List<Double> getScores() {
return scores;
}
private static void createAndShowGui() {
List<Double> scores = new ArrayList<>();
Random random = new Random();
int maxDataPoints = 40;
int maxScore = 10;
for (int i = 0; i < maxDataPoints; i++) {
scores.add((double) random.nextDouble() * maxScore);
// scores.add((double) i);
}
GraphPanel mainPanel = new GraphPanel(scores);
mainPanel.setPreferredSize(new Dimension(800, 600));
JFrame frame = new JFrame("DrawGraph");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I'll have to give you a cheeky but working answer. I couldn't manage to rotate a JLabel, so I decided the easiest way would be to just draw the String... (My excuse: You also draw the Strings in the graph, so this would "match" better ;) ) The class MainPanel contains 4 seperate panels: One for the title, one for each axis and one for the GraphPanel. Visualization:
Full code:
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.awt.Font;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class GraphPanel extends JPanel {
private int width = 800;
private int heigth = 400;
private int padding = 25;
private int labelPadding = 25;
private Color lineColor = new Color(44, 102, 230, 180);
private Color pointColor = new Color(100, 100, 100, 180);
private Color gridColor = new Color(200, 200, 200, 200);
private static final Stroke GRAPH_STROKE = new BasicStroke(2f);
private int pointWidth = 4;
private int numberYDivisions = 10;
private List<Double> scores;
public GraphPanel(List<Double> scores) {
this.scores = scores;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
double xScale = ((double) getWidth() - (2 * padding) - labelPadding) / (scores.size() - 1);
double yScale = ((double) getHeight() - 2 * padding - labelPadding) / (getMaxScore() - getMinScore());
List<Point> graphPoints = new ArrayList<>();
for (int i = 0; i < scores.size(); i++) {
int x1 = (int) (i * xScale + padding + labelPadding);
int y1 = (int) ((getMaxScore() - scores.get(i)) * yScale + padding);
graphPoints.add(new Point(x1, y1));
}
// draw white background
g2.setColor(Color.WHITE);
g2.fillRect(padding + labelPadding, padding, getWidth() - (2 * padding) - labelPadding,
getHeight() - 2 * padding - labelPadding);
g2.setColor(Color.BLACK);
// create hatch marks and grid lines for y axis.
for (int i = 0; i < numberYDivisions + 1; i++) {
int x0 = padding + labelPadding;
int x1 = pointWidth + padding + labelPadding;
int y0 = getHeight()
- ((i * (getHeight() - padding * 2 - labelPadding)) / numberYDivisions + padding + labelPadding);
int y1 = y0;
if (scores.size() > 0) {
g2.setColor(gridColor);
g2.drawLine(padding + labelPadding + 1 + pointWidth, y0, getWidth() - padding, y1);
g2.setColor(Color.BLACK);
String yLabel = ((int) ((getMinScore()
+ (getMaxScore() - getMinScore()) * ((i * 1.0) / numberYDivisions)) * 100)) / 100.0 + "";
FontMetrics metrics = g2.getFontMetrics();
int labelWidth = metrics.stringWidth(yLabel);
g2.drawString(yLabel, x0 - labelWidth - 5, y0 + (metrics.getHeight() / 2) - 3);
}
g2.drawLine(x0, y0, x1, y1);
}
// and for x axis
for (int i = 0; i < scores.size(); i++) {
if (scores.size() > 1) {
int x0 = i * (getWidth() - padding * 2 - labelPadding) / (scores.size() - 1) + padding + labelPadding;
int x1 = x0;
int y0 = getHeight() - padding - labelPadding;
int y1 = y0 - pointWidth;
if ((i % ((int) ((scores.size() / 20.0)) + 1)) == 0) {
g2.setColor(gridColor);
g2.drawLine(x0, getHeight() - padding - labelPadding - 1 - pointWidth, x1, padding);
g2.setColor(Color.BLACK);
String xLabel = i + "";
FontMetrics metrics = g2.getFontMetrics();
int labelWidth = metrics.stringWidth(xLabel);
g2.drawString(xLabel, x0 - labelWidth / 2, y0 + metrics.getHeight() + 3);
}
g2.drawLine(x0, y0, x1, y1);
}
}
// create x and y axes
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, padding + labelPadding, padding);
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, getWidth() - padding,
getHeight() - padding - labelPadding);
Stroke oldStroke = g2.getStroke();
g2.setColor(lineColor);
g2.setStroke(GRAPH_STROKE);
for (int i = 0; i < graphPoints.size() - 1; i++) {
int x1 = graphPoints.get(i).x;
int y1 = graphPoints.get(i).y;
int x2 = graphPoints.get(i + 1).x;
int y2 = graphPoints.get(i + 1).y;
g2.drawLine(x1, y1, x2, y2);
}
g2.setStroke(oldStroke);
g2.setColor(pointColor);
for (int i = 0; i < graphPoints.size(); i++) {
int x = graphPoints.get(i).x - pointWidth / 2;
int y = graphPoints.get(i).y - pointWidth / 2;
int ovalW = pointWidth;
int ovalH = pointWidth;
g2.fillOval(x, y, ovalW, ovalH);
}
}
// #Override
// public Dimension getPreferredSize() {
// return new Dimension(width, heigth);
// }
private double getMinScore() {
double minScore = Double.MAX_VALUE;
for (Double score : scores) {
minScore = Math.min(minScore, score);
}
return minScore;
}
private double getMaxScore() {
double maxScore = Double.MIN_VALUE;
for (Double score : scores) {
maxScore = Math.max(maxScore, score);
}
return maxScore;
}
public void setScores(List<Double> scores) {
this.scores = scores;
invalidate();
this.repaint();
}
public List<Double> getScores() {
return scores;
}
private static void createAndShowGui() {
List<Double> scores = new ArrayList<>();
Random random = new Random();
int maxDataPoints = 40;
int maxScore = 10;
for (int i = 0; i < maxDataPoints; i++) {
scores.add((double) random.nextDouble() * maxScore);
// scores.add((double) i);
}
MainPanel mainPanel = new MainPanel(scores);
mainPanel.setPreferredSize(new Dimension(800, 600));
JFrame frame = new JFrame("DrawGraph");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
//Main changes underneath
static class MainPanel extends JPanel {
public MainPanel(List<Double> scores) {
setLayout(new BorderLayout());
JLabel title = new JLabel("Variation of Distance with time");
title.setFont(new Font("Arial", Font.BOLD, 25));
title.setHorizontalAlignment(JLabel.CENTER);
JPanel graphPanel = new GraphPanel(scores);
VerticalPanel vertPanel = new VerticalPanel();
HorizontalPanel horiPanel = new HorizontalPanel();
add(title, BorderLayout.NORTH);
add(horiPanel, BorderLayout.SOUTH);
add(vertPanel, BorderLayout.WEST);
add(graphPanel, BorderLayout.CENTER);
}
class VerticalPanel extends JPanel {
public VerticalPanel() {
setPreferredSize(new Dimension(25, 0));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D gg = (Graphics2D) g;
gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Font font = new Font("Arial", Font.PLAIN, 15);
String string = "Time (s)";
FontMetrics metrics = g.getFontMetrics(font);
int width = metrics.stringWidth(string);
int height = metrics.getHeight();
gg.setFont(font);
drawRotate(gg, getWidth(), (getHeight() + width) / 2, 270, string);
}
public void drawRotate(Graphics2D gg, double x, double y, int angle, String text) {
gg.translate((float) x, (float) y);
gg.rotate(Math.toRadians(angle));
gg.drawString(text, 0, 0);
gg.rotate(-Math.toRadians(angle));
gg.translate(-(float) x, -(float) y);
}
}
class HorizontalPanel extends JPanel {
public HorizontalPanel() {
setPreferredSize(new Dimension(0, 25));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D gg = (Graphics2D) g;
gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Font font = new Font("Arial", Font.PLAIN, 15);
String string = "Distance (m)";
FontMetrics metrics = g.getFontMetrics(font);
int width = metrics.stringWidth(string);
int height = metrics.getHeight();
gg.setFont(font);
gg.drawString(string, (getWidth() - width) / 2, 11);
}
}
}
}
Pic of the working program (Keep in mind that the title is centered relative to the width of the whole panel, not the graph):
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class MainPanel extends JPanel {
public MainPanel(List<Double> scores) {
setLayout(new BorderLayout());
JLabel title = new JLabel("Variation of Distance with time");
title.setFont(new Font("Arial", Font.BOLD, 25));
title.setHorizontalAlignment(JLabel.CENTER);
JPanel graphPanel = new GraphPanel(scores);
MainPanel.VerticalPanel vertPanel = new MainPanel.VerticalPanel();
MainPanel.HorizontalPanel horiPanel = new MainPanel.HorizontalPanel();
add(title, BorderLayout.NORTH);
add(horiPanel, BorderLayout.SOUTH);
add(vertPanel, BorderLayout.WEST);
add(graphPanel, BorderLayout.CENTER);
}
class VerticalPanel extends JPanel {
public VerticalPanel() {
setPreferredSize(new Dimension(25, 0));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D gg = (Graphics2D) g;
gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Font font = new Font("Arial", Font.PLAIN, 15);
String string = "Time (s)";
FontMetrics metrics = g.getFontMetrics(font);
int width = metrics.stringWidth(string);
int height = metrics.getHeight();
gg.setFont(font);
drawRotate(gg, getWidth(), (getHeight() + width) / 2, 270, string);
}
public void drawRotate(Graphics2D gg, double x, double y, int angle, String text) {
gg.translate((float) x, (float) y);
gg.rotate(Math.toRadians(angle));
gg.drawString(text, 0, 0);
gg.rotate(-Math.toRadians(angle));
gg.translate(-(float) x, -(float) y);
}
}
class HorizontalPanel extends JPanel {
public HorizontalPanel() {
setPreferredSize(new Dimension(0, 25));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D gg = (Graphics2D) g;
gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Font font = new Font("Arial", Font.PLAIN, 15);
String string = "Distance (m)";
FontMetrics metrics = g.getFontMetrics(font);
int width = metrics.stringWidth(string);
int height = metrics.getHeight();
gg.setFont(font);
gg.drawString(string, (getWidth() - width) / 2, 11);
}
}
static class GraphPanel extends JPanel {
// private List<Double> scores;
private int width = 800;
private int heigth = 400;
private int padding = 25;
private int labelPadding = 25;
private Color lineColor = new Color(44, 102, 230, 180);
private Color pointColor = new Color(100, 100, 100, 180);
private Color gridColor = new Color(200, 200, 200, 200);
private static final Stroke GRAPH_STROKE = new BasicStroke(2f);
private int pointWidth = 4;
private int numberYDivisions = 10;
private List<Double> scores;
public GraphPanel(List<Double> scores) {
this.scores = scores;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
double xScale = ((double) getWidth() - (2 * padding) - labelPadding) / (scores.size() - 1);
double yScale = ((double) getHeight() - 2 * padding - labelPadding) / (getMaxScore() - getMinScore());
List<Point> graphPoints = new ArrayList<>();
for (int i = 0; i < scores.size(); i++) {
int x1 = (int) (i * xScale + padding + labelPadding);
int y1 = (int) ((getMaxScore() - scores.get(i)) * yScale + padding);
graphPoints.add(new Point(x1, y1));
}
// draw white background
g2.setColor(Color.WHITE);
g2.fillRect(padding + labelPadding, padding, getWidth() - (2 * padding) - labelPadding, getHeight() - 2 * padding - labelPadding);
g2.setColor(Color.BLACK);
// create hatch marks and grid lines for y axis.
for (int i = 0; i < numberYDivisions + 1; i++) {
int x0 = padding + labelPadding;
int x1 = pointWidth + padding + labelPadding;
int y0 = getHeight() - ((i * (getHeight() - padding * 2 - labelPadding)) / numberYDivisions + padding + labelPadding);
int y1 = y0;
if (scores.size() > 0) {
g2.setColor(gridColor);
g2.drawLine(padding + labelPadding + 1 + pointWidth, y0, getWidth() - padding, y1);
g2.setColor(Color.BLACK);
String yLabel = ((int) ((getMinScore() + (getMaxScore() - getMinScore()) * ((i * 1.0) / numberYDivisions)) * 100)) / 100.0 + "";
FontMetrics metrics = g2.getFontMetrics();
int labelWidth = metrics.stringWidth(yLabel);
g2.drawString(yLabel, x0 - labelWidth - 5, y0 + (metrics.getHeight() / 2) - 3);
}
g2.drawLine(x0, y0, x1, y1);
}
// and for x axis
for (int i = 0; i < scores.size(); i++) {
if (scores.size() > 1) {
int x0 = i * (getWidth() - padding * 2 - labelPadding) / (scores.size() - 1) + padding + labelPadding;
int x1 = x0;
int y0 = getHeight() - padding - labelPadding;
int y1 = y0 - pointWidth;
if ((i % ((int) ((scores.size() / 20.0)) + 1)) == 0) {
g2.setColor(gridColor);
g2.drawLine(x0, getHeight() - padding - labelPadding - 1 - pointWidth, x1, padding);
g2.setColor(Color.BLACK);
String xLabel = i + "";
FontMetrics metrics = g2.getFontMetrics();
int labelWidth = metrics.stringWidth(xLabel);
g2.drawString(xLabel, x0 - labelWidth / 2, y0 + metrics.getHeight() + 3);
}
g2.drawLine(x0, y0, x1, y1);
}
}
// create x and y axes
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, padding + labelPadding, padding);
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, getWidth() - padding, getHeight() - padding - labelPadding);
Stroke oldStroke = g2.getStroke();
g2.setColor(lineColor);
g2.setStroke(GRAPH_STROKE);
for (int i = 0; i < graphPoints.size() - 1; i++) {
int x1 = graphPoints.get(i).x;
int y1 = graphPoints.get(i).y;
int x2 = graphPoints.get(i + 1).x;
int y2 = graphPoints.get(i + 1).y;
g2.drawLine(x1, y1, x2, y2);
}
g2.setStroke(oldStroke);
g2.setColor(pointColor);
for (int i = 0; i < graphPoints.size(); i++) {
int x = graphPoints.get(i).x - pointWidth / 2;
int y = graphPoints.get(i).y - pointWidth / 2;
int ovalW = pointWidth;
int ovalH = pointWidth;
g2.fillOval(x, y, ovalW, ovalH);
}
}
// #Override
// public Dimension getPreferredSize() {
// return new Dimension(width, heigth);
// }
private double getMinScore() {
double minScore = Double.MAX_VALUE;
for (Double score : scores) {
minScore = Math.min(minScore, score);
}
return minScore;
}
private double getMaxScore() {
double maxScore = Double.MIN_VALUE;
for (Double score : scores) {
maxScore = Math.max(maxScore, score);
}
return maxScore;
}
public void setScores(List<Double> scores) {
this.scores = scores;
invalidate();
this.repaint();
}
public List<Double> getScores() {
return scores;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, heigth);
}
class VerticalPanel extends JPanel {
public VerticalPanel() {
setPreferredSize(new Dimension(25, 0));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D gg = (Graphics2D) g;
gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Font font = new Font("Arial", Font.PLAIN, 15);
String string = "Time (s)";
FontMetrics metrics = g.getFontMetrics(font);
int width = metrics.stringWidth(string);
int height = metrics.getHeight();
gg.setFont(font);
drawRotate(gg, getWidth(), (getHeight() + width) / 2, 270, string);
}
public void drawRotate(Graphics2D gg, double x, double y, int angle, String text) {
gg.translate((float) x, (float) y);
gg.rotate(Math.toRadians(angle));
gg.drawString(text, 0, 0);
gg.rotate(-Math.toRadians(angle));
gg.translate(-(float) x, -(float) y);
}
}
class HorizontalPanel extends JPanel {
public HorizontalPanel() {
setPreferredSize(new Dimension(0, 25));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D gg = (Graphics2D) g;
gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Font font = new Font("Arial", Font.PLAIN, 15);
String string = "Distance (m)";
FontMetrics metrics = g.getFontMetrics(font);
int width = metrics.stringWidth(string);
int height = metrics.getHeight();
gg.setFont(font);
gg.drawString(string, (getWidth() - width) / 2, 11);
}
}
}
}
The calling method will be something like:
public void btnPlotActionPerformed(java.awt.event.ActionEvent evt) {
MainPanel daGraph = new MainPanel(plotValues);
Object option[] = {" Exit "};
JOptionPane.showOptionDialog(this, daGraph, "Distance-Time Graph", JOptionPane.PLAIN_MESSAGE, JOptionPane.PLAIN_MESSAGE, null, option, null);
}

Drawing a simple line graph in Java

In my program I want to draw a simple score line graph. I have a text file and on each line is an integer score, which I read in and want to pass as argument to my graph class. I'm having some trouble implementing the graph class and all the examples I've seen have their methods in the same class as their main, which I won't have.
I want to be able to pass my array to the object and generate a graph, but when calling my paint method it is asking me for a Graphics g... This is what I have so far:
public class Graph extends JPanel {
public void paintGraph (Graphics g){
ArrayList<Integer> scores = new ArrayList<Integer>(10);
Random r = new Random();
for (int i : scores){
i = r.nextInt(20);
System.out.println(r);
}
int y1;
int y2;
for (int i = 0; i < scores.size(); i++){
y1 = scores.get(i);
y2 = scores.get(i+1);
g.drawLine(i, y1, i+1, y2);
}
}
}
For now I have inserted a simple random number generator to fill up my array.
I have an existing frame and basically want to instantiate the Graph class and mount the panel onto my frame. I'm really sorry that this question seems so jumbled by the way, but I've had little sleep...
The code in my main statement is:
testFrame = new JFrame();
testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Graph graph = new Graph();
testFrame.add(graph);
I'm not sure exactly what an SSCE is but this is my attempt at one:
public class Test {
JFrame testFrame;
public Test() {
testFrame = new JFrame();
testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Graph graph = new Graph();
testFrame.add(graph);
testFrame.setBounds(100, 100, 764, 470);
testFrame.setVisible(true);
}
Graph.java
public class Graph extends JPanel {
public Graph() {
setSize(500, 500);
}
#Override
public void paintComponent(Graphics g) {
Graphics2D gr = (Graphics2D) g; // This is if you want to use Graphics2D
// Now do the drawing here
ArrayList<Integer> scores = new ArrayList<Integer>(10);
Random r = new Random();
for (int i : scores) {
i = r.nextInt(20);
System.out.println(r);
}
int y1;
int y2;
for (int i = 0; i < scores.size() - 1; i++) {
y1 = (scores.get(i)) * 10;
y2 = (scores.get(i + 1)) * 10;
gr.drawLine(i * 10, y1, (i + 1) * 10, y2);
}
}
}
Problems with your code and suggestions:
Again you need to change the preferredSize of the component (here the Graph JPanel), not the size
Don't set the JFrame's bounds.
Call pack() on your JFrame after adding components to it and before calling setVisible(true)
Your foreach loop won't work since the size of your ArrayList is 0 (test it to see that this is correct). Instead use a for loop going from 0 to 10.
You should not have program logic inside of your paintComponent(...) method but only painting code. So I would make the ArrayList a class variable and fill it inside of the class's constructor.
For example:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawGraph extends JPanel {
private static final int MAX_SCORE = 20;
private static final int PREF_W = 800;
private static final int PREF_H = 650;
private static final int BORDER_GAP = 30;
private static final Color GRAPH_COLOR = Color.green;
private static final Color GRAPH_POINT_COLOR = new Color(150, 50, 50, 180);
private static final Stroke GRAPH_STROKE = new BasicStroke(3f);
private static final int GRAPH_POINT_WIDTH = 12;
private static final int Y_HATCH_CNT = 10;
private List<Integer> scores;
public DrawGraph(List<Integer> scores) {
this.scores = scores;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
double xScale = ((double) getWidth() - 2 * BORDER_GAP) / (scores.size() - 1);
double yScale = ((double) getHeight() - 2 * BORDER_GAP) / (MAX_SCORE - 1);
List<Point> graphPoints = new ArrayList<Point>();
for (int i = 0; i < scores.size(); i++) {
int x1 = (int) (i * xScale + BORDER_GAP);
int y1 = (int) ((MAX_SCORE - scores.get(i)) * yScale + BORDER_GAP);
graphPoints.add(new Point(x1, y1));
}
// create x and y axes
g2.drawLine(BORDER_GAP, getHeight() - BORDER_GAP, BORDER_GAP, BORDER_GAP);
g2.drawLine(BORDER_GAP, getHeight() - BORDER_GAP, getWidth() - BORDER_GAP, getHeight() - BORDER_GAP);
// create hatch marks for y axis.
for (int i = 0; i < Y_HATCH_CNT; i++) {
int x0 = BORDER_GAP;
int x1 = GRAPH_POINT_WIDTH + BORDER_GAP;
int y0 = getHeight() - (((i + 1) * (getHeight() - BORDER_GAP * 2)) / Y_HATCH_CNT + BORDER_GAP);
int y1 = y0;
g2.drawLine(x0, y0, x1, y1);
}
// and for x axis
for (int i = 0; i < scores.size() - 1; i++) {
int x0 = (i + 1) * (getWidth() - BORDER_GAP * 2) / (scores.size() - 1) + BORDER_GAP;
int x1 = x0;
int y0 = getHeight() - BORDER_GAP;
int y1 = y0 - GRAPH_POINT_WIDTH;
g2.drawLine(x0, y0, x1, y1);
}
Stroke oldStroke = g2.getStroke();
g2.setColor(GRAPH_COLOR);
g2.setStroke(GRAPH_STROKE);
for (int i = 0; i < graphPoints.size() - 1; i++) {
int x1 = graphPoints.get(i).x;
int y1 = graphPoints.get(i).y;
int x2 = graphPoints.get(i + 1).x;
int y2 = graphPoints.get(i + 1).y;
g2.drawLine(x1, y1, x2, y2);
}
g2.setStroke(oldStroke);
g2.setColor(GRAPH_POINT_COLOR);
for (int i = 0; i < graphPoints.size(); i++) {
int x = graphPoints.get(i).x - GRAPH_POINT_WIDTH / 2;
int y = graphPoints.get(i).y - GRAPH_POINT_WIDTH / 2;;
int ovalW = GRAPH_POINT_WIDTH;
int ovalH = GRAPH_POINT_WIDTH;
g2.fillOval(x, y, ovalW, ovalH);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
List<Integer> scores = new ArrayList<Integer>();
Random random = new Random();
int maxDataPoints = 16;
int maxScore = 20;
for (int i = 0; i < maxDataPoints ; i++) {
scores.add(random.nextInt(maxScore));
}
DrawGraph mainPanel = new DrawGraph(scores);
JFrame frame = new JFrame("DrawGraph");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Which will create a graph that looks like so:
Just complementing Hovercraft Full Of Eels's solution:
I reworked his code, tweaked it a bit, adding a grid, axis labels and now the Y-axis goes from the minimum value present up to the maximum value. I planned on adding a couple of getters/setters but I didn't need them, you can add them if you want.
Here is the Gist link, I'll also paste the code below: GraphPanel on Gist
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class GraphPanel extends JPanel {
private int width = 800;
private int heigth = 400;
private int padding = 25;
private int labelPadding = 25;
private Color lineColor = new Color(44, 102, 230, 180);
private Color pointColor = new Color(100, 100, 100, 180);
private Color gridColor = new Color(200, 200, 200, 200);
private static final Stroke GRAPH_STROKE = new BasicStroke(2f);
private int pointWidth = 4;
private int numberYDivisions = 10;
private List<Double> scores;
public GraphPanel(List<Double> scores) {
this.scores = scores;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
double xScale = ((double) getWidth() - (2 * padding) - labelPadding) / (scores.size() - 1);
double yScale = ((double) getHeight() - 2 * padding - labelPadding) / (getMaxScore() - getMinScore());
List<Point> graphPoints = new ArrayList<>();
for (int i = 0; i < scores.size(); i++) {
int x1 = (int) (i * xScale + padding + labelPadding);
int y1 = (int) ((getMaxScore() - scores.get(i)) * yScale + padding);
graphPoints.add(new Point(x1, y1));
}
// draw white background
g2.setColor(Color.WHITE);
g2.fillRect(padding + labelPadding, padding, getWidth() - (2 * padding) - labelPadding, getHeight() - 2 * padding - labelPadding);
g2.setColor(Color.BLACK);
// create hatch marks and grid lines for y axis.
for (int i = 0; i < numberYDivisions + 1; i++) {
int x0 = padding + labelPadding;
int x1 = pointWidth + padding + labelPadding;
int y0 = getHeight() - ((i * (getHeight() - padding * 2 - labelPadding)) / numberYDivisions + padding + labelPadding);
int y1 = y0;
if (scores.size() > 0) {
g2.setColor(gridColor);
g2.drawLine(padding + labelPadding + 1 + pointWidth, y0, getWidth() - padding, y1);
g2.setColor(Color.BLACK);
String yLabel = ((int) ((getMinScore() + (getMaxScore() - getMinScore()) * ((i * 1.0) / numberYDivisions)) * 100)) / 100.0 + "";
FontMetrics metrics = g2.getFontMetrics();
int labelWidth = metrics.stringWidth(yLabel);
g2.drawString(yLabel, x0 - labelWidth - 5, y0 + (metrics.getHeight() / 2) - 3);
}
g2.drawLine(x0, y0, x1, y1);
}
// and for x axis
for (int i = 0; i < scores.size(); i++) {
if (scores.size() > 1) {
int x0 = i * (getWidth() - padding * 2 - labelPadding) / (scores.size() - 1) + padding + labelPadding;
int x1 = x0;
int y0 = getHeight() - padding - labelPadding;
int y1 = y0 - pointWidth;
if ((i % ((int) ((scores.size() / 20.0)) + 1)) == 0) {
g2.setColor(gridColor);
g2.drawLine(x0, getHeight() - padding - labelPadding - 1 - pointWidth, x1, padding);
g2.setColor(Color.BLACK);
String xLabel = i + "";
FontMetrics metrics = g2.getFontMetrics();
int labelWidth = metrics.stringWidth(xLabel);
g2.drawString(xLabel, x0 - labelWidth / 2, y0 + metrics.getHeight() + 3);
}
g2.drawLine(x0, y0, x1, y1);
}
}
// create x and y axes
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, padding + labelPadding, padding);
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, getWidth() - padding, getHeight() - padding - labelPadding);
Stroke oldStroke = g2.getStroke();
g2.setColor(lineColor);
g2.setStroke(GRAPH_STROKE);
for (int i = 0; i < graphPoints.size() - 1; i++) {
int x1 = graphPoints.get(i).x;
int y1 = graphPoints.get(i).y;
int x2 = graphPoints.get(i + 1).x;
int y2 = graphPoints.get(i + 1).y;
g2.drawLine(x1, y1, x2, y2);
}
g2.setStroke(oldStroke);
g2.setColor(pointColor);
for (int i = 0; i < graphPoints.size(); i++) {
int x = graphPoints.get(i).x - pointWidth / 2;
int y = graphPoints.get(i).y - pointWidth / 2;
int ovalW = pointWidth;
int ovalH = pointWidth;
g2.fillOval(x, y, ovalW, ovalH);
}
}
// #Override
// public Dimension getPreferredSize() {
// return new Dimension(width, heigth);
// }
private double getMinScore() {
double minScore = Double.MAX_VALUE;
for (Double score : scores) {
minScore = Math.min(minScore, score);
}
return minScore;
}
private double getMaxScore() {
double maxScore = Double.MIN_VALUE;
for (Double score : scores) {
maxScore = Math.max(maxScore, score);
}
return maxScore;
}
public void setScores(List<Double> scores) {
this.scores = scores;
invalidate();
this.repaint();
}
public List<Double> getScores() {
return scores;
}
private static void createAndShowGui() {
List<Double> scores = new ArrayList<>();
Random random = new Random();
int maxDataPoints = 40;
int maxScore = 10;
for (int i = 0; i < maxDataPoints; i++) {
scores.add((double) random.nextDouble() * maxScore);
// scores.add((double) i);
}
GraphPanel mainPanel = new GraphPanel(scores);
mainPanel.setPreferredSize(new Dimension(800, 600));
JFrame frame = new JFrame("DrawGraph");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
It looks like this:
Or simply use the JFreechart library - http://www.jfree.org/jfreechart/ .
There exist many open source projects that handle all the drawing of line charts for you with a couple of lines of code. Here's how you can draw a line chart from data in a couple text (CSV) file with the XChart library. Disclaimer: I'm the lead developer of the project.
In this example, two text files exist in ./CSV/CSVChartRows/. Notice that each row in the files represents a data point to be plotted and that each file represents a different series. series1 contains x, y, and error bar data, whereas series2 contains just x and y, data.
series1.csv
1,12,1.4
2,34,1.12
3,56,1.21
4,47,1.5
series2.csv
1,56
2,34
3,12
4,26
Source Code
public class CSVChartRows {
public static void main(String[] args) throws Exception {
// import chart from a folder containing CSV files
XYChart chart = CSVImporter.getChartFromCSVDir("./CSV/CSVChartRows/", DataOrientation.Rows, 600, 400);
// Show it
new SwingWrapper(chart).displayChart();
}
}
Resulting Plot
Override the paintComponent method of your panel so you can custom draw. Like this:
#Override
public void paintComponent(Graphics g) {
Graphics2D gr = (Graphics2D) g; //this is if you want to use Graphics2D
//now do the drawing here
...
}
Hovercraft Full Of Eels' answer is very good, but i had to change it a bit in order to get it working on my program:
int y1 = (int) ((this.height - 2 * BORDER_GAP) - (values.get(i) * yScale - BORDER_GAP));
instead of
int y1 = (int) (scores.get(i) * yScale + BORDER_GAP);
because if i used his way the graphic would be upside down
(you'd see it if you used hardcoded values (e.g 1,3,5,7,9) instead of random values)

Bar Chart in java

I want to change the height of each bar (for example 10 for red part and 20 for blue part). But when I increase the height value it will increase the chart from bottom whereas I want the change to top! Do you know what is wrong with it?
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ChartPanel extends JPanel {
private double[] values;
private String[] names;
private String title;
public ChartPanel(double[] v, String[] n, String t) {
names = n;
values = v;
title = t;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (values == null || values.length == 0)
return;
double minValue = 0;
double maxValue = 0;
for (int i = 0; i < values.length; i++) {
if (minValue > values[i])
minValue = values[i];
if (maxValue < values[i])
maxValue = values[i];
}
Dimension d = getSize();
int clientWidth = d.width;
int clientHeight = d.height;
int barWidth = clientWidth / values.length;
Font titleFont = new Font("SansSerif", Font.BOLD, 20);
FontMetrics titleFontMetrics = g.getFontMetrics(titleFont);
Font labelFont = new Font("SansSerif", Font.PLAIN, 10);
FontMetrics labelFontMetrics = g.getFontMetrics(labelFont);
int titleWidth = titleFontMetrics.stringWidth(title);
int y = titleFontMetrics.getAscent();
int x = (clientWidth - titleWidth) / 2;
g.setFont(titleFont);
g.drawString(title, x, y);
int top = titleFontMetrics.getHeight();
int bottom = labelFontMetrics.getHeight();
if (maxValue == minValue)
return;
double scale = (clientHeight - top - bottom) / (maxValue - minValue);
y = clientHeight - labelFontMetrics.getDescent();
g.setFont(labelFont);
for (int i = 0; i < values.length; i++) {
int valueX = i * barWidth + 1;
int valueY = top;
int height = (int) (values[i] * scale);
if (values[i] >= 0)
valueY += (int) ((maxValue - values[i]) * scale);
else {
valueY += (int) (maxValue * scale);
height = -height;
}
g.setColor(Color.red);
g.fillRect(valueX, valueY, barWidth - 80, height);
g.setColor(Color.black);
g.drawRect(valueX, valueY, barWidth - 80, height);
g.setColor(Color.blue);
g.fillRect(valueX, valueY + 20, barWidth - 80, height-20 );
g.setColor(Color.black);
g.drawRect(valueX, valueY + 20, barWidth - 80, height-20 );
int labelWidth = labelFontMetrics.stringWidth(names[i]);
x = i * barWidth + (barWidth - labelWidth) / 2;
g.drawString(names[i], x, y);
}
}
public static void main(String[] argv) {
JFrame f = new JFrame();
f.setSize(400, 300);
double[] values = new double[3];
String[] names = new String[3];
values[0] = 1;
names[0] = "Item 1";
values[1] = 2;
names[1] = "Item 2";
values[2] = 4;
names[2] = "Item 3";
f.getContentPane().add(new ChartPanel(values, names, "title"));
WindowListener wndCloser = new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
};
f.addWindowListener(wndCloser);
f.setVisible(true);
}
}
Because Java's graphics uses the top left corner as the origin, when you add to the height it will increase down instead of up. Try changing this:
g.setColor(Color.red);
g.fillRect(valueX, valueY, barWidth - 80, height);
g.setColor(Color.black);
g.drawRect(valueX, valueY, barWidth - 80, height);
To this:
g.setColor(Color.red);
g.fillRect(valueX, valueY - 20, barWidth - 80, height);
g.setColor(Color.black);
g.drawRect(valueX, valueY - 20, barWidth - 80, height);
I tried this and it added to the red portion of the bar at the top.
Here is a shot of the original code:
And with the change:

Categories

Resources