/*
* 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
*/