• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1552
  • Last Modified:

swing / Pie chart

How to implement GUI for pie chart using Swing..
0
man068
Asked:
man068
  • 6
  • 2
1 Solution
 
OviCommented:
Depicted from "Swing by example"

/**
      Charts2D
*/
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.util.*;

import javax.swing.*;
import javax.swing.border.*;

public class Charts2D
      extends JFrame
{

      public Charts2D()
      {
            super("2D Charts");
            setSize(720, 280);
            getContentPane().setLayout(new GridLayout(1, 3, 10, 0));
            getContentPane().setBackground(Color.white);

            int nData = 8;
            int[] xData = new int[nData];
            int[] yData = new int[nData];
            for (int k=0; k<nData; k++)
            {
                  xData[k] = k;
                  yData[k] = (int)(Math.random()*100);
                  if (k > 0)
                        yData[k] = (yData[k-1] + yData[k])/2;
            }

            JChart2D chart = new JChart2D(JChart2D.CHART_LINE, nData, xData,
                  yData, "Line Chart");
            chart.setStroke(new BasicStroke(5f, BasicStroke.CAP_ROUND,
                  BasicStroke.JOIN_MITER));
            chart.setLineColor(new Color(0, 128, 128));
            getContentPane().add(chart);

            chart = new JChart2D(JChart2D.CHART_COLUMN,
                  nData, xData, yData, "Column Chart");
            GradientPaint gp = new GradientPaint(0, 100,
                  Color.white, 0, 300, Color.blue, true);
            chart.setGradient(gp);
            chart.setEffectIndex(JChart2D.EFFECT_GRADIENT);
            chart.setDrawShadow(true);
            getContentPane().add(chart);

            chart = new JChart2D(JChart2D.CHART_PIE, nData, xData,
                  yData, "Pie Chart");
            ImageIcon icon = new ImageIcon("hubble.gif");
            chart.setForegroundImage(icon.getImage());
            chart.setEffectIndex(JChart2D.EFFECT_IMAGE);
            chart.setDrawShadow(true);
            getContentPane().add(chart);

            WindowListener wndCloser = new WindowAdapter()
            {
                  public void windowClosing(WindowEvent e)
                  {
                        System.exit(0);
                  }
            };
            addWindowListener(wndCloser);
            
            setVisible(true);
      }

      public static void main(String argv[])
      {
            new Charts2D();
      }

}

class JChart2D
      extends JPanel
{
      public static final int CHART_LINE = 0;
      public static final int CHART_COLUMN = 1;
      public static final int CHART_PIE = 2;

      public static final int EFFECT_PLAIN = 0;
      public static final int EFFECT_GRADIENT = 1;
      public static final int EFFECT_IMAGE = 2;

      protected int m_chartType = CHART_LINE;
      protected JLabel m_title;
      protected ChartPanel m_chart;

      protected int m_nData;
      protected int[] m_xData;
      protected int[] m_yData;
      protected int m_xMin;
      protected int m_xMax;
      protected int m_yMin;
      protected int m_yMax;
      protected double[] m_pieData;

      protected int    m_effectIndex = EFFECT_PLAIN;
      protected Stroke m_stroke;
      protected GradientPaint m_gradient;
      protected Image  m_foregroundImage;
      protected Color  m_lineColor = Color.black;
      protected Color  m_columnColor = Color.blue;
      protected int    m_columnWidth = 12;
      protected boolean m_drawShadow = false;

      public JChart2D(int type, int nData,
            int[] yData, String text)
      {
            this(type, nData, null, yData, text);
      }

      public JChart2D(int type, int nData, int[] xData,
            int[] yData, String text)
      {
            super(new BorderLayout());
            setBackground(Color.white);
            m_title = new JLabel(text, JLabel.CENTER);
            add(m_title, BorderLayout.NORTH);

            m_chartType = type;

            if (xData==null)
            {
                  xData = new int[nData];
                  for (int k=0; k<nData; k++)
                        xData[k] = k;
            }
            if (yData == null)
                  throw new IllegalArgumentException("yData can't be null");
            if (nData > yData.length)
                  throw new IllegalArgumentException("Insufficient yData length");
            if (nData > xData.length)
                  throw new IllegalArgumentException("Insufficient xData length");
            m_nData = nData;
            m_xData = xData;
            m_yData = yData;

            m_xMin = m_xMax = 0;      // To include 0 into the interval
            m_yMin = m_yMax = 0;
            for (int k=0; k<m_nData; k++)
            {
                  m_xMin = Math.min(m_xMin, m_xData[k]);
                  m_xMax = Math.max(m_xMax, m_xData[k]);
                  m_yMin = Math.min(m_yMin, m_yData[k]);
                  m_yMax = Math.max(m_yMax, m_yData[k]);
            }
            if (m_xMin == m_xMax)
                  m_xMax++;
            if (m_yMin == m_yMax)
                  m_yMax++;

            if (m_chartType == CHART_PIE)
            {
                  double sum = 0;
                  for (int k=0; k<m_nData; k++)
                  {
                        m_yData[k] = Math.max(m_yData[k], 0);
                        sum += m_yData[k];
                  }
                  m_pieData = new double[m_nData];
                  for (int k=0; k<m_nData; k++)
                        m_pieData[k] = m_yData[k]*360.0/sum;
            }

            m_chart = new ChartPanel();
            add(m_chart, BorderLayout.CENTER);
      }

      public void setEffectIndex(int effectIndex)
      {
            m_effectIndex = effectIndex;
            repaint();
      }

      public int getEffectIndex()
      {
            return m_effectIndex;
      }

      public void setStroke(Stroke stroke)
      {
            m_stroke = stroke;
            m_chart.repaint();
      }

      public void setForegroundImage(Image img)
      {
            m_foregroundImage = img;
            repaint();
      }

      public Image getForegroundImage()
      {
            return m_foregroundImage;
      }

      public Stroke getStroke()
      {
            return m_stroke;
      }

      public void setGradient(GradientPaint gradient)
      {
            m_gradient = gradient;
            repaint();
      }

      public GradientPaint getGradient()
      {
            return m_gradient;
      }

      public void setColumnWidth(int columnWidth)
      {
            m_columnWidth = columnWidth;
            m_chart.calcDimensions();
            m_chart.repaint();
      }

      public int setColumnWidth()
      {
            return m_columnWidth;
      }

      public void setColumnColor(Color c)
      {
            m_columnColor = c;
            m_chart.repaint();
      }

      public Color getColumnColor()
      {
            return m_columnColor;
      }

      public void setLineColor(Color c)
      {
            m_lineColor = c;
            m_chart.repaint();
      }

      public Color getLineColor()
      {
            return m_lineColor;
      }

      public void setDrawShadow(boolean drawShadow)
      {
            m_drawShadow = drawShadow;
            m_chart.repaint();
      }

      public boolean getDrawShadow()
      {
            return m_drawShadow;
      }

      class ChartPanel
            extends JComponent
      {
            int m_xMargin = 5;
            int m_yMargin = 5;
            int m_pieGap = 10;

            int m_x;
            int m_y;
            int m_w;
            int m_h;
            
            ChartPanel()
            {
                  enableEvents(ComponentEvent.COMPONENT_RESIZED);
            }

            protected void processComponentEvent(ComponentEvent e)
            {
                  calcDimensions();
            }

            public void calcDimensions()
            {
                  Dimension d = getSize();
                  m_x = m_xMargin;
                  m_y = m_yMargin;
                  m_w = d.width-2*m_xMargin;
                  m_h = d.height-2*m_yMargin;
                  if (m_chartType == CHART_COLUMN)
                  {
                        m_x += m_columnWidth/2;
                        m_w -= m_columnWidth;
                  }
            }

            public int xChartToScreen(int x)
            {
                  return m_x + (x-m_xMin)*m_w/(m_xMax-m_xMin);
            }

            public int yChartToScreen(int y)
            {
                  return m_y + (m_yMax-y)*m_h/(m_yMax-m_yMin);
            }

            public void paintComponent(Graphics g)
            {
                  int x0 = 0;
                  int y0 = 0;
                  if (m_chartType != CHART_PIE)
                  {
                        g.setColor(Color.black);
                        x0 = xChartToScreen(0);
                        g.drawLine(x0, m_y, x0, m_y+m_h);
                        y0 = yChartToScreen(0);
                        g.drawLine(m_x, y0, m_x+m_w, y0);
                  }

                  Graphics2D g2 = (Graphics2D) g;
                  g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
                  g2.setRenderingHint(RenderingHints.KEY_RENDERING,
                        RenderingHints.VALUE_RENDER_QUALITY);

                  if (m_stroke != null)
                        g2.setStroke(m_stroke);

                  GeneralPath path = new GeneralPath();
                  switch (m_chartType)
                  {
                  case CHART_LINE:
                        g2.setColor(m_lineColor);
                        path.moveTo(xChartToScreen(m_xData[0]),
                              yChartToScreen(m_yData[0]));
                        for (int k=1; k<m_nData; k++)
                              path.lineTo(xChartToScreen(m_xData[k]),
                                    yChartToScreen(m_yData[k]));
                        g2.draw(path);
                        break;

                  case CHART_COLUMN:
                        for (int k=0; k<m_nData; k++)
                        {
                                        m_xMax ++;
                              int x = xChartToScreen(m_xData[k]);
                              int w = m_columnWidth;
                              int y1 = yChartToScreen(m_yData[k]);
                              int y = Math.min(y0, y1);
                              int h = Math.abs(y1 - y0);
                              Shape rc = new Rectangle2D.Double(x, y, w, h);
                              path.append(rc, false);
                                        m_xMax --;
                        }

                        if (m_drawShadow)
                        {
                              AffineTransform s0 = new AffineTransform(
                                    1.0, 0.0, 0.0, -1.0, x0, y0);
                              s0.concatenate(AffineTransform.getScaleInstance(
                                    1.0, 0.5));
                              s0.concatenate(AffineTransform.getShearInstance(
                                    0.5, 0.0));
                              s0.concatenate(new AffineTransform(
                                    1.0, 0.0, 0.0, -1.0, -x0, y0));
                              g2.setColor(Color.gray);
                              Shape shadow = s0.createTransformedShape(path);
                              g2.fill(shadow);
                        }

                        if (m_effectIndex==EFFECT_GRADIENT && 
                              m_gradient != null)
                        {
                              g2.setPaint(m_gradient);
                              g2.fill(path);
                        }
                        else if (m_effectIndex==EFFECT_IMAGE && 
                              m_foregroundImage != null)
                              fillByImage(g2, path,0);
                        else
                        {
                              g2.setColor(m_columnColor);
                              g2.fill(path);
                        }
                        g2.setColor(m_lineColor);
                        g2.draw(path);
                        break;
                  
                  case CHART_PIE:
                        double start = 0.0;
                        double finish = 0.0;
                        int ww = m_w - 2*m_pieGap;
                        int hh = m_h - 2*m_pieGap;
                        if (m_drawShadow)
                        {
                              ww -= m_pieGap;
                              hh -= m_pieGap;
                        }

                        for (int k=0; k<m_nData; k++)
                        {
                              finish = start+m_pieData[k];
                              double f1 = Math.min(90-start, 90-finish);
                              double f2 = Math.max(90-start, 90-finish);
                              Shape shp = new Arc2D.Double(m_x, m_y, ww, hh,
                                    f1, f2-f1, Arc2D.PIE);
                              double f = (f1 + f2)/2*Math.PI/180;
                              AffineTransform s1 = AffineTransform.
                                    getTranslateInstance(m_pieGap*Math.cos(f),
                                    -m_pieGap*Math.sin(f));
                              s1.translate(m_pieGap, m_pieGap);
                              Shape piece = s1.createTransformedShape(shp);
                              path.append(piece, false);
                              start = finish;
                        }

                        if (m_drawShadow)
                        {
                              AffineTransform s0 = AffineTransform.
                                    getTranslateInstance(m_pieGap, m_pieGap);
                              g2.setColor(Color.gray);
                              Shape shadow = s0.createTransformedShape(path);
                              g2.fill(shadow);
                        }

                        if (m_effectIndex==EFFECT_GRADIENT && 
                              m_gradient != null)
                        {
                              g2.setPaint(m_gradient);
                              g2.fill(path);
                        }
                        else if (m_effectIndex==EFFECT_IMAGE && 
                              m_foregroundImage != null)
                              fillByImage(g2, path,0);
                        else
                        {
                              g2.setColor(m_columnColor);
                              g2.fill(path);
                        }

                        g2.setColor(m_lineColor);
                        g2.draw(path);
                        break;
                  }
            }

            protected void fillByImage(Graphics2D g2, Shape shape, int xOffset)
            {
                  if (m_foregroundImage == null)
                        return;
                  int wImg = m_foregroundImage.getWidth(this);
                  int hImg = m_foregroundImage.getHeight(this);
                  if (wImg <=0 || hImg <= 0)
                        return;
                  g2.setClip(shape);
                  Rectangle bounds = shape.getBounds();
                  for (int xx = bounds.x+xOffset; xx < bounds.x+bounds.width;
                        xx += wImg)
                  for (int yy = bounds.y; yy < bounds.y+bounds.height;
                        yy += hImg)
                        g2.drawImage(m_foregroundImage, xx, yy, this);
            }
      }
}
0
 
OviCommented:
Chapter 23. Java2D
In this chapter:
· Java2D API overview
· Rendering charts
· Rendering text strings
· Rendering images
23.1      Java2D API overview
Java 2 offers a very powerful new rendering model known as Java2D. This model consists of a set of classes and interfaces for advanced 2D line art, text, and image rendering. Although this API is not considered a part of Swing, it is closely related to Swing and may be effectively used to develop sophisticated Swing applications.

Note: Packages java.awt.print (discussed in chapter 22) and com.sun.image.codec.jpeg (discussed in chapter 13) are also considered part of the Java2D API.

This chapter includes a Java2D API overview and shows how to use this API for chart rendering, enhanced label creation, and advanced image rendering. Below we briefly discuss the classes and interfaces that are fundamental to the 2D API. Note, however, that a complete description of all Java2D features lies beyond the scope of this book.
23.1.1      The Shape interface
abstract interface java.awt.Shape
This interface provides the definition for a 2D geometrical object. Most of the classes contained in the java.awt.geom package implement this interface. These classes define such things as points, lines, arcs, rectangles, round rectangles, ellipses, and more complex shapes such as cubic and quadratic parametric curves. These geometries allow a high degree of flexibility and with them, we can create almost any shape imaginable. We can render these geometries into a 2D graphics context using its draw() or fill() methods (see below). We can also perform boolean operations on multiple shapes such as union, intersection, exclusive or, etc. using the java.awt.geom.Area class. The geometry of each Shape's boundary is defined as a path which is represented by a set of line segments and curves encapsulated in a PathIterator instance (we will not discuss the details of this here).

Several overloaded contains() methods determine whether a given point or rectangle lies inside a Shape, and the getBounds() method returns a Shape's minimum bounding rectangle.
23.1.2      GeneralPath
class java.awt.geom.GeneralPath
This class implements the Shape interface and represents a geometric path constructed from several line segments, or quadratic and cubic curves. Particularly important is its append(Shape s, boolean connect) method which provides us with a way to append one shape to another by optionally connecting their paths with a line segment.

GeneralPath maintains a current coordinate at all times which represents the coordinate that, if we were to add a line segment, would be the beginning of the added line segment. To do this we use its lineTo() method passing it two floats representing the destination coordinate. Similarly, we can use its moveTo() and quadTo() methods to add a point or curve to the path.
23.1.3      Rectangle2D
class java.awt.geom.Rectangle2D
This class serves as a superclass for three classes: the well-known java.awt.Rectangle class, Rectangle2D.Double, and Rectangle2D.Float. These classes not only provide new ways to work with rectangles, but also allow us to specify a rectangle's coordinates in int, float, or double form. Along with Rectangle2D, the java.awt.geom package also includes a set of classes which provide new functionality to familiar graphical primitives, such as Dimension2D, Line2D, and Point2D. Each of these classes allow us to specify their coordinates using ints, floats, or doubles  through appropriate subclasses.
23.1.4      AffineTransform
class java.awt.geom.AffineTransform
This class encapsulates a general form affine transformation between two coordinate systems. This transformation is essentially a coordinate transformation represented by a 3x3 matrix with an implied last row ([0 0 1]) mapping each x and y in the bounding rectangle of a Shape to a new x' and y' according to the following:

x' = m00 x + m01 y + m02
y' = m10 x + m11 y + m12

The mxx's represent the first two rows of a 3x3 matrix. These formulas are quite simple to understand and can be rewritten, for most operations, as the following:

x' = (scaleX * x) + (shearX * y) + offsetX
y' = (scaleY * x) + (shearY * y) + offsetY

Thess transformations preserve lines and parallelism (i.e. parallel lines are mapped to parallel lines). We use them to perform scaling, shearing, and translation. To construct an AffineTransform we use either the double or float version of the following constructor:

    AffineTransform(m00, m10, m01, m11, m02, m12)

Note the order of the parameters. This directly corresponds to the columns of our matrix described above.

Rotation also preserves parallelism. Given an angle of rotation in radians, q :

x' = x*(cosq) + y*(-sinq) + offsetX
y' = x*(sinq) + y*(cosq) + offsetY

Note that (degrees * p/180) = radians.

The Java2D graphics context (see below) maintains a transform attribute, just as it maintains a color and font attribute. Whenever we draw or fill a shape, this operation will be performed according to the current state of the transform attribute. We can create an instance of AffineTransform by specifying the first two rows of the matrix as described above. Alternatively, we can use static methods to create specific types of transformations: getRotateInstance(), getScaleInstance(), getShearInstance(), or getTranslateInstance(). We can use the concatenate() method to concatenate multiple transformations successively. We can also compose specific transformations with an existing AffineTransform using its rotate(), scale(), shear(), and translate() methods.

AffineTransforms are widely used throughout the 2D API as parameters to methods requiring a transformation to produce various visual effects.
23.1.5      The Stroke interface
abstract interface java.awt.Stroke
This interface defines only one method: createStrokedShape(Shape p), which generates a Shape that is the outline of the given Shape parameter. This outline can be of various size, shape, and décor. The only implementing class is BasicStroke (see below). We use Strokes to define line styles for drawing in the Java2D graphics context. To set the stroke attribute of a given Graphics2D we use its setStroke() method.
23.1.6      BasicStroke
class java.awt.BasicStroke
This class implements the Stroke interface and defines a set of rendering attributes specifying how to render the outline of a Shape. These attributes consist of line width, join style, end-cap style, and dash style:
The line width (often called the pen width) is the thickness measured perpendicular to its trajectory.
The end-cap style specifies whether round, butt, or square ends are used to render the ends of line segments: CAP_ROUND, CAP_BUTT, and CAP_SQUARE.
The join style specifies how to render the joints between segments. This can be one of bevel, miter, or round: JOIN_BEVEL, JOIN_MITER, and JOIN_ROUND.
The dash style defines a pattern of opaque and transparent regions rendered along a line segment.
23.1.7      The Paint interface
abstract interface java.awt.Paint
This interface defines how colors and color patterns may be assigned to the 2D graphics context for use in drawing and filling operations. Some important implementing classes are Color, GradientPaint, and TexturePaint. We use Paints to define fill patterns for filling in Shapes in the Java2D graphics context. To set the paint attribute of a given Graphics2D we use its setPaint() method.
23.1.8      GradientPaint
class java.awt.GradientPaint
This class implements the Paint interface and renders a shape by using a linear color gradient. The gradient is determined by two 2D points and two colors associated with them. The gradient can optionally be cyclical which means that between both points it will cycle through shades of each color several times, rather than just once. We use the Graphics2D.setPaint() method to assign a GradientPaint instance to a Graphics2D object. We can then call the fill() method to fill a specified Shape with this gradient. Note that this class provides an easy way to produce remarkable visual effects using only a few lines of code.
23.1.9      TexturePaint
class java.awt.TexturePaint
This class implements the Paint interface and is used to fill Shapes with a texture stored in a BufferedImage. We use the Graphics2D.setPaint() method to assign a TexturePaint instance to a Graphics2D object. We can call the fill() method to fill a specified Shape with this texture. Note that the BufferedImages used for a texture are expected to be small, as a TexturePaint object makes a copy of its data and stores it internally; it does not reference the provided BufferedImage. It is also important to reuse TexturePaint objects, rather than create new ones, whenever possible.
23.1.10      Graphics2D
class java.awt.Graphics2D
This class extends the java.awt.Graphics class to provide a more sophisticated API for working with geometry, transformations, colors, fill patterns and line styles, and text layout. In Java 2, the Graphics object passed to a component's paint() method is really a Graphics2D object. So we can use this class in our paint() implementation by simply casting our Graphics object to a Graphics2D:

  public void paint(Graphics g)
  {
    Graphics2D g2 = (Graphics2D) g;
    // Use Graphics2D ...

We can assign attributes to a Graphics2D instance using methods such as setTransform(), setStroke() or setPaint(), as we discussed above. We can then call draw() to outline a given Shape instance using the assigned Stroke, and we can call fill() to fill a given Shape with the assigned Color, GradientPaint, or TexturePaint. Depending on the state of the transform attribute, Shapes will be translated, rotated, scaled, or sheared appropriately as they are drawn (see AffineTransform). We can modify the current transform directly with methods rotate(), scale(), shear(), and translate(). We can also assign it a new transform using its setTransform() method, or compose the current transform with a given one using its transform() method.

A Graphics2D object can maintain preferences for specific rendering algorithms to use depending on whether speed or quality is the priority. These are called rendering hints. They can be assigned using the setRenderingHint() method and are stored as key/value pairs. Valid keys and values are defined in the RenderingHints class. Two of these pairs are especially important to us, as the examples in this chapter will always use them:

By setting the KEY_ANTIALIASING property to VALUE_ANTIALIAS_ON you can take advantage of a technique used to render objects with smoothly blended edges (by using intermediate colors to render a border between, say, black and white areas).
By setting the the KEY_RENDERING property to VALUE_RENDER_QUALITY, appropriate rendering algorithms will always be chosen to ensure the best output quality.
23.1.11      GraphicsEnvironment
class java.awt.GraphicsEnvironment
This class is capable of retrieving the collection of GraphicsDevice and Font instances available to a Java application on the running platform. GraphicsDevices can reside on the local machine or any number of remote machines. A GraphicsDevice instance describes, surprisingly, a graphics device such as a screen or printer.

Recall from chapter 2 that we normally reference GraphicsEnvironment to retrieve the names of all available fonts:

  String[] fontNames = GraphicsEnvironment.getLocalGraphicsEnvironment().
    getAvailableFontFamilyNames();
23.1.12      BufferedImage
class java.awt.image.BufferedImage
This class represents an Image stored in memory providing methods for storing, interpreting, and rendering pixel data. It is used widely throughout the 2D API and we've already seen it in chapters 13 and 22. In particular, you can create a BufferedImage, retrieve its associated Graphics2D instance to render into, perform the rendering, and use the result as an image for, among other things, painting directly into another graphics context (we used this technique in the construction of our print preview component). This is also similar to how RepaintManager handles the buffering of all Swing components, as we discussed in chapter 2.
23.1.13      FontRenderContext
class java.awt.font.FontRenderContext
Instances of this class encapsulate information needed to correctly measure text. This includes rendering hints and target device specific information such as resolution (dots-per-inch).  A FontRenderContext instance representing the current state of the 2D graphics context can be retrieved using Graphics2D's getFontRenderContext() method. FontRenderContext is usually used in association with text formatting using Fonts and TextLayouts.
23.1.14      TextLayout
class java.awt.font.TextLayout
Instances of this class represent an immutable graphical representation of styled text--that is, they cannot change (this class does not contain any set accessors). Only new instances can be created, and a FontRenderContext instance is required to do this. We render a TextLayout in the 2D graphcis context using that TextLayout's draw() method. This class is very powerful and supports such things as hit detection, which will return the character a mouse press occurs on, as well as support for bi-directional text and split cursors. A particularly noteworthy method is getOutline(AffineTransform tx), which returns a Shape instance outlining the text.
23.2      Rendering charts
In this section we'll demonstrate the advantages of using the Java2D API for rendering charts. The following example introduces a custom component which is capable of rendering line graphs, bar charts, and pie charts using strokes, color gradients, and background images. This application demonstrates how to build such charts taking into account issues such as axis positioning and scaling based on the given coordinate data. Be prepared for a bit of math.


Understanding the Code (Above example)
Class Charts2D
This class provides the frame encompassing this example. It creates an array of equidistant x-coordinates and random y-coordinates to be drawn in the charts. Three instances of our custom JChart2D class (see below) are created and placed in the frame using a GridLayout. The methods used to provide setup and initialization for our chart are built into the JChart2D class and will be explained below.
Class JChart2D
Several constants are defined for use as the available chart type and visual effect options:
int CHART_LINE: specifies a line chart.
int CHART_COLUMN: specifies a column chart.
int CHART_PIE: specifies a pie chart.
int EFFECT_PLAIN: use no visual effects (homogeneous chart).
int EFFECT_GRADIENT: use a color gradient to fill the chart.
int EFFECT_IMAGE: use an image to fill the chart.

Several instance variables are defined to hold data used by this class:
JLabel m_title: label used to display a chart's title.
ChartPanel m_chart: custom component used to display a chart's body (see below).
int m_nData: number of points in the chart.
int[] m_xData: array of x-coordinates in the chart.
int[] m_yData: array of y-coordinates in the chart.
int m_xMin: minimum x-coordinate.
int m_xMax: maximum x-coordinate.
int m_yMin: minimum y-coordinate.
int m_yMax: maximum y-coordinate.
double[] m_pieData: angles for each piece of the pie chart.
int m_chartType: maintains the chart's type (one of the constants listed above).
int m_effectIndex: maintains the chart's effect index (one of the constants listed above).
Stroke m_stroke: stroke instance used to outline the chart.
GradientPaint m_gradient: color gradient used to fill the chart (this only takes effect when m_effectIndex is set to EFFECT_GRADIENT).
Image m_foregroundImage: image used to fill the chart (this only takes effect when m_effectIndex is set to EFFECT_IMAGE).
Color m_lineColor: color used to outline the chart.
Color m_columnColor: color used to fill the chart (this only takes effect when m_effectIndex is set to EFFECT_PLAIN -- this is its default setting).
int m_columnWidth: width of columns in the column chart.
boolean m_drawShadow: flag to draw a shadow for column or pie chart.

Two constructors are provided in the JChart2D class. The first one takes four parameters and simply calls the second, passing it the given parameters and using a null value for a fifth. This second constructor is where a JChart2D is actually created and its five parameters are:
int type: the type of this chart (CHART_LINE, CHART_COLUMN, or CHART_PIE).
int nData: number of data points in this chart.
int[] xData: an array of x-coordinates for this chart (may be null -- this is passed as null from the first constructor).
int[] yData: an array of y-coordinates for this chart.
String text: this chart's title.

The constructor validates the input data and initializes all instance variables. In the case of a pie chart, an array, m_pieData, is created, which contains sectors with angles normalized to 360 degrees (the sum value used here was calculated previous to this code as the sum of all m_yData[] values):

     m_pieData = new double[m_nData];
     for (int k=0; k<m_nData; k++)
       m_pieData[k] = m_yData[k]*360.0/sum;

This chart component extends JPanel and contains two child components managed using a BorderLayout: JLabel m_title, which displays the chart's title in the NORTH region, and an instance of our custom ChartPanel component, m_chart, which is placed in the CENTER region.

The rest of the code for this class consists of set/get methods supporting instance variables declared in this class and does not require further explanation.
Class JChart2D.ChartPanel
This inner class extends JComponent and represents the custom component that is actually responsible for  rendering our charts. Several instance variables are declared:
int m_xMargin: the left and right margin size of the rendering area.
int m_yMargin: the top and bottom margin size of the rendering area.
int m_pieGap: radial shift for pieces of pie (i.e. spacing between each).
int m_x: left coordinate of the rendering area.
int m_y: top coordinate of the rendering area.
int m_w: width of the rendering area.
int m_h: height of the rendering area.

The ChartPanel constructor enables the processing of component resize events. When such an event occurs, the processComponentEvent() method triggers a call to calcDimensions() (note that this event will normally be generated when ChartPanel is added to a container for the first time). This method retrieves the current component's size, calculates the coordinates of the rendering area, and stores them in the appropriate instance variables listed above. In the case of a column chart, we offset the rendering area by an additional half of the column width, and then shrink it by a full column width. Otherwise, the first and the last columns will be rendered on top of the chart's border.

Methods xChartToScreen() and yChartToScreen() calculate screen coordinates from chart coordinates as illustrated in figure 23.2. We need to scale the chart data so the chart will occupy the entire component region, taking into account the margins. To get the necessary scaling ratios we divide the dimensions of the chart component (minus the margins) by the difference between max and min values of the chart data. These methods are used in rendering the line and column charts because they are based on coordinate data. The only sizing information the pie chart needs is m_w and m_h, as it does not rely on coordinate data.
The paintComponent() method performs the actual chart rendering. The coordinate axes are drawn first for line and column charts. Then we cast the Graphics instance to a Graphics2D so we have access to Java2D features. As we discussed earlier, we use two rendering hints and assign them with the setRenderingHint() method: anti-aliasing and the preference to render quality over speed. If the m_stroke instance variable has been initialized, the Graphics2D stroke attribute is set using the setStroke() method. The rest of the paintComponent() method is placed into a switch block with cases for each chart type. Before the switch block is entered we create a GeneralPath which we will use to construct each chart using the methods we described in section 23.1.2.

The line chart is the simplest case. It is drawn as a broken line through the array of points representing the chart data. First we start the GeneralPath out by passing the first coordinate of data using moveTo(). Then  we iterate through the chart data adding lines to the path using its lineTo() method. Once we've done this we are ready to render it and use the Graphics2D  draw() method to do so.

Note: The Java2D API provides ways to draw quadratic and cubic curves passing through 3 and 4 given points respectively. Unfortunately this functionality is not suitable for drawing a smooth line chart with interpolation.

The column chart is drawn as a set of vertical bars with a common baseline corresponding to the 0-value of the chart, y0 (note that this value is always included in the [m_yMin, m_yMax] interval). The GeneralPath instance accumulates these bars as Rectangle2D.Double instances using its append() method, passing false for the  line connection option.

If the m_drawShadow flag is set, the next step forms and draws a shadow from these bars, which should be viewed as standing vertically. AffineTransform s0 is constructed to accomplish this in four steps:
1. Transform from screen coordinates to chart coordinates.
2. Scale y-axis by a factor of 0.5.
3. Shear x-axis by a factor of 1.0.
4. Transform chart coordinates back to screen coordinates.

As soon as this AffineTransform is constructed, we create a corresponding transformed version of our path Shape using AffineTransform's createTransformedShape() method. We then set the current color to gray and render it into the 2D graphics context using the fill() method. Finally the set of bars is drawn on the screen. Depending on the m_effectIndex setting we fill this shape with the gradient color, image (by calling our custom fillByImage() method), or with a solid color.

The pie chart is drawn as pieces of a circle with a common center. The larger the chart's value is for a given point, the larger the corresponding angle of that piece is. For an interesting resemblance with a cut pie, all pieces are shifted apart from the common center in the radial direction. To draw such a pie we first build each piece by iterating through the chart's data. Using class Arc2D.Double with its PIE setting provides a convenient way to build a slice of pie. We then translate this slice away from the pie's center in the radial direction using an AffineTransform and its createTransformShape() method. Each resulting shape is appended to our GeneralPath instance.

If the m_drawShadow flag is set, we form and draw a shadow from these pieces. Since this chart can be viewed as laying on a flat surface, the shadow has the same shape as the chart itself, but is translated in the south-east direction. Finally the set of pie pieces is drawn on the screen using the selected visual effect. Since at this point we operate with the chart as a single Shape (remember a GeneralPath is a Shape), the code is the same as for the column chart.

The custom fillByImage() method uses the given Shape instance's bounds as the Graphics2D clipping area, and, in a doubly nested for loop, fills this region using our previously assigned m_foregroundImage. (Note that the third parameter to this method, int xOffset, is used for horizontal displacement which we do not make use of in this example. However, we will see this method again in the next example where we will need this functionality.)

Running the Code

Figure 23.1 shows our Charts2D application containing three charts: line, column, and pie. Try modifying the settings specified in Charts2D class to try charts with various combinations of available visual effects. Also try resizing the frame container and note how each chart is scaled accordingly.

Our JChart2D component can easily be plugged into any Swing application. Since we have implemented full scalability and correct coordinate mapping, we have the beginnings of a professional chart component. The next step would be to add informative strings to the axis as well as pie pieces, bars, and data points of the line chart.
23.3      Rendering strings
In this section we'll demonstrate advantages of using Java2D for rendering strings. This is especially useful  for relatively big fonts used to display titles. The following example introduces a custom label component which is capable of rendering strings with various visual effects, including such things as animation using an image, continuously changing foreground color, and outlining.


Please provide email for complete book (if you want)
0
 
man068Author Commented:
that was great ovi....
thanks for the answer... one more thing . can get the complete book? is it available online...? so that i can download it and use it....
my mail id is
man068@hotmail.com
0
Never miss a deadline with monday.com

The revolutionary project management tool is here!   Plan visually with a single glance and make sure your projects get done.

 
OviCommented:
Hi,  I send today to your email address the entire book (9 Mb)in two main parts. Unfortunatelly, I cannot find the URL from where I've got'it, anyway is the manuscript version (the book was published in december 1999), downloaded from the net. First part contain the examples , second, the chapters. If is not possible to send'it in this way, I will use smaller archives. WinRar is fine for you ? You can look for the $$ version at http://www.amazon.com/exec/obidos/ASIN/1884777848/o/qid=948781077/sr=2-1/104-3376319-6682842
0
 
OviCommented:
It seems that hotmail does'n permit you to exeed some specific storage capacity. Please make an mail account at www.usa.net or give me another method to send you the book.
0
 
OviCommented:
I've founded, go to : http://manning.spindoczine.com/sbe at the bottom of the page you have all chapters.
0
 
man068Author Commented:
thank you very much.
0
 
OviCommented:
Have a nice programming....:)
0

Featured Post

Never miss a deadline with monday.com

The revolutionary project management tool is here!   Plan visually with a single glance and make sure your projects get done.

  • 6
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now