/* * Open Source Physics software is free software as described near the bottom of this code file. * * For additional information and documentation on Open Source Physics please see: * <http://www.opensourcephysics.org/> */ package org.opensourcephysics.display; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.GeneralPath; import org.opensourcephysics.numerics.Function; /** * FunctionDrawer draws a function from xmin to xmax. * * The function will be evaluated at every screen pixel unless the domain is set * using the initialize method. * * @author Wolfgang Christian * @author Joshua Gould * @version 1.0 */ public class FunctionDrawer implements Drawable, Measurable, Function { protected double[] xrange = new double[2]; protected double[] yrange = new double[2]; protected int numpts = 0; protected GeneralPath generalPath = new GeneralPath(); protected Function function; protected boolean filled = false; protected boolean measured = false; // set to true if function has been initialized. public Color color = Color.black; public boolean functionChanged = false; /** * Contstucts a FunctionDrawer with optimum resolution. * * @param f the function that will be drawn. */ public FunctionDrawer(Function f) { function = f; } /** * Creates the function drawer and initialzies the domain with the given values. * @param f Function * @param xmin double * @param xmax double * @param numpts int * @param filled boolean fills the area under the curve with the drawing color when true */ public FunctionDrawer(Function f, double xmin, double xmax, int numpts, boolean filled) { this(f); initialize(xmin, xmax, numpts, filled); } /** * Evalutes the function. * @param x * @return value of the function */ public double evaluate(double x) { return function.evaluate(x); } /** * Initialize the function range and the number of display points. * @param xmin the beginning value of the range. * @param xmax the ending value for the range * @param numpts the number of points to display * @param filled fills the area under the curve with the drawing color when true */ public void initialize(double xmin, double xmax, int numpts, boolean filled) { if(numpts<1) { return; } this.filled = filled; this.xrange[0] = xmin; this.xrange[1] = xmax; this.numpts = numpts; generalPath.reset(); if(numpts<1) { return; } yrange[0] = function.evaluate(xmin); yrange[1] = yrange[0]; // starting values for ymin and ymax if(filled) { generalPath.moveTo((float) xrange[0], 0); generalPath.lineTo((float) xrange[0], (float) yrange[0]); } else { generalPath.moveTo((float) xrange[0], (float) yrange[0]); } double x = xrange[0]; double dx = (xmax-xmin)/(numpts); for(int i = 0; i<numpts; i++) { x = x+dx; double y = function.evaluate(x); generalPath.lineTo((float) x, (float) y); if(y<yrange[0]) { yrange[0] = y; // the minimum value } if(y>yrange[1]) { yrange[1] = y; // the maximum value } } if(filled) { generalPath.lineTo((float) x, 0); generalPath.closePath(); } measured = true; } /** * Gets the general path that draws this function. * @return GeneralPath */ public GeneralPath getPath() { return(GeneralPath) (generalPath.clone()); } /** * Get the range of x values over which the function has been evaluated. * * @return double[2] the xmin and xmax values */ public double[] getXRange() { return xrange; } /** * Get the minimum and maximum y values for the function. * * @return double[2] the ymin and ymax values */ public double[] getYRange() { return yrange; } protected void checkRange(DrawingPanel panel) { // check to see if the range or function has changed if((xrange[0]==panel.getXMin())&&(xrange[1]==panel.getXMax())&&(numpts==panel.getWidth())&&!functionChanged) { return; } functionChanged = false; xrange[0] = panel.getXMin(); xrange[1] = panel.getXMax(); numpts = panel.getWidth(); generalPath.reset(); if(numpts<1) { return; } yrange[0] = function.evaluate(xrange[0]); yrange[1] = yrange[0]; // starting values for ymin and ymax if(filled) { generalPath.moveTo((float) xrange[0], 0); generalPath.lineTo((float) xrange[0], (float) yrange[0]); } else { generalPath.moveTo((float) xrange[0], (float) yrange[0]); } double x = xrange[0]; double dx = (xrange[1]-xrange[0])/(numpts); for(int i = 0; i<numpts; i++) { x = x+dx; double y = function.evaluate(x); if(!Double.isNaN(x)&&!Double.isNaN(y)) { y = Math.min(y, 1.0e+12); y = Math.max(y, -1.0e+12); generalPath.lineTo((float) x, (float) y); if(y<yrange[0]) { yrange[0] = y; // the minimum value } if(y>yrange[1]) { yrange[1] = y; // the maximum value } } } if(filled) { generalPath.lineTo((float) x, 0); generalPath.closePath(); } } /** * Draw the function on a drawing panel. * @param panel the drawing panel * @param g the graphics context */ public void draw(DrawingPanel panel, Graphics g) { if(!measured) { checkRange(panel); } Graphics2D g2 = (Graphics2D) g; g2.setColor(color); // transform from world to pixel coordinates Shape s = generalPath.createTransformedShape(panel.getPixelTransform()); if(filled) { g2.fill(s); g2.draw(s); } else { g2.draw(s); } } /** * Fills the area under the curve when true. * @param _filled boolean */ public void setFilled(boolean _filled) { filled = _filled; } /** * Sets the drawing color. * @param c Color */ public void setColor(Color c) { color = c; } // Implementation of measured interface. public boolean isMeasured() { return measured; } public double getXMin() { return xrange[0]; } public double getXMax() { return xrange[1]; } public double getYMin() { return yrange[0]; } public double getYMax() { return yrange[1]; } } /* * Open Source Physics software is free software; you can redistribute * it and/or modify it under the terms of the GNU General Public License (GPL) as * published by the Free Software Foundation; either version 2 of the License, * or(at your option) any later version. * Code that uses any portion of the code in the org.opensourcephysics package * or any subpackage (subdirectory) of this package must must also be be released * under the GNU GPL license. * * This software 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; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA * or view the license online at http://www.gnu.org/copyleft/gpl.html * * Copyright (c) 2007 The Open Source Physics project * http://www.opensourcephysics.org */