JSci.awt.Graph2D.vm Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jsci Show documentation
Show all versions of jsci Show documentation
JSci is a set of open source Java packages. The aim is to encapsulate scientific methods/principles in the most natural way possible. As such they should greatly aid the development of scientific based software.
It offers: abstract math interfaces, linear algebra (support for various matrix and vector types), statistics (including probability distributions), wavelets, newtonian mechanics, chart/graph components (AWT and Swing), MathML DOM implementation, ...
Note: some packages, like javax.comm, for the astro and instruments package aren't listed as dependencies (not available).
The newest version!
package ${package};
import java.awt.*;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
#if(!($api == "AWT"))import JSci.awt.*;#end
import JSci.maths.ExtraMath;
/**
* The ${className} superclass provides an abstract encapsulation of 2D graphs.
* There is some support for the handling of NaN values.
* @version 1.8
* @author Mark Hale
*/
public abstract class ${className} extends ${extendsClassName} implements GraphDataListener {
public final static int LINEAR_SCALE = 0;
public final static int LOG_SCALE = 1;
/**
* Data model.
*/
protected Graph2DModel model;
/**
* Origin.
*/
protected Point origin = new Point();
protected Graph2D.DataMarker dataMarker = Graph2D.DataMarker.NONE;
/**
* Series colors.
*/
protected Color seriesColor[]={Color.black,Color.blue,Color.green,Color.red,Color.yellow,Color.cyan,Color.lightGray,Color.magenta,Color.orange,Color.pink};
/**
* Axis numbering.
*/
protected boolean xNumbering = true, yNumbering = true;
protected NumberFormat xNumberFormat = new DecimalFormat("${numberFormat}");
protected NumberFormat yNumberFormat = new DecimalFormat("${numberFormat}");
protected boolean xAxisLine = true, yAxisLine = true;
protected boolean gridLines = false;
private final Color gridLineColor = Color.lightGray;
/**
* Axis scaling.
*/
private float xScale,yScale;
/**
* Axis scaling type.
*/
private int xScaleType, yScaleType;
/**
* Axis extrema.
*/
private float minX, minY, maxX, maxY;
private float scaledMinX, scaledMinY, scaledMaxX, scaledMaxY;
private boolean autoXExtrema=true, autoYExtrema=true;
private float xGrowth, yGrowth;
/**
* Axis numbering increment.
*/
private final float xIncPixels = 40.0f;
private final float yIncPixels = 40.0f;
private float xInc,yInc;
private boolean autoXInc=true,autoYInc=true;
/**
* Padding.
*/
protected final int scalePad=5;
protected final int axisPad=25;
protected int leftAxisPad;
/**
* Constructs a 2D graph.
*/
public ${className}(Graph2DModel gm) {
model=gm;
model.addGraphDataListener(this);
dataChanged(new GraphDataEvent(model));
}
/**
* Sets the data plotted by this graph to the specified data.
*/
public final void setModel(Graph2DModel gm) {
model.removeGraphDataListener(this);
model=gm;
model.addGraphDataListener(this);
dataChanged(new GraphDataEvent(model));
}
/**
* Returns the model used by this graph.
*/
public final Graph2DModel getModel() {
return model;
}
/**
* Implementation of GraphDataListener.
* Supports {@link JSci.awt.GraphDataEvent#isIncremental() incremental} updates.
* Application code will not use this method explicitly, it is used internally.
*/
public void dataChanged(GraphDataEvent e) {
if(e.isIncremental()) {
Graphics g = getOffscreenGraphics();
if(g == null)
return;
final int series = e.getSeries();
if(series == GraphDataEvent.ALL_SERIES) {
model.firstSeries();
int n = 0;
do {
final int i = model.seriesLength() - 1;
incrementalRescale(model.getXCoord(i), model.getYCoord(i));
g.setColor(seriesColor[n]);
drawDataPoint(g, i);
n++;
} while(model.nextSeries());
} else {
model.firstSeries();
int n=0;
for(; nseriesColor.length) {
Color tmp[]=seriesColor;
seriesColor=new Color[n];
System.arraycopy(tmp,0,seriesColor,0,tmp.length);
for(int i=tmp.length; i maxX)
max = autoXExtrema ? Math.max(x, maxX+xGrowth) : maxX+xGrowth;
else
max = maxX;
rescaleX(min, max);
if(y < minY)
min = autoYExtrema ? Math.min(y, minY-yGrowth) : minY-yGrowth;
else
min = minY;
if(y > maxY)
max = autoYExtrema ? Math.max(y, maxY+yGrowth) : maxY+yGrowth;
else
max = maxY;
rescaleY(min, max);
}
/**
* Turns axis numbering on/off.
* Default is on.
*/
public final void setNumbering(boolean flag) {
setNumbering(flag, flag);
}
public final void setNumbering(boolean xFlag, boolean yFlag) {
xNumbering = xFlag;
yNumbering = yFlag;
leftAxisPad = axisPad;
if(yNumbering && getFont() != null) {
// adjust leftAxisPad to accomodate y-axis numbering
final FontMetrics metrics = getFontMetrics(getFont());
final int maxYNumLen = metrics.stringWidth(yNumberFormat.format(maxY));
final int minYNumLen = metrics.stringWidth(yNumberFormat.format(minY));
int yNumPad = Math.max(minYNumLen, maxYNumLen);
if(scaledMinX<0.0f) {
final int negXLen = (int)((Math.max(getSize().width,getMinimumSize().width)-2*(axisPad+scalePad))*scaledMinX/(scaledMinX-scaledMaxX));
yNumPad = Math.max(yNumPad-negXLen, 0);
}
leftAxisPad += yNumPad;
}
rescale();
}
public void addNotify() {
super.addNotify();
// getFont() is now not null
// recalculate padding
setNumbering(xNumbering, yNumbering);
}
/**
* Sets the display format used for axis numbering.
* Convenience method.
* @see #setXNumberFormat(NumberFormat)
* @see #setYNumberFormat(NumberFormat)
*/
public final void setNumberFormat(NumberFormat format) {
xNumberFormat = format;
yNumberFormat = format;
setNumbering(xNumbering, yNumbering);
}
/**
* Sets the display format used for x-axis numbering.
*/
public final void setXNumberFormat(NumberFormat format) {
xNumberFormat = format;
setNumbering(xNumbering, yNumbering);
}
/**
* Sets the display format used for y-axis numbering.
*/
public final void setYNumberFormat(NumberFormat format) {
yNumberFormat = format;
setNumbering(xNumbering, yNumbering);
}
/**
* Turns the axis lines on/off.
*/
public final void setAxisLines(boolean xFlag, boolean yFlag) {
xAxisLine = xFlag;
yAxisLine = yFlag;
redraw();
}
/**
* Turns grid lines on/off.
* Default is off.
*/
public final void setGridLines(boolean flag) {
gridLines = flag;
redraw();
}
/**
* Sets the x-axis scale type.
* @param t a _SCALE constant.
*/
public final void setXScale(int t) {
if(xScaleType != t) {
xScaleType = t;
dataChanged(new GraphDataEvent(model));
}
}
/**
* Sets the y-axis scale type.
* @param t a _SCALE constant.
*/
public final void setYScale(int t) {
if(yScaleType != t) {
yScaleType = t;
dataChanged(new GraphDataEvent(model));
}
}
/**
* Sets the x-axis numbering increment.
* @param dx use 0.0f for auto-adjusting (default).
*/
public final void setXIncrement(float dx) {
if(dx < 0.0f) {
throw new IllegalArgumentException("Increment should be positive.");
} else if(dx == 0.0f) {
if(!autoXInc) {
autoXInc = true;
rescale();
}
} else {
autoXInc = false;
if(dx != xInc) {
xInc = dx;
rescale();
}
}
}
/**
* Returns the x-axis numbering increment.
*/
public final float getXIncrement() {
return xInc;
}
/**
* Sets the y-axis numbering increment.
* @param dy use 0.0f for auto-adjusting (default).
*/
public final void setYIncrement(float dy) {
if(dy < 0.0f) {
throw new IllegalArgumentException("Increment should be positive.");
} else if(dy == 0.0f) {
if(!autoYInc) {
autoYInc = true;
rescale();
}
} else {
autoYInc = false;
if(dy != yInc) {
yInc = dy;
rescale();
}
}
}
/**
* Returns the y-axis numbering increment.
*/
public final float getYIncrement() {
return yInc;
}
/**
* Sets the minimum/maximum values on the x-axis.
* Set both min and max to 0.0f for auto-adjusting (default).
*/
public final void setXExtrema(float min, float max) {
if(min==0.0f && max==0.0f) {
autoXExtrema=true;
// determine min and max from model
min=Float.POSITIVE_INFINITY;
max=Float.NEGATIVE_INFINITY;
float tmp;
model.firstSeries();
do {
for(int i=0;i 0.0f;
if(autoYInc) {
yInc = (float) ExtraMath.round((double)yIncPixels/(double)yScale, 1);
if(yInc == 0.0f)
yInc = Float.MIN_VALUE;
}
//assert yInc > 0.0f;
origin.x=leftAxisPad-Math.round(scaledMinX*xScale);
origin.y=thisHeight-axisPad+Math.round(scaledMinY*yScale);
redraw();
}
/**
* Converts a data point to screen coordinates.
*/
protected final Point dataToScreen(float x,float y) {
if(xScaleType == LOG_SCALE)
x = (float) Math.log(x);
if(yScaleType == LOG_SCALE)
y = (float) Math.log(y);
return scaledDataToScreen(x, y);
}
/**
* Converts a scaled data point to screen coordinates.
*/
protected final Point scaledDataToScreen(float x, float y) {
return new Point(origin.x+Math.round(xScale*x), origin.y-Math.round(yScale*y));
}
/**
* Converts a screen point to data coordinates.
*/
protected final Point2D.Float screenToData(Point p) {
double x = (double)(p.x-origin.x) / (double)xScale;
double y = (double)(origin.y-p.y) / (double)yScale;
if(xScaleType == LOG_SCALE)
x = Math.exp(x);
if(yScaleType == LOG_SCALE)
y = Math.exp(y);
return new Point2D.Float((float)x, (float)y);
}
/**
* Draws the graph axes.
*/
protected final void drawAxes(Graphics g) {
#if($api == "Swing")
// Swing optimised
final int width = getWidth();
final int height = getHeight();
#else
final Dimension size = getSize();
final int width = size.width;
final int height = size.height;
#end
g.setColor(getForeground());
// grid lines and numbering
if(gridLines || xNumbering) {
// x-axis numbering and vertical grid lines
float xAxisY;
if(scaledMinY > 0.0f)
xAxisY = scaledMinY;
else if(scaledMaxY < 0.0f)
xAxisY = scaledMaxY;
else
xAxisY = 0.0f;
for(double x=(scaledMinX>0.0f)?scaledMinX:xInc; x<=scaledMaxX; x+=xInc) {
Point p=scaledDataToScreen((float)x, xAxisY);
if(gridLines) {
g.setColor(gridLineColor);
g.drawLine(p.x, axisPad-scalePad, p.x, height-(axisPad-scalePad));
g.setColor(getForeground());
}
if(xNumbering) {
drawXLabel(g, x, p);
}
}
for(double x=-xInc; x>=scaledMinX; x-=xInc) {
Point p=scaledDataToScreen((float)x, xAxisY);
if(gridLines) {
g.setColor(gridLineColor);
g.drawLine(p.x, axisPad-scalePad, p.x, height-(axisPad-scalePad));
g.setColor(getForeground());
}
if(xNumbering) {
drawXLabel(g, x, p);
}
}
}
if(gridLines || yNumbering) {
// y-axis numbering and horizontal grid lines
float yAxisX;
if(scaledMinX > 0.0f)
yAxisX = scaledMinX;
else if(scaledMaxX < 0.0f)
yAxisX = scaledMaxX;
else
yAxisX = 0.0f;
for(double y=(scaledMinY>0.0f)?scaledMinY:yInc; y<=scaledMaxY; y+=yInc) {
Point p=scaledDataToScreen(yAxisX, (float)y);
if(gridLines) {
g.setColor(gridLineColor);
g.drawLine(leftAxisPad-scalePad, p.y, width-(axisPad-scalePad), p.y);
g.setColor(getForeground());
}
if(yNumbering) {
drawYLabel(g, y, p);
}
}
for(double y=-yInc; y>=scaledMinY; y-=yInc) {
Point p=scaledDataToScreen(yAxisX, (float)y);
if(gridLines) {
g.setColor(gridLineColor);
g.drawLine(leftAxisPad-scalePad, p.y, width-(axisPad-scalePad), p.y);
g.setColor(getForeground());
}
if(yNumbering) {
drawYLabel(g, y, p);
}
}
}
// axis lines
if(xAxisLine) {
// horizontal axis
if(scaledMinY > 0.0f) {
// draw at bottom
g.drawLine(leftAxisPad-scalePad, height-axisPad, width-(axisPad-scalePad), height-axisPad);
} else if(scaledMaxY < 0.0f) {
// draw at top
g.drawLine(leftAxisPad-scalePad, axisPad, width-(axisPad-scalePad), axisPad);
} else {
// draw through y origin
g.drawLine(leftAxisPad-scalePad, origin.y, width-(axisPad-scalePad), origin.y);
}
}
if(yAxisLine) {
// vertical axis
if(scaledMinX > 0.0f) {
// draw at left
g.drawLine(leftAxisPad, axisPad-scalePad, leftAxisPad, height-(axisPad-scalePad));
} else if(scaledMaxX < 0.0f) {
// draw at right
g.drawLine(width-axisPad, axisPad-scalePad, width-axisPad, height-(axisPad-scalePad));
} else {
// draw through x origin
g.drawLine(origin.x, axisPad-scalePad, origin.x, height-(axisPad-scalePad));
}
}
}
protected void drawXLabel(Graphics g, double x, Point p) {
double scaledX;
if(xScaleType == LOG_SCALE)
scaledX = Math.exp(x);
else
scaledX = x;
String str = xNumberFormat.format(scaledX);
FontMetrics metrics=g.getFontMetrics();
int strWidth=metrics.stringWidth(str);
int strHeight=metrics.getHeight();
g.drawLine(p.x,p.y,p.x,p.y+5);
g.drawString(str,p.x-strWidth/2,p.y+5+strHeight);
}
protected void drawYLabel(Graphics g, double y, Point p) {
double scaledY;
if(yScaleType == LOG_SCALE)
scaledY = Math.exp(y);
else
scaledY = y;
String str = yNumberFormat.format(scaledY);
FontMetrics metrics=g.getFontMetrics();
int strWidth=metrics.stringWidth(str);
int strHeight=metrics.getHeight();
g.drawLine(p.x,p.y,p.x-5,p.y);
g.drawString(str,p.x-8-strWidth,p.y+strHeight/3);
}
/**
* Draws the graph data.
* Override this method to change how the graph data is plotted.
*/
protected void drawData(Graphics g) {
// points
model.firstSeries();
g.setColor(seriesColor[0]);
int i;
for(i=0; i