/****************************************************************************************
* Copyright (c) 2014 Michael Goldbach <michael@wildplot.com> *
* *
* This program is free software; you can redistribute it and/or modify it under *
* the terms of the GNU General Public License as published by the Free Software *
* Foundation; either version 3 of the License, or (at your option) any later *
* version. *
* *
* This program 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 program. If not, see <http://www.gnu.org/licenses/>. *
****************************************************************************************/
package com.wildplot.android.rendering;
import com.wildplot.android.rendering.graphics.wrapper.*;
import com.wildplot.android.rendering.interfaces.Drawable;
import com.wildplot.android.rendering.interfaces.Function2D;
import com.wildplot.android.rendering.interfaces.StepFunction2D;
/**
* FunctionDrawer is used to draw a mathematical function on a given PlotSheet
*
*
*/
public class FunctionDrawer implements Drawable {
private boolean isStepFunction = false;
private double extraScaleFactor = 1;
private boolean autoscale = false;
private double scaleFactor = 10;
private double binSize = 1;
private boolean isOnFrame = false;
private double yOffset = 0;
private float size = 1;
private double leftLimit = 0;
private double rightLimit = 0;
private boolean hasLimit = false;
/**
* true when warning for pole positions is allready given
*/
private boolean warned = false;
/**
* the function which will be plotted with this FunctionDrawer
*/
private Function2D function;
/**
* the PlotSheet on which this FunctionDrawer is drawing upon
*/
private PlotSheet plotSheet;
/**
* the color of the function graph
*/
private ColorWrap color = new ColorWrap(255,0,0);
private boolean isOnReset = false;
/**
* Constructor for a FunctionDrawer object
* @param function given function which is drawn
* @param plotSheet the sheet the function will be drawn onto
* @param color color of the function
*/
public FunctionDrawer(Function2D function, PlotSheet plotSheet, ColorWrap color) {
this.function = function;
this.plotSheet = plotSheet;
this.color = color;
}
/**
* Constructor for a FunctionDrawer object
* @param function given function which is drawn
* @param plotSheet the sheet the function will be drawn onto
* @param color color of the function
*/
public FunctionDrawer(Function2D function, PlotSheet plotSheet, ColorWrap color, double leftLimit, double rightLimit) {
this.function = function;
this.plotSheet = plotSheet;
this.color = color;
this.hasLimit = true;
this.leftLimit = leftLimit;
this.rightLimit = rightLimit;
}
/* (non-Javadoc)
* @see rendering.Drawable#paint(java.awt.Graphics)
*/
@Override
public void paint(GraphicsWrap g) {
isOnReset = false;
if(function instanceof StepFunction2D) {
this.isStepFunction = true;
}
StrokeWrap oldStroke = g.getStroke();
g.setStroke(new BasicStrokeWrap(this.size)); // set stroke width of 10
ColorWrap oldColor = g.getColor();
RectangleWrap field = g.getClipBounds();
g.setColor(color);
if(autoscale){
double[] start = this.plotSheet.toCoordinatePoint(0, 0, field);
double[] end = this.plotSheet.toCoordinatePoint(0,
0+this.plotSheet.getFrameThickness()[PlotSheet.UPPER_FRAME_THICKNESS_INDEX], field);
this.scaleFactor = Math.abs(end[1] - start[1]);
// this.scaleFactor *= binSize;
} else {
this.scaleFactor = 1.0;
}
if(this.isOnFrame)
yOffset = plotSheet.getyRange()[0];
double[] drawingPoint = plotSheet.toCoordinatePoint(field.x,0,field);
if(this.isOnFrame)
drawingPoint = plotSheet.toCoordinatePoint(field.x+this.plotSheet.getFrameThickness()[PlotSheet.LEFT_FRAME_THICKNESS_INDEX],0,field);
double f_x = function.f(drawingPoint[0])*scaleFactor*extraScaleFactor;
double f_x_old = f_x;
float[] coordStart = plotSheet.toGraphicPoint(drawingPoint[0],f_x,field);
if(this.isOnFrame)
coordStart = plotSheet.toGraphicPoint(drawingPoint[0],this.yOffset-f_x,field);
float[] coordEnd = coordStart;
float leftStart = field.x+1;
float rightEnd = field.width + field.x;
if(this.isOnFrame){
leftStart = field.x+this.plotSheet.getFrameThickness()[PlotSheet.LEFT_FRAME_THICKNESS_INDEX]+1;
rightEnd = field.width + field.x-this.plotSheet.getFrameThickness()[PlotSheet.RIGHT_FRAME_THICKNESS_INDEX];
}
if(this.hasLimit){
leftStart = plotSheet.xToGraphic(leftLimit, field);
rightEnd = plotSheet.xToGraphic(rightLimit, field);
}
for(int i = Math.round(leftStart); i< rightEnd; i++) {
if(isOnReset)
return;
drawingPoint = plotSheet.toCoordinatePoint(i,0,field);
coordEnd = coordStart;
f_x_old = f_x;
f_x = function.f(drawingPoint[0])*scaleFactor*extraScaleFactor;
coordStart = plotSheet.toGraphicPoint(drawingPoint[0],f_x,field);
if(this.isOnFrame)
coordStart = plotSheet.toGraphicPoint(drawingPoint[0],this.yOffset-f_x,field);
double overlap = 0.2 * (plotSheet.getyRange()[1] - plotSheet.getyRange()[0]);
if(f_x_old != Double.NaN && f_x!= Double.NaN &&
f_x_old != Double.NEGATIVE_INFINITY && f_x!= Double.NEGATIVE_INFINITY &&
f_x_old != Double.POSITIVE_INFINITY && f_x!= Double.POSITIVE_INFINITY &&
f_x_old <= plotSheet.getyRange()[1] + overlap && f_x_old >= plotSheet.getyRange()[0] - overlap &&
f_x <= plotSheet.getyRange()[1] + overlap && f_x >= plotSheet.getyRange()[0] - overlap) {
g.drawLine(coordStart[0], coordStart[1], coordEnd[0], coordEnd[1]);
} else if(!warned) {
System.err.println("Could not draw part of function, possible pole or out of reach");
warned = true;
}
}
g.setStroke(oldStroke);
g.setColor(oldColor);
}
public double getMaxValue(int pixelResolution){
RectangleWrap field = new RectangleWrap(pixelResolution, pixelResolution);
double[] drawingPoint = plotSheet.toCoordinatePoint(field.x,0,field);
double max = Double.NEGATIVE_INFINITY;
for(int i = 0; i< pixelResolution; i++) {
drawingPoint = plotSheet.toCoordinatePoint(i,0,field);
double f_x = function.f(drawingPoint[0]);
if(f_x > max)
max=f_x;
}
return max;
}
/**
* @param size the size to set
*/
public void setSize(float size) {
this.size = size;
}
public boolean isOnFrame() {
return this.isOnFrame;
}
/**
* unset the axis to draw on the border between outer frame and plot
*/
public void unsetOnFrame() {
this.isOnFrame = false;
yOffset = 0;
}
/**
* set the axis to draw on the border between outer frame and plot
*/
public void setOnFrame(double extraSpace) {
this.isOnFrame = true;
yOffset = plotSheet.getyRange()[0]-extraSpace;
}
public void setOnFrame() {
setOnFrame(0);
}
public void setAutoscale(double binWidth) {
this.binSize = binWidth;
this.autoscale = true;
}
public void unsetAutoscale() {
this.autoscale = false;
}
public double getExtraScaleFactor() {
return extraScaleFactor;
}
public void setExtraScaleFactor(double extraScaleFactor) {
this.extraScaleFactor = extraScaleFactor;
}
@Override
public void abortAndReset() {
isOnReset = true;
}
@Override
public boolean isClusterable() {
return true;
}
@Override
public boolean isCritical() {
return false;
}
}