/*
* 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.display.axes;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.text.DecimalFormat;
import org.opensourcephysics.display.DrawableTextLine;
import org.opensourcephysics.display.DrawingPanel;
import org.opensourcephysics.display.Interactive;
import org.opensourcephysics.display.TextLine;
/**
* A superclass for the x axis and y axis.
*
* @author Wolfgang Christian
* @version 1.0
*/
abstract public class XYAxis implements Interactive {
/** Field DRAW_IN_DISPLAY */
public static final int DRAW_IN_DISPLAY = 0;
/** Field DRAW_IN_GUTTER */
public static final int DRAW_IN_GUTTER = 1;
/** Field DRAW_AT_LOCATION */
public static final int DRAW_AT_LOCATION = 2;
/** Field LINEAR */
public static final int LINEAR = 0;
protected double x = 0;
protected double y = 0;
boolean enabled = false; // enables interaction
/** Field LOG10 */
public static final int LOG10 = 1;
int locationType = DRAW_IN_DISPLAY;
int axisType = LINEAR;
String logBase = "10"; //$NON-NLS-1$
DecimalFormat labelFormat = new DecimalFormat("0.0"); //$NON-NLS-1$
DecimalFormat integerFormat = new DecimalFormat("000"); //$NON-NLS-1$
double label_step = -14;
double label_start = 2;
DrawableTextLine axisLabel = new DrawableTextLine("x", 0, 0); //$NON-NLS-1$
Font labelFont = new Font("Dialog", Font.PLAIN, 12); //$NON-NLS-1$
int label_exponent = 0;
String label_string[] = new String[0]; // String to contain the labels.
double label_value[] = new double[0]; // The actual values of the axis labels
double decade_multiplier = 1;
int label_count = 0; // The number of labels
double location = 0; // The position of the axis
Font titleFont = new Font("Dialog", Font.PLAIN, 12); //$NON-NLS-1$
boolean showMajorGrid = false;
// Color majorGridColor = new Color(223, 223, 223); // light gray
Color majorGridColor = new Color(0, 0, 0, 32); // light gray
/**
* Constructor XYAxis
*/
public XYAxis() {
axisLabel.setJustification(TextLine.CENTER);
axisLabel.setFont(labelFont);
}
/**
* Draws the axis in a drawing panel.
* @param panel
* @param g
*/
abstract public void draw(DrawingPanel panel, Graphics g);
/**
* Method setLabelFormat
*
* @param format
*/
public void setLabelFormat(DecimalFormat format) {
if(format!=null) {
labelFormat = format;
}
}
/**
* Method setLabelFormat
*
* @param formatString
*/
public void setLabelFormat(String formatString) {
labelFormat = new DecimalFormat(formatString);
}
/**
* Sets the location type.
*
* @param _locationType
*/
public void setLocationType(int _locationType) {
locationType = _locationType;
}
/**
* Sets the location type.
*
* @param _location
*/
public void setLocation(double _location) {
location = _location;
}
/**
* Method setAxisType
*
* @param type
*/
public void setAxisType(int type) {
axisType = type;
}
/**
* Sets the title.
*
* The font names understood are those understood by java.awt.Font.decode().
* If the font name is null, the font remains unchanged.
*
* @param title
* @param font_name an optional font name
*/
public void setTitle(String title, String font_name) {
axisLabel.setText(title);
if((font_name==null)||font_name.equals("")) { //$NON-NLS-1$
return;
}
axisLabel.setFont(Font.decode(font_name));
}
/**
* Sets the title.
* @param title
*/
public void setTitle(String title) {
axisLabel.setText(title);
}
/**
* Set the title font. The font names understood are those understood by
* java.awt.Font.decode().
*
* @param name A font name.
*/
public void setTitleFont(String name) {
if((name!=null)&&!name.equals("")) { //$NON-NLS-1$
titleFont = Font.decode(name);
}
}
public void setShowMajorGrid(boolean show) {
showMajorGrid = show;
}
protected void drawMultiplier(int xpix, int ypix, int exponent, Graphics2D g2) {
Font oldFont = g2.getFont();
g2.drawString("10", xpix, ypix); //$NON-NLS-1$
g2.setFont(g2.getFont().deriveFont(Font.PLAIN, 9.0f));
g2.drawString(""+exponent, xpix+16, ypix-6); //$NON-NLS-1$
g2.setFont(oldFont);
}
/**
* Calculates the axis labels.
* @param minimum
* @param maximum
* @param numTicks
*/
public void calculateLabels(double minimum, double maximum, int numTicks) {
numTicks = Math.min(19, numTicks);
double min = minimum, max = maximum;
if(maximum<minimum) {
// the routines only work for min<max so switch values
min = maximum;
max = minimum;
} else {
max = maximum;
min = minimum;
}
switch(axisType) {
case LINEAR :
calculateLinearLabels(min, max, numTicks);
break;
case LOG10 :
calculateLogLabels(min, max, numTicks);
break;
default :
calculateLinearLabels(min, max, numTicks);
break;
}
}
/**
* Calculates the axis labels for logarithmic scales.
* @param minimum
* @param maximum
*/
private void calculateLogLabels(double minimum, double maximum, int numTicks) {
label_exponent = 0;
decade_multiplier = 1;
int label_step = 1; // shadow the member variables since we want to do integer arithmetic
int label_start = (int) Math.ceil(minimum);
if(label_start-minimum>0.998) {
label_start -= 1;
}
int val = label_start;
label_count = 1;
do { // make sure that we get at least one decade
val += label_step;
label_count++;
} while(val<=maximum-label_step);
label_string = new String[label_count];
label_value = new double[label_count];
for(int i = 0; i<label_count; i++) {
val = label_start+i*label_step;
label_string[i] = integerFormat.format(val);
label_value[i] = val;
}
}
/**
* put your documentation comment here
* @param minimum
* @param maximum
*/
private void calculateLinearLabels(double minimum, double maximum, int numTicks) {
// System.out.println("maximum="+maximum);
double val;
int i;
int j;
if((Math.abs(minimum)==0)&&(Math.abs(maximum)==0)) {
maximum = minimum+1.0e-6;
}
if(Math.abs(minimum)>Math.abs(maximum)) {
label_exponent = ((int) Math.floor(log10(Math.abs(minimum))/2.0))*2;
} else {
label_exponent = ((int) Math.floor(log10(Math.abs(maximum))/2.0))*2;
}
if((maximum-minimum)>10*numTicks*Double.MIN_VALUE) {
label_step = RoundUp((maximum-minimum)/numTicks); // try for 8 labels
} else {
label_step = 1;
}
label_start = Math.floor(minimum/label_step)*label_step;
while((label_step>0)&&(label_start<minimum)) {
label_start += label_step;
}
val = label_start;
label_count = 1;
while(val<=maximum-label_step) {
val += label_step;
label_count++;
}
label_string = new String[label_count];
label_value = new double[label_count];
for(i = 0; i<label_count; i++) {
val = label_start+i*label_step;
if(label_exponent<0) {
for(j = label_exponent; j<0; j++) {
val *= 10;
}
} else {
for(j = 0; j<label_exponent; j++) {
val /= 10;
}
}
label_string[i] = labelFormat.format(val);
label_value[i] = val;
}
decade_multiplier = 1;
if(label_exponent<0) {
for(j = label_exponent; j<0; j++) {
decade_multiplier /= 10;
}
} else {
for(j = 0; j<label_exponent; j++) {
decade_multiplier *= 10;
}
}
}
/**
* Round up the passed value to a NICE value.
*/
private double RoundUp(double val) {
int exponent;
int i;
exponent = (int) (Math.floor(log10(val)));
if(exponent<0) {
for(i = exponent; i<0; i++) {
val *= 10.0;
}
} else {
for(i = 0; i<exponent; i++) {
val /= 10.0;
}
}
if(val>5.0) {
val = 10.0;
} else if(val>2.0) {
val = 5.0;
} else if(val>1.0) {
val = 2.0;
} else {
val = 1.0;
}
if(exponent<0) {
for(i = exponent; i<0; i++) {
val /= 10.0;
}
} else {
for(i = 0; i<exponent; i++) {
val *= 10.0;
}
}
return val;
}
// implements measurable interface
/**
* Gets the minimum x needed to draw this object.
* @return minimum
*/
public double getXMin() {
return 0;
}
/**
* Gets the maximum x needed to draw this object.
* @return maximum
*/
public double getXMax() {
return 0;
}
/**
* Gets the minimum y needed to draw this object.
* @return minimum
*/
public double getYMin() {
return 0;
}
/**
* Gets the maximum y needed to draw this object.
* @return minimum
*/
public double getYMax() {
return 0;
}
/**
* Determines if information is available to set min/max values.
* Objects that store data, Datasets for example, usually return zero if data is null.
*
* @return true if min/max values are valid
*/
public boolean isMeasured() {
return false;
}
// implements interactive drawable interface
public Interactive findInteractive(DrawingPanel panel, int _xpix, int _ypix) {
return null;
}
public void setEnabled(boolean _enabled) {
enabled = _enabled;
}
public boolean isEnabled() {
return enabled;
}
public void setXY(double x, double y) {}
public void setX(double x) {}
public void setY(double y) {}
public double getX() {
return x;
}
public double getY() {
return y;
}
/**
* @param x a double value
* @return The log<sub>10</sub>
*
* @throws ArithmeticException
*/
static public double log10(double x) throws ArithmeticException {
if(x<=0.0) {
throw new ArithmeticException("range exception"); //$NON-NLS-1$
}
return Math.log(x)/2.30258509299404568401;
}
}
/*
* 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
*/