In this section we look at adding graphics to histograms. See Chapter
3: Tech : Histograms and Chapter
4 : Tech : More Histograms for earlier discussions of histogramming.
To simplify our histogram class hierarchy, we start with a new
base Histogram
class (code also shown below) that combines
the attributes and methods of the earlier BasicHist
and BetterHist
classes, which we created to illustrate class and inheritance concepts.
We added several new methods to our histogram to get or set various
values describing the histogram. We will extend Histogram
with various subclasses later. (See Chapter
8: Tech. Also, the Chapter
8: Tech : Refactoring section talks about reorganization of
classes as we did here.)
For our display of histograms we create the HistPanel class. This class
extends PlotPanel
and overrides its PaintContents()
method to draw the histogram graph. (See the Starting
to Plot section for an explanation of how to use PlotPanel.)
The class takes a Histogram
object in the constructor. A new histogram can replace the current
one with the setHistgram(Histogram hist) method.
We will use this histogramming panel in several demonstration projects
in the course.
DrawHistApplet.java
- create an instance of the Histogram class and fill it
with values chosen from a Gaussian random number generator.
Then create an instance of HistPanel and add it to the
content pane.
+ New classes:
HistPanel.java
- PlotPanel subclass that displays instances of Histogram.
It draws retangles to represent the bin values as columns.
The PlotPanel methods provide the scale information, axes
labeling. title, etc.
Histogram.java
- our new histogram base class combines the previous
BasicHist
and BetterHist
classes. It includes the basics of histograms such as
bin array, over/under flow variables, etc. In subsequent
chapters we will create subclasses of this class with
additional capabilities.
+ Previous classes:
Chapter
6: Tech: PlotPanel.java,
PlotFormat.java
|
Java Techniques
& Other Programming Notes
- The Graphics
class methods use integer values for the drawing positions
and dimensions.When coordinates depend on floating point
calculations there will be round off errors. These
errors can build up in a series of drawing commands that
sum rounded off values. In the paintBars() method in HistPanel
we reduce the effect by using floating point values to
do the position computations before casting to the integer
values.
- Java2D
methods offer the option of using floating-point values
to reduce the round off problem.
- The HistPanel
class properties include a number of variables for the
display settings such as the colors of the frame and so
forth. Additional getter/setter methods could be added
to allow for access and modifications to these variables.
|
import
javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
/**
* Create an instance of Histogram,
add a Gaussian generated
* distribution of data to it and
display it.
**/
public class DrawHistApplet extends JApplet
{
Histogram fHistogram = null;
public void init () {
Container content_pane = getContentPane
();
// Create a histogram with Gaussian
distribution.
makeHist ();
// Create an instance of a JPanel
sub-class
HistPanel hist_panel = new HistPanel
(fHistogram);
// And add one or more panels to
the JApplet panel.
content_pane.add (hist_panel);
} // init
/**
* Create the histogram
and a set of data drawn
* from a Gaussian random
number generator.
**/
void makeHist () {
// Create an instance of the Random
class for
// producing our random values.
java.util.Random r = new java.util.Random
();
// Them method nextGaussian in the
class Random produces
// a value centered at 0.0 and a
standard deviation
// of 1.0.
// Create an instance of our basic
gHistogram class.
// Make it wide enough enough to
include most of the
// gaussian values.
fHistogram = new Histogram ("Gaussian
Distribution", "random values",
10,-2.0,2.0);
// Fill histogram with Gaussian
distribution
for (int i=0; i<100000; i++) {
double
val = r.nextGaussian ();
fHistogram.add
(val);
}
} // makeHist
} // class DrawHistApplet
|
import
java.awt.*;
import javax.swing.*;
/** Display histogram data on a PlotPanel subclass.
**/
public class HistPanel extends PlotPanel {
// Histogram reference
protected Histogram fHistogram;
// Histogram bar parameters.
protected Color fBarLineColor = Color.DARK_GRAY;
protected Color fBarFillColor = Color.PINK;
int fGap = 2; // 2 pixels between
bars
// Fractional margin between highest bar and
top frame
double fTopMargin = 0.05;
// Fractional margnin between side bars and
frame
double fSideMargin = 0.01;
// Arrays to hold numbers for axes scale values
double [] fXScaleValue;
double [] fYScaleValue;
// Number of values on each axis
int fNumYScaleValues = 2;
int fNumXScaleValues = 5;
/** Create the panel with the histogram. **/
public HistPanel (Histogram histogram) {
fHistogram = histogram;
getScaling ();
}
/** Switch to a new histogram. **/
public void setHistogram (Histogram histogram){
fHistogram = histogram;
getScaling ();
repaint ();
}
/**
* Get the values for the scale values
on
* the plot axes.
**/
void getScaling () {
fYScaleValue = new double[fNumYScaleValues];
// Use lowest value of 0;
fYScaleValue[0] = 0.0;
fXScaleValue = new double[fNumXScaleValues];
// First get the low and high values;
fXScaleValue[0] = fHistogram.getLo
();
fXScaleValue[fNumXScaleValues-1]
= fHistogram.getHi ();
// Then calculate the difference
between the values
// (assumes linear scale)
double range = fXScaleValue[fNumXScaleValues-1]
-
fXScaleValue[0];
double dx = range/(fNumXScaleValues-1);
// Now set the intermediate scale
values.
for (int i=1; i < (fNumXScaleValues-1);
i++) {
fXScaleValue[i]
= i*dx + fXScaleValue[0];
}
} // getScaling
/** Optional bar color settings. **/
public void setBarColors (Color line, Color
fill) {
if ( line != null) fBarLineColor
= line;
if ( fill != null) fBarFillColor
= fill;
}
/**
* Overrides the abstract method
in PlotPanel superclass.
* Draw the vertical bars that represent
the bin contents.
* Draw also the numbers for the
scales on the axes.
**/
void paintContents (Graphics g) {
// Get the histogram max value and
bin data
int max_data_value = fHistogram.getMax
();
int [] bins = fHistogram.getBins
();
if (max_data_value == 0) return;
Color old_color=g.getColor (); //
remember color for later
// Dimensions of the drawing area
for the bars
int side_space = (int)
(fFrameWidth*fSideMargin);
int draw_width= fFrameWidth - 2
* side_space- (bins.length-1)*fGap;
int draw_height= (int)(fFrameHeight*
(1.0 - fTopMargin));
// To avoid build up of round off
errors, the bar
// positions will be calculated
from a FP value.
float step_width= (float)draw_width/(float)bins.length;
int bar_width = Math.round (step_width);
step_width += fGap;
// Scale the bars to the maximum
bin value.
float scale_factor= (float)draw_height/max_data_value;
int start_x = fFrameX + side_space;
int start_y = fFrameY + fFrameHeight;
for (int i=0; i < bins.length; i++)
{
int bar_height
= (int)(bins[i] * scale_factor);
int bar_x
= (int)(i * step_width) + start_x;
// Bar drawn
from top left corner
int bar_y=
start_y-bar_height;
g.setColor
(fBarLineColor);
g.drawRect
(bar_x,bar_y, bar_width ,bar_height);
g.setColor
(fBarFillColor);
g.fillRect
(bar_x+1, bar_y+1, bar_width-2, bar_height-1);
}
// Find the scale value of the full
frame height.
fYScaleValue[1] = (double)
(fFrameHeight/scale_factor);
// Draw the numbers along the axes.
drawAxesNumbers (g, fXScaleValue,
fYScaleValue);
g.setColor (old_color); //reset
original color
} // paintContents
// Methods overriding those in PlotPanel
String getTitle () {
return fHistogram.getTitle ();
}
String getXLabel () {
return fHistogram.getXLabel ();
}
} // class HistPanel
|
/**
This class provides the bare essentials for a histogram.**/
public class Histogram
{
protected String fTitle = "Histogram";
protected String fXLabel = "Data";
protected int [] fBins;
protected int fNumBins;
protected int fUnderflows;
protected int fOverflows;
protected double fLo;
protected double fHi;
protected double fRange;
/** The constructor will create an array of a
given
* number of bins. The range of the
histogram given
* by the upper and lower limit values.
**/
public Histogram (int numBins, double lo, double
hi) {
// Check for bad range values.
// Could throw an exception but will
just
// use default values;
if (hi < lo) {
lo = 0.0;
hi = 1.0;
}
if (numBins <= 0) numBins = 1;
fNumBins = numBins;
fBins = new int[fNumBins];
fLo = lo;
fHi = hi;
fRange = fHi - fLo;
} // ctor
// This constructor includes the title and horizontal
// axis label.
public Histogram (String title, String xLabel,
int fNumBins, double lo, double hi) {
this (fNumBins, lo, hi);// Invoke
overloaded constructor
fTitle= title;
fXLabel = xLabel;
} // ctor
//--- Histogram description --------------------------------
/** Get to title string. **/
public String getTitle ()
{ return fTitle; }
/** Set the title. **/
public void setTitle (String title)
{ fTitle = title; }
/** Get to the horizontal axis label. **/
public String getXLabel ()
{ return fXLabel; }
/** Set the horizontal axis label. **/
public void setXLabel (String xLabel)
{ fXLabel = xLabel; }
//--- Bin info access --------------------------------------
/** Get the low end of the range. **/
public double getLo ()
{ return fLo; }
/** Get the high end of the range.**/
public double getHi ()
{ return fHi; }
/** Get the number of entries in the largest bin.
**/
public int getMax () {
int max = 0;
for (int i=0; i < fNumBins;i++)
if (max <
fBins[i]) max = fBins[i];
return max;
}
/**
* This method returns a reference
to the fBins.
* Note that this means the values
of the histogram
* could be altered by the caller object.
**/
public int [] getBins () {
return fBins;
}
/** Get the number of entries in the smallest
bin.**/
public int getMin () {
int min = getMax ();
for (int i=0; i < fNumBins; i++)
if (min >
fBins[i]) min = fBins[i];
return min;
}
/** Get the total number of entries not counting
* overflows and underflows.
**/
public int getTotal () {
int total = 0;
for (int i=0; i < fNumBins; i++)
total +=
fBins[i];
return total;
}
/**
* Add an entry to a bin.
* @param x double value added if it
is in the range:
* lo <= x < hi
**/
public void add (double x) {
if (x >= fHi) fOverflows++;
else if (x < fLo) fUnderflows++;
else {
double val = x - fLo;
// Casting to int will
round off to lower
// integer value.
int bin = (int)
(fNumBins * (val/fRange) );
// Increment the corresponding
bin.
fBins[bin]++;
}
}
/** Clear the histogram bins and the over and
under flows.**/
public void clear () {
for (int i=0; i < fNumBins; i++) {
fBins[i] = 0;
fOverflows = 0;
fUnderflows= 0;
}
}
/**
* Provide access to the value in the
bin element
* specified by bin_num.
* Return the underflows if bin value
negative,
* Return the overflows if bin value
more than
* the number of bins.
**/
public int getValue (int bin_num) {
if (bin_num < 0)
return fUnderflows;
else if (bin_num >= fNumBins)
return fOverflows;
else
return fBins[bin_num];
}
/**
* Get the average and standard deviation
of the
* distribution of entries.
* @return double array
**/
public double [] getStats () {
int total = 0;
double wt_total = 0;
double wt_total2 = 0;
double [] stat = new double[2];
double bin_width = fRange/fNumBins;
for (int i=0; i < fNumBins;i++) {
total += fBins[i];
double bin_mid = (i
- 0.5) * bin_width + fLo;
wt_total +=
fBins[i] * bin_mid;
wt_total2 += fBins[i]
* bin_mid * bin_mid;
}
if (total > 0) {
stat[0] = wt_total / total;
double av2 = wt_total2
/ total;
stat[1] = Math.sqrt (av2
- stat[0] * stat[0]);
} else {
stat[0] = 0.0;
stat[1] = -1.0;
}
return stat;
}// getStats()
/**
* Create the histogram from a user derived array
along with the
* under and overflow values.
* The low and high range values that the histogram
* corresponds to must be in passed as well.
*
* @param userBins array of int values.
* @param under number of underflows.
* @param over number of overflows.
* @param lo value of the lower range limit.
* @param hi value of the upper range limit.
**/
public void pack (int [] user_bins,
int under, int over,
double lo, double hi) {
fNumBins = user_bins.length;
fBins = new int[fNumBins];
for (int i = 0; i < fNumBins; i++)
{
fBins[i] = user_bins[i];
}
fLo = lo;
fHi = hi;
fRange = fHi-fLo;
fUnderflows = under;
fOverflows = over;
}// pack()
}// class Histogram
|
References & Web Resources
Latest update: Oct. 30, 2004
|