View Single Post
  #2 (permalink)  
Old 08-07-2007, 09:44 AM
toby toby is offline
Member
 
Join Date: Jul 2007
Posts: 40
toby is on a distinguished road
Here's a suggestion. You can keep the BarChart model separate from the view (the rendering of the chart) and the event code. MVC = model view controller.
Code:
import java.awt.*; import java.awt.event.*; import java.awt.font.*; import java.awt.geom.*; import java.text.NumberFormat; import javax.swing.*; import javax.swing.event.*; public class BarModelTest { BarChartModel chartModel = new BarChartModel(); BarChartPanel chartPanel = new BarChartPanel(); JSpinner barSpinner; JSpinner heightSpinner; NumberFormat nf; public BarModelTest() { nf = NumberFormat.getInstance(); nf.setMaximumFractionDigits(1); double[] values = { 19.7, 47.2, 32.4 }; int n = values.length; String[] labels = new String[n]; for(int j = 0; j < n; j++) labels[j] = nf.format(values[j]); chartModel.setValuesAndLabels(values, labels, n); chartPanel.setDataAndLabels(values, labels); chartPanel.setSelection(chartModel.getCurrentBar()); } private Box getUIPanel() { int bar = chartModel.getCurrentBar() +1; int nbr = chartModel.getNbrBars(); SpinnerNumberModel barModel = new SpinnerNumberModel(bar,1,nbr,1); barSpinner = new JSpinner(barModel); double val = chartModel.getBarValue(chartModel.getCurrentBar()); SpinnerNumberModel heightModel = new SpinnerNumberModel(val,10.0,100.0,0.1); heightSpinner = new JSpinner(heightModel); int bars = chartModel.getNbrBars(); int max = chartModel.getMaxBars(); SpinnerNumberModel sizeModel = new SpinnerNumberModel(bars,1,max,1); final JSpinner sizeSpinner = new JSpinner(sizeModel); ChangeListener cl = new ChangeListener() { public void stateChanged(ChangeEvent e) { JSpinner spinner = (JSpinner)e.getSource(); Number value = (Number)spinner.getValue(); if(spinner == barSpinner) { int index = value.intValue() -1; chartModel.setCurrentBar(index); heightSpinner.setValue(chartModel.getBarValue(index)); chartPanel.setSelection(index); } if(spinner == heightSpinner) { int selected = chartModel.getCurrentBar(); double barVal = value.doubleValue(); String label = nf.format(barVal); chartModel.setBarValue(selected, barVal); chartModel.setBarLabel(selected, label); chartPanel.setValueAndLabel(selected, barVal, label); } if(spinner == sizeSpinner) { int val = value.intValue(); SpinnerNumberModel model = (SpinnerNumberModel)barSpinner.getModel(); model.setMaximum(val); if(model.getNumber().intValue() > val) model.setValue(value); if(val > chartModel.getNbrBars()) { double height = ((Number)heightSpinner.getValue()).doubleValue(); chartModel.addBar(height, nf.format(height)); } else if(val < chartModel.getNbrBars()) { chartModel.removeBar(); } chartPanel.setDataAndLabels(chartModel.values, chartModel.labels); } } }; barSpinner.addChangeListener(cl); heightSpinner.addChangeListener(cl); sizeSpinner.addChangeListener(cl); Box box = Box.createHorizontalBox(); box.add(Box.createHorizontalGlue()); box.add(new JLabel("bar")); box.add(barSpinner); box.add(Box.createHorizontalGlue()); box.add(new JLabel("cell height")); box.add(heightSpinner); box.add(Box.createHorizontalGlue()); box.add(new JLabel("bars")); box.add(sizeSpinner); box.add(Box.createHorizontalGlue()); return box; } public static void main(String[] args) { BarModelTest test = new BarModelTest(); JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.getContentPane().add(test.chartPanel); f.getContentPane().add(test.getUIPanel(), "Last"); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); } } class BarChartPanel extends JPanel { double[] data = new double[0]; String[] labels = new String[0]; AffineTransform at; double maxValue; int selectedBar = -1; final int SPAD = 30; final int PAD = 20; public void setValueAndLabel(int index, double value, String label) { data[index] = value; labels[index] = label; // if value exceeds top of ordinate -> rescale if(value > maxValue) { maxValue = value; if(at != null) at = null; } repaint(); } public void setDataAndLabels(double[] values, String[] labels) { data = values; this.labels = labels; // new data -> rescale all data double max = getDataMax(); if(Math.abs(max - maxValue) > 1.0) { maxValue = max; if(at != null) at = null; } repaint(); } public void setSelection(int index) { selectedBar = index; repaint(); } protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D)g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); double w = getWidth(); double h = getHeight(); double xInc = (w-2*PAD)/data.length; if(at == null) initTransform(); g2.setPaint(new Color(51,51,51)); // ordinate g2.draw(new Line2D.Double(PAD, PAD, PAD, h-PAD)); // abcissa g2.draw(new Line2D.Double(PAD, h-PAD, w-PAD, h-PAD)); // draw labels Color color = g2.getColor(); Color selectedColor = Color.red; Font font = g2.getFont().deriveFont(14f); g2.setFont(font); FontRenderContext frc = g2.getFontRenderContext(); for(int j = 0; j < labels.length; j++) { float width =(float)font.getStringBounds(labels[j], frc).getWidth(); LineMetrics lm = font.getLineMetrics(labels[j], frc); float height = lm.getHeight(); float sx = (float)(PAD + j*xInc + (xInc - width)/2); float sy = (float)(h-SPAD + (height+SPAD)/2); // show selection g2.setPaint((j == selectedBar) ? selectedColor : color); g2.drawString(labels[j], sx, sy); } // plot data g2.setPaint(Color.blue); double x = PAD, lastY; double zero = modelToView(0); for(int j = 0; j < data.length; j++) { double y = modelToView(data[j]); g2.draw(new Line2D.Double(x, y, x+xInc, y)); if(j > 0) { if(data[j-1] < data[j]) g2.draw(new Line2D.Double(x, y, x, zero)); if(j < data.length-1 && data[j+1] <= data[j]) g2.draw(new Line2D.Double(x+xInc, y, x+xInc, zero)); } else if(j == 0 && data.length > 1 && data[0] >= data[1]) { g2.draw(new Line2D.Double(x+xInc, y, x+xInc, zero)); } if(j == data.length-1) { // last bar, close g2.draw(new Line2D.Double(x+xInc, y, x+xInc, zero)); } x += xInc; lastY = y; } } public Dimension getPreferredSize() { return new Dimension(400,400); } private double modelToView(double y) { Point2D.Double dest = new Point2D.Double(); Point2D.Double src = new Point2D.Double(0, y); at.transform(src, dest); return dest.y; } private void initTransform() { double w = getWidth(); double h = getHeight(); double maxY = getDataMax(); double xScale = (w-2*PAD)/data.length; double yScale = (h-2*PAD)/maxY; // move to origin double x = PAD; double y = h-PAD; at = AffineTransform.getTranslateInstance(x,y); // scale and flip(-) about the abcissa at.scale(xScale, -yScale); } private double getDataMax() { double max = Double.MIN_VALUE; for(int j = 0; j < data.length; j++) { if(data[j] > max) max = data[j]; } return max; } } class BarChartModel { double[] values = new double[0]; String[] labels = new String[0]; int currentBar = 0; int numBars = 0; int MAX_BARS = 5; public void setValuesAndLabels(double[] values, String[] labels, int nbr) { this.values = values; this.labels = labels; numBars = nbr; } public int getCurrentBar() { return currentBar; } public void setCurrentBar(int value) { currentBar = value; } public int getNbrBars() { return numBars; } public int getMaxBars() { return MAX_BARS; } public double getBarValue(int idx) { return values[idx]; } public void setBarValue(int idx, double value) { values[idx] = value; } public String getBarLabel(int idx) { return labels[idx]; } public void setBarLabel(int idx, String value) { labels[idx] = value; } public void addBar(double val, String lbl) { addValue(val); addLabel(lbl); numBars = values.length; } public void removeBar() { // remove currentBar numBars--; double[] tempValues = new double[numBars]; String[] tempLabels = new String[numBars]; for(int j = 0, k = 0; j < values.length; j++) { if(j == currentBar) { continue; } else { tempValues[k] = values[j]; tempLabels[k++] = labels[j]; } } values = tempValues; labels = tempLabels; } private void addValue(double value) { double[] temp = new double[numBars+1]; System.arraycopy(values, 0, temp, 0, numBars); temp[numBars] = value; values = temp; } private void addLabel(String label) { String[] temp = new String[numBars+1]; System.arraycopy(labels, 0, temp, 0, numBars); temp[numBars] = label; labels = temp; } }
Reply With Quote