/************************************************************************** /* This class is part of the Java Plotlib Toolkit. /* /* Copyright (c) 1999-2004 by Bernhard Bablok (bablokb@gmx.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 Library General Public License for more details. /* /* You should have received a copy of the GNU General Public License /* along with this program; see the file COPYING. If not, write to /* the Free Software Foundation Inc., 59 Temple Place - Suite 330, /* Boston, MA 02111-1307 USA /**************************************************************************/ package de.bablokb.plotlib.core; import java.util.*; import java.awt.*; import java.awt.geom.*; /** The main driver for the plotting objects. This driver sets drawing defaults and supplies a transformation object to transform mathematical coordinates to pixel coordinates.

The transformation is defined by setting the range (mathematical coordinates) and the size (pixel coordinates) of the plot. The latter is usually done by the paint() method of a Canvas or Component.

If plots with differing ranges on a single canvas have to be done, simply create two PlottingContexts and use them one after the other.

The actual plotting is done by Plottable-objects themselves by passing a PlottingContext-object to their plot()-method @see de.bablokb.plotlib.core.Plottable @version $Revision: 1.2 $ @author $Author: bablokb $ */ public class PlottingContext { /** Internal helper constant for log-transformation. */ private final static double LOG10 = Math.log(10); /** Transformation constant. */ public final static int LINEAR = 1, // use only standard mapping LOGLINEAR = 2, // transform x-values logarithmically LINEARLOG = 3, // transform y-values logarithmically LOGLOG = 4; // transform both x- and y-values logarithmically /** Type of transformation. Defaults to LINEAR. */ private int iTransType = LINEAR; /** Range (mathematical coordinates) to be plotted. */ private Rectangle2D iRange = null; /** Size (pixel coordinates) of plot. */ private Dimension2D iSize = null; /** Transformation matrix. */ private AffineTransform iAf = null; /** Graphics2D object. */ private Graphics2D iG2 = null; /** Component object. */ private Component iComponent = null; ///////////////////////////////////////////////////////////////////////////// /** Create and initialize the plot context. */ public PlottingContext() { } ///////////////////////////////////////////////////////////////////////////// /** Set transformation type. */ public void setTransformationType(int type) { iTransType = type; } ///////////////////////////////////////////////////////////////////////////// /** Query transformation type. */ public int getTransformationType() { return iTransType; } ///////////////////////////////////////////////////////////////////////////// /** Set the range. */ public void setRange(Rectangle2D range) { iRange = range; iAf = null; } ///////////////////////////////////////////////////////////////////////////// /** Query the range. */ public Rectangle2D getRange() { return iRange; } ///////////////////////////////////////////////////////////////////////////// /** Set the plot size. */ public void setSize(Dimension2D size) { iSize = size; iAf = null; } ///////////////////////////////////////////////////////////////////////////// /** Query the size. */ public Dimension2D getSize() { return iSize; } ///////////////////////////////////////////////////////////////////////////// /** Set the Graphics2D object. */ public void setG2(Graphics2D g2) { iG2 = g2; } ///////////////////////////////////////////////////////////////////////////// /** Query the Graphics2 object. */ public Graphics2D getG2() { return iG2; } ///////////////////////////////////////////////////////////////////////////// /** Set the component object. */ public void setComponent(Component c) { iComponent = c; } ///////////////////////////////////////////////////////////////////////////// /** Query the component object. */ public Component getComponent() { return iComponent; } ///////////////////////////////////////////////////////////////////////////// /** Query the x-ratio. This is the amount in mathematical coordinates which maps to a unit in device coordinates (e.g. a pixel) in the direction of the x-axis.

FIXME: do we have to take care of special devices, e.g. printers? */ public double getXratio() { if (iRange == null) throw new MissingRangeException(); else if (iSize == null) throw new MissingSizeException(); return iRange.getWidth()/iSize.getWidth(); } ///////////////////////////////////////////////////////////////////////////// /** Query the y-ratio. This is the amount in mathematical coordinates which maps to a unit in device coordinates (e.g. a pixel) in the direction of the y-axis.

FIXME: do we have to take care of special devices, e.g. printers? */ public double getYratio() { if (iRange == null) throw new MissingRangeException(); else if (iSize == null) throw new MissingSizeException(); return iRange.getHeight()/iSize.getHeight(); } ///////////////////////////////////////////////////////////////////////////// /** Set defaults from system properties. Properties can be specified on the commandline or by importing a properties file. Supported properties:

FIXME: Setting the background color for the component results in a call to paint(), thus resulting in an endless loop (if setDefaults() is called from paint()). Is this a bug or a feature?

FIXME: What about additional defaults, e.g. plotlib.symbols.size? */ public void setDefaults() { Color col = Color.getColor("plotlib.foreground.color"); if (col != null && iG2 != null) iG2.setColor(col); float width = Plotlib.THIN; try { width = Float.parseFloat(System.getProperty("plotlib.width")); } catch (Exception e) { } BasicStroke s = new BasicStroke(width); iG2.setStroke(s); } ///////////////////////////////////////////////////////////////////////////// /** Calculate the affine transformation, which transforms mathematical coordinates into Java2D coordinates. */ public AffineTransform getMatrix() { if (iRange == null) throw new MissingRangeException(); else if (iSize == null) throw new MissingSizeException(); else if (iG2 == null) throw new MissingG2Exception(); if (iAf != null) return iAf; double xmin = iRange.getMinX(), xdel = iRange.getWidth(), ydel = iRange.getHeight(), ymax = iRange.getMinY() + ydel, pxmax = iSize.getWidth(), pymax = iSize.getHeight(); iAf = new AffineTransform(pxmax/xdel,0.0,0.0,-pymax/ydel, -pxmax*xmin/xdel,pymax*ymax/ydel); return iAf; } ///////////////////////////////////////////////////////////////////////////// /** Transform a given Point2D. */ public Point2D transform(Point2D p) { AffineTransform af = getMatrix(); Point2D ptSrc = (Point2D) p.clone(), ptDst = (Point2D) p.clone(); if (iTransType == LOGLINEAR || iTransType == LOGLOG) ptSrc.setLocation(Math.log(ptSrc.getX())*LOG10,ptSrc.getY()); if (iTransType == LINEARLOG || iTransType == LOGLOG) ptSrc.setLocation(ptSrc.getX(),Math.log(ptSrc.getY())*LOG10); af.transform(ptSrc,ptDst); return ptDst; } ///////////////////////////////////////////////////////////////////////////// /** Transform a given Line2D. */ public Line2D transform(Line2D l) { Line2D lDst = (Line2D) l.clone(); lDst.setLine(transform(l.getP1()),transform(l.getP2())); return lDst; } ///////////////////////////////////////////////////////////////////////////// /** Transform a given Rectangle2D. */ public Rectangle2D transform(Rectangle2D r) { Rectangle2D rDst = (Rectangle2D) r.clone(); Point2D.Double p1 = new Point2D.Double(rDst.getMinX(),rDst.getMinY()); Point2D.Double p2 = new Point2D.Double(rDst.getMaxX(),rDst.getMaxY()); rDst.setFrameFromDiagonal(transform(p1),transform(p2)); return rDst; } }