/***********************************************************************************************
* Copyright 2002 (C) Nathaniel G. Auvil. All Rights Reserved.
*
* Redistribution and use of this software and associated documentation ("Software"), with or
* without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain copyright statements and notices.
* Redistributions must also contain a copy of this document.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. The name "jCharts" or "Nathaniel G. Auvil" must not be used to endorse or promote
* products derived from this Software without prior written permission of Nathaniel G.
* Auvil. For written permission, please contact nathaniel_auvil@users.sourceforge.net
*
* 4. Products derived from this Software may not be called "jCharts" nor may "jCharts" appear
* in their names without prior written permission of Nathaniel G. Auvil. jCharts is a
* registered trademark of Nathaniel G. Auvil.
*
* 5. Due credit should be given to the jCharts Project (http://jcharts.sourceforge.net/).
*
* THIS SOFTWARE IS PROVIDED BY Nathaniel G. Auvil AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* jCharts OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
************************************************************************************************/
package org.krysalis.jcharts;
import org.krysalis.jcharts.chartData.ChartDataException;
import org.krysalis.jcharts.imageMap.ImageMap;
import org.krysalis.jcharts.properties.ChartProperties;
import org.krysalis.jcharts.properties.LegendProperties;
import org.krysalis.jcharts.properties.PropertyException;
import org.krysalis.jcharts.test.HTMLGenerator;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.Serializable;
import java.text.AttributedString;
/*************************************************************************************
* Base class of all charts.
*
* @author Nathaniel Auvil, Sandor Dornbush
* @version $Id: Chart.java,v 1.3 2003/06/21 19:44:35 nathaniel_auvil Exp $
************************************************************************************/
public abstract class Chart implements Serializable
{
//---Be aware that the calls BufferedImage.getGraphics() and BufferedImage.createGraphics(), actually
//--- create a new Graphics2D Object on each invocation. So keep a reference here.
private Graphics2D graphics2D;
private int width;
private int height;
private Legend legend;
//---general properties of all chart types.
private ChartProperties chartProperties;
//---used to generate an image map for the image
private boolean generateImageMap = false;
private ImageMap imageMap = null;
private BufferedImage bufferedImage = null;
/******************************************************************************************
* Constructor
*
* @param legendProperties
* @param chartProperties
* @param pixelWidth
* @param pixelHeight
*******************************************************************************************/
public Chart( LegendProperties legendProperties, ChartProperties chartProperties, int pixelWidth, int pixelHeight )
{
this.width = pixelWidth;
this.height = pixelHeight;
this.chartProperties = chartProperties;
if( legendProperties != null )
{
this.legend = new Legend( this, legendProperties );
legendProperties.setSize( new Dimension( width, height ) );
}
}
/******************************************************************************************
* Returns flag indicating whether to generate an ImageMap
*
* @return boolean
*******************************************************************************************/
public boolean getGenerateImageMapFlag()
{
return this.generateImageMap;
}
/******************************************************************************************
* Returns the BufferedImage used to generate the ImageMap. Only should be called on
* binary format images, such as PNG and JPG, as it will not work on SVG.
*
* This is a HACK and the design of jCharts should do better than this!!!!!!
*
* @return BufferedImage
*******************************************************************************************/
public BufferedImage getBufferedImage()
{
return this.bufferedImage;
}
/******************************************************************************************
* Renders the chart into a BufferedImage so that we can calculate all the Image Map
* coordinates.
*
* @throws ChartDataException
* @throws PropertyException
*******************************************************************************************/
public void renderWithImageMap() throws ChartDataException, PropertyException
{
this.bufferedImage = new BufferedImage( this.getImageWidth(), this.getImageHeight(), BufferedImage.TYPE_INT_RGB );
this.setGraphics2D( this.bufferedImage.createGraphics() );
this.generateImageMap = true;
this.render();
}
/******************************************************************************************
* Call this to kick off rendering of the chart
*
* @throws ChartDataException
* @throws PropertyException
*******************************************************************************************/
public void render() throws ChartDataException, PropertyException
{
if( this.chartProperties.useAntiAliasing() )
{
this.graphics2D.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
}
else
{
this.graphics2D.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
}
Rectangle2D.Float rectangle = new Rectangle2D.Float( 0, 0, this.width, this.height );
//---fill the background
this.graphics2D.setPaint( this.chartProperties.getBackgroundPaint() );
this.graphics2D.fill( rectangle );
//---draw a border around the chart if desired
if( this.chartProperties.getBorderStroke() != null )
{
//---there is a one pixel difference when you fill versus draw a rectangle
rectangle.width -= 1;
rectangle.height -= 1;
this.chartProperties.getBorderStroke().draw( this.graphics2D, rectangle );
}
//---draw the chart
this.renderChart();
}
/*************************************************************************************************
* Displays the chart title and returns the height of the title PLUS title padding.
*
* @param chartTitle
* @param fontRenderContext
* @return float the height required by the title. If no title is displayed, zero is returned.
**************************************************************************************************/
protected float renderChartTitle( String chartTitle, FontRenderContext fontRenderContext )
{
float height = 0;
if( chartTitle != null )
{
//---the y value of where to write the current line
float currentLine = this.getChartProperties().getEdgePadding();
// change title into an AttributedString with the font as an attribute
AttributedString s = new AttributedString( chartTitle );
s.addAttribute( TextAttribute.FONT, this.getChartProperties().getTitleFont().getFont() );
//---get LineBreakMeasurer on the attributed string...it will break the text for us
LineBreakMeasurer measurer = new LineBreakMeasurer( s.getIterator(), fontRenderContext );
//---set the text color
this.getGraphics2D().setPaint( this.getChartProperties().getTitleFont().getPaint() );
//---draw each title line. Subtract padding from each side for printable width.
float wrappingWidth= this.getImageWidth() - ( this.getChartProperties().getEdgePadding() * 2 );
TextLayout titleTextLayout= null;
while( ( titleTextLayout = measurer.nextLayout( wrappingWidth ) ) != null )
{
//---set the current line to where the title will be written
currentLine += titleTextLayout.getAscent();
titleTextLayout.draw( this.getGraphics2D(),
( ( this.getImageWidth() - titleTextLayout.getAdvance() ) / 2 ),
currentLine );
//---keep track of total height of all the title lines
height += titleTextLayout.getAscent() + titleTextLayout.getDescent();
}
//---add in the padding between the title and the top of the chart.
height += this.getChartProperties().getTitlePadding();
}
return height;
}
/******************************************************************************************
*
* @throws ChartDataException
* @throws PropertyException
*******************************************************************************************/
abstract protected void renderChart() throws ChartDataException, PropertyException;
/******************************************************************************************
* Returns the BufferedImage width
*
* @return int
*******************************************************************************************/
public final int getImageWidth()
{
return this.width;
}
/******************************************************************************************
* Returns the BufferedImage height
*
* @return int
*******************************************************************************************/
public final int getImageHeight()
{
return this.height;
}
/******************************************************************************************
* Returns the general properties Object.
*
* @return ChartProperties
*******************************************************************************************/
public final ChartProperties getChartProperties()
{
return this.chartProperties;
}
/******************************************************************************************
* Returns the Legend. Will be NULL if no Legend is desired.
*
* @return Legend
*******************************************************************************************/
protected final Legend getLegend()
{
return this.legend;
}
/******************************************************************************************
* Returns flag indicating if there is a Legend.
*
* @return boolean
*******************************************************************************************/
public final boolean hasLegend()
{
return this.legend != null;
}
/******************************************************************************************
* Sets the graphics object to render the chart on by the encoder.
*
* @param graphics2D
*******************************************************************************************/
public final void setGraphics2D( Graphics2D graphics2D )
{
this.graphics2D = graphics2D;
}
/******************************************************************************************
* Shortcut method to get Graphics2D. Be aware that the call BufferedImage.getGraphics()
* and BufferedImage.createGraphics(), actually create a new Grpahics2D Object on each
* invocation. This returns the member reference so calls to this are not creating a new
* Object each time.
*
* @return Graphics2D
*******************************************************************************************/
public final Graphics2D getGraphics2D()
{
return this.graphics2D;
}
/**********************************************************************************************
* To optimze performance of the ImageMap Object, we create it once we know how many data
* elements are in the chart which is dependent on the AxisChart or PieChart2D
*
* @param imageMap
**********************************************************************************************/
public final void setImageMap( ImageMap imageMap )
{
this.imageMap = imageMap;
}
/**********************************************************************************************
*
* @return imageMap
**********************************************************************************************/
public final ImageMap getImageMap()
{
return this.imageMap;
}
/**********************************************************************************************
* Enables the testing routines to display the contents of this Object.
*
* @param htmlGenerator
* @param imageFileName
* @param imageMap if this is NULL we are not creating image map data in html
**********************************************************************************************/
public void toHTML( HTMLGenerator htmlGenerator, String imageFileName, ImageMap imageMap )
{
htmlGenerator.chartTableStart( this.getClass().getName(), imageFileName, imageMap );
htmlGenerator.chartTableRowStart();
this.chartProperties.toHTML( htmlGenerator );
htmlGenerator.chartTableRowEnd();
if( this.legend != null )
{
htmlGenerator.chartTableRowStart();
this.getLegend().toHTML( htmlGenerator );
htmlGenerator.chartTableRowEnd();
}
htmlGenerator.chartTableEnd();
}
}