/* * 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.display2d; import java.awt.Color; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.awt.image.WritableRaster; import java.util.ArrayList; import org.opensourcephysics.display.DisplayColors; import org.opensourcephysics.display.DrawingPanel; import org.opensourcephysics.display.Measurable; /** * DataRaster maps (x,y) data onto an image. * * The image has the same size as the data panel. * Every data point renders itself as one pixel. * * @author Wolfgang Christian * @version 1.0 */ public class DataRaster implements Measurable { /** * The drawing panel that determines the image size. */ public DrawingPanel primaryDrawingPanel = null; Color backgroundColor; ArrayList<ImageData> imageDatasets = new ArrayList<ImageData>(); boolean visible = true; protected double xmin = -1; protected double xmax = 1; protected double ymin = -1; protected double ymax = 1; protected int alpha = 255; // the alpha value for the image protected BufferedImage image; protected int maxPoints = 0x2ffff; // the maximum number of points that will be saved as image data. double xppu = 0; // x pixels per unit during last drawing double yppu = 0; /** * Constructs a DataRaster object that maps (x,y) data to image pixels. * * @param dp the drawing panel that will be used to calculate the image size * @param _xmin the mininum x value that can be mapped * @param _xmax the maximum x value that can be mapped * @param _ymin the mininum y value that can be mapped * @param _ymax the maximum y value that can be mapped */ public DataRaster(DrawingPanel dp, double _xmin, double _xmax, double _ymin, double _ymax) { primaryDrawingPanel = dp; if(primaryDrawingPanel!=null) { primaryDrawingPanel.setPixelScale(); } xmin = Math.min(_xmin, _xmax); xmax = Math.max(_xmin, _xmax); ymin = Math.min(_ymin, _ymax); ymax = Math.max(_ymin, _ymax); image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); // make a 1x1 image to start backgroundColor = new Color(image.getRGB(0, 0)); } /** * Appends an (x,y) datum to the image. * * @param x * @param y * @param dataIndex Description of Parameter */ public void append(int dataIndex, double x, double y) { checkIndex(dataIndex).append(x, y); } /** * Sets the data point marker color. * * @param dataIndex * @param color */ public void setColor(int dataIndex, Color color) { checkIndex(dataIndex).setColor(color); } /** * Clears all data from all Datasets. */ public void clear() { for(int i = 0, n = imageDatasets.size(); i<n; i++) { (imageDatasets.get(i)).clear(); } render(); } /** * Clears data from the i-th dataset. * @param i the dataset index */ public void clear(int i) { if(i<imageDatasets.size()){ imageDatasets.get(i).clear(); render(); } } /** * Ensures that the image data exists * * @param dataIndex */ protected ImageData checkIndex(int dataIndex) { //while(dataIndex>=imageDatasets.size()) { for(int i = imageDatasets.size()-1; i<dataIndex; i++) { ImageData d = new ImageData(DisplayColors.getLineColor(dataIndex)); imageDatasets.add(d); } return imageDatasets.get(dataIndex); } /** * Paints a new image using the existing data. * * returns the image buffer */ public synchronized BufferedImage render() { if(primaryDrawingPanel==null) { return null; } int xrange = primaryDrawingPanel.xToPix(xmax)-primaryDrawingPanel.xToPix(xmin); int yrange = primaryDrawingPanel.yToPix(ymin)-primaryDrawingPanel.yToPix(ymax); xrange = Math.min(xrange, primaryDrawingPanel.getWidth()); yrange = Math.min(yrange, primaryDrawingPanel.getHeight()); if((Math.abs(xrange)==0)||(Math.abs(yrange)==0)) { return null; // Produces exception in IE (Paco) } image = new BufferedImage(Math.abs(xrange), Math.abs(yrange), BufferedImage.TYPE_INT_ARGB); backgroundColor = new Color(image.getRGB(0, 0)); for(int i = 0, n = imageDatasets.size(); i<n; i++) { (imageDatasets.get(i)).render(); } return image; } /** * Draw the image containing the dataset pixels. * * @param panel the panel containing this data raster * @param g the graphics context upon which to draw */ public void draw(DrawingPanel panel, Graphics g) { if(!visible) { return; } if(primaryDrawingPanel!=panel) { return; // can only draw on one panel for now. } int xrange = panel.xToPix(xmax)-panel.xToPix(xmin); int yrange = panel.yToPix(ymin)-panel.yToPix(ymax); xrange = Math.min(xrange, panel.getWidth()); yrange = Math.min(yrange, panel.getHeight()); if((xrange==0)||(xrange==0)) { return; } // render a new image if the scale or image size change if((Math.abs(xrange)!=image.getWidth())|| // image size change (Math.abs(yrange)!=image.getHeight())||( // image size change xppu!=primaryDrawingPanel.getXPixPerUnit())||( // scale change yppu!=primaryDrawingPanel.getYPixPerUnit())) { render(); } double xmin = Math.max(primaryDrawingPanel.getXMin(), this.xmin); double ymax = Math.min(primaryDrawingPanel.getYMax(), this.ymax); if((image!=null)&&(image.getWidth()>1)) { g.drawImage(image, panel.xToPix(xmin), panel.yToPix(ymax), panel); // blast the image into the panel } } public boolean isMeasured() { return true; } public void setXMin(double _value) { xmin = _value; } public void setXMax(double _value) { xmax = _value; } public void setYMin(double _value) { ymin = _value; } public void setYMax(double _value) { ymax = _value; } public void setMinMax(double _minx, double _maxx, double _miny, double _maxy) { xmin = _minx; xmax = _maxx; ymin = _miny; ymax = _maxy; } public double getXMin() { return xmin; } public double getXMax() { return xmax; } public double getYMin() { return ymin; } public double getYMax() { return ymax; } public double getWidth() { return image.getRaster().getWidth(); } public double getHeight() { return image.getRaster().getHeight(); } public Color getBackgroundColor() { return backgroundColor; } int xToPix(int x) { return(int) (image.getRaster().getWidth()*(x-xmin)/(xmax-xmin)); } int yToPix(int y) { return(int) (image.getRaster().getHeight()*(ymax-y)/(ymax-ymin)); } public Color getPixColor(int xpix, int ypix) { return new Color(image.getRGB(xpix, ypix)); } /** * Sets the visibility of the DataRaster. * Drawing will be disabled if visible is false. * * @param isVisible */ public void setVisible(boolean isVisible) { visible = isVisible; } /** * ImageData stores data so that the raster can be recreated if the panel size changes. */ class ImageData { private int[] color; float[][] data; int nextPoint = 0; ImageData(Color c) { setColor(c); data = new float[2][64]; // create an array to hold 64 data points } void setColor(Color c) { color = new int[4]; color[0] = c.getRed(); color[1] = c.getGreen(); color[2] = c.getBlue(); color[3] = alpha; } private synchronized void increaseCapacity(int size) { size = Math.min(size, maxPoints); // do not let the number of data points exceed maxPoints float[][] newData = new float[2][size]; int newNext = Math.min(nextPoint, (3*size)/4); // drop 1/4 of the old data if the size is no longer increasing System.arraycopy(data[0], nextPoint-newNext, newData[0], 0, newNext); System.arraycopy(data[1], nextPoint-newNext, newData[1], 0, newNext); nextPoint = newNext; data = newData; } synchronized void clear() { data = new float[2][64]; // create an array to hold 64 data points nextPoint = 0; } void append(double x, double y) { if(Double.isNaN(x)||Double.isInfinite(x)||Double.isNaN(y)||Double.isInfinite(y)) { return; // do not append bad data } if(nextPoint>=data[0].length) { increaseCapacity(data[0].length*2); } data[0][nextPoint] = (float) x; // save value for later use data[1][nextPoint] = (float) y; nextPoint++; WritableRaster raster = image.getRaster(); if(raster.getWidth()<2) { return; // image is too small } double xmin = Math.max(primaryDrawingPanel.getXMin(), DataRaster.this.xmin); double ymax = Math.min(primaryDrawingPanel.getYMax(), DataRaster.this.ymax); int i = (int) (primaryDrawingPanel.getXPixPerUnit()*(x-xmin)+0.5); int j = (int) (primaryDrawingPanel.getYPixPerUnit()*(ymax-y)+0.5); if((i<0)||(j<0)||(i>=raster.getWidth())||(j>=raster.getHeight())) { return; // outside the image } try { raster.setPixel(i, j, color); // set the image pixel } catch(Exception ex) { System.out.println("Error setting raster in ImageData append."); //$NON-NLS-1$ } } void render() { WritableRaster raster = image.getRaster(); double xmin = Math.max(primaryDrawingPanel.getXMin(), DataRaster.this.xmin); double ymax = Math.min(primaryDrawingPanel.getYMax(), DataRaster.this.ymax); xppu = primaryDrawingPanel.getXPixPerUnit(); yppu = primaryDrawingPanel.getYPixPerUnit(); for(int c = 0; c<nextPoint; c++) { int i = (int) (xppu*(data[0][c]-xmin)+0.5); int j = (int) (yppu*(ymax-data[1][c])+0.5); if((i<0)||(j<0)||(i>=raster.getWidth())||(j>=raster.getHeight())) { continue; // outside the image raster } try { raster.setPixel(i, j, color); // set the image } catch(Exception ex) { System.out.println("Error setting raster in ImageData render."); //$NON-NLS-1$ } } } } // end of ImageData inner class } /* * 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 */