PlotWith3DAxes.java from BIRT at Krugle
Show PlotWith3DAxes.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.Iterator;
import java.util.List;
import org.eclipse.birt.chart.computation.DataPointHints;
import org.eclipse.birt.chart.computation.DataSetIterator;
import org.eclipse.birt.chart.computation.Engine3D;
import org.eclipse.birt.chart.computation.IConstants;
import org.eclipse.birt.chart.computation.UserDataSetHints;
import org.eclipse.birt.chart.computation.Vector;
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.event.Text3DRenderEvent;
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.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.Location3D;
import org.eclipse.birt.chart.model.attribute.Orientation;
import org.eclipse.birt.chart.model.attribute.impl.BoundsImpl;
import org.eclipse.birt.chart.model.attribute.impl.InsetsImpl;
import org.eclipse.birt.chart.model.attribute.impl.Location3DImpl;
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.LabelImpl;
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.data.impl.DateTimeDataSetImpl;
import org.eclipse.birt.chart.model.data.impl.NumberDataSetImpl;
import org.eclipse.birt.chart.model.data.impl.TextDataSetImpl;
import org.eclipse.birt.chart.plugin.ChartEnginePlugin;
import org.eclipse.birt.chart.render.ISeriesRenderingHints;
import org.eclipse.birt.chart.util.CDateTime;
import org.eclipse.birt.chart.util.ChartUtil;
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 class PlotWith3DAxes extends PlotWithAxes
{
private final double SPACE_THRESHOLD;
private Engine3D engine;
private Bounds cachedAdjustedBounds;
protected double dZAxisPlotSpacing = 0;
/**
* @param _ids
* @param _cwa
* @param _rtc
* @throws IllegalArgumentException
* @throws ChartException
*/
public PlotWith3DAxes( IDisplayServer _ids, ChartWithAxes _cwa,
RunTimeContext _rtc ) throws IllegalArgumentException,
ChartException
{
cwa = _cwa;
ids = _ids;
rtc = _rtc;
dPointToPixel = ids.getDpiResolution( ) / 72d;
SPACE_THRESHOLD = 5 * dPointToPixel;
if ( cwa.isTransposed( ) )
{
// Not support transposed for 3D chart.
throw new ChartException( ChartEnginePlugin.ID,
ChartException.COMPUTATION,
"exception.no.transposed.3D.chart", //$NON-NLS-1$
Messages.getResourceBundle( getRunTimeContext( ).getULocale( ) ) );
}
buildAxes( ); // CREATED ONCE
}
private Bounds getAdjustedPlotBounds( boolean refresh )
{
if ( !refresh && cachedAdjustedBounds != null )
{
return cachedAdjustedBounds;
}
final Bounds bo = getPlotBounds( ).adjustedInstance( getPlotInsets( ) );
double h, w;
Label la;
// approximate estimation of axes label space
// TODO better estimation
la = LabelImpl.copyInstance( aax.getPrimaryBase( ).getLabel( ) );
la.getCaption( ).setValue( "X" ); //$NON-NLS-1$
h = computeHeight( ids, la );
la = LabelImpl.copyInstance( aax.getAncillaryBase( ).getLabel( ) );
la.getCaption( ).setValue( "X" ); //$NON-NLS-1$
h = Math.max( h, computeHeight( ids, la ) );
la = LabelImpl.copyInstance( aax.getPrimaryOrthogonal( ).getLabel( ) );
la.getCaption( ).setValue( "X" ); //$NON-NLS-1$
w = computeWidth( ids, la );
// consider axes lable space.
bo.adjust( InsetsImpl.create( 0, 0, h, w ) );
cachedAdjustedBounds = bo;
return bo;
}
public final Location getPanningOffset( )
{
final Bounds bo = getAdjustedPlotBounds( false );
double xOff = bo.getLeft( );
double yOff = bo.getTop( );
// TODO read custom panning setting
return LocationImpl.create( xOff, yOff );
}
/**
* Returns the 3D engine for this render.
*/
public final Engine3D get3DEngine( )
{
if ( engine == null )
{
//TODO read custom light direction setting
// Use a fixed light direction here.
Vector lightDirection = new Vector( -1, 1, -1, false );
final Bounds bo = getPlotBounds( ).adjustedInstance( getPlotInsets( ) );
double width = bo.getWidth( );
double height = bo.getHeight( );
engine = new Engine3D( cwa.getRotation( ),
lightDirection,
width,
height,
500 * dPointToPixel,
1500 * dPointToPixel,
10 * dPointToPixel,
10000 * dPointToPixel,
100 );
}
return engine;
}
private double detectZoomScale( Engine3D engine, double xdz, double xOff,
double yOff, double width, double height )
{
double zlen = 1 * dPointToPixel;
double xlen = xdz * dPointToPixel;
double ylen = ( xdz + 1 ) * dPointToPixel / 2d;
List vertexList = new ArrayList( );
Location3D bbl = Location3DImpl.create( -xlen / 2, -ylen / 2, -zlen / 2 );
Location3D bbr = Location3DImpl.create( xlen / 2, -ylen / 2, -zlen / 2 );
Location3D bfl = Location3DImpl.create( -xlen / 2, -ylen / 2, zlen / 2 );
Location3D bfr = Location3DImpl.create( xlen / 2, -ylen / 2, zlen / 2 );
Location3D tbl = Location3DImpl.create( -xlen / 2, ylen / 2, -zlen / 2 );
Location3D tbr = Location3DImpl.create( xlen / 2, ylen / 2, -zlen / 2 );
Location3D tfl = Location3DImpl.create( -xlen / 2, ylen / 2, zlen / 2 );
Location3D tfr = Location3DImpl.create( xlen / 2, ylen / 2, zlen / 2 );
vertexList.add( bbl );
vertexList.add( bbr );
vertexList.add( bfl );
vertexList.add( bfr );
vertexList.add( tbl );
vertexList.add( tbr );
vertexList.add( tfl );
vertexList.add( tfr );
Text3DRenderEvent event = new Text3DRenderEvent( this );
double maxLeft = Double.MAX_VALUE;
double maxRight = -Double.MAX_VALUE;
double maxTop = Double.MAX_VALUE;
double maxBottom = -Double.MAX_VALUE;
Location p2d;
double x, y;
for ( Iterator itr = vertexList.iterator( ); itr.hasNext( ); )
{
Location3D p3d = (Location3D) itr.next( );
event.setLocation3D( Location3DImpl.create( p3d.getX( ),
p3d.getY( ),
p3d.getZ( ) ) );
if ( engine.processEvent( event, xOff, yOff ) != null )
{
p2d = event.getLocation( );
x = p2d.getX( );
y = p2d.getY( );
if ( x < maxLeft )
{
maxLeft = x;
}
if ( x > maxRight )
{
maxRight = x;
}
if ( y < maxTop )
{
maxTop = y;
}
if ( y > maxBottom )
{
maxBottom = y;
}
}
}
double vSpace = maxTop - yOff;
double hSpace = maxLeft - xOff;
if ( yOff + height - maxBottom < maxTop - yOff )
{
vSpace = yOff + height - maxBottom;
}
if ( xOff + width - maxRight < maxLeft - xOff )
{
hSpace = xOff + width - maxRight;
}
double minSpace = Math.min( hSpace, vSpace );
double lastMinspace = 0;
boolean fit = hSpace > 0 && vSpace > 0;
double lastScale = 1;
double scale = lastScale;
boolean iterateStarted = false;
if ( !fit )
{
// if even the minimum scale failed, return with no iteration
return 1;
}
while ( ChartUtil.mathGT( Math.abs( minSpace - lastMinspace ), 0 )
&& ( fit && minSpace > SPACE_THRESHOLD || !fit ) )
{
if ( fit && !iterateStarted )
{
// double zoomin
scale = lastScale * 2;
}
else
{
// dichotomia zoomin/out
scale = ( lastScale + scale ) / 2;
}
maxLeft = Double.MAX_VALUE;
maxRight = -Double.MAX_VALUE;
maxTop = Double.MAX_VALUE;
maxBottom = -Double.MAX_VALUE;
boolean forceBreak = false;
// check all 8 points.
for ( Iterator itr = vertexList.iterator( ); itr.hasNext( ); )
{
Location3D p3d = (Location3D) itr.next( );
event.setLocation3D( Location3DImpl.create( p3d.getX( ) * scale,
p3d.getY( ) * scale,
p3d.getZ( ) * scale ) );
if ( engine.processEvent( event, xOff, yOff ) != null )
{
p2d = event.getLocation( );
x = p2d.getX( );
y = p2d.getY( );
// System.out.println( "x: " + x + ", y: " + y );
if ( x < maxLeft )
{
maxLeft = x;
}
if ( x > maxRight )
{
maxRight = x;
}
if ( y < maxTop )
{
maxTop = y;
}
if ( y > maxBottom )
{
maxBottom = y;
}
}
else
{
fit = false;
forceBreak = true;
break;
}
}
if ( !forceBreak )
{
vSpace = maxTop - yOff;
hSpace = maxLeft - xOff;
if ( yOff + height - maxBottom < maxTop - yOff )
{
vSpace = yOff + height - maxBottom;
}
if ( xOff + width - maxRight < maxLeft - xOff )
{
hSpace = xOff + width - maxRight;
}
fit = vSpace > 0 && hSpace > 0;
}
if ( fit )
{
lastMinspace = minSpace;
minSpace = Math.min( hSpace, vSpace );
double nextScale = 2 * scale - lastScale;
lastScale = scale;
scale = nextScale;
}
else if ( !iterateStarted )
{
iterateStarted = true;
}
}
return lastScale;
}
private double computeAxisZoomFactor( Engine3D engine, double start,
double end, Location3D startVertext, Location3D endVertext,
double xOff, double yOff )
{
Text3DRenderEvent event = new Text3DRenderEvent( this );
Location p2d;
double x1, y1, x2, y2;
event.setLocation3D( startVertext );
if ( engine.processEvent( event, xOff, yOff ) != null )
{
p2d = event.getLocation( );
x1 = p2d.getX( );
y1 = p2d.getY( );
event.setLocation3D( endVertext );
if ( engine.processEvent( event, xOff, yOff ) != null )
{
p2d = event.getLocation( );
x2 = p2d.getX( );
y2 = p2d.getY( );
return Math.sqrt( ( y2 - y1 )
* ( y2 - y1 )
+ ( x2 - x1 )
* ( x2 - x1 ) )
/ ( end - start );
}
}
return 1;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.birt.chart.computation.withaxes.PlotWithAxes#compute(org.eclipse.birt.chart.model.attribute.Bounds)
*/
public void compute( Bounds bo ) throws ChartException,
IllegalArgumentException
{
bo = bo.scaledInstance( dPointToPixel ); // CONVERSION
boPlotBackground = BoundsImpl.copyInstance( bo );
// MUST BE 3-D DIMENSION ONLY HERE.
iDimension = getDimension( cwa.getDimension( ) );
assert iDimension == IConstants.THREE_D;
dXAxisPlotSpacing = cwa.getPlot( ).getHorizontalSpacing( )
* dPointToPixel; // CONVERSION
dYAxisPlotSpacing = cwa.getPlot( ).getVerticalSpacing( )
* dPointToPixel; // CONVERSION
dZAxisPlotSpacing = dXAxisPlotSpacing;
final Axis[] axa = cwa.getPrimaryBaseAxes( );
final Axis axPrimaryBase = axa[0];
final Axis axPrimaryOrthogonal = cwa.getPrimaryOrthogonalAxis( axPrimaryBase );
final Axis axAncillaryBase = cwa.getAncillaryBaseAxis( axPrimaryBase );
// INITIALIZE AXES DATASETS
Object oaData = null;
// COMPUTE PRIMARY BASE DATA
OneAxis oaxPrimaryBase = aax.getPrimaryBase( );
int iPrimaryAxisType = getAxisType( axPrimaryBase );
if ( iPrimaryAxisType == TEXT || oaxPrimaryBase.isCategoryScale( ) )
{
oaData = getTypedDataSet( axPrimaryBase, iPrimaryAxisType, 0 );
}
else if ( ( iPrimaryAxisType & NUMERICAL ) == NUMERICAL )
{
oaData = getMinMax( axPrimaryBase, iPrimaryAxisType );
}
else if ( ( iPrimaryAxisType & DATE_TIME ) == DATE_TIME )
{
oaData = getMinMax( axPrimaryBase, iPrimaryAxisType );
}
DataSetIterator dsiPrimaryBase = ( oaData instanceof DataSetIterator ) ? (DataSetIterator) oaData
: new DataSetIterator( oaData, iPrimaryAxisType );
oaData = null;
// COMPUTE ANCILLARY BASE DATA
OneAxis oaxAncillaryBase = aax.getAncillaryBase( );
int iAncillaryAxisType = getAxisType( axAncillaryBase );
if ( iAncillaryAxisType == TEXT || oaxAncillaryBase.isCategoryScale( ) )
{
oaData = getAncillaryDataSet( axAncillaryBase,
axPrimaryOrthogonal,
iAncillaryAxisType );
}
else if ( ( iAncillaryAxisType & NUMERICAL ) == NUMERICAL )
{
oaData = getMinMax( axAncillaryBase, iAncillaryAxisType );
}
else if ( ( iAncillaryAxisType & DATE_TIME ) == DATE_TIME )
{
oaData = getMinMax( axAncillaryBase, iAncillaryAxisType );
}
DataSetIterator dsiAncillary = ( oaData instanceof DataSetIterator ) ? (DataSetIterator) oaData
: new DataSetIterator( oaData, iAncillaryAxisType );
oaData = null;
// COMPUTE ORTHOGONAL DATA
OneAxis oaxPrimaryOrthogonal = aax.getPrimaryOrthogonal( );
int iOrthogonalAxisType = getAxisType( axPrimaryOrthogonal );
DataSetIterator dsiOrthogonal = null;
if ( ( iOrthogonalAxisType & NUMERICAL ) == NUMERICAL
|| ( iOrthogonalAxisType & DATE_TIME ) == DATE_TIME )
{
dsiOrthogonal = new DataSetIterator( getMinMax( axPrimaryOrthogonal,
iOrthogonalAxisType ),
iOrthogonalAxisType );
}
else
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.DATA_FORMAT,
"exception.orthogonal.axis.numerical.datetime", //$NON-NLS-1$
Messages.getResourceBundle( rtc.getULocale( ) ) );
}
// COMPUTE AUTOMATIC ZOOM SCALE AND FACTOR
double dXDZ = dsiPrimaryBase.size( ) * 1d / dsiAncillary.size( );
final Bounds adjustedBounds = getAdjustedPlotBounds( true );
double xOffset = adjustedBounds.getLeft( );
double yOffset = adjustedBounds.getTop( );
double zoomScale = detectZoomScale( get3DEngine( ),
dXDZ,
xOffset,
yOffset,
adjustedBounds.getWidth( ),
adjustedBounds.getHeight( ) );
double dWZ = zoomScale * dPointToPixel;
double dW = dXDZ * zoomScale * dPointToPixel;
double dH = ( dW + dWZ ) / 2;
double dX = -dW / 2;
double dY = -dH / 2;
double dZ = -dWZ / 2;
Location panningOffset = getPanningOffset( );
double xZoom = computeAxisZoomFactor( get3DEngine( ),
dX,
dX + dW,
Location3DImpl.create( dX, dY, dZ ),
Location3DImpl.create( dX + dW, dY, dZ ),
panningOffset.getX( ),
panningOffset.getY( ) );
double yZoom = computeAxisZoomFactor( get3DEngine( ),
dY,
dY + dH,
Location3DImpl.create( dX, dY, dZ ),
Location3DImpl.create( dX, dY + dH, dZ ),
panningOffset.getX( ),
panningOffset.getY( ) );
double zZoom = computeAxisZoomFactor( get3DEngine( ),
dZ,
dZ + dWZ,
Location3DImpl.create( dX, dY, dZ ),
Location3DImpl.create( dX, dY, dZ + dWZ ),
panningOffset.getX( ),
panningOffset.getY( ) );
double dStart, dEnd;
// COMPUTE PRIMARY-BASE-AXIS PROPERTIES AND ITS SCALE
AutoScale scPrimaryBase = null;
dStart = dX;
dEnd = dX + dW;
Scale sc = axPrimaryBase.getScale( );
scPrimaryBase = AutoScale.computeScale( ids,
oaxPrimaryBase,
dsiPrimaryBase,
iPrimaryAxisType,
dStart,
dEnd,
sc.getMin( ),
sc.getMax( ),
sc.isSetStep( ) ? new Double( sc.getStep( ) ) : null,
axPrimaryBase.getFormatSpecifier( ),
rtc,
FORWARD,
xZoom );
oaxPrimaryBase.set( scPrimaryBase ); // UPDATE SCALE ON PRIMARY-BASE
// AXIS
// COMPUTE ANCILLARY-BASE-AXIS PROPERTIES AND ITS SCALE
AutoScale scAncillaryBase = null;
dStart = dZ;
dEnd = dZ + dWZ;
sc = axAncillaryBase.getScale( );
scAncillaryBase = AutoScale.computeScale( ids,
oaxAncillaryBase,
dsiAncillary,
iAncillaryAxisType,
dStart,
dEnd,
sc.getMin( ),
sc.getMax( ),
sc.isSetStep( ) ? new Double( sc.getStep( ) ) : null,
axAncillaryBase.getFormatSpecifier( ),
rtc,
FORWARD,
zZoom );
oaxAncillaryBase.set( scAncillaryBase ); // UPDATE SCALE ON
// ANCILLARY-BASE AXIS
// COMPUTE PRIMARY-ORTHOGONAL-AXIS PROPERTIES AND ITS SCALE
AutoScale scPrimaryOrthogonal = null;
dStart = dY;
dEnd = dY + dH;
sc = axPrimaryOrthogonal.getScale( );
scPrimaryOrthogonal = AutoScale.computeScale( ids,
oaxPrimaryOrthogonal,
dsiOrthogonal,
iOrthogonalAxisType,
dStart,
dEnd,
sc.getMin( ),
sc.getMax( ),
sc.isSetStep( ) ? new Double( sc.getStep( ) ) : null,
axPrimaryOrthogonal.getFormatSpecifier( ),
rtc,
FORWARD,
yZoom );
oaxPrimaryOrthogonal.set( scPrimaryOrthogonal ); // UPDATE SCALE ON
// PRIMARY-ORTHOGONAL AXIS
// Here we ignore the intersection Value/Max setting, always
// use the Intersection.Min for 3D chart.
double dYAxisLocationOnX = dX;
double dYAxisLocationOnZ = dZ;
double dXAxisLocation = dY;
double dZAxisLocation = dY;
// SETUP THE FULL DATASET FOR THE PRIMARY ORTHOGONAL AXIS
iOrthogonalAxisType = getAxisType( axPrimaryOrthogonal );
oaData = getTypedDataSet( axPrimaryOrthogonal, iOrthogonalAxisType, 0 );
scPrimaryOrthogonal.setData( dsiOrthogonal );
// Setup the full dataset for the ancillary base axis.
iAncillaryAxisType = getAxisType( axAncillaryBase );
if ( iAncillaryAxisType != IConstants.TEXT )
{
scAncillaryBase.setData( getTypedDataSet( axAncillaryBase,
iAncillaryAxisType,
0 ) );
}
// SETUP THE FULL DATASET FOR THE PRIMARY ORTHOGONAL AXIS
iPrimaryAxisType = getAxisType( axPrimaryBase );
if ( iPrimaryAxisType != IConstants.TEXT )
{
scPrimaryBase.setData( getTypedDataSet( axPrimaryBase,
iPrimaryAxisType,
0 ) );
}
scPrimaryBase.resetShifts( );
scAncillaryBase.resetShifts( );
scPrimaryOrthogonal.resetShifts( );
// UPDATE FOR OVERLAYS
final OneAxis axPH = aax.getPrimaryBase( );
final OneAxis axPV = aax.getPrimaryOrthogonal( );
final OneAxis axAB = aax.getAncillaryBase( );
// keep old invocation to ensure compatibility.
axPH.setAxisCoordinate( dXAxisLocation );
axPV.setAxisCoordinate( dYAxisLocationOnX );
axAB.setAxisCoordinate( dZAxisLocation );
// set new 3D axis coordinate. this coordinate has been normalized to
// Zero-coordinates.
axPH.setAxisCoordinate3D( Location3DImpl.create( 0,
dXAxisLocation,
dYAxisLocationOnZ ) );
axPV.setAxisCoordinate3D( Location3DImpl.create( dYAxisLocationOnX,
0,
dYAxisLocationOnZ ) );
axAB.setAxisCoordinate3D( Location3DImpl.create( dYAxisLocationOnX,
dZAxisLocation,
0 ) );
}
/**
* @param ax
* @param orthogonalAxis
* @param iType
* @return
* @throws ChartException
* @throws IllegalArgumentException
*/
protected final DataSetIterator getAncillaryDataSet( Axis ax,
Axis orthogonalAxis, int iType ) throws ChartException,
IllegalArgumentException
{
final Series[] sea = ax.getRuntimeSeries( );
final Series[] osea = orthogonalAxis.getRuntimeSeries( );
if ( sea.length == 0 || osea.length == 0 ) // TBD: PULL FROM SAMPLE
// DATA
{
if ( ( iType & NUMERICAL ) == NUMERICAL )
{
// TODO consistent with orthogonal sereis length;
return new DataSetIterator( new Double[]{
new Double( 1 ), new Double( 2 )
} );
}
else if ( ( iType & DATE_TIME ) == DATE_TIME )
{
// TODO consistent with orthogonal sereis length;
return new DataSetIterator( new Calendar[]{
new CDateTime( ), new CDateTime( )
} );
}
else if ( ( iType & TEXT ) == TEXT )
{
// use orthogonal sereis identifier instead.
List data = new ArrayList( );
for ( int i = 0; i < osea.length; i++ )
{
data.add( String.valueOf( osea[i].getSeriesIdentifier( ) ) );
}
return new DataSetIterator( (String[]) data.toArray( new String[data.size( )] ) );
}
}
// Assume always use the first ancillary axis.
DataSetIterator dsi = getTypedDataSet( sea[0], iType );
List data = new ArrayList( );
for ( int i = 0; i < osea.length; i++ )
{
if ( dsi.hasNext( ) )
{
data.add( dsi.next( ) );
}
else if ( ( iType & NUMERICAL ) == NUMERICAL )
{
data.add( new Double( 0 ) );
}
else if ( ( iType & DATE_TIME ) == DATE_TIME )
{
data.add( new CDateTime( ) );
}
else if ( ( iType & TEXT ) == TEXT )
{
data.add( osea[i].getSeriesIdentifier( ) );
}
}
if ( ( iType & NUMERICAL ) == NUMERICAL )
{
return new DataSetIterator( NumberDataSetImpl.create( data ) );
}
else if ( ( iType & DATE_TIME ) == DATE_TIME )
{
return new DataSetIterator( DateTimeDataSetImpl.create( data ) );
}
else if ( ( iType & TEXT ) == TEXT )
{
return new DataSetIterator( TextDataSetImpl.create( data ) );
}
return null;
}
private final Object getMinMax( Axis ax, int iType ) throws ChartException,
IllegalArgumentException
{
final Series[] sea = ax.getRuntimeSeries( );
final int iSeriesCount = sea.length;
DataSet ds;
Object oV1, oV2, oMin = null, oMax = null;
PluginSettings ps = PluginSettings.instance( );
IDataSetProcessor iDSP = null;
for ( int i = 0; i < iSeriesCount; i++ )
{
if ( sea[i].isStacked( ) )
{
// 3D chart can't be stacked.
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 ( ax.isPercent( ) )
{
// 3D Chart axis doesn't support Percent.
throw new IllegalArgumentException( MessageFormat.format( Messages.getResourceBundle( rtc.getULocale( ) )
.getString( "exception.no.stack.percent.3D.chart" ), //$NON-NLS-1$
new Object[]{
ax
} ) );
}
// 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;
}
/*
* (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 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 OneAxis oaxAncillaryBase = aax.getAncillaryBase( );
final AutoScale scBase = oaxBase.getScale( );
final AutoScale scOrthogonal = oaxOrthogonal.getScale( );
final AutoScale scAncillary = oaxAncillaryBase.getScale( );
final int iXTickCount = scBase.getTickCount( );
final int iZTickCount = scAncillary.getTickCount( );
final double dXUnitSize = scBase.getUnitSize( );
final double dZUnitSize = scAncillary.getUnitSize( );
final boolean bZCategoryTextStyle = scAncillary.isCategoryScale( )
|| scAncillary.getType( ) == IConstants.TEXT;
double[] daXTickCoordinates = scBase.getTickCordinates( );
double[] daZTickCoordinates = scAncillary.getTickCordinates( );
Object oDataBase = null;
DataSetIterator dsiDataBase = scBase.getData( );
Object oDataOrthogonal;
DataSetIterator dsiDataOrthogonal = getTypedDataSet( seOrthogonal,
oaxOrthogonal.getScale( ).getType( ) );
DataSetIterator dsiDataAncillary = scAncillary.getData( );
double dOrthogonalZero = 0;
if ( ( scOrthogonal.getType( ) & NUMERICAL ) == NUMERICAL )
{
dOrthogonalZero = getLocation( scOrthogonal, 0 );
}
else
{
dOrthogonalZero = scOrthogonal.getStart( );
}
double dAncillaryZero = 0;
if ( ( scAncillary.getType( ) & NUMERICAL ) == NUMERICAL )
{
dAncillaryZero = getLocation( scAncillary, 0 );
}
else
{
dAncillaryZero = scAncillary.getStart( );
}
double dX = 0, dY = 0, dZ = 0, dXLength = 0, dZLength = 0;
Location3D lo3d;
final int iBaseCount = dsiDataBase.size( );
final int iOrthogonalCount = dsiDataOrthogonal.size( );
DataPointHints[] dpa = null;
int seriesIndex = 0;
Series[] rss = oaxOrthogonal.getModelAxis( ).getRuntimeSeries( );
for ( int i = 0; i < rss.length; i++ )
{
if ( rss[i] == seOrthogonal )
{
seriesIndex = i;
break;
}
}
Object oDataAncillary = null;
for ( int i = 0; i < seriesIndex; i++ )
{
if ( dsiDataAncillary.hasNext( ) )
{
dsiDataAncillary.next( );
}
}
if ( dsiDataAncillary.hasNext( ) )
{
oDataAncillary = dsiDataAncillary.next( );
}
if ( iBaseCount != iOrthogonalCount ) // DO NOT COMPUTE DATA POINT
// HINTS
// FOR OUT-OF-SYNC DATA
{
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( ) );
FormatSpecifier fsSDAncillary = null;
if ( oaxAncillaryBase.getModelAxis( )
.getSeriesDefinitions( )
.size( ) > 0 )
{
fsSDAncillary = ( (SeriesDefinition) oaxAncillaryBase.getModelAxis( )
.getSeriesDefinitions( )
.get( 0 ) ).getFormatSpecifier( );
}
// 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( );
if ( fsSeries == null )
{
fsSeries = fsSDAncillary;
}
}
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 = daXTickCoordinates[0] + dXUnitSize * i;
if ( bZCategoryTextStyle )
{
dZ = daZTickCoordinates[0]
+ dZUnitSize
* seriesIndex;
}
else
{
try
{
dZ = getLocation( scAncillary, oDataAncillary );
}
catch ( IllegalArgumentException e )
{
dZ = dAncillaryZero;
}
catch ( ChartException e )
{
dZ = dAncillaryZero;
}
}
try
{
dX = getLocation( scOrthogonal, oDataOrthogonal );
}
catch ( IllegalArgumentException nvex )
{
// dX = dOrthogonalZero;
dX = Double.NaN;
}
catch ( ChartException dfex )
{
dX = dOrthogonalZero; // FOR CUSTOM DATA ELEMENTS
}
}
else
{
dX = daXTickCoordinates[0] + dXUnitSize * i;
if ( bZCategoryTextStyle )
{
dZ = daZTickCoordinates[0]
+ dZUnitSize
* seriesIndex;
}
else
{
try
{
dZ = getLocation( scAncillary, oDataAncillary );
}
catch ( IllegalArgumentException e )
{
dZ = dAncillaryZero;
}
catch ( ChartException e )
{
dZ = dAncillaryZero;
}
}
try
{
dY = getLocation( scOrthogonal, oDataOrthogonal );
}
catch ( IllegalArgumentException nvex )
{
// dY = dOrthogonalZero;
dX = Double.NaN;
}
catch ( ChartException dfex )
{
dY = dOrthogonalZero; // FOR CUSTOM DATA ELEMENTS
}
}
}
else
{
// Do not support scatter for 3D chart.
throw new ChartException( ChartEnginePlugin.ID,
ChartException.COMPUTATION,
"exception.scatter.3D.not.supported", //$NON-NLS-1$
Messages.getResourceBundle( rtc.getULocale( ) ) );
}
lo3d = Location3DImpl.create( dX, dY, dZ );
dXLength = ( i < iXTickCount - 1 ) ? daXTickCoordinates[i + 1]
- daXTickCoordinates[i] : 0;
dZLength = ( seriesIndex < iZTickCount - 1 ) ? daZTickCoordinates[seriesIndex + 1]
- daZTickCoordinates[seriesIndex]
: 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( 1d / iOrthogonalCount );
}
dpa[i] = new DataPointHints( oDataBase,
oDataOrthogonal,
oDataAncillary,
percentileValue,
seOrthogonal.getDataPoint( ),
fsBase,
fsOrthogonal,
fsSeries,
fsPercentile,
i,
lo3d,
new double[]{
dXLength, dZLength
},
rtc );
udsh.next( dpa[i] );
}
}
return new SeriesRenderingHints3D( this,
oaxBase.getAxisCoordinate( ),
oaxAncillaryBase.getAxisCoordinate( ),
scOrthogonal.getStart( ),
dOrthogonalZero,
scOrthogonal.getEnd( ) - scOrthogonal.getStart( ),
daXTickCoordinates,
daZTickCoordinates,
dpa,
scBase,
scOrthogonal,
scAncillary,
dsiDataBase,
dsiDataOrthogonal,
dsiDataAncillary );
}
/*
* (non-Javadoc)
*
* @see org.eclipse.birt.chart.computation.withaxes.PlotWithAxes#buildAxes()
*/
void buildAxes( ) throws IllegalArgumentException, ChartException
{
final Axis[] axa = cwa.getPrimaryBaseAxes( );
final Axis axPrimaryBase = axa[0]; // NOTE: FOR REL 1 AXIS RENDERS, WE
// SUPPORT A SINGLE PRIMARY BASE AXIS
// ONLY
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 axAncillaryBase = cwa.getAncillaryBaseAxis( axPrimaryBase );
// if ( !axAncillaryBase.isSetOrientation( ) )
{
axAncillaryBase.setOrientation( Orientation.HORIZONTAL_LITERAL );
}
validateAxis( axAncillaryBase );
if ( axPrimaryBase.getAssociatedAxes( ).size( ) > 1 )
{
throw new ChartException( ChartEnginePlugin.ID,
ChartException.COMPUTATION,
"exception.multi.orthogonal.3D.not.supported", //$NON-NLS-1$
Messages.getResourceBundle( rtc.getULocale( ) ) );
}
aax = new AllAxes( null );
insCA = aax.getInsets( );
aax.swapAxes( cwa.isTransposed( ) );
// SETUP THE PRIMARY BASE-AXIS PROPERTIES AND ITS SCALE
final OneAxis oaxPrimaryBase = new OneAxis( axPrimaryBase,
IConstants.BASE_AXIS );
oaxPrimaryBase.set( getOrientation( IConstants.BASE ),
transposeLabelPosition( IConstants.BASE,
getLabelPosition( axPrimaryBase.getLabelPosition( ) ) ),
transposeLabelPosition( IConstants.BASE,
getLabelPosition( axPrimaryBase.getTitlePosition( ) ) ),
axPrimaryBase.isSetCategoryAxis( )
&& axPrimaryBase.isCategoryAxis( ) );
oaxPrimaryBase.setGridProperties( axPrimaryBase.getMajorGrid( )
.getLineAttributes( ),
axPrimaryBase.getMinorGrid( ).getLineAttributes( ),
axPrimaryBase.getMajorGrid( ).getTickAttributes( ),
axPrimaryBase.getMinorGrid( ).getTickAttributes( ),
transposeTickStyle( IConstants.BASE,
getTickStyle( axPrimaryBase, MAJOR ) ),
transposeTickStyle( IConstants.BASE,
getTickStyle( axPrimaryBase, MINOR ) ),
axPrimaryBase.getScale( ).getMinorGridsPerUnit( ) );
oaxPrimaryBase.set( axPrimaryBase.getLabel( ), axPrimaryBase.getTitle( ) );
oaxPrimaryBase.set( 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,
IConstants.ORTHOGONAL_AXIS );
oaxPrimaryOrthogonal.set( getOrientation( IConstants.ORTHOGONAL ),
transposeLabelPosition( IConstants.ORTHOGONAL,
getLabelPosition( axPrimaryOrthogonal.getLabelPosition( ) ) ),
transposeLabelPosition( IConstants.ORTHOGONAL,
getLabelPosition( axPrimaryOrthogonal.getTitlePosition( ) ) ),
axPrimaryOrthogonal.isSetCategoryAxis( )
&& axPrimaryOrthogonal.isCategoryAxis( ) );
oaxPrimaryOrthogonal.setGridProperties( axPrimaryOrthogonal.getMajorGrid( )
.getLineAttributes( ),
axPrimaryOrthogonal.getMinorGrid( ).getLineAttributes( ),
axPrimaryOrthogonal.getMajorGrid( ).getTickAttributes( ),
axPrimaryOrthogonal.getMinorGrid( ).getTickAttributes( ),
transposeTickStyle( IConstants.ORTHOGONAL,
getTickStyle( axPrimaryOrthogonal, MAJOR ) ),
transposeTickStyle( IConstants.ORTHOGONAL,
getTickStyle( axPrimaryOrthogonal, MINOR ) ),
axPrimaryOrthogonal.getScale( ).getMinorGridsPerUnit( ) );
oaxPrimaryOrthogonal.set( axPrimaryOrthogonal.getLabel( ),
axPrimaryOrthogonal.getTitle( ) );
oaxPrimaryOrthogonal.set( getIntersection( axPrimaryOrthogonal ) );
oaxPrimaryOrthogonal.set( axPrimaryOrthogonal.getLineAttributes( ) );
aax.definePrimary( oaxPrimaryOrthogonal ); // ADD TO AXIS SET
// SETUP THE ANCILLARY BASE-AXIS PROPERTIES AND ITS SCALE
final OneAxis oaxAncillaryBase = new OneAxis( axAncillaryBase,
IConstants.ANCILLARY_AXIS );
oaxAncillaryBase.set( IConstants.HORIZONTAL,
getLabelPosition( axAncillaryBase.getLabelPosition( ) ),
getLabelPosition( axAncillaryBase.getTitlePosition( ) ),
axAncillaryBase.isSetCategoryAxis( )
&& axAncillaryBase.isCategoryAxis( ) );
oaxAncillaryBase.setGridProperties( axAncillaryBase.getMajorGrid( )
.getLineAttributes( ),
axAncillaryBase.getMinorGrid( ).getLineAttributes( ),
axAncillaryBase.getMajorGrid( ).getTickAttributes( ),
axAncillaryBase.getMinorGrid( ).getTickAttributes( ),
getTickStyle( axAncillaryBase, MAJOR ),
getTickStyle( axAncillaryBase, MINOR ),
axAncillaryBase.getScale( ).getMinorGridsPerUnit( ) );
oaxAncillaryBase.set( axAncillaryBase.getLabel( ),
axAncillaryBase.getTitle( ) ); // ASSOCIATE FONT, ETC.
oaxAncillaryBase.set( getIntersection( axAncillaryBase ) );
oaxAncillaryBase.set( axAncillaryBase.getLineAttributes( ) );
aax.defineAncillaryBase( oaxAncillaryBase ); // ADD TO AXIS SET
}
}
See more files for this project here