You can see the story in these three pictures:
- A simple arithmetic plot with a proper trend line
- An incorrect logarithmic plot where the straight line is only correct at the end points
- A correct logarithmic plot. Note that the trend line intersects the data line at the same spots as on the arithmetic plot.
Code: Select all
public void draw(final Graphics2D g2, final XYPlot plot, final Rectangle2D dataArea,
final ValueAxis domainAxis, final ValueAxis rangeAxis,
final int rendererIndex, final PlotRenderingInfo info) {
final PlotOrientation orientation = plot.getOrientation();
final RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
plot.getDomainAxisLocation(), orientation);
final RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
plot.getRangeAxisLocation(), orientation);
final Shape shape;
final Shape hotspot;
if( rangeAxis instanceof LogAxis ) {
shape = createPolyLine( dataArea, domainAxis, rangeAxis,
orientation, domainEdge, rangeEdge);
hotspot = shape;
} else {
shape = createSimpleLine( dataArea, domainAxis, rangeAxis,
orientation, domainEdge, rangeEdge );
hotspot = ShapeUtilities.createLineRegion((Line2D)shape, 1.0f);
}
g2.setPaint(this.config.paint);
g2.setStroke(this.config.stroke);
g2.draw( shape );
final String toolTip = this.getToolTipText();
final String url = this.getURL();
this.addEntity(info, hotspot, rendererIndex, toolTip, url);
}
//--------------------------------------------------
/**
* A straight line between two java coordinates.
*/
public Shape createSimpleLine(final Rectangle2D dataArea,
final ValueAxis domainAxis, final ValueAxis rangeAxis,
final PlotOrientation orientation,
final RectangleEdge domainEdge, final RectangleEdge rangeEdge) {
final float j2DX1;
final float j2DX2;
final float j2DY1;
final float j2DY2;
if (orientation == PlotOrientation.VERTICAL) {
j2DX1 = (float) domainAxis.valueToJava2D(this.config.x1, dataArea,
domainEdge);
j2DY1 = (float) (rangeAxis.valueToJava2D(this.config.y1, dataArea,
rangeEdge) - this.config.y1offset);
j2DX2 = (float) domainAxis.valueToJava2D(this.config.x2, dataArea,
domainEdge);
j2DY2 = (float) (rangeAxis.valueToJava2D(this.config.y2, dataArea,
rangeEdge) - this.config.y2offset);
}
else {
assert( orientation == PlotOrientation.HORIZONTAL );
j2DY1 = (float) domainAxis.valueToJava2D(this.config.x1, dataArea,
domainEdge);
j2DX1 = (float) rangeAxis.valueToJava2D(this.config.y1, dataArea,
rangeEdge);
j2DY2 = (float) domainAxis.valueToJava2D(this.config.x2, dataArea,
domainEdge);
j2DX2 = (float) rangeAxis.valueToJava2D(this.config.y2, dataArea,
rangeEdge);
}
final Line2D line = new Line2D.Float(j2DX1, j2DY1, j2DX2, j2DY2);
return line;
}
//--------------------------------------------------
/**
* Use the slope of the data values to plot explicit points.
*/
public Shape createPolyLine(final Rectangle2D dataArea,
final ValueAxis domainAxis, final ValueAxis rangeAxis,
final PlotOrientation orientation,
final RectangleEdge domainEdge, final RectangleEdge rangeEdge) {
final ArrayList<Float> xList = new ArrayList<Float>();
final ArrayList<Float> yList = new ArrayList<Float>();
// Convert the incoming y-offsets from pixel to data values.
// Specifically, find the desired java2d locations, then convert
// back to data values.
final double y1offsetValue =
this.config.y1 -
rangeAxis.java2DToValue(
(rangeAxis.valueToJava2D(this.config.y1, dataArea, rangeEdge) -
this.config.y1offset),
dataArea, rangeEdge );
final double y2offsetValue =
this.config.y2 -
rangeAxis.java2DToValue(
(rangeAxis.valueToJava2D(this.config.y2, dataArea, rangeEdge) -
this.config.y2offset),
dataArea, rangeEdge );
final double y1 = this.config.y1 - y1offsetValue;
final double y2 = this.config.y2 - y2offsetValue;
final double xDelta = (this.config.x2 - this.config.x1) / 50;
final double m = // slope
(y2 - y1) / (this.config.x2 - this.config.x1);
final double b = // y-intercept
y1 - ( m * this.config.x1 );
for(double x = this.config.x1; x <= this.config.x2; x += xDelta ) {
final double y = (m * x) + b;
float j2DX = (float) domainAxis.valueToJava2D(x, dataArea, domainEdge);
float j2DY = (float) rangeAxis.valueToJava2D(y, dataArea, rangeEdge);
xList.add( j2DX );
yList.add( j2DY );
}
final GeneralPath path = new GeneralPath();
path.moveTo( xList.get( 0 ), yList.get( 0 ) );
for(int i=1; i<xList.size(); i++) {
path.lineTo( xList.get(i), yList.get(i) );
}
return path;
}