Code Search for Developers
 
 
  

PlotWith2DAxes.java from BIRT at Krugle


Show PlotWith2DAxes.java syntax highlighted

/***********************************************************************
 * Copyright (c) 2004 Actuate Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * Actuate Corporation - initial API and implementation
 ***********************************************************************/

package org.eclipse.birt.chart.computation.withaxes;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Map;

import org.eclipse.birt.chart.computation.DataPointHints;
import org.eclipse.birt.chart.computation.DataSetIterator;
import org.eclipse.birt.chart.computation.IConstants;
import org.eclipse.birt.chart.computation.LegendItemRenderingHints;
import org.eclipse.birt.chart.computation.UserDataSetHints;
import org.eclipse.birt.chart.datafeed.IDataSetProcessor;
import org.eclipse.birt.chart.device.IDisplayServer;
import org.eclipse.birt.chart.engine.i18n.Messages;
import org.eclipse.birt.chart.exception.ChartException;
import org.eclipse.birt.chart.factory.RunTimeContext;
import org.eclipse.birt.chart.log.ILogger;
import org.eclipse.birt.chart.model.ChartWithAxes;
import org.eclipse.birt.chart.model.attribute.AxisType;
import org.eclipse.birt.chart.model.attribute.Bounds;
import org.eclipse.birt.chart.model.attribute.DataPoint;
import org.eclipse.birt.chart.model.attribute.DataPointComponent;
import org.eclipse.birt.chart.model.attribute.DataPointComponentType;
import org.eclipse.birt.chart.model.attribute.FormatSpecifier;
import org.eclipse.birt.chart.model.attribute.Location;
import org.eclipse.birt.chart.model.attribute.Orientation;
import org.eclipse.birt.chart.model.attribute.impl.LocationImpl;
import org.eclipse.birt.chart.model.component.Axis;
import org.eclipse.birt.chart.model.component.Label;
import org.eclipse.birt.chart.model.component.Scale;
import org.eclipse.birt.chart.model.component.Series;
import org.eclipse.birt.chart.model.component.impl.SeriesImpl;
import org.eclipse.birt.chart.model.data.DataSet;
import org.eclipse.birt.chart.model.data.NumberDataElement;
import org.eclipse.birt.chart.model.data.SeriesDefinition;
import org.eclipse.birt.chart.model.impl.ChartWithAxesImpl;
import org.eclipse.birt.chart.plugin.ChartEnginePlugin;
import org.eclipse.birt.chart.render.AxesRenderer;
import org.eclipse.birt.chart.render.IAxesDecorator;
import org.eclipse.birt.chart.render.ISeriesRenderingHints;
import org.eclipse.birt.chart.util.CDateTime;
import org.eclipse.birt.chart.util.PluginSettings;
import org.eclipse.emf.common.util.EList;

import com.ibm.icu.util.Calendar;

/**
 * This class is capable of computing the content of a chart (with axes) based
 * on preferred sizes, text rotation, fit ability, scaling, etc and prepares it
 * for rendering.
 * 
 * WARNING: This is an internal class and subject to change
 */
public final class PlotWith2DAxes extends PlotWithAxes
{

	/**
	 * This complex reference is used in rendering stacked series otherwise
	 * unused.
	 */
	private StackedSeriesLookup ssl = null;

	/**
	 * The default constructor
	 * 
	 * @param _ids
	 *            The display server using which the chart is computed
	 * @param _cwa
	 *            An instance of the model (ChartWithAxes)
	 * @throws ValidationException
	 * @throws UndefinedValueException
	 * @throws UnexpectedInputException
	 */
	public PlotWith2DAxes( IDisplayServer _ids, ChartWithAxes _cwa,
			RunTimeContext _rtc ) throws IllegalArgumentException,
			ChartException
	{
		cwa = _cwa;
		ids = _ids;
		rtc = _rtc;
		ssl = new StackedSeriesLookup( _rtc );
		dPointToPixel = ids.getDpiResolution( ) / 72d;
		buildAxes( ); // CREATED ONCE
	}

	/**
	 * Internally maps the EMF model to internal (non-public) rendering fast
	 * data structures
	 */
	final void buildAxes( ) throws IllegalArgumentException, ChartException
	{
		final Axis[] axa = cwa.getPrimaryBaseAxes( );
		// NOTE: FOR REL 1 AXIS RENDERS, WE SUPPORT A SINGLE PRIMARY BASE AXIS
		// ONLY
		final Axis axPrimaryBase = axa[0];
		if ( !axPrimaryBase.isSetOrientation( ) )
		{
			axPrimaryBase.setOrientation( Orientation.HORIZONTAL_LITERAL );
		}
		validateAxis( axPrimaryBase );

		final Axis axPrimaryOrthogonal = cwa.getPrimaryOrthogonalAxis( axPrimaryBase );
		if ( !axPrimaryOrthogonal.isSetOrientation( ) )
		{
			axPrimaryOrthogonal.setOrientation( Orientation.VERTICAL_LITERAL );
		}
		validateAxis( axPrimaryOrthogonal );

		final Axis[] axaOverlayOrthogonal = cwa.getOrthogonalAxes( axPrimaryBase,
				false );
		aax = new AllAxes( cwa.getPlot( )
				.getClientArea( )
				.getInsets( )
				.scaledInstance( dPointToPixel ) ); // CONVERSION
		insCA = aax.getInsets( );

		final boolean isTransposed = cwa.isTransposed( );

		aax.swapAxes( isTransposed );

		// SETUP THE PRIMARY BASE-AXIS PROPERTIES AND ITS SCALE
		final OneAxis oaxPrimaryBase = new OneAxis( axPrimaryBase );
		oaxPrimaryBase.set( getOrientation( IConstants.BASE ),
				transposeLabelPosition( IConstants.BASE, getLabelPosition(
				// isTransposed ? switchPosition(
						// axPrimaryBase.getLabelPosition( ) )
						// :
						axPrimaryBase.getLabelPosition( ) ) ),
				transposeLabelPosition( IConstants.BASE, getLabelPosition(
				// isTransposed ? switchPosition(
						// axPrimaryBase.getTitlePosition( ) )
						// :
						axPrimaryBase.getTitlePosition( ) ) ),
				axPrimaryBase.isSetCategoryAxis( )
						&& axPrimaryBase.isCategoryAxis( ) );
		oaxPrimaryBase.setGridProperties( axPrimaryBase.getMajorGrid( )
				.getLineAttributes( ),
				axPrimaryBase.getMinorGrid( ).getLineAttributes( ),
				axPrimaryBase.getMajorGrid( ).getTickAttributes( ),
				axPrimaryBase.getMinorGrid( ).getTickAttributes( ),
				transposeTickStyle( IConstants.BASE,
				// isTransposed ? switchTickStyle( getTickStyle( axPrimaryBase,
						// MAJOR ) )
						// :
						getTickStyle( axPrimaryBase, MAJOR ) ),
				transposeTickStyle( IConstants.BASE,
				// isTransposed ? switchTickStyle( getTickStyle( axPrimaryBase,
						// MINOR ) )
						// :
						getTickStyle( axPrimaryBase, MINOR ) ),
				axPrimaryBase.getScale( ).getMinorGridsPerUnit( ) );

		oaxPrimaryBase.set( axPrimaryBase.getLabel( ), axPrimaryBase.getTitle( ) );
		oaxPrimaryBase.set(
		// isTransposed ? switchIntersection( getIntersection( axPrimaryBase ) )
		// :
		getIntersection( axPrimaryBase ) );
		oaxPrimaryBase.set( axPrimaryBase.getLineAttributes( ) );
		aax.definePrimary( oaxPrimaryBase ); // ADD TO AXIS SET

		// SETUP THE PRIMARY ORTHOGONAL-AXIS PROPERTIES AND ITS SCALE
		final OneAxis oaxPrimaryOrthogonal = new OneAxis( axPrimaryOrthogonal );
		oaxPrimaryOrthogonal.set( getOrientation( IConstants.ORTHOGONAL ),
				transposeLabelPosition( IConstants.ORTHOGONAL,
						getLabelPosition( !isTransposed ? switchPosition( axPrimaryOrthogonal.getLabelPosition( ) )
								: axPrimaryOrthogonal.getLabelPosition( ) ) ),
				transposeLabelPosition( IConstants.ORTHOGONAL,
						getLabelPosition( !isTransposed ? switchPosition( axPrimaryOrthogonal.getTitlePosition( ) )
								: axPrimaryOrthogonal.getTitlePosition( ) ) ),
				axPrimaryOrthogonal.isSetCategoryAxis( )
						&& axPrimaryOrthogonal.isCategoryAxis( ) );
		oaxPrimaryOrthogonal.setGridProperties( axPrimaryOrthogonal.getMajorGrid( )
				.getLineAttributes( ),
				axPrimaryOrthogonal.getMinorGrid( ).getLineAttributes( ),
				axPrimaryOrthogonal.getMajorGrid( ).getTickAttributes( ),
				axPrimaryOrthogonal.getMinorGrid( ).getTickAttributes( ),
				transposeTickStyle( IConstants.ORTHOGONAL,
						!isTransposed ? switchTickStyle( getTickStyle( axPrimaryOrthogonal,
								MAJOR ) )
								: getTickStyle( axPrimaryOrthogonal, MAJOR ) ),
				transposeTickStyle( IConstants.ORTHOGONAL,
						!isTransposed ? switchTickStyle( getTickStyle( axPrimaryOrthogonal,
								MINOR ) )
								: getTickStyle( axPrimaryOrthogonal, MINOR ) ),
				axPrimaryOrthogonal.getScale( ).getMinorGridsPerUnit( ) );

		oaxPrimaryOrthogonal.set( axPrimaryOrthogonal.getLabel( ),
				axPrimaryOrthogonal.getTitle( ) );
		oaxPrimaryOrthogonal.set(
		// !isTransposed ?
		switchIntersection( getIntersection( axPrimaryOrthogonal ) )
		// : getIntersection( axPrimaryOrthogonal )
		);
		oaxPrimaryOrthogonal.set( axPrimaryOrthogonal.getLineAttributes( ) );
		aax.definePrimary( oaxPrimaryOrthogonal ); // ADD TO AXIS SET

		// SETUP THE OVERLAY AXES
		aax.initOverlays( axaOverlayOrthogonal.length,
				getOrientation( IConstants.ORTHOGONAL ) );
		OneAxis oaxOverlayOrthogonal;
		for ( int i = 0; i < axaOverlayOrthogonal.length; i++ )
		{
			if ( !axaOverlayOrthogonal[i].isSetOrientation( ) )
			{
				axaOverlayOrthogonal[i].setOrientation( Orientation.VERTICAL_LITERAL );
			}
			validateAxis( axaOverlayOrthogonal[i] );

			oaxOverlayOrthogonal = new OneAxis( axaOverlayOrthogonal[i] );
			oaxOverlayOrthogonal.set( getOrientation( IConstants.ORTHOGONAL ),
					transposeLabelPosition( IConstants.ORTHOGONAL,
							getLabelPosition( !isTransposed ? switchPosition( axaOverlayOrthogonal[i].getLabelPosition( ) )
									: axaOverlayOrthogonal[i].getLabelPosition( ) ) ),
					transposeLabelPosition( IConstants.ORTHOGONAL,
							getLabelPosition( !isTransposed ? switchPosition( axaOverlayOrthogonal[i].getTitlePosition( ) )
									: axaOverlayOrthogonal[i].getTitlePosition( ) ) ),
					axaOverlayOrthogonal[i].isSetCategoryAxis( )
							&& axaOverlayOrthogonal[i].isCategoryAxis( ) );
			oaxOverlayOrthogonal.setGridProperties( axaOverlayOrthogonal[i].getMajorGrid( )
					.getLineAttributes( ),
					axaOverlayOrthogonal[i].getMinorGrid( ).getLineAttributes( ),
					axaOverlayOrthogonal[i].getMajorGrid( ).getTickAttributes( ),
					axaOverlayOrthogonal[i].getMinorGrid( ).getTickAttributes( ),
					transposeTickStyle( IConstants.ORTHOGONAL,
							!isTransposed ? switchTickStyle( getTickStyle( axaOverlayOrthogonal[i],
									MAJOR ) )
									: getTickStyle( axaOverlayOrthogonal[i],
											MAJOR ) ),
					transposeTickStyle( IConstants.ORTHOGONAL,
							!isTransposed ? switchTickStyle( getTickStyle( axaOverlayOrthogonal[i],
									MINOR ) )
									: getTickStyle( axaOverlayOrthogonal[i],
											MINOR ) ),
					axaOverlayOrthogonal[i].getScale( ).getMinorGridsPerUnit( ) );

			oaxOverlayOrthogonal.set( axaOverlayOrthogonal[i].getLabel( ),
					axaOverlayOrthogonal[i].getTitle( ) );
			oaxOverlayOrthogonal.set( axaOverlayOrthogonal[i].getLineAttributes( ) );
			oaxOverlayOrthogonal.set(
			// !isTransposed ?
			switchIntersection( getIntersection( axaOverlayOrthogonal[i] ) )
			// : getIntersection( axaOverlayOrthogonal[i] )
			);
			aax.defineOverlay( i, oaxOverlayOrthogonal );
		}

		// BUILD STACKED STRUCTURE (FOR STACKED SERIES) ASSOCIATED WITH EACH
		// ORTHOGONAL AXIS
		ssl = StackedSeriesLookup.create( cwa, rtc );
	}

	/**
	 * This method pulls out the 'min' and 'max' value for all datasets
	 * associated with a single axis using the custom data source processor
	 * implementation
	 * 
	 * @param ax
	 *            The orthogonal axis for which the min/max values are being
	 *            computed
	 * @param iType
	 *            The renderer's axis data type
	 * 
	 * @return
	 */
	private final Object getMinMax( Axis ax, int iType ) throws ChartException,
			IllegalArgumentException
	{
		final Series[] sea = ax.getRuntimeSeries( );
		final int iSeriesCount = sea.length;
		Series se;
		DataSet ds;

		Object oV1, oV2, oMin = null, oMax = null;

		PluginSettings ps = PluginSettings.instance( );
		IDataSetProcessor iDSP = null;
		// ANY STACKED SERIES ASSOCIATED WITH AXIS 'ax'
		boolean bAnyStacked = false;

		for ( int i = 0; i < iSeriesCount; i++ )
		{
			if ( sea[i].isStacked( ) )
			{
				if ( sea[i].canBeStacked( ) )
				{
					bAnyStacked = true;
					continue;
				}
				else
				{
					throw new IllegalArgumentException( MessageFormat.format( Messages.getResourceBundle( rtc.getULocale( ) )
							.getString( "exception.unstackable.is.stacked" ), //$NON-NLS-1$
							new Object[]{
								sea[i]
							} ) );
				}
			}

			iDSP = ps.getDataSetProcessor( sea[i].getClass( ) );
			ds = sea[i].getDataSet( );

			oV1 = iDSP.getMinimum( ds );
			oV2 = iDSP.getMaximum( ds );

			if ( ( iType & NUMERICAL ) == NUMERICAL )
			{
				if ( oV1 != null ) // SETUP THE MINIMUM VALUE FOR ALL DATASETS
				{
					if ( oMin == null )
					{
						oMin = oV1;
					}
					else
					{
						final double dV1 = asDouble( oV1 ).doubleValue( );
						if ( Math.min( asDouble( oMin ).doubleValue( ), dV1 ) == dV1 )
						{
							oMin = oV1;
						}
					}
				}

				if ( oV2 != null ) // SETUP THE MAXIMUM VALUE FOR ALL DATASETS
				{
					if ( oMax == null )
					{
						oMax = oV2;
					}
					else
					{
						final double dV2 = asDouble( oV2 ).doubleValue( );
						if ( Math.max( asDouble( oMax ).doubleValue( ), dV2 ) == dV2 )
						{
							oMax = oV2;
						}
					}
				}
			}
			else if ( ( iType & DATE_TIME ) == DATE_TIME )
			{
				if ( oV1 != null ) // SETUP THE MINIMUM VALUE FOR ALL DATASETS
				{
					if ( oMin == null )
					{
						oMin = oV1;
					}
					else
					{
						final CDateTime cdtV1 = asDateTime( oV1 );
						final CDateTime cdtMin = asDateTime( oMin );
						if ( cdtV1.before( cdtMin ) )
						{
							oMin = cdtV1;
						}
					}
				}

				if ( oV2 != null ) // SETUP THE MAXIMUM VALUE FOR ALL DATASETS
				{
					if ( oMax == null )
					{
						oMax = oV2;
					}
					else
					{
						final CDateTime cdtV2 = asDateTime( oV2 );
						final CDateTime cdtMax = asDateTime( oMax );
						if ( cdtV2.after( cdtMax ) )
						{
							oMax = cdtV2;
						}
					}
				}
			}
		}

		// ONLY NUMERIC VALUES ARE SUPPORTED IN STACKED ELEMENT COMPUTATIONS
		if ( bAnyStacked || ax.isPercent( ) )
		{
			if ( ax.getType( ).getValue( ) == AxisType.DATE_TIME )
			{
				throw new IllegalArgumentException( MessageFormat.format( Messages.getResourceBundle( rtc.getULocale( ) )
						.getString( "exception.stacked.datetime.axis.series" ), //$NON-NLS-1$
						new Object[]{
							ax
						} ) );
			}
			Object oValue;
			int iSeriesPerGroup;
			double dGroupMin, dGroupMax, dValue, dAbsTotal, dPercentMax = 0, dPercentMin = 0;
			double dAxisMin = Double.MAX_VALUE, dAxisMax = -Double.MAX_VALUE;
			ArrayList alSeriesGroupsPerAxis = ssl.getStackGroups( ax );
			ArrayList alSeriesPerGroup;
			StackGroup sg;
			DataSetIterator[] dsi = new DataSetIterator[ssl.getSeriesCount( ax )];

			if ( alSeriesGroupsPerAxis == null )
			{
				throw new ChartException( ChartEnginePlugin.ID,
						ChartException.DATA_SET,
						"exception.internal.stack.series.setup", //$NON-NLS-1$
						new Object[]{
							ax
						},
						Messages.getResourceBundle( rtc.getULocale( ) ) );
			}
			logger.log( ILogger.INFORMATION,
					Messages.getString( "info.processing.stacked.info", //$NON-NLS-1$
							new Object[]{
								ax
							},
							rtc.getULocale( ) ) );
			int iSeriesIndex, iDataSetCount = ssl.getUnitCount( );

			for ( int k = 0; k < iDataSetCount; k++ ) // PER UNIT
			{
				dAbsTotal = 0;
				iSeriesIndex = 0;
				for ( int i = 0; i < alSeriesGroupsPerAxis.size( ); i++ )
				{
					sg = (StackGroup) alSeriesGroupsPerAxis.get( i );
					alSeriesPerGroup = sg.getSeries( );
					iSeriesPerGroup = alSeriesPerGroup.size( );

					if ( iSeriesPerGroup > 0 )
					{
						se = (Series) alSeriesPerGroup.get( 0 );
						ds = se.getDataSet( );
						if ( dsi[iSeriesIndex] == null )
						{
							dsi[iSeriesIndex] = new DataSetIterator( ds );
							if ( ( dsi[iSeriesIndex].getDataType( ) & IConstants.NUMERICAL ) != IConstants.NUMERICAL )
							{
								throw new ChartException( ChartEnginePlugin.ID,
										ChartException.DATA_SET,
										"exception.percent.stacked.non.numerical", //$NON-NLS-1$ 
										Messages.getResourceBundle( rtc.getULocale( ) ) );
							}
						}

						// ALL SERIES MUST HAVE THE SAME DATASET ELEMENT COUNT
						iDataSetCount = dsi[iSeriesIndex].size( );

						dGroupMin = 0;
						dGroupMax = 0;
						if ( ax.isPercent( ) )
						{
							dAbsTotal = 0;
						}

						for ( int j = 0; j < iSeriesPerGroup; j++ )
						{
							se = (Series) alSeriesPerGroup.get( j );
							if ( j > 0 ) // ALREADY DONE FOR '0'
							{
								if ( dsi[iSeriesIndex] == null )
								{
									ds = se.getDataSet( ); // DATA SET
									dsi[iSeriesIndex] = new DataSetIterator( ds );
									if ( ( dsi[iSeriesIndex].getDataType( ) & IConstants.NUMERICAL ) != IConstants.NUMERICAL )
									{
										throw new ChartException( ChartEnginePlugin.ID,
												ChartException.DATA_SET,
												"exception.percent.stacked.non.numerical", //$NON-NLS-1$ 
												Messages.getResourceBundle( rtc.getULocale( ) ) );
									}
								}
							}
							// EACH ROW OF DATA
							oValue = dsi[iSeriesIndex].next( );
							if ( oValue != null ) // NULL CHECK
							{
								// EXTRACT WRAPPED VALUE
								dValue = ( (Double) oValue ).doubleValue( );
								dAbsTotal += Math.abs( dValue );
								if ( dValue > 0 )
								{
									dGroupMax += dValue; // UPDATE MAX
								}
								else if ( dValue < 0 )
								{
									dGroupMin += dValue; // UPDATE MIN
								}
							}
							iSeriesIndex++;
						}
						final AxisSubUnit au = ssl.getSubUnit( sg, k );
						au.setPositiveTotal( dGroupMax );
						au.setNegativeTotal( dGroupMin );

						// FOR EACH UNIT, UPDATE THE MIN/MAX BASED ON ALL
						// STACKED SERIES
						dAxisMax = Math.max( dGroupMax, dAxisMax );
						dAxisMin = Math.min( dGroupMin, dAxisMin );
						if ( ax.isPercent( ) )
						{
							if ( dAbsTotal != 0d )
							{
								dPercentMax = Math.max( ( dGroupMax / dAbsTotal ) * 100d,
										dPercentMax );
								dPercentMin = Math.min( ( dGroupMin / dAbsTotal ) * 100d,
										dPercentMin );
							}
						}
					}
				}
			}
			if ( ax.isPercent( ) ) // HANDLE PERCENT
			{
				if ( dPercentMax >= 100 )
					dPercentMax = 99.9;
				if ( dPercentMin <= -100 )
					dPercentMin = -99.9;
				if ( dPercentMax == 0 && dPercentMin == 0 )
				{
					dPercentMax = 99;
				}
				dAxisMin = dPercentMin;
				dAxisMax = dPercentMax;
			}
			if ( ( iType & LOGARITHMIC ) == LOGARITHMIC )
			{
				dAxisMin = 1;
			}
			oMin = new Double( dAxisMin );
			oMax = new Double( dAxisMax );
		}

		// IF NO DATASET WAS FOUND BECAUSE NO SERIES WERE ATTACHED TO AXES,
		// SIMULATE MIN/MAX VALUES
		if ( oMin == null && oMax == null )
		{
			if ( iType == DATE_TIME )
			{
				oMin = new CDateTime( 1, 1, 2005 );
				oMax = new CDateTime( 1, 1, 2006 );
			}
			else if ( ( iType & NUMERICAL ) == NUMERICAL )
			{
				if ( ( iType & PERCENT ) == PERCENT )
				{
					oMin = new Double( 0 );
					oMax = new Double( 99.99 );
				}
				else if ( ( iType & LOGARITHMIC ) == LOGARITHMIC )
				{
					oMin = new Double( 1 );
					oMax = new Double( 999 );
				}
				else
				{
					oMin = new Double( -0.9 );
					oMax = new Double( 0.9 );
				}
			}
		}

		if ( iType == DATE_TIME )
		{
			try
			{
				return new Calendar[]{
						asDateTime( oMin ), asDateTime( oMax )
				};
			}
			catch ( ClassCastException ex )
			{
				throw new ChartException( ChartEnginePlugin.ID,
						ChartException.DATA_SET,
						"exception.numerical.data.datetime.axis", //$NON-NLS-1$ 
						Messages.getResourceBundle( rtc.getULocale( ) ) );

			}
		}
		else if ( ( iType & NUMERICAL ) == NUMERICAL )
		{
			try
			{
				return new double[]{
						asDouble( oMin ).doubleValue( ),
						asDouble( oMax ).doubleValue( )
				};
			}
			catch ( ClassCastException ex )
			{
				throw new ChartException( ChartEnginePlugin.ID,
						ChartException.DATA_SET,
						"exception.datetime.data.numerical.axis", //$NON-NLS-1$ 
						Messages.getResourceBundle( rtc.getULocale( ) ) );
			}
		}
		return null;
	}

	/**
	 * This method computes the entire chart within the given bounds. If the
	 * dataset has changed but none of the axis attributes have changed, simply
	 * re-compute without 'rebuilding axes'.
	 * 
	 * @param bo
	 * 
	 */
	public final void compute( Bounds bo ) throws ChartException,
			IllegalArgumentException
	{
		bo = bo.scaledInstance( dPointToPixel ); // CONVERSION
		dSeriesThickness = ( ids.getDpiResolution( ) / 72d )
				* cwa.getSeriesThickness( );

		// MAINTAIN IN LOCAL VARIABLES FOR PERFORMANCE/CONVENIENCE
		double dX = bo.getLeft( ) + insCA.getLeft( );
		double dY = bo.getTop( ) + insCA.getTop( );
		double dW = bo.getWidth( ) - insCA.getLeft( ) - insCA.getRight( );
		double dH = bo.getHeight( ) - insCA.getTop( ) - insCA.getBottom( );

		iDimension = getDimension( cwa.getDimension( ) );
		dXAxisPlotSpacing = cwa.getPlot( ).getHorizontalSpacing( )
				* dPointToPixel; // CONVERSION
		dYAxisPlotSpacing = cwa.getPlot( ).getVerticalSpacing( )
				* dPointToPixel; // CONVERSION

		if ( iDimension == TWO_5_D )
		{
			dY += dSeriesThickness;
			dH -= dSeriesThickness;
			dW -= dSeriesThickness;

			bo.setHeight( dH );
			bo.setTop( dY );
			bo.setWidth( dW );
		}

		// PLACE OVERLAYS FIRST TO REDUCE VIRTUAL PLOT BOUNDS
		if ( aax.getOverlayCount( ) > 0 )
		{
			if ( aax.areAxesSwapped( ) ) // ORTHOGONAL OVERLAYS = HORIZONTAL
			{
				updateOverlayScales( aax, dX, dX + dW, dY, dH );
				dY = aax.getStart( );
				dH = aax.getLength( );
			}
			else
			// ORTHOGONAL OVERLAYS = VERTICAL
			{
				updateOverlayScales( aax, dY - dH, dY, dX, dW );
				dX = aax.getStart( );
				dW = aax.getLength( );
			}
		}

		double dStart, dEnd;
		final Axis[] axa = cwa.getPrimaryBaseAxes( );
		final Axis axPrimaryBase = axa[0];
		final Axis axPrimaryOrthogonal = cwa.getPrimaryOrthogonalAxis( axPrimaryBase );
		Scale sc = axPrimaryBase.getScale( );

		// COMPUTE PRIMARY-BASE-AXIS PROPERTIES AND ITS SCALE
		AutoScale scPrimaryBase = null;
		OneAxis oaxPrimaryBase = aax.getPrimaryBase( );
		int iAxisType = getAxisType( axPrimaryBase );

		Object oaData = null;
		if ( iAxisType == TEXT || oaxPrimaryBase.isCategoryScale( ) )
		{
			oaData = getTypedDataSet( axPrimaryBase, iAxisType, 0 );
		}
		else if ( ( iAxisType & NUMERICAL ) == NUMERICAL )
		{
			oaData = getMinMax( axPrimaryBase, iAxisType );
		}
		else if ( ( iAxisType & DATE_TIME ) == DATE_TIME )
		{
			oaData = getMinMax( axPrimaryBase, iAxisType );
		}

		DataSetIterator dsi = ( oaData instanceof DataSetIterator ) ? (DataSetIterator) oaData
				: new DataSetIterator( oaData, iAxisType );
		oaData = null;

		dStart = ( aax.areAxesSwapped( ) ) ? dY + dH : dX;
		dEnd = ( aax.areAxesSwapped( ) ) ? dY : dStart + dW;

		int iDirection = AUTO;
		if ( isRightToLeft( ) )
		{
			// check if already swapped.
			if ( !aax.areAxesSwapped( ) )
			{
				double dTmp = dStart;
				dStart = dEnd;
				dEnd = dTmp;
				iDirection = BACKWARD;
			}
			else
			{
				dStart = dY;
				dEnd = dY + dH;
				iDirection = FORWARD;
			}
		}

		scPrimaryBase = AutoScale.computeScale( ids,
				oaxPrimaryBase,
				dsi,
				iAxisType,
				dStart,
				dEnd,
				sc.getMin( ),
				sc.getMax( ),
				sc.isSetStep( ) ? new Double( sc.getStep( ) ) : null,
				axPrimaryBase.getFormatSpecifier( ),
				rtc,
				iDirection );
		// UPDATE SCALE ON PRIMARY-BASE AXIS
		oaxPrimaryBase.set( scPrimaryBase );

		// COMPUTE PRIMARY-ORTHOGONAL-AXIS PROPERTIES AND ITS SCALE
		AutoScale scPrimaryOrthogonal = null;
		OneAxis oaxPrimaryOrthogonal = aax.getPrimaryOrthogonal( );
		iAxisType = getAxisType( axPrimaryOrthogonal );
		oaData = null;
		if ( ( iAxisType & NUMERICAL ) == NUMERICAL
				|| ( iAxisType & DATE_TIME ) == DATE_TIME )
		{
			dsi = new DataSetIterator( getMinMax( axPrimaryOrthogonal,
					iAxisType ), iAxisType );
		}
		else
		{
			throw new ChartException( ChartEnginePlugin.ID,
					ChartException.DATA_FORMAT,
					"exception.orthogonal.axis.numerical.datetime", //$NON-NLS-1$
					Messages.getResourceBundle( rtc.getULocale( ) ) );
		}

		dStart = ( aax.areAxesSwapped( ) ) ? dX : dY + dH;
		dEnd = ( aax.areAxesSwapped( ) ) ? dX + dW : dY;
		sc = axPrimaryOrthogonal.getScale( );
		scPrimaryOrthogonal = AutoScale.computeScale( ids,
				oaxPrimaryOrthogonal,
				dsi,
				iAxisType,
				dStart,
				dEnd,
				sc.getMin( ),
				sc.getMax( ),
				sc.isSetStep( ) ? new Double( sc.getStep( ) ) : null,
				axPrimaryOrthogonal.getFormatSpecifier( ),
				rtc,
				AUTO );
		// UPDATE SCALE ON PRIMARY-ORTHOGONAL AXIS
		oaxPrimaryOrthogonal.set( scPrimaryOrthogonal );

		// ITERATIVELY ADJUST THE PRIMARY ORTHOGONAL AXIS POSITION DUE TO THE
		// SCALE, START/END LABELS
		double dYAxisLocation = adjustHorizontal( dX, dW, aax );

		// ITERATIVELY ADJUST THE PRIMARY BASE AXIS POSITION DUE TO THE SCALE,
		// START/END LABELS
		double dXAxisLocation = adjustVerticalDueToHorizontal( dY, dH, aax );

		// SETUP THE FULL DATASET FOR THE PRIMARY ORTHOGONAL AXIS
		iAxisType = getAxisType( axPrimaryOrthogonal );
		oaData = getTypedDataSet( axPrimaryOrthogonal, iAxisType, 0 );
		scPrimaryOrthogonal.setData( dsi );

		// SETUP THE FULL DATASET FOR THE PRIMARY ORTHOGONAL AXIS
		iAxisType = getAxisType( axPrimaryBase );
		if ( iAxisType != IConstants.TEXT )
		{
			scPrimaryBase.setData( getTypedDataSet( axPrimaryBase, iAxisType, 0 ) );
		}

		scPrimaryBase.resetShifts( );
		scPrimaryOrthogonal.resetShifts( );

		// UPDATE THE SIZES OF THE OVERLAY AXES
		updateOverlayAxes( aax );
		growBaseAxis( aax, bo );

		// UPDATE FOR OVERLAYS
		final OneAxis axPH = aax.areAxesSwapped( ) ? aax.getPrimaryOrthogonal( )
				: aax.getPrimaryBase( );
		final OneAxis axPV = aax.areAxesSwapped( ) ? aax.getPrimaryBase( )
				: aax.getPrimaryOrthogonal( );

		axPH.setAxisCoordinate( dXAxisLocation );
		axPV.setAxisCoordinate( dYAxisLocation );

		double[] daX = axPH.getScale( ).getEndPoints( );
		double[] daY = axPV.getScale( ).getEndPoints( );

		if ( axPH.getScale( ).getDirection( ) == BACKWARD )
		{
			boPlotBackground.setLeft( daX[1] - insCA.getLeft( ) );
			boPlotBackground.setWidth( daX[0]
					- daX[1]
					+ insCA.getLeft( )
					+ insCA.getRight( )
					+ 1 );
		}
		else
		{
			boPlotBackground.setLeft( daX[0] - insCA.getLeft( ) );
			boPlotBackground.setWidth( daX[1]
					- daX[0]
					+ insCA.getLeft( )
					+ insCA.getRight( )
					+ 1 );
		}

		if ( axPV.getScale( ).getDirection( ) == FORWARD )
		{
			boPlotBackground.setTop( daY[0] - insCA.getTop( ) );
			boPlotBackground.setHeight( daY[1]
					- daY[0]
					+ insCA.getTop( )
					+ insCA.getBottom( )
					+ 1 );
		}
		else
		{
			boPlotBackground.setTop( daY[1] - insCA.getTop( ) );
			boPlotBackground.setHeight( daY[0]
					- daY[1]
					+ insCA.getTop( )
					+ insCA.getBottom( )
					+ 1 );
		}
		if ( iDimension == TWO_5_D )
		{
			boPlotBackground.delta( dSeriesThickness, -dSeriesThickness, 0, 0 );
		}
	}

	/**
	 * This method attempts to stretch the base axis so it fits snugly (w.r.t.
	 * horizontal/vertical spacing) with the overlay axes (if any)
	 * 
	 * @param aax
	 */
	private final void growBaseAxis( AllAxes aax, Bounds bo )
			throws ChartException
	{
		OneAxis oaxBase = aax.getPrimaryBase( );
		OneAxis oaxOrthogonal = aax.getPrimaryOrthogonal( );
		AutoScale scBase = oaxBase.getScale( );

		if ( aax.getOverlayCount( ) <= 0 )
		{
			// no overlay, just return.
			return;
		}

		if ( !aax.areAxesSwapped( ) ) // STANDARD ORIENTATION
		{
			// IF PRIMARY ORTHOGONAL AXIS IS NOT ON THE RIGHT
			if ( ( isRightToLeft( ) && oaxOrthogonal.getIntersectionValue( )
					.getType( ) != IConstants.MIN )
					|| ( !isRightToLeft( ) && oaxOrthogonal.getIntersectionValue( )
							.getType( ) != IConstants.MAX ) )
			{
				// IF ANY OVERLAY ORTHOGONAL AXES ARE ON THE RIGHT
				if ( ( isRightToLeft( ) && aax.anyOverlayPositionedAt( IConstants.MIN ) )
						|| ( !isRightToLeft( ) && aax.anyOverlayPositionedAt( IConstants.MAX ) ) )
				{
					scBase.computeAxisStartEndShifts( ids,
							oaxBase.getLabel( ),
							HORIZONTAL,
							oaxBase.getLabelPosition( ),
							aax );
					if ( isRightToLeft( ) )
					{
						double dLeftThreshold = bo.getLeft( );
						double dEnd = scBase.getEnd( );
						final double dEndShift = scBase.getEndShift( );
						if ( dEnd - dEndShift > dLeftThreshold )
						{
							dEnd -= dEndShift;
							double dStart = scBase.getStart( );
							scBase.computeTicks( ids,
									oaxBase.getLabel( ),
									oaxBase.getLabelPosition( ),
									HORIZONTAL,
									dStart,
									dEnd,
									false,
									null );
						}
					}
					else
					{
						double dRightThreshold = bo.getLeft( ) + bo.getWidth( );
						double dEnd = scBase.getEnd( );
						final double dEndShift = scBase.getEndShift( );
						if ( dEnd + dEndShift < dRightThreshold )
						{
							dEnd += dEndShift;
							scBase.computeTicks( ids,
									oaxBase.getLabel( ),
									oaxBase.getLabelPosition( ),
									HORIZONTAL,
									scBase.getStart( ),
									dEnd,
									false,
									null );
						}
					}
				}
			}
			// IF PRIMARY ORTHOGONAL AXIS IS NOT ON THE LEFT
			else if ( ( isRightToLeft( ) && oaxOrthogonal.getIntersectionValue( )
					.getType( ) != IConstants.MAX )
					|| ( !isRightToLeft( ) && oaxOrthogonal.getIntersectionValue( )
							.getType( ) != IConstants.MIN ) )
			{
				// IF ANY OVERLAY ORTHOGONAL AXES ARE ON THE LEFT
				if ( ( isRightToLeft( ) && aax.anyOverlayPositionedAt( IConstants.MAX ) )
						|| ( !isRightToLeft( ) && aax.anyOverlayPositionedAt( IConstants.MIN ) ) )
				{
					scBase.computeAxisStartEndShifts( ids,
							oaxBase.getLabel( ),
							HORIZONTAL,
							oaxBase.getLabelPosition( ),
							aax );
					if ( isRightToLeft( ) )
					{
						double dRightThreshold = bo.getLeft( ) + bo.getWidth( );
						double dStart = scBase.getStart( );
						final double dStartShift = scBase.getStartShift( );
						if ( dStart + dStartShift < dRightThreshold )
						{
							dStart += dStartShift;
							double dEnd = scBase.getEnd( );
							scBase.computeTicks( ids,
									oaxBase.getLabel( ),
									oaxBase.getLabelPosition( ),
									HORIZONTAL,
									dStart,
									dEnd,
									false,
									null );
						}
					}
					else
					{
						double dLeftThreshold = bo.getLeft( );
						double dStart = scBase.getStart( );
						final double dEndShift = scBase.getEndShift( );
						final double dStartShift = scBase.getStartShift( );
						if ( dStart - dStartShift > dLeftThreshold )
						{
							dStart -= dStartShift;
							final double dEnd = scBase.getEnd( ) + dEndShift;
							scBase.computeTicks( ids,
									oaxBase.getLabel( ),
									oaxBase.getLabelPosition( ),
									HORIZONTAL,
									dStart,
									dEnd,
									false,
									null );
						}
					}
				}
			}
		}
		else
		{
			// !Note if it's transposed, the intersection is not swapped.

			// IF PRIMARY ORTHOGONAL AXIS IS NOT AT THE TOP
			if ( oaxOrthogonal.getIntersectionValue( ).getType( ) != IConstants.MAX )
			{
				// IF ANY OVERLAY ORTHOGONAL AXES ARE AT THE TOP
				if ( aax.anyOverlayPositionedAt( IConstants.MAX ) )
				{
					scBase.computeAxisStartEndShifts( ids,
							oaxBase.getLabel( ),
							VERTICAL,
							oaxBase.getLabelPosition( ),
							aax );

					if ( isRightToLeft( ) )
					{
						double dBottomThreshold = bo.getTop( ) + bo.getHeight( );
						double dEnd = scBase.getEnd( );
						final double dEndShift = Math.floor( scBase.getEndShift( ) );
						if ( dEnd + dEndShift < dBottomThreshold )
						{
							dEnd += dEndShift;
							final double dStart = scBase.getStart( );
							scBase.computeTicks( ids,
									oaxBase.getLabel( ),
									oaxBase.getLabelPosition( ),
									VERTICAL,
									dStart,
									dEnd,
									false,
									null );
						}
					}
					else
					{
						double dTopThreshold = bo.getTop( );
						double dEnd = scBase.getEnd( );
						final double dEndShift = Math.floor( scBase.getEndShift( ) );
						if ( dEnd - dEndShift > dTopThreshold )
						{
							dEnd = dEnd - dEndShift;
							final double dStart = scBase.getStart( );
							scBase.computeTicks( ids,
									oaxBase.getLabel( ),
									oaxBase.getLabelPosition( ),
									VERTICAL,
									dStart,
									dEnd,
									false,
									null );
						}
					}
				}
			}

			// IF PRIMARY ORTHOGONAL AXIS IS NOT AT THE BOTTOM
			else if ( oaxOrthogonal.getIntersectionValue( ).getType( ) != IConstants.MIN )
			{
				// IF ANY OVERLAY ORTHOGONAL AXES IS AT THE BOTTOM
				if ( aax.anyOverlayPositionedAt( IConstants.MIN ) )
				{
					scBase.computeAxisStartEndShifts( ids,
							oaxBase.getLabel( ),
							VERTICAL,
							oaxBase.getLabelPosition( ),
							aax );
					if ( isRightToLeft( ) )
					{
						double dTopThreshold = bo.getTop( );
						double dStart = scBase.getStart( );
						final double dStartShift = scBase.getStartShift( );
						if ( dStart - dStartShift > dTopThreshold )
						{
							dStart -= dStartShift;
							final double dEnd = scBase.getEnd( );
							scBase.computeTicks( ids,
									oaxBase.getLabel( ),
									oaxBase.getLabelPosition( ),
									VERTICAL,
									dStart,
									dEnd,
									false,
									null );
						}
					}
					else
					{
						double dBottomThreshold = bo.getTop( ) + bo.getHeight( );
						double dStart = scBase.getStart( );
						final double dStartShift = scBase.getStartShift( );
						if ( dStart + dStartShift < dBottomThreshold )
						{
							dStart += dStartShift;
							final double dEnd = scBase.getEnd( );
							scBase.computeTicks( ids,
									oaxBase.getLabel( ),
									oaxBase.getLabelPosition( ),
									VERTICAL,
									dStart,
									dEnd,
									false,
									null );
						}
					}
				}
			}
		}
	}

	/**
	 * 
	 * @param aax
	 * @param dAxisStart
	 * @param dAxisEnd
	 * @param dBlockStart
	 * @param dBlockLength
	 * 
	 * @throws PluginException
	 * @throws DataSetException
	 * @throws GenerationException
	 */
	private final void updateOverlayScales( AllAxes aax, double dAxisStart,
			double dAxisEnd, double dBlockStart, double dBlockLength )
			throws ChartException, IllegalArgumentException
	{
		final Axis[] axa = ( (ChartWithAxesImpl) cwa ).getPrimaryBaseAxes( );
		final Axis axPrimaryBase = axa[0];
		final Axis[] axaOrthogonal = ( (ChartWithAxesImpl) cwa ).getOrthogonalAxes( axPrimaryBase,
				false );

		IntersectionValue iv;
		AutoScale sc = null;
		OneAxis oaxOverlay = null;
		int iTickStyle, iAxisType, j, iTitleLocation;
		int iOverlayCount = aax.getOverlayCount( );
		int iOrientation = aax.getOrientation( );
		double dStart, dEnd, dAxisLabelsThickness;
		Label laAxisTitle;
		Scale scModel;

		Series[] sea = cwa.getSeries( IConstants.ORTHOGONAL );
		Map seriesRenderingHints = rtc.getSeriesRenderers( );

		// ITERATE THROUGH EACH OVERLAY ORTHOGONAL AXIS
		for ( int i = 0; i < iOverlayCount; i++ )
		{
			// GO BACKWARDS TO ENSURE CORRECT RENDERING ORDER
			j = iOverlayCount - i - 1;
			// UPDATE A PREVIOUSLY DEFINED OVERLAY AXIS AUTO COMPUTE SCALE
			oaxOverlay = aax.getOverlay( j );
			iTickStyle = oaxOverlay.getCombinedTickStyle( );
			iTitleLocation = oaxOverlay.getTitlePosition( );
			laAxisTitle = oaxOverlay.getTitle( );
			iAxisType = getAxisType( axaOrthogonal[j] );

			scModel = axaOrthogonal[j].getScale( );
			sc = AutoScale.computeScale( ids,
					oaxOverlay,
					new DataSetIterator( getMinMax( axaOrthogonal[j], iAxisType ),
							iAxisType ),
					iAxisType,
					dAxisStart,
					dAxisEnd,
					scModel.getMin( ),
					scModel.getMax( ),
					scModel.isSetStep( ) ? new Double( scModel.getStep( ) )
							: null,
					axaOrthogonal[j].getFormatSpecifier( ),
					rtc,
					AUTO );

			oaxOverlay.set( sc );
			iv = oaxOverlay.getIntersectionValue( );

			// UPDATE AXIS ENDPOINTS DUE TO ITS AXIS LABEL SHIFTS
			dStart = sc.getStart( );
			dEnd = sc.getEnd( );
			sc.computeTicks( ids,
					oaxOverlay.getLabel( ),
					oaxOverlay.getLabelPosition( ),
					iOrientation,
					dStart,
					dEnd,
					true,
					null );
			if ( !sc.isStepFixed( ) )
			{
				final Object[] oaMinMax = sc.getMinMax( );
				while ( !sc.checkFit( ids,
						oaxOverlay.getLabel( ),
						oaxOverlay.getLabelPosition( ) ) )
				{
					if ( !sc.zoomOut( ) )
					{
						break;
					}
					sc.updateAxisMinMax( oaMinMax[0], oaMinMax[1] );
					sc.computeTicks( ids,
							oaxOverlay.getLabel( ),
							oaxOverlay.getLabelPosition( ),
							iOrientation,
							dStart,
							dEnd,
							true,
							null );
				}
			}
			dAxisLabelsThickness = sc.computeAxisLabelThickness( ids,
					oaxOverlay.getLabel( ),
					iOrientation );

			// Compute axes decoration thickness, the value sequence is either
			// [left,right] or
			// [top, bottom]
			double[] dDecorationThickness = {
					0, 0
			};

			for ( int t = 0; t < sea.length; t++ )
			{
				LegendItemRenderingHints lirh = (LegendItemRenderingHints) seriesRenderingHints.get( sea[t] );

				if ( lirh != null
						&& lirh.getRenderer( ) instanceof AxesRenderer )
				{
					IAxesDecorator iad = ( (AxesRenderer) lirh.getRenderer( ) ).getAxesDecorator( oaxOverlay );

					if ( iad != null )
					{
						double[] thickness = iad.computeDecorationThickness( ids,
								oaxOverlay );

						if ( thickness[0] > dDecorationThickness[0] )
						{
							dDecorationThickness[0] = thickness[0];
						}
						if ( thickness[1] > dDecorationThickness[1] )
						{
							dDecorationThickness[1] = thickness[1];
						}
					}
				}
			}

			double dAxisTitleThickness = 0;
			sc.resetShifts( );

			if ( iOrientation == VERTICAL )
			{
				// COMPUTE THE THICKNESS OF THE AXIS INCLUDING AXIS LABEL BOUNDS
				// AND AXIS-PLOT SPACING
				double dX = 0, dX1 = 0, dX2 = 0;
				final boolean bTicksLeft = ( iTickStyle & TICK_LEFT ) == TICK_LEFT;
				final boolean bTicksRight = ( iTickStyle & TICK_RIGHT ) == TICK_RIGHT;
				final double dAppliedYAxisPlotSpacing = dYAxisPlotSpacing;
				if ( laAxisTitle.isVisible( ) )
				{
					final String sPreviousValue = laAxisTitle.getCaption( )
							.getValue( );
					laAxisTitle.getCaption( )
							.setValue( rtc.externalizedMessage( sPreviousValue ) );
					dAxisTitleThickness = computeBox( ids,
							iTitleLocation,
							laAxisTitle,
							0,
							0 ).getWidth( );
					laAxisTitle.getCaption( ).setValue( sPreviousValue );
				}

				// COMPUTE VALUES FOR x1, x, x2
				// x = HORIZONTAL LOCATION OF Y-AXIS ALONG PLOT
				// x1 = LEFT EDGE OF Y-AXIS BAND (DUE TO AXIS LABELS, TICKS,
				// SPACING)
				// x2 = RIGHT EDGE OF Y-AXIS BAND (DUE TO AXIS LABELS, TICKS,
				// SPACING)
				if ( iv.getType( ) == IntersectionValue.MIN )
				{
					// NOTE: ENSURE CODE SYMMETRY WITH 'iaLabelPositions[i] ==
					// RIGHT'
					dX = dBlockStart;
					dX -= dAppliedYAxisPlotSpacing;
					dX1 = dX;
					dX2 = dX;
					if ( bTicksLeft )
					{
						dX1 -= TICK_SIZE;
					}
					if ( oaxOverlay.getLabelPosition( ) == LEFT )
					{
						dX1 -= Math.max( dAxisLabelsThickness,
								dDecorationThickness[0] );
						dX2 += Math.max( bTicksRight ? TICK_SIZE : 0,
								dAppliedYAxisPlotSpacing );
					}
					else if ( oaxOverlay.getLabelPosition( ) == RIGHT )
					{
						dX1 -= dDecorationThickness[0];
						dX2 += Math.max( ( bTicksRight ? TICK_SIZE : 0 )
								+ dAxisLabelsThickness,
								dAppliedYAxisPlotSpacing );
					}

					if ( iTitleLocation == LEFT )
					{
						dX1 -= dAxisTitleThickness;
					}
					else if ( iTitleLocation == RIGHT )
					{
						dX2 += dAxisTitleThickness;
					}

					// ENSURE THAT WE DON'T GO BEHIND THE LEFT PLOT BLOCK EDGE
					if ( dX1 < dBlockStart )
					{
						final double dDelta = ( dBlockStart - dX1 );
						dX1 = dBlockStart;
						dX += dDelta;
						dX2 += dDelta;
					}
					dBlockStart += ( dX2 - dX1 ); // SHIFT LEFT EDGE >>
				}
				else if ( iv.getType( ) == IntersectionValue.MAX )
				{
					// NOTE: ENSURE CODE SYMMETRY WITH 'InsersectionValue.MIN'
					dX = dBlockStart + dBlockLength;
					dX += dAppliedYAxisPlotSpacing;
					dX1 = dX;
					dX2 = dX;
					if ( bTicksRight )
					{
						dX2 += TICK_SIZE;
					}

					if ( oaxOverlay.getLabelPosition( ) == RIGHT )
					{
						dX2 += Math.max( dAxisLabelsThickness,
								dDecorationThickness[1] );
						dX1 -= Math.max( bTicksLeft ? TICK_SIZE : 0,
								dAppliedYAxisPlotSpacing );
					}
					else if ( oaxOverlay.getLabelPosition( ) == LEFT )
					{
						dX1 -= Math.max( ( bTicksLeft ? TICK_SIZE : 0 )
								+ dAxisLabelsThickness,
								dAppliedYAxisPlotSpacing );
						dX2 += dDecorationThickness[1];
					}

					if ( iTitleLocation == LEFT )
					{
						dX1 -= dAxisTitleThickness;
					}
					else if ( iTitleLocation == RIGHT )
					{
						dX2 += dAxisTitleThickness;
					}

					// ENSURE THAT WE DON'T GO AHEAD OF THE RIGHT PLOT BLOCK
					// EDGE
					if ( dX2 > dBlockStart + dBlockLength )
					{
						final double dDelta = dX2
								- ( dBlockStart + dBlockLength );
						dX2 = dBlockStart + dBlockLength;
						dX -= dDelta;
						dX1 -= dDelta;
					}
				}
				dBlockLength -= dX2 - dX1; // SHIFT RIGHT EDGE <<

				double dDelta = 0;
				if ( iv.getType( ) == IntersectionValue.MIN )
				{
					dDelta = -insCA.getLeft( );
				}
				else if ( iv.getType( ) == IntersectionValue.MAX )
				{
					dDelta = insCA.getRight( );
				}

				oaxOverlay.setAxisCoordinate( dX + dDelta );
				// dX1<=>dX<=>dX2 INCORPORATES TITLE
				oaxOverlay.setTitleCoordinate( ( iTitleLocation == LEFT ) ? dX1
						+ dDelta
						- 1 : dX2 + 1 - dAxisTitleThickness + dDelta );
			}
			else if ( iOrientation == HORIZONTAL )
			{
				// COMPUTE THE THICKNESS OF THE AXIS INCLUDING AXIS LABEL BOUNDS
				// AND AXIS-PLOT SPACING
				double dY = 0, dY1 = dY, dY2 = dY;
				final boolean bTicksAbove = ( iTickStyle & TICK_ABOVE ) == TICK_ABOVE;
				final boolean bTicksBelow = ( iTickStyle & TICK_BELOW ) == TICK_BELOW;
				final double dAppliedXAxisPlotSpacing = dXAxisPlotSpacing;
				if ( laAxisTitle.isVisible( ) )
				{
					final String sPreviousValue = laAxisTitle.getCaption( )
							.getValue( );
					laAxisTitle.getCaption( )
							.setValue( rtc.externalizedMessage( sPreviousValue ) );
					dAxisTitleThickness = computeBox( ids,
							iTitleLocation,
							laAxisTitle,
							0,
							0 ).getHeight( );
					laAxisTitle.getCaption( ).setValue( sPreviousValue );
				}

				// COMPUTE VALUES FOR y1, y, y2
				// y = VERTICAL LOCATION OF X-AXIS ALONG PLOT
				// y1 = UPPER EDGE OF X-AXIS (DUE TO AXIS LABELS, TICKS,
				// SPACING)
				// y2 = LOWER EDGE OF X-AXIS (DUE TO AXIS LABELS, TICKS,
				// SPACING)
				if ( ( isRightToLeft( ) && iv.getType( ) == IntersectionValue.MIN )
						|| ( !isRightToLeft( ) && iv.getType( ) == IntersectionValue.MAX ) ) // ABOVE
				// THE
				// PLOT
				{
					dY = dBlockStart;
					dY -= dAppliedXAxisPlotSpacing;
					dY1 = dY;
					dY2 = dY;
					if ( bTicksAbove )
					{
						dY1 -= TICK_SIZE;
					}
					if ( oaxOverlay.getLabelPosition( ) == ABOVE )
					{
						dY1 -= Math.max( dAxisLabelsThickness,
								dDecorationThickness[0] );
						dY2 += Math.max( bTicksBelow ? TICK_SIZE : 0,
								dAppliedXAxisPlotSpacing );
					}
					else if ( oaxOverlay.getLabelPosition( ) == BELOW )
					{
						dY1 -= dDecorationThickness[0];
						dY2 += Math.max( ( bTicksBelow ? TICK_SIZE : 0 )
								+ dAxisLabelsThickness,
								dAppliedXAxisPlotSpacing );
					}

					if ( iTitleLocation == ABOVE )
					{
						dY1 -= dAxisTitleThickness;
					}
					else if ( iTitleLocation == BELOW )
					{
						dY2 += dAxisTitleThickness;
					}

					// ENSURE THAT WE DON'T GO BEHIND THE LEFT PLOT BLOCK EDGE
					if ( dY1 < dBlockStart )
					{
						final double dDelta = ( dBlockStart - dY1 );
						dY1 = dBlockStart;
						dY += dDelta;
						dY2 += dDelta;
					}
					dBlockStart += ( dY2 - dY1 ); // SHIFT TOP EDGE >>
				}
				else if ( ( isRightToLeft( ) && iv.getType( ) == IntersectionValue.MAX )
						|| ( !isRightToLeft( ) && iv.getType( ) == IntersectionValue.MIN ) ) // BELOW
				// THE
				// PLOT
				{
					// NOTE: ENSURE CODE SYMMETRY WITH 'InsersectionValue.MIN'
					dY = dBlockStart + dBlockLength;
					dY += dAppliedXAxisPlotSpacing;
					dY1 = dY;
					dY2 = dY;
					if ( bTicksBelow )
					{
						dY2 += TICK_SIZE;
					}

					if ( oaxOverlay.getLabelPosition( ) == BELOW )
					{
						dY2 += Math.max( dAxisLabelsThickness,
								dDecorationThickness[1] );
						dY1 -= Math.max( bTicksAbove ? TICK_SIZE : 0,
								dAppliedXAxisPlotSpacing );
					}
					else if ( oaxOverlay.getLabelPosition( ) == ABOVE )
					{
						dY1 -= Math.max( ( bTicksAbove ? TICK_SIZE : 0 )
								+ dAxisLabelsThickness,
								dAppliedXAxisPlotSpacing );
						dY2 += dDecorationThickness[1];
					}

					if ( iTitleLocation == ABOVE )
					{
						dY1 -= dAxisTitleThickness;
					}
					else if ( iTitleLocation == BELOW )
					{
						dY2 += dAxisTitleThickness;
					}

					// ENSURE THAT WE DON'T GO AHEAD OF THE RIGHT PLOT BLOCK
					// EDGE
					if ( dY2 > dBlockStart + dBlockLength )
					{
						final double dDelta = dY2
								- ( dBlockStart + dBlockLength );
						dY2 = dBlockStart + dBlockLength;
						dY -= dDelta;
						dY1 -= dDelta;
					}
				}
				double dDelta = 0;
				if ( ( isRightToLeft( ) && iv.getType( ) == IntersectionValue.MIN )
						|| ( !isRightToLeft( ) && iv.getType( ) == IntersectionValue.MAX ) )
				{
					dDelta = -insCA.getTop( );
				}
				else if ( ( isRightToLeft( ) && iv.getType( ) == IntersectionValue.MAX )
						|| ( !isRightToLeft( ) && iv.getType( ) == IntersectionValue.MIN ) )
				{
					dDelta = insCA.getBottom( );
				}

				oaxOverlay.setAxisCoordinate( dY + dDelta );
				oaxOverlay.setTitleCoordinate( ( iTitleLocation == ABOVE ) ? dY1
						+ dDelta
						- 1
						: dY2 + 1 - dAxisTitleThickness + dDelta // dY1<=>dX<=>dY2
				// INCORPORATES
				// TITLE
				);

				dBlockLength -= ( dY2 - dY1 ); // SHIFT BOTTOM EDGE <<
			}
		}

		aax.setBlockCordinates( dBlockStart, dBlockLength );
	}

	private final void updateOverlayAxes( AllAxes aax ) throws ChartException,
			IllegalArgumentException
	{
		int iDirection = ( aax.getOrientation( ) == HORIZONTAL ) ? 1 : -1;
		final Axis[] axa = cwa.getPrimaryBaseAxes( );
		final Axis axPrimaryBase = axa[0]; // NOTE: FOR REL 1 AXIS RENDERS, WE
		// SUPPORT A SINGLE PRIMARY BASE AXIS ONLY
		final Axis[] axaOverlayOrthogonal = cwa.getOrthogonalAxes( axPrimaryBase,
				false );

		OneAxis axOverlay, axPrimary = aax.getPrimaryOrthogonal( );
		AutoScale scOA, sc = axPrimary.getScale( );
		double dStart, dEnd;
		Object[] oaMinMax;
		int iAxisType = ( aax.getOverlayCount( ) > 0 ) ? aax.getOverlay( 0 )
				.getScale( )
				.getType( ) : 0;

		Label la;
		for ( int i = 0; i < aax.getOverlayCount( ); i++ )
		{
			axOverlay = aax.getOverlay( i );
			la = axOverlay.getLabel( );
			scOA = axOverlay.getScale( );
			scOA.setEndPoints( scOA.getStart( )
					- scOA.getStartShift( )
					* iDirection, scOA.getEnd( )
					+ scOA.getEndShift( )
					* iDirection );

			dStart = sc.getStart( );
			dEnd = sc.getEnd( );

			scOA.setEndPoints( dStart, dEnd );
			scOA.computeTicks( ids,
					la,
					axOverlay.getLabelPosition( ),
					aax.getOrientation( ),
					dStart,
					dEnd,
					false,
					null );
			if ( !scOA.isStepFixed( ) )
			{
				oaMinMax = scOA.getMinMax( );
				while ( !scOA.checkFit( ids, la, axOverlay.getLabelPosition( ) ) )
				{
					if ( !scOA.zoomOut( ) )
					{
						break;
					}
					scOA.updateAxisMinMax( oaMinMax[0], oaMinMax[1] );
					scOA.computeTicks( ids,
							la,
							axOverlay.getLabelPosition( ),
							aax.getOrientation( ),
							dStart,
							dEnd,
							false,
							null );
				}
			}

			// SETUP THE FULL DATASET FOR THE PRIMARY ORTHOGONAL AXIS
			scOA.setData( getTypedDataSet( axaOverlayOrthogonal[i],
					iAxisType,
					0 ) );
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.birt.chart.computation.withaxes.PlotWithAxes#getSeriesRenderingHints(org.eclipse.birt.chart.model.data.SeriesDefinition,
	 *      org.eclipse.birt.chart.model.component.Series)
	 */
	public final ISeriesRenderingHints getSeriesRenderingHints(
			SeriesDefinition sdOrthogonal, Series seOrthogonal )
			throws ChartException, IllegalArgumentException
	{
		if ( seOrthogonal == null
				|| seOrthogonal.getClass( ) == SeriesImpl.class )
		{
			// EMPTY PLOT RENDERING TECHNIQUE
			return null;
		}
		OneAxis oaxOrthogonal = findOrthogonalAxis( seOrthogonal );
		if ( oaxOrthogonal == null )
		{
			throw new ChartException( ChartEnginePlugin.ID,
					ChartException.NOT_FOUND,
					"exception.axis.series.link.broken", //$NON-NLS-1$
					new Object[]{
						seOrthogonal
					},
					Messages.getResourceBundle( rtc.getULocale( ) ) );
		}
		final OneAxis oaxBase = aax.getPrimaryBase( );
		final SeriesDefinition sdBase = (SeriesDefinition) oaxBase.getModelAxis( )
				.getSeriesDefinitions( )
				.get( 0 );

		final AutoScale scBase = oaxBase.getScale( );
		final AutoScale scOrthogonal = oaxOrthogonal.getScale( );
		final int iTickCount = scBase.getTickCount( );
		int iUnitCount = iTickCount;
		final int iDirection = scBase.getDirection( );

		// convert to signed unit size.
		final double dUnitSize = ( iDirection == BACKWARD || ( iDirection == AUTO && aax.areAxesSwapped( ) ) ) ? -scBase.getUnitSize( )
				: scBase.getUnitSize( );

		if ( scBase.getType( ) == IConstants.DATE_TIME )
		{
			// TBD: HANDLE DATETIME VALUE VS TEXT AXIS
		}

		double[] daTickCoordinates = scBase.getTickCordinates( );
		Object oDataBase = null;
		DataSetIterator dsiDataBase = scBase.getData( );
		Object oDataOrthogonal;
		DataSetIterator dsiDataOrthogonal = getTypedDataSet( seOrthogonal,
				oaxOrthogonal.getScale( ).getType( ) );
		double dOrthogonalZero = 0;
		if ( ( scOrthogonal.getType( ) & NUMERICAL ) == NUMERICAL )
		{
			dOrthogonalZero = getLocation( scOrthogonal, 0 );
		}
		else
		{
			dOrthogonalZero = scOrthogonal.getStart( );
		}
		double dBaseZero = ( ( scBase.getType( ) & NUMERICAL ) == IConstants.NUMERICAL && !oaxBase.isCategoryScale( ) ) ? getLocation( scBase,
				0 )
				: scBase.getStart( );

		if ( scBase.getType( ) == TEXT || oaxBase.isCategoryScale( ) )
		{
			iUnitCount--;
		}

		double dX = 0, dY = 0, dLength = 0;
		Location lo;

		final int iBaseCount = dsiDataBase.size( );
		final int iOrthogonalCount = dsiDataOrthogonal.size( );
		DataPointHints[] dpa = null;

		// DO NOT COMPUTE DATA POINT HINTS FOR OUT-OF-SYNC DATA
		if ( iBaseCount != iOrthogonalCount )
		{
			logger.log( ILogger.INFORMATION,
					Messages.getString( "exception.base.orthogonal.inconsistent.count", //$NON-NLS-1$
							new Object[]{
									new Integer( iBaseCount ),
									new Integer( iOrthogonalCount )
							},
							rtc.getULocale( ) ) );
		}
		else
		{
			dpa = new DataPointHints[iBaseCount];
			final boolean bScatter = ( oaxBase.getScale( ).getType( ) != IConstants.TEXT && !oaxBase.isCategoryScale( ) );

			// OPTIMIZED PRE-FETCH FORMAT SPECIFIERS FOR ALL DATA POINTS
			final DataPoint dp = seOrthogonal.getDataPoint( );
			final EList el = dp.getComponents( );
			DataPointComponent dpc;
			DataPointComponentType dpct;
			FormatSpecifier fsBase = null, fsOrthogonal = null, fsSeries = null, fsPercentile = null;
			for ( int i = 0; i < el.size( ); i++ )
			{
				dpc = (DataPointComponent) el.get( i );
				dpct = dpc.getType( );
				if ( dpct == DataPointComponentType.BASE_VALUE_LITERAL )
				{
					fsBase = dpc.getFormatSpecifier( );
					if ( fsBase == null ) // BACKUP
					{
						fsBase = sdBase.getFormatSpecifier( );
					}
				}
				else if ( dpct == DataPointComponentType.ORTHOGONAL_VALUE_LITERAL )
				{
					fsOrthogonal = dpc.getFormatSpecifier( );
					if ( fsOrthogonal == null
							&& seOrthogonal.eContainer( ) instanceof SeriesDefinition )
					{
						fsOrthogonal = ( (SeriesDefinition) seOrthogonal.eContainer( ) ).getFormatSpecifier( );
					}
				}
				else if ( dpct == DataPointComponentType.SERIES_VALUE_LITERAL )
				{
					fsSeries = dpc.getFormatSpecifier( );
				}
				else if ( dpct == DataPointComponentType.PERCENTILE_ORTHOGONAL_VALUE_LITERAL )
				{
					fsPercentile = dpc.getFormatSpecifier( );
				}
			}

			dsiDataBase.reset( );
			dsiDataOrthogonal.reset( );

			UserDataSetHints udsh = new UserDataSetHints( seOrthogonal.getDataSets( ) );
			udsh.reset( );

			double total = 0;
			boolean isZeroValue = true;

			// get total orthogonal value.
			for ( int i = 0; i < iOrthogonalCount; i++ )
			{
				Object v = dsiDataOrthogonal.next( );

				if ( v instanceof Number )
				{
					if ( ( (Number) v ).doubleValue( ) != 0.0 )
					{
						isZeroValue = false;
					}
					total += ( (Number) v ).doubleValue( );
				}
				else if ( v instanceof NumberDataElement )
				{
					if ( ( (NumberDataElement) v ).getValue( ) != 0.0 )
					{
						isZeroValue = false;
					}
					total += ( (NumberDataElement) v ).getValue( );
				}
			}

			dsiDataOrthogonal.reset( );

			for ( int i = 0; i < iBaseCount; i++ )
			{
				oDataBase = dsiDataBase.next( );
				oDataOrthogonal = dsiDataOrthogonal.next( );

				if ( !bScatter )
				{
					if ( aax.areAxesSwapped( ) )
					{
						dY = daTickCoordinates[0] + dUnitSize * i;
						try
						{
							dX = getLocation( scOrthogonal, oDataOrthogonal );
						}
						catch ( IllegalArgumentException nvex )
						{
							// dX = dOrthogonalZero;
							dX = Double.NaN;
						}
						catch ( ChartException dfex )
						{
							dX = dOrthogonalZero; // FOR CUSTOM DATA ELEMENTS
						}
					}
					else
					{
						dX = daTickCoordinates[0] + dUnitSize * i;
						try
						{
							dY = getLocation( scOrthogonal, oDataOrthogonal );
						}
						catch ( IllegalArgumentException nvex )
						{
							// dY = dOrthogonalZero;
							dY = Double.NaN;
						}
						catch ( ChartException dfex )
						{
							dY = dOrthogonalZero; // FOR CUSTOM DATA ELEMENTS
						}
					}
				}
				else
				{
					// SCATTER CHARTS (BASE AXIS != CATEGORY AXIS)
					try
					{
						dX = getLocation( scBase, oDataBase );
					}
					catch ( IllegalArgumentException nvex )
					{
						dX = dBaseZero;
					}
					catch ( ChartException dfex )
					{
						dX = dBaseZero; // FOR CUSTOM DATA ELEMENTS
					}

					try
					{
						dY = getLocation( scOrthogonal, oDataOrthogonal );
					}
					catch ( IllegalArgumentException nvex )
					{
						// dY = dOrthogonalZero; // MAP TO ZERO
						dX = Double.NaN;
					}
					catch ( ChartException dfex )
					{
						dY = dOrthogonalZero; // FOR CUSTOM DATA ELEMENTS
					}

					if ( aax.areAxesSwapped( ) )
					{
						final double dTemp = dX;
						dX = dY;
						dY = dTemp;
					}
				}
				lo = LocationImpl.create( dX, dY );
				dLength = ( i < iTickCount - 1 ) ? daTickCoordinates[i + 1]
						- daTickCoordinates[i] : 0;

				Object percentileValue = null;

				if ( total != 0 )
				{
					if ( oDataOrthogonal instanceof Number )
					{
						percentileValue = new Double( ( (Number) oDataOrthogonal ).doubleValue( )
								/ total );
					}
					else if ( oDataOrthogonal instanceof NumberDataElement )
					{
						percentileValue = new Double( ( (NumberDataElement) oDataOrthogonal ).getValue( )
								/ total );
					}
				}
				else if ( isZeroValue == true )
				{
					percentileValue = new Double( 1.0 / iOrthogonalCount );
				}

				dpa[i] = new DataPointHints( oDataBase,
						oDataOrthogonal,
						seOrthogonal.getSeriesIdentifier( ),
						percentileValue,
						seOrthogonal.getDataPoint( ),
						fsBase,
						fsOrthogonal,
						fsSeries,
						fsPercentile,
						i,
						lo,
						dLength,
						rtc );

				udsh.next( dpa[i] );
			}
		}
		return new SeriesRenderingHints( this,
				oaxBase.getAxisCoordinate( ),
				scOrthogonal.getStart( ),
				dOrthogonalZero,
				dSeriesThickness,
				daTickCoordinates,
				dpa,
				scBase,
				scOrthogonal,
				ssl,
				dsiDataBase,
				dsiDataOrthogonal );
	}

	/**
	 * 
	 * @return
	 */
	public final StackedSeriesLookup getStackedSeriesLookup( )
	{
		return ssl;
	}

}




See more files for this project here

BIRT

BIRT is an open source, Eclipse-based reporting system that integrates with your application to produce compelling reports for both web and PDF.

Project homepage: http://www.eclipse.org/birt/phoenix/
Programming language(s): Java,XML
License: gpl2

  AllAxes.java
  AutoScale.java
  AxisSubUnit.java
  Grid.java
  IntersectionValue.java
  OneAxis.java
  PlotWith2DAxes.java
  PlotWith3DAxes.java
  PlotWithAxes.java
  SeriesRenderingHints.java
  SeriesRenderingHints3D.java
  StackGroup.java
  StackedSeriesLookup.java
  package.html