/* * Copyright (C) 2004 The Concord Consortium, Inc., * 10 Concord Crossing, Concord, MA 01742 * * Web Site: http://www.concord.org * Email: info@concord.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * END LICENSE */ package org.concord.swing.graph; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Image; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.util.Vector; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; /** * Plot a function of time. */ public class LineGraph extends JComponent implements ValueGraph { final static double DEFAULT_ORIGIN = 0.0; final static double DEFAULT_MAXIMUM = 100.0; final static int DEFAULT_CELL_SIZE = 20; protected Font smallFont = new Font("Arial",Font.PLAIN,9); protected Image bi; protected Graphics big; protected int width = 0, height = 0; protected Vector functions = new Vector(); protected Vector visibleFunctions = new Vector(); protected Vector colorList = new Vector(); protected double yOrigin = DEFAULT_ORIGIN, yMax= DEFAULT_MAXIMUM; protected boolean showingNegative = false; protected int cellSize = DEFAULT_CELL_SIZE; protected int cursorIndex = -1; protected Color cursorColor = Color.red; protected Color backColor, gridColor, textColor; protected boolean drawBoundsValues = false; protected DecimalFormat format = (DecimalFormat) NumberFormat.getNumberInstance(); protected ChangeListener sliderChanged = new ChangeListener() { public void stateChanged(ChangeEvent event) { if (slider instanceof JSlider) setScaleMax(slider.getMaximum() - slider.getValue()); LineGraph.this.repaint(); } }; protected JSlider slider; protected ComponentAdapter sizeChanged = new ComponentAdapter() { public void componentResized(ComponentEvent event) { setLineGraphSize(LineGraph.this.getSize()); } }; public LineGraph() { setBackColor(Color.black); setGridColor(new Color(0,100,0)); setTextColor(Color.green); format.setMinimumFractionDigits(2); format.setMaximumFractionDigits(2); DecimalFormatSymbols dfs=new DecimalFormatSymbols(); dfs.setDecimalSeparator('.'); format.setDecimalFormatSymbols(dfs); addComponentListener(sizeChanged); drawBoundsValues = true; } public void setBackColor(Color color) { backColor = color; } public void setGridColor(Color color) { gridColor = color; } public void setTextColor(Color color) { textColor = color; } public boolean isShowingNegative() { return showingNegative; } public int getCursorIndex() { return cursorIndex; } public Color getCursorColor() { return cursorColor; } public void setCursorIndex(int value) { cursorIndex = value; } public void setCursorColor(Color value) { cursorColor = value; } public void setShowingNegative(boolean negative) { showingNegative = true; } public int getCellSize() { return cellSize; } public void setCellSize(int cell) { cellSize = cell; } /** * Sets the color associated with a particular index. * <p> * @param color - Color of graph element. * @param index - int index of graph element. */ public void setColor(Color color, int index) { int n = colorList.size(); if (index >= n) { colorList.setSize(index + 1); } colorList.setElementAt(color, index); } /** * Returns color associated with a particular index. * <p> * @param index - int index of graph element. * @return - Color color of graph element. */ public Color getColor(int index) { int n = colorList.size(); Color color = colors[index % colors.length]; if (index < n) { Object object = colorList.elementAt(index); if (object instanceof Color) return (Color) object; } return color; } public JSlider getSlider() { if (slider == null) { setSlider(new JSlider()); } return slider; } public void setSlider(JSlider slider) { if (this.slider instanceof JSlider) { this.slider.removeChangeListener(sliderChanged); } this.slider = slider; if (this.slider instanceof JSlider) { this.slider.addChangeListener(sliderChanged); } } public void setYOrigin(double yOrigin) { this.yOrigin = yOrigin; } public void setYOrigin(float yOrigin) { setYOrigin((double) yOrigin); } public double getMax() { return yMax; } public void setMax(double yMax) { this.yMax = yMax; } public void setScaleMax(int scale) { double value = (((double) scale) - 50.0d) / 10.0d; yMax = Math.pow(10.0d, value); } protected double log10(double x) { return Math.log(x) / Math.log(10.0d); } public void setSliderValue() { int value = (int) (10.0d * log10(getMax()) + 50.0d); value = slider.getMaximum() - value; if (value < 0) value = 0; slider.setValue(value); } // public void setSliderValue() // { // double targetMax = getMax(); // int value = (int) (10.0d * log10(getMax()) + 50.0d); // value = slider.getMaximum() - value; // if (value < 0) // value = 0; // slider.setValue(value); // while (targetMax > getMax()) // { // slider.setValue(slider.getValue()+1); // } // } public void setMax(float yMax) { setMax((double) yMax); } public void setBoundsValuesVisible(boolean visible) { drawBoundsValues = visible; } public void reset() { functions.removeAllElements(); colorList.removeAllElements(); } protected void setLineGraphSize(Dimension size) { bi = createImage(size.width, size.height); } protected double [] getFunction(int index, double x) { int n = functions.size(); if (index >= n) { functions.setSize(index + 1); visibleFunctions.setSize(index + 1); } double [] function = (double []) functions.elementAt(index); if (function == null) { function = new double[getSize().width]; functions.setElementAt(function, index); visibleFunctions.setElementAt(function, index); for (int i = 0; i < function.length; i++) { function[i] = x; } } return function; } public void setFunctionVisible(boolean visible, int index) { double [] function = getFunction(index, 0.0); visibleFunctions.setElementAt(visible ? function : null, index); } public boolean isFunctionVisible(int index) { return (index < functions.size()) && (visibleFunctions.elementAt(index) != null); } /** * Updates graph element at index with new value. * Uses index mapping to determine the graph element. * @param x - double value to update. * @param index - int index of graph element. */ public void updateValue(double x, int index) { double [] function = getFunction(index, x); for (int i = 1; i < function.length; i++) { function[i-1] = function[i]; } function[function.length - 1] = x; repaint(); } public void updateValue(double x) { updateValue(x, 0); } public void updateValue(float x, int index) { updateValue((double) x, index); } public void updateValue(float x) { updateValue((double) x); } public void paintComponent(Graphics g) { super.paintComponent(g); update(g); } public void update(Graphics g) { Dimension dim = getSize(); width = dim.width; height = dim.height; int nx = (width / cellSize) + 1; int ny = (height / cellSize) + 1; if (bi == null) bi = createImage(width, height); big = bi.getGraphics(); big.setColor(backColor); big.fillRect(0, 0, width, height); big.setColor(gridColor); for(int i = 0; i < nx; i++) { big.drawLine(cellSize * i, 1, cellSize * i, height - 1); } for(int i = 0; i < ny; i++) { big.drawLine(1, cellSize * i, width - 1, cellSize * i); } double yUnit = showingNegative ? yOrigin + (double) (height / (2 * yMax)) : (double) (height / yMax); double yMin = showingNegative ? -yMax : yOrigin; for (int f = 0; f < functions.size(); f++) { Color fColor = getColor(f); big.setColor(fColor); double [] function = (double []) visibleFunctions.elementAt(f); if (function != null) { int x1 = 0; int y1 = (int) ((yMax - function[0]) * yUnit); for (int i = 1; i < function.length; i++) { int x2 = i; int y2 = (int) ((yMax - function[i]) * yUnit); big.drawLine(x1, y1, x2, y2); if (i == cursorIndex) { big.setColor(cursorColor); big.drawLine(x1, 0, x1, height); big.setColor(fColor); } x1 = x2; y1 = y2; } } } if (drawBoundsValues) { big.setColor(textColor); if (showingNegative) big.drawString(format.format(yOrigin), 0, height / 2); big.drawString(format.format(yMax), 0, 15); big.drawString(format.format(yMin), 0, height); } g.drawImage(bi, 0, 0, null); } public void sleep(int time) { try { Thread.sleep(time); } catch (Exception e) { } } public static void main(String [] args) { LineGraph graph = new LineGraph(); JFrame frame = new JFrame(); frame.getContentPane().add(graph, "Center"); frame.getContentPane().add(graph.getSlider(), "South"); graph.setShowingNegative(true); graph.setMax(2.0); frame.setSize(400, 300); frame.setVisible(true); double unit = 3.14159d / 100; for (int i = 0; i < 400; i++) { double sine = Math.sin(unit * i); double cosine = Math.cos(unit * i); graph.setCursorIndex(600 - i); graph.updateValue(sine, 0); graph.updateValue(cosine, 1); graph.updateValue(sine + cosine, 2); graph.sleep(50); } } }