//******************************************************************************
//
// File:    XYPlot.java
// Package: edu.rit.numeric.plot.impl
// Unit:    Class edu.rit.numeric.plot.impl.XYPlot
//
// This Java source file is copyright (C) 2009 by Alan Kaminsky. All rights
// reserved. For further information, contact the author, Alan Kaminsky, at
// ark@cs.rit.edu.
//
// This Java source file is part of the Parallel Java Library ("PJ"). PJ 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 3 of the License, or (at your option) any later version.
//
// PJ 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.
//
// A copy of the GNU General Public License is provided in the file gpl.txt. You
// may also obtain a copy of the GNU General Public License on the World Wide
// Web at http://www.gnu.org/licenses/gpl.html.
//
//******************************************************************************

package edu.rit.numeric.plot.impl;

import edu.rit.numeric.CurveSmoothing;

import edu.rit.swing.Displayable;
import edu.rit.swing.Drawable;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.Stroke;

import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;

import java.util.ArrayList;

/**
 * Class XYPlot provides an XY plot. The plot has an X axis, a Y axis, and one
 * or more plot series (type {@linkplain PlotSeries}). The plot may optionally
 * have a {@linkplain Grid}. Labels (class {@linkplain Label}) may be added to a
 * plot and are drawn after drawing all the plot series.
 *
 * @author  Alan Kaminsky
 * @version 06-Oct-2009
 */
public class XYPlot
	implements Displayable
	{

// Exported constants.

	/**
	 * The default margin (54).
	 */
	public static final double DEFAULT_MARGIN = 54.0;

	/**
	 * The default background paint (white).
	 */
	public static final Paint DEFAULT_BACKGROUND = Color.white;

// Hidden data members.

	/**
	 * X axis.
	 */
	protected NumericalAxis myXAxis;

	/**
	 * Y axis.
	 */
	protected NumericalAxis myYAxis;

	/**
	 * Grid.
	 */
	protected Grid myGrid;

	/**
	 * List of plot series.
	 */
	protected ArrayList myPlotSeries = new ArrayList();

	/**
	 * List of labels.
	 */
	protected ArrayList

* This XY plot is drawn so that the upper left corner (including the * margins) is located at the display coordinates (0, 0) in the graphics * context. * * @param g2d 2-D graphics context. */ public void draw (Graphics2D g2d) { double sx1, sy1, sx2, sy2; double[] x1, y1, x2, y2; double dx1, dy1, dx2, dy2; // Save graphics context. AffineTransform oldTransform = g2d.getTransform(); Stroke oldStroke = g2d.getStroke(); Paint oldPaint = g2d.getPaint(); Shape oldClip = g2d.getClip(); // Compute origin of plotting area. double originx = myLeftMargin; double originy = myHeight - myBottomMargin; // Compute clip rectangle for plotting area. double plotWidth = myWidth - myLeftMargin - myRightMargin; double plotHeight = myHeight - myTopMargin - myBottomMargin; Shape plottingArea = new Rectangle2D.Double (/*x*/ myLeftMargin - 3, /*y*/ myTopMargin - 3, /*w*/ plotWidth + 6, /*h*/ plotHeight + 6); // Draw grid if any. if (myGrid != null) { g2d.translate (originx, originy); myGrid.drawHorizontalGridlines (g2d, myYAxis, plotWidth); myGrid.drawVerticalGridlines (g2d, myXAxis, plotHeight); g2d.setTransform (oldTransform); } // Draw X axis. double xaxisy = myYAxis.getDisplayDistance (myYAxis.getCrossing()); g2d.translate (originx, originy - xaxisy); myXAxis.drawHorizontal (g2d); g2d.setTransform (oldTransform); // Draw Y axis. double yaxisx = myXAxis.getDisplayDistance (myXAxis.getCrossing()); g2d.translate (originx + yaxisx, originy); myYAxis.drawVertical (g2d); g2d.setTransform (oldTransform); // Draw each plot series. for (PlotSeries thePlotSeries : myPlotSeries) { Drawable theDots = thePlotSeries.getDots(); Stroke theStroke = thePlotSeries.getStroke(); Paint thePaint = thePlotSeries.getColor(); // Draw line segments or smooth Bezier curves, clipped to the // plotting area. if (theStroke != null && thePaint != null) { // If there are no lines, draw nothing. int n = thePlotSeries.getLineCount(); if (n > 0) { g2d.clip (plottingArea); g2d.translate (originx, originy); g2d.setStroke (theStroke); g2d.setPaint (thePaint); // Get starting and ending coordinates of each segment. x1 = new double [n]; y1 = new double [n]; x2 = new double [n]; y2 = new double [n]; for (int i = 0; i < n; ++ i) { sx1 = thePlotSeries.getLineX1 (i); sy1 = thePlotSeries.getLineY1 (i); sx2 = thePlotSeries.getLineX2 (i); sy2 = thePlotSeries.getLineY2 (i); x1[i] = myXAxis.getDisplayDistance (sx1); y1[i] = -myYAxis.getDisplayDistance (sy1); x2[i] = myXAxis.getDisplayDistance (sx2); y2[i] = -myYAxis.getDisplayDistance (sy2); } // Get and draw a general path object. g2d.draw (thePlotSeries.isSmooth() ? getCurvedPath (x1, y1, x2, y2, n) : getStraightPath (x1, y1, x2, y2, n)); g2d.setTransform (oldTransform); g2d.setClip (oldClip); } } // Draw each dot that falls within the plotting area. if (theDots != null) { int n = thePlotSeries.getDotCount(); for (int i = 0; i < n; ++ i) { // Get point location. sx1 = thePlotSeries.getDotX (i); sy1 = thePlotSeries.getDotY (i); if (myXAxis.includesValue (sx1) && myYAxis.includesValue (sy1)) { // Translate the display origin to the point's location. dx1 = myXAxis.getDisplayDistance (sx1); dy1 = myYAxis.getDisplayDistance (sy1); g2d.translate (originx + dx1, originy - dy1); // Draw the dot. theDots.draw (g2d); g2d.setTransform (oldTransform); } } } } // Draw each label. for (Label theLabel : myLabels) { // Translate the display origin to the point's location. if (theLabel.getPositioning() / Label.PIXEL_COORDINATES == 1) { dx1 = theLabel.getLocationX(); dy1 = theLabel.getLocationY(); } else { dx1 = myXAxis.getDisplayDistance (theLabel.getLocationX()); dy1 = myYAxis.getDisplayDistance (theLabel.getLocationY()); } g2d.translate (originx + dx1, originy - dy1); // Draw the label. theLabel.draw (g2d); g2d.setTransform (oldTransform); } // Restore graphics context. g2d.setStroke (oldStroke); g2d.setPaint (oldPaint); } /** * Returns a path consisting of straight line segments. */ private static GeneralPath getStraightPath (double[] x1, double[] y1, double[] x2, double[] y2, int n) { GeneralPath path = new GeneralPath(); path.moveTo ((float) x1[0], (float) y1[0]); for (int i = 0; i < n; ++ i) { path.lineTo ((float) x2[i], (float) y2[i]); if (i < n-1 && (x2[i] != x1[i+1] || y2[i] != y1[i+1])) { path.moveTo ((float) x1[i+1], (float) y1[i+1]); } } return path; } /** * Returns a path consisting of Bezier curve segments. */ private static GeneralPath getCurvedPath (double[] x1, double[] y1, double[] x2, double[] y2, int n) { GeneralPath path = new GeneralPath(); // Working storage for Bezier control points. double[] xu = new double [n+1]; double[] xa = new double [n+1]; double[] xc = new double [n+1]; double[] yu = new double [n+1]; double[] ya = new double [n+1]; double[] yc = new double [n+1]; // Iterate over all sequences of contiguous segments. first = index of // first segment in sequence. last = 1 + index of last segment in // sequence. int first = 0; int last; while (first < n) { // Scan until we hit a noncontiguous segment. xu[first] = x1[first]; yu[first] = y1[first]; last = first + 1; while (last < n && x2[last-1] == x1[last] && y2[last-1] == y1[last]) { xu[last] = x1[last]; yu[last] = y1[last]; ++ last; } xu[last] = x2[last-1]; yu[last] = y2[last-1]; // Get number of segments in sequence. int len = last - first; if (len == 1) { // Only one segment in sequence. Draw a straight line. path.moveTo ((float) xu[first], (float) yu[first]); path.lineTo ((float) xu[last], (float) yu[last]); } else { // More than one segment in sequence. Compute Bezier control // points. CurveSmoothing.computeBezierOpen (xu, xa, xc, first, len+1); CurveSmoothing.computeBezierOpen (yu, ya, yc, first, len+1); // Draw Bezier curves. path.moveTo ((float) xu[first], (float) yu[first]); for (int i = first; i < last; ++ i) { path.curveTo ((float) xa[i], (float) ya[i], (float) xc[i], (float) yc[i], (float) xu[i+1], (float) yu[i+1]); } } // Go to the next sequence of contiguous segments. first = last; } return path; } /** * Returns this displayable object's bounding box. This is the smallest * rectangle that encloses all of this displayable object. */ public Rectangle2D getBoundingBox() { return myBoundingBox; } /** * Returns this displayable object's background paint. */ public Paint getBackgroundPaint() { return myBackgroundPaint; } }

Ads help maintain this website.