📄 plotter.java
字号:
/* * YALE - Yet Another Learning Environment * Copyright (C) 2002, 2003 * Simon Fischer, Ralf Klinkenberg, Ingo Mierswa, * Katharina Morik, Oliver Ritthoff * Artificial Intelligence Unit * Computer Science Department * University of Dortmund * 44221 Dortmund, Germany * email: yale@ls8.cs.uni-dortmund.de * web: http://yale.cs.uni-dortmund.de/ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. */package edu.udo.cs.yale.gui;import edu.udo.cs.yale.Statistics;import edu.udo.cs.yale.StatisticsListener;import javax.swing.JPanel;import java.awt.geom.Point2D;import java.awt.geom.Line2D;import java.awt.geom.GeneralPath;import java.awt.geom.Rectangle2D;import java.awt.BasicStroke;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Color;import java.awt.Font;import java.awt.Stroke;import java.awt.BasicStroke;import java.awt.Point;import java.awt.event.*;import java.awt.geom.AffineTransform;import java.awt.font.FontRenderContext;import java.util.SortedSet;import java.util.TreeSet;import java.util.List;import java.util.LinkedList;import java.util.Iterator;import java.util.Comparator;import java.text.DecimalFormat;public class Plotter extends JPanel implements StatisticsListener { private static final Color[] LINE_COLORS = { Color.black, Color.red, Color.blue, Color.orange, new Color(0,200,0) }; private static final Stroke[] LINE_STROKES = { new BasicStroke(), new BasicStroke(1f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10f, new float[] {6f,4f}, 0f ), new BasicStroke(1f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10f, new float[] {6f,4f,2f,4f}, 0f ), new BasicStroke(1f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10f, new float[] {3f,3f}, 0f ), }; public static final LineStyle[] LINE_STYLES = new LineStyle[LINE_COLORS.length*LINE_STROKES.length]; static { for (int i = 0; i < LINE_STROKES.length; i++) { for (int j = 0; j < LINE_COLORS.length; j++) { LINE_STYLES[i*LINE_COLORS.length + j] = new LineStyle(LINE_COLORS[j], LINE_STROKES[i]); } } } public static class LineStyle { private Color color; private Stroke stroke; private LineStyle(Color color, Stroke stroke) { this.color = color; this.stroke = stroke; } public void set(Graphics2D g) { g.setColor(color); g.setStroke(stroke); } } private class Plot extends TreeSet { private int styleIndex; private Plot(int styleIndex) { super(new Comparator() { public int compare(Object o1, Object o2) { Point2D p1 = (Point2D)o1; Point2D p2 = (Point2D)o2; double dif = p1.getX() - p2.getX(); if (dif == 0.0) return 0; else return (dif < 0) ? -1 : 1; } }); this.styleIndex = styleIndex; } private LineStyle getLineStyle() { return LINE_STYLES[styleIndex%LINE_STYLES.length]; } } private class RotationListener extends MouseAdapter implements MouseMotionListener { private Point dragStart; private int rotXStart; private int rotZStart; public void mousePressed(MouseEvent e) { dragStart = e.getPoint(); rotXStart = rotX; rotZStart = rotZ; } public void mouseMoved(MouseEvent e) { } public void mouseDragged(MouseEvent e) { rotX = (rotXStart + (int)(dragStart.getY() - e.getY())) % 180; if (rotX < 0) rotX += 180; rotZ = (rotZStart + (int)(dragStart.getX()- e.getX())) % 360; if (rotZ < 0) rotZ += 360; statisticsUpdated(null); } } private int rotX = 60, rotZ = 30; private static final int[] TICS = {1,2,5}; private static final Font LABEL_FONT = new Font("Lucida Sans", Font.PLAIN, 11).deriveFont(AffineTransform.getScaleInstance(1, -1)); private static final Color GRID_COLOR = Color.lightGray; private static final int MARGIN = 20; private static final int LABEL_MARGIN_X = 15; private static final int LABEL_MARGIN_Y = 50; private Statistics statistics; private List plots = new LinkedList(); private double minX, maxX, minY, maxY, xTicSize, yTicSize; private int xAxis = 0; private int yAxis = -1; private boolean[] columns; private boolean hasChanged = false; private java.awt.Image cachedImage = null; private AffineTransform transform; private GNUPlotDialog gnuPlotDialog = new GNUPlotDialog(this); public Plotter(Statistics statistics) { this.statistics = statistics; statistics.addStatisticsListener(this); columns = new boolean[statistics.getNumberOfColumns()]; prepareData(); setBackground(Color.white); RotationListener listener = new RotationListener(); addMouseMotionListener(listener); addMouseListener(listener); } public Statistics getStatistics() { return statistics; } public void showGNUPlotDialog() { gnuPlotDialog.show(); } private void prepareData() { plots.clear(); maxX = maxY = Double.NEGATIVE_INFINITY; minX = minY = Double.POSITIVE_INFINITY; for (int column = 0; column < columns.length; column++) { if (columns[column]) { SortedSet points = new Plot(column); Iterator i = statistics.iterator(); while (i.hasNext()) { Object[] row = (Object[])i.next(); double x = Double.NaN; double y = Double.NaN; try { if ((row[column] != null) && (row[xAxis] != null)) { x = Double.parseDouble(row[xAxis].toString()); y = Double.parseDouble(row[column].toString()); points.add(new Point2D.Double(x, y)); minX = Math.min(x, minX); maxX = Math.max(x, maxX); minY = Math.min(y, minY); maxY = Math.max(y, maxY); } } catch (NumberFormatException e) { throw new IllegalArgumentException("Not a numerical data column: "+column); } } plots.add(points); } } if (statistics.getNumberOfRows() == 0) { minX = minY = 0; maxX = maxY = 1; } if (minX == maxX) { minX -= 0.5; maxX += 0.5; } if (minY == maxY) { minY -= 0.5; maxY += 0.5; } xTicSize = getTicSize(minX, maxX); yTicSize = getTicSize(minY, maxY); minX = Math.floor(minX / xTicSize) * xTicSize; maxX = Math.ceil(maxX / xTicSize) * xTicSize; minY = Math.floor(minY / yTicSize) * yTicSize; maxY = Math.ceil(maxY / yTicSize) * yTicSize; } private double getTicSize(double min, double max) { double delta = (max - min) / 5; double e = Math.floor(Math.log(delta) / Math.log(10)); double factor = Math.pow(10, e); for (int i = TICS.length-1; i >= 0 ; i--) { if (TICS[i]*factor <= delta) return TICS[i] * factor; } return factor; } private void drawPoints(Graphics2D g, double dx, double dy, double sx, double sy) { if (plots.size() == 0) return; int c = 0; Iterator p = plots.iterator(); while (p.hasNext()) { Plot plot = (Plot)p.next(); if (plot.size() > 0) { plot.getLineStyle().set(g); GeneralPath path = new GeneralPath(); Iterator i = plot.iterator(); boolean first = true; while (i.hasNext()) { Point2D point = (Point2D)i.next(); float gSpaceX = (float)((point.getX()+dx)*sx); float gSpaceY = (float)((point.getY()+dy)*sy); if (first) path.moveTo(gSpaceX, gSpaceY); else path.lineTo(gSpaceX, gSpaceY); java.awt.geom.RectangularShape circle = new java.awt.geom.Ellipse2D.Double(gSpaceX - 2, gSpaceY - 2, 5, 5); g.fill(circle); first = false; } g.draw(path); c = (c+1)%LINE_STYLES.length; } } } private void drawGrid(Graphics2D g, double dx, double dy, double sx, double sy) { DecimalFormat format = new DecimalFormat("0.00E0"); g.setFont(LABEL_FONT); g.setStroke(new BasicStroke(1)); for (int i = 0; i < 12; i++) { double x = i*xTicSize + minX; double y = i*yTicSize + minY; g.setColor(GRID_COLOR); g.draw(new Line2D.Double((x+dx) *sx, (minY+dy)*sy, (x+dx) *sx, (maxY+dy)*sy)); g.draw(new Line2D.Double((minX+dx)*sx, (y+dy) *sy, (maxX+dx)*sx, (y+dy) *sy)); g.setColor(Color.black); String label = format.format(y)+" "; Rectangle2D stringBounds = LABEL_FONT.getStringBounds(label, g.getFontRenderContext()); g.drawString(label, (float)((minX+dx)*sx - stringBounds.getWidth()), (float)((y+dy)*sy - stringBounds.getHeight()/2 - stringBounds.getY())); label = format.format(x); stringBounds = LABEL_FONT.getStringBounds(label, g.getFontRenderContext()); g.drawString(label, (float)((x+dx)*sx - stringBounds.getWidth()/2), (float)((minY+dy)*sy + stringBounds.getHeight())); } } private void draw(Graphics2D g, int pixWidth, int pixHeight) { Graphics2D coordinateSpace = (Graphics2D)g.create(); coordinateSpace.translate(LABEL_MARGIN_Y, LABEL_MARGIN_X); transform.translate(LABEL_MARGIN_Y, LABEL_MARGIN_X); double sx = ((double)pixWidth-LABEL_MARGIN_Y) / (maxX-minX); double sy = ((double)pixHeight-LABEL_MARGIN_X) / (maxY-minY); transform.scale(sx, sy); transform.translate(-minX, -minY); drawGrid(coordinateSpace, -minX, -minY, sx, sy); drawPoints(coordinateSpace, -minX, -minY, sx, sy); coordinateSpace.dispose(); } public void paintComponent(Graphics graphics) { super.paintComponent(graphics); if (yAxis == -1) { paint2DPlots(graphics); } else { createGNUPlot(graphics); if (cachedImage != null) graphics.drawImage(cachedImage, 0, 0, null); } } private void createGNUPlot(Graphics graphics) { if (hasChanged) { int count = 0; for (int i = 0; i < columns.length; i++) if (columns[i]) count++; if (count > 0) { int[] z = new int[count]; int j = 0; for (int i = 0; i < columns.length; i++) { if (columns[i]) z[j++] = i; } try { cachedImage = statistics.createGNUPlot(xAxis, yAxis, z, gnuPlotDialog.getGNUPlotCommands()+"\n"+ "set view "+rotX+","+rotZ+""); hasChanged = false; } catch (Exception e) { SwingTools.showSimpleErrorMessage("Error executing gnuplot", e); } } } } private void paint2DPlots(Graphics graphics) { prepareData(); if (plots.size() == 0) return; Graphics2D g = (Graphics2D)graphics; g.setColor(Color.black); int pixWidth = getWidth() - 2*MARGIN; int pixHeight = getHeight() - 2*MARGIN; transform = new AffineTransform(); Graphics2D scaled = (Graphics2D)g.create(); scaled.translate(MARGIN, MARGIN); transform.translate(MARGIN, MARGIN); scaled.translate(0, pixHeight+1); transform.translate(0, pixHeight+1); scaled.scale(1,-1); transform.scale(1,-1); draw(scaled, pixWidth, pixHeight); scaled.dispose(); String xAxisLabel = statistics.getColumnName(xAxis); Rectangle2D stringBounds = LABEL_FONT.getStringBounds(xAxisLabel, g.getFontRenderContext()); g.drawString(xAxisLabel, MARGIN + (int)(pixWidth/2-stringBounds.getWidth()/2), MARGIN + (int)(pixHeight + stringBounds.getY())+3); } public AffineTransform getTransform() { return transform; } public void statisticsUpdated(Statistics source) { hasChanged = true; repaint(); } public void setXAxis(int xAxis) { if (this.xAxis != xAxis) { this.hasChanged = true; this.xAxis = xAxis; } } public void setYAxis(int yAxis) { if (this.yAxis != yAxis) { this.hasChanged = true; this.yAxis = yAxis; } } public void plotColumn(int index, boolean plot) { if (this.columns[index] != plot) { this.hasChanged = true; columns[index] = plot; } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -