summaryrefslogtreecommitdiffstats
path: root/libkdchart/KDChartLinesPainter.cpp
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2011-07-04 22:38:03 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2011-07-04 22:38:03 +0000
commitdadc34655c3ab961b0b0b94a10eaaba710f0b5e8 (patch)
tree99e72842fe687baea16376a147619b6048d7e441 /libkdchart/KDChartLinesPainter.cpp
downloadkmymoney-dadc34655c3ab961b0b0b94a10eaaba710f0b5e8.tar.gz
kmymoney-dadc34655c3ab961b0b0b94a10eaaba710f0b5e8.zip
Added kmymoney
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kmymoney@1239792 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'libkdchart/KDChartLinesPainter.cpp')
-rw-r--r--libkdchart/KDChartLinesPainter.cpp947
1 files changed, 947 insertions, 0 deletions
diff --git a/libkdchart/KDChartLinesPainter.cpp b/libkdchart/KDChartLinesPainter.cpp
new file mode 100644
index 0000000..8b80341
--- /dev/null
+++ b/libkdchart/KDChartLinesPainter.cpp
@@ -0,0 +1,947 @@
+/* -*- Mode: C++ -*-
+ KDChart - a multi-platform charting engine
+*/
+
+/****************************************************************************
+ ** Copyright (C) 2001-2003 Klarälvdalens Datakonsult AB. All rights reserved.
+ **
+ ** This file is part of the KDChart library.
+ **
+ ** This file may be distributed and/or modified under the terms of the
+ ** GNU General Public License version 2 as published by the Free Software
+ ** Foundation and appearing in the file LICENSE.GPL included in the
+ ** packaging of this file.
+ **
+ ** Licensees holding valid commercial KDChart licenses may use this file in
+ ** accordance with the KDChart Commercial License Agreement provided with
+ ** the Software.
+ **
+ ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ **
+ ** See http://www.klaralvdalens-datakonsult.se/?page=products for
+ ** information about KDChart Commercial License Agreements.
+ **
+ ** Contact [email protected] if any conditions of this
+ ** licensing are not clear to you.
+ **
+ **********************************************************************/
+#include "KDChartLinesPainter.h"
+#include <KDChartParams.h>
+#include <KDChartPropertySet.h>
+
+#include <qpainter.h>
+
+#if COMPAT_QT_VERSION >= 0x030000
+#include <qvaluevector.h>
+#else
+#include <qarray.h>
+#endif
+
+#include <stdlib.h>
+
+/**
+ \class KDChartLinesPainter KDChartLinesPainter.h
+
+ \brief A chart painter implementation that can paint line charts.
+*/
+
+/**
+ Constructor. Sets up internal data structures as necessary.
+
+ \param params the KDChartParams structure that defines the chart
+ \param data the data that will be displayed as a chart
+*/
+KDChartLinesPainter::KDChartLinesPainter( KDChartParams* params ) :
+ KDChartAxesPainter( params )
+{
+ // This constructor intentionally left blank so far; we cannot setup the
+ // geometry yet since we do not know the size of the painter.
+}
+
+
+/**
+ Destructor.
+*/
+KDChartLinesPainter::~KDChartLinesPainter()
+{
+ // intentionally left blank
+}
+
+
+/**
+ Paints the actual data area. Data regions will only be added if \a
+ regions is not 0.
+
+ \param painter the QPainter onto which the chart should be painted
+ \param data the data that will be displayed as a chart
+ \param paint2nd specifies whether the main chart or the additional chart is to be drawn now
+ \param regions a pointer to a list of regions that will be filled
+ with regions representing the data segments, if not null
+*/
+void KDChartLinesPainter::paintData( QPainter* painter,
+ KDChartTableDataBase* data,
+ bool paint2nd,
+ KDChartDataRegionList* regions )
+{
+ paintDataInternal( painter, data,
+ true, // center points
+ params()->lineMarker() && !params()->threeDLines(), // line markers yes/no, 3D lines have no markers
+ false, // not an area
+ paint2nd,
+ regions );
+}
+
+/**
+ \internal
+
+ Does the actual painting of a line or an area chart and is provided
+ with the appropriate parameters from \c
+ KDChartLinesPainter::paintData() and
+ KDChartAreaPainter::paintData().
+
+ \param painter the QPainter onto which the chart should be painted
+ \param data the data that will be displayed as a chart
+ \param centerThePoints if true, the value points will be centered in
+ their segment (typically used for line charts), if false, the first
+ value point will be on the ordinate, the second one at the right
+ margin of the chart.
+ \param isArea true if an area will be drawn, false for lines. Using
+ this parameter should be avoided.
+ \param paint2nd specifies whether the main chart or the additional chart is to be drawn now
+ \param regions a pointer to a list of regions that will be filled
+ with regions representing the data segments, if not null
+*/
+void KDChartLinesPainter::paintDataInternal( QPainter* painter,
+ KDChartTableDataBase* data,
+ bool centerThePoints,
+ bool drawMarkers,
+ bool isArea,
+ bool paint2nd,
+ KDChartDataRegionList* regions )
+{
+ mCenterThePoints = centerThePoints;
+ mDrawMarkers = drawMarkers;
+ mIsArea = isArea;
+ mChartType = paint2nd ? params()->additionalChartType()
+ : params()->chartType();
+
+ KDChartAxesPainter::paintData( painter, data, paint2nd, regions );
+}
+
+
+#define DEGTORAD(d) (d)*M_PI/180
+
+/*!
+ Projects a point in a space defined by its x, y, and z coordinates
+ into a point onto a plane, given two rotation angles around the x
+ resp. y axis.
+*/
+QPoint KDChartLinesPainter::project( int x, int y, int z )
+{
+
+ double xrad = DEGTORAD( params()->threeDLineXRotation() );
+ double yrad = DEGTORAD( params()->threeDLineYRotation() );
+ QPoint ret( static_cast<int>( x*cos( yrad ) + z * sin( yrad ) ),
+ static_cast<int>( y*cos( xrad ) - z * sin( xrad ) ) );
+ return ret;
+}
+
+bool KDChartLinesPainter::isNormalMode() const
+{
+ return KDChartParams::LineNormal == params()->lineChartSubType();
+}
+
+int KDChartLinesPainter::clipShiftUp( bool, double ) const
+{
+ return 0;
+}
+
+
+class MyPoint
+{
+public:
+ MyPoint() : bValid( false ), bSkipThis( false ), cellValue( 0.0 ) {}
+ void set( int x, int y, double value ) {
+ bValid = true;
+ p.setX( x );
+ p.setY( y );
+ cellValue = value;
+ }
+ void setSkipThis( bool skipThis ) {
+ bSkipThis = skipThis;
+ }
+ QPoint p;
+ bool bValid;
+ bool bSkipThis;
+ double cellValue;
+};
+
+
+void KDChartLinesPainter::specificPaintData( QPainter* painter,
+ const QRect& /*ourClipRect*/,
+ KDChartTableDataBase* data,
+ KDChartDataRegionList* regions,
+ const KDChartAxisParams* ordinatePara,
+ bool /*bNormalMode*/,
+ uint chart,
+ double logWidth,
+ double /*areaWidthP1000*/,
+ double logHeight,
+ double axisYOffset,
+ double minColumnValue,
+ double maxColumnValue,
+ double columnValueDistance,
+ uint /*chartDatasetStart*/,
+ uint /*chartDatasetEnd*/,
+ uint datasetStart,
+ uint datasetEnd )
+{
+ if( !data ) return;
+
+
+ abscissaInfos ai;
+ ai.bCenterThePoints = mCenterThePoints;
+ calculateAbscissaInfos( *params(), *data,
+ datasetStart, datasetEnd,
+ logWidth, _dataRect,
+ ai );
+ mCenterThePoints = ai.bCenterThePoints;
+
+ bool bOrdinateDecreasing = ordinatePara
+ ? ordinatePara->axisValuesDecreasing()
+ : false;
+ bool bOrdinateIsLogarithmic
+ = ordinatePara
+ ? (KDChartAxisParams::AxisCalcLogarithmic == ordinatePara->axisCalcMode())
+ : false;
+
+ //const double ordinatePixelsPerUnit = logHeight / columnValueDistance;
+ const double ordinatePixelsPerUnit
+ = ( ordinatePara
+ && (0.0 != ordinatePara->trueAxisDeltaPixels())
+ && (0.0 != ordinatePara->trueAxisDelta()))
+ ? ordinatePara->trueAxisDeltaPixels() / ordinatePara->trueAxisDelta()
+ : logHeight / columnValueDistance;;
+ //qDebug("ordinatePixelsPerUnit: %f",ordinatePixelsPerUnit);
+
+
+ const bool showThreeDLines = !mIsArea && params()->threeDLines();
+
+ enum { Normal, Stacked, Percent } mode = Normal;
+ if ( ( ( mChartType == KDChartParams::Line )
+ && ( params()->lineChartSubType() == KDChartParams::LineNormal ) )
+ || ( ( mChartType == KDChartParams::Area )
+ && ( params()->areaChartSubType() == KDChartParams::AreaNormal ) ) )
+ mode = Normal;
+ else if ( ( ( mChartType == KDChartParams::Line )
+ && ( params()->lineChartSubType() == KDChartParams::LineStacked ) )
+ || ( ( mChartType == KDChartParams::Area )
+ && ( params()->areaChartSubType() == KDChartParams::AreaStacked ) ) )
+ mode = Stacked;
+ else if ( ( ( mChartType == KDChartParams::Line )
+ && ( params()->lineChartSubType() == KDChartParams::LinePercent ) )
+ || ( ( mChartType == KDChartParams::Area )
+ && ( params()->areaChartSubType() == KDChartParams::AreaPercent ) ) )
+ mode = Percent;
+ else
+ qDebug( "Internal error in KDChartLinesPainter::paintDataInternal(): Unknown subtype" );
+
+
+ QMap < int, double > currentValueSums;
+ if ( mode == Stacked || mode == Percent ) {
+ // this array is only used for stacked and percent lines, no need
+ // to waste time initializing it for normal types
+ for ( int value = 0; value < ai.numValues; ++value )
+ currentValueSums[ value ] = 0.0;
+ }
+ QMap < int, double > totalValueSums;
+
+ // compute the position of the 0 axis
+ double zeroXAxisI;
+ if ( mode == Percent ) {
+ if ( minColumnValue == 0.0 )
+ zeroXAxisI = logHeight + axisYOffset;
+ else if( maxColumnValue == 0.0 )
+ zeroXAxisI = _dataRect.y() + axisYOffset;
+ else
+ zeroXAxisI = logHeight / 2.0 + _dataRect.y();
+ } else
+ zeroXAxisI = ordinatePara->axisZeroLineStartY() - _dataRect.y();
+
+
+ // compute how to shift of the points in case we want them in the
+ // middle of their respective columns
+ int xShift = mCenterThePoints ? static_cast < int > ( ai.pointDist * 0.5 ) : 0;
+
+
+ // calculate all points' positions
+ // ===============================
+ int arrayNumDatasets = 0;
+ int arrayNumValues = ai.bAbscissaHasTrueAxisDtValues
+ ? data->cols()
+ : ai.numValues;
+ int dataset;
+ for( dataset = datasetEnd;
+ ( dataset >= static_cast < int > ( datasetStart ) && dataset >= 0 );
+ --dataset )
+ ++arrayNumDatasets;
+#if COMPAT_QT_VERSION >= 0x030000
+ QValueVector<MyPoint> allPoints(
+#else
+ QArray<MyPoint> allPoints(
+#endif
+ arrayNumDatasets * arrayNumValues );
+
+ KDChartPropertySet curPropSet;
+ int curPropSetId = KDChartPropertySet::UndefinedID;
+
+ for( dataset = datasetEnd; ( dataset >= (int)datasetStart && dataset >= 0 ); --dataset ) {
+
+ int prevPointX = -1;
+ int prevPointY = -1;
+
+ const KDChartParams::LineMarkerStyle
+ defaultMarkerStyle = params()->lineMarkerStyle( dataset );
+ const QPen default2DPen( params()->lineColor().isValid()
+ ? params()->lineColor()
+ : params()->dataColor( dataset ),
+ params()->lineWidth(),
+ params()->lineStyle( dataset ) );
+
+ if( ai.bAbscissaHasTrueAxisDtValues )
+ ai.numValues = data->cols();
+
+ QVariant vValY;
+ QVariant vValX;
+ int cellPropID;
+ for( int value = 0; value < ai.numValues; ++value ) {
+ //if ( mode == Percent )
+ // valueTotal = data->colAbsSum( value );
+ double valueTotal = 0.0; // Will only be used for Percent
+ if( mode == Percent ) {
+ valueTotal = 0.0;
+ // iterate over datasets of this axis only:
+ for ( uint dataset2 = datasetStart;
+ dataset2 <= datasetEnd;
+ ++dataset2 ) {
+ if( data->cellCoord( dataset2, value, vValY, 1 ) &&
+ QVariant::Double == vValY.type() )
+ valueTotal += vValY.toDouble();
+ }
+ }
+
+ if( data->cellContent( dataset, value, vValY, vValX, cellPropID ) &&
+ QVariant::Double == vValY.type() &&
+ ( !ai.bCellsHaveSeveralCoordinates || QVariant::Invalid != vValX.type() ) ){
+ //qDebug("a. cellPropID: %i",cellPropID);
+
+ // calculate Ordinate axis value
+ // -----------------------------
+ double cellValue = vValY.toDouble();
+ double drawValue = 0.0;
+ // PENDING(kalle) This does not work for AreaPercent yet
+ if ( mode == Stacked )
+ drawValue = ( cellValue + currentValueSums[ value ] ) * ordinatePixelsPerUnit;
+ else if ( mode == Percent )
+ drawValue = ( ( cellValue + currentValueSums[ value ] ) / valueTotal ) * 100.0 * ordinatePixelsPerUnit;
+ else {
+ // LineNormal or AreaNormal
+ if( bOrdinateIsLogarithmic ){
+ if( 0.0 < cellValue )
+ drawValue = log10( cellValue ) * ordinatePixelsPerUnit;
+ else
+ drawValue = -10250.0;
+ //qDebug("\nlogarithmic calc - cellValue: %f drawValue: %f",
+ // cellValue, drawValue );
+ }else{
+ drawValue = cellValue * ordinatePixelsPerUnit * (bOrdinateDecreasing ? -1.0 : 1.0);
+ //qDebug("\nlinear calc - cellValue: %f\n - drawValue: %f",
+ // cellValue, drawValue );
+ }
+ }
+
+
+ // calculate Abscissa axis value
+ // -----------------------------
+ double xValue;
+ bool skipMe = !calculateAbscissaAxisValue( vValX, ai, value,
+ xValue );
+
+
+ // calculate and store the point and region / draw the marker
+ // ----------------------------------------------------------
+ if( !skipMe ){
+ // prevent the point from being toooo far
+ // below the bottom (or above the top, resp.)
+ // of the cliprect
+ double pY = QMIN( zeroXAxisI - drawValue,
+ (logHeight + axisYOffset) * 3 );
+ pY = QMAX( pY, -(logHeight + axisYOffset) * 3 );
+ // specify the Point
+ int myPointX = static_cast < int > ( xValue ) + xShift;
+ int myPointY = static_cast < int > ( pY );
+
+ if( cellPropID == curPropSetId &&
+ myPointX == prevPointX &&
+ myPointY == prevPointY ){
+ allPoints[ static_cast < int > ( datasetEnd-dataset )
+ * arrayNumValues + value ].setSkipThis( true );
+ skipMe = true;
+ //qDebug("skipped");
+ }else{
+ // use typecast to make it compile on windows using qt232
+ allPoints[ static_cast < int > ( datasetEnd-dataset )
+ * arrayNumValues + value ].set( myPointX, myPointY, cellValue );
+ //qDebug("ok");
+ }
+ if( !skipMe ){
+ // --------------------------------------------------------
+ // determine any 'extra' properties assigned to this cell
+ // by traversing the property set chain (if necessary)
+ // --------------------------------------------------------
+ if( cellPropID != curPropSetId ){
+ //qDebug("b. ( curPropSetId: %i )",curPropSetId);
+ //qDebug("b. cellPropID: %i",cellPropID);
+ //qDebug(curPropSet.name().latin1());
+ if( cellPropID != KDChartPropertySet::UndefinedID &&
+ params()->calculateProperties( cellPropID,
+ curPropSet ) ){
+ curPropSetId = cellPropID;
+ //qDebug("c. curPropSetId: %i",curPropSetId);
+ //qDebug(curPropSet.name().latin1());
+ }else{
+ curPropSetId = KDChartPropertySet::UndefinedID;
+ }
+ }
+ // make sure any extra horiz. and/or vert. lines and/or markers
+ // are drawn *before* the data lines and/or markers are painted
+ if( mChartType == KDChartParams::Line ){
+ if( curPropSetId != KDChartPropertySet::UndefinedID ){
+ drawExtraLinesAndMarkers(
+ curPropSet,
+ default2DPen,
+ defaultMarkerStyle,
+ myPointX, myPointY,
+ painter,
+ ai.abscissaPara,
+ ordinatePara,
+ logWidth/1000.0,
+ logHeight/1000.0,
+ false );
+ }
+ }
+ prevPointX = myPointX;
+ prevPointY = myPointY;
+ }
+ }
+ // calculate running sum for stacked and percent
+ if ( mode == Stacked || mode == Percent ) {
+ if( cellValue == KDCHART_POS_INFINITE )
+ currentValueSums[ value ] = KDCHART_POS_INFINITE;
+ else if( currentValueSums[ value ] != KDCHART_POS_INFINITE )
+ currentValueSums[ value ] += cellValue;
+ }
+ }
+ }
+ }
+
+
+
+ QPointArray previousPoints; // no vector since only areas need it,
+ // and these do not support 3d yet
+
+ // Store some (dataset-independend) default values
+ // to be used unless other properties
+ // have been specified for the respective data cell:
+ //
+ const bool defaultDrawMarkers = mDrawMarkers;
+
+ for ( dataset = datasetEnd; ( dataset >= (int)datasetStart && dataset >= 0 ); --dataset ) {
+
+ // Store some (dataset-dependend) default values
+ // to be used unless other properties
+ // have been specified for the respective data cell:
+ //
+ const QPen default2DPen( params()->lineColor().isValid()
+ ? params()->lineColor()
+ : params()->dataColor( dataset ),
+ params()->lineWidth(),
+ params()->lineStyle( dataset ) );
+ bool currentDrawMarkers = defaultDrawMarkers;
+ const KDChartParams::LineMarkerStyle markerStyle = params()->lineMarkerStyle( dataset );
+
+ // the +2 is for the areas (if any)
+ QPtrVector< QPointArray > points( 2 );
+ points.setAutoDelete( true );
+ /* Pending Michel - we need to keep track of the
+ * non rotated points for 3D lines
+ */
+ QPtrVector< QPointArray > oripoints( 2 );
+ oripoints.setAutoDelete( true );
+
+ int i = 0;
+ for( i = 0; i < 2; ++i ) {
+ points.insert( i, new QPointArray( ai.numValues + 2 ) );
+ oripoints.insert( i, new QPointArray( ai.numValues + 2 ) );
+ }
+
+ if( ai.bAbscissaHasTrueAxisDtValues )
+ ai.numValues = data->cols();
+
+ int point = 0;
+
+ for ( int value = 0; value < ai.numValues; ++value ) {
+
+ // determine and store marker properties assigned to this cell
+ // -----------------------------------------------------------
+ currentDrawMarkers = defaultDrawMarkers;
+ int cellPropID;
+ if( data->cellProp( dataset, value, cellPropID ) &&
+ cellPropID != curPropSetId ){
+ if( cellPropID != KDChartPropertySet::UndefinedID &&
+ params()->calculateProperties( cellPropID,
+ curPropSet ) )
+ curPropSetId = cellPropID;
+ else
+ curPropSetId = KDChartPropertySet::UndefinedID;
+ }
+ if( curPropSetId != KDChartPropertySet::UndefinedID ){
+ // we can safely call the following functions and ignore their
+ // return values since they will touch the parameters' values
+ // if the propSet *contains* corresponding own values only.
+ int iDummy;
+ curPropSet.hasOwnShowMarker( iDummy, currentDrawMarkers );
+ }
+
+
+ int iVec = static_cast < int > ( datasetEnd-dataset ) * arrayNumValues + value;
+ if( allPoints[ iVec ].bValid && !allPoints[ iVec ].bSkipThis ){
+ const MyPoint& mp = allPoints[iVec];
+
+
+ //qDebug("\np.x() %i p.y() %i", p.x(), p.y() );
+ // For 3D lines, we need two points (that lie
+ // behind each other on the Z axis). For 2D lines and
+ // areas, we need only one point.
+ if( showThreeDLines ) {
+ points[0]->setPoint( point, project( mp.p.x(), mp.p.y(),
+ (datasetStart+dataset)*params()->threeDLineDepth() ) );
+ points[1]->setPoint( point, project( mp.p.x(), mp.p.y(),
+ (datasetStart+dataset + 1)*params()->threeDLineDepth() ) );
+ oripoints[0]->setPoint( point, mp.p.x(), mp.p.y() );
+ oripoints[1]->setPoint( point, mp.p.x() - (datasetStart+dataset + 1)*params()->threeDLineDepth(),
+ mp.p.y() - (datasetStart+dataset + 1)*params()->threeDLineDepth() );
+
+ } else
+ // 2D lines or areas
+ points[0]->setPoint( point, mp.p );
+ ++point;
+
+ int x = mp.p.x();
+ int y = QMAX(QMIN(mp.p.y(),
+ static_cast < int > (logHeight +axisYOffset)),
+ 0);
+ bool markerIsOutside = y != mp.p.y();
+ // draw the marker and store the region
+ if ( currentDrawMarkers ){
+ uint theAlignment = Qt::AlignCenter;
+ bool hasOwnSize = false;
+ int theWidth = 0;
+ int theHeight = 0;
+ QColor theColor(params()->dataColor( dataset ));
+ int theStyle = markerStyle;
+ if( curPropSetId != KDChartPropertySet::UndefinedID ){
+ // we can safely call the following functions and ignore their
+ // return values since they will touch the parameters' values
+ // if the propSet *contains* corresponding own values only.
+ int iDummy;
+ curPropSet.hasOwnMarkerAlign( iDummy, theAlignment );
+ curPropSet.hasOwnMarkerColor( iDummy, theColor );
+ curPropSet.hasOwnMarkerStyle( iDummy, theStyle );
+ QSize size(theWidth, theHeight);
+ hasOwnSize = curPropSet.hasOwnMarkerSize(iDummy, size);
+ if( hasOwnSize ){
+ theWidth = size.width();
+ theHeight = size.height();
+ }
+ }
+
+ drawMarker( painter,
+ params(),
+ _areaWidthP1000, _areaHeightP1000,
+ _dataRect.x(),
+ _dataRect.y(),
+ markerIsOutside
+ ? KDChartParams::LineMarker1Pixel
+ : theStyle,
+ theColor,
+ QPoint(x,y),
+ dataset, value, chart, regions,
+ hasOwnSize ? &theWidth : 0,
+ hasOwnSize ? &theHeight : 0,
+ theAlignment );
+
+ }
+ // store the region
+ else if( regions ) {
+ QRect rect(
+ QPoint( x-params()->lineWidth()-1, y-params()->lineWidth()-1 ),
+ QPoint( x+params()->lineWidth()+1, y+params()->lineWidth()+1 )
+ );
+ rect.moveBy( _dataRect.x(), _dataRect.y() );
+ regions->append(
+ new KDChartDataRegion(dataset, value, chart, rect) );
+ }
+
+ }
+ }
+ if ( point ) {
+ bool bDrawLines = (0 != params()->lineWidth());
+
+ if ( mIsArea ) {
+ // first draw with the fill brush, no pen, with the
+ // zero axis points or upper border points added for the first
+ // dataset or with the previous points reversed for all other
+ // datasets.
+ painter->setPen( QPen( Qt::NoPen ) );
+ const QBrush datasetBrush( params()->dataColor( dataset ), Qt::SolidPattern );
+ painter->setBrush( datasetBrush );
+ QBrush currentBrush( datasetBrush );
+
+ if ( mode == Normal || dataset == (int)datasetEnd ) {
+ /// first dataset (or any dataset in normal mode, where
+ /// the datasets overwrite each other)
+
+ // no 3d handling for areas yet
+ QPoint lastPoint = points[0]->point( point - 1 );
+
+ // zeroXAxisI can be too far below the abscissa, but it's
+ // the only thing we have. Likewise can 0 be too far above
+ // the upper boundary, but again it's the only thing we
+ // have, at the rest is clipped anyway.
+ int yCoord;
+ if ( params()->areaLocation() == KDChartParams::AreaBelow ||
+ mode == Percent )
+ yCoord = static_cast<int>(zeroXAxisI);
+ else
+ yCoord = static_cast<int>(axisYOffset);
+
+ // old: draw the complete area in on go:
+ /*
+ // no 3d handling for areas yet
+ points[0]->setPoint( point, lastPoint.x(), yCoord );
+ point++;
+
+ QPoint firstPoint = points[0]->point( 0 );
+ points[0]->setPoint( point, firstPoint.x(), yCoord );
+ point++;
+
+ painter->drawPolygon( *points[0], false, 0, point );
+ */
+
+ // new: draw individual area segments:
+ curPropSetId = KDChartPropertySet::UndefinedID;
+ for( int value = 0; value < point-1; ++value ) {
+
+ int cellPropID;
+ if( data->cellProp( dataset, value, cellPropID ) &&
+ cellPropID != curPropSetId ){
+
+ if( cellPropID != KDChartPropertySet::UndefinedID &&
+ params()->calculateProperties( cellPropID,
+ curPropSet ) ){
+ curPropSetId = cellPropID;
+ }else{
+ curPropSetId = KDChartPropertySet::UndefinedID;
+ }
+ // preset with default value
+ QBrush theAreaBrush = datasetBrush;
+
+ if( curPropSetId != KDChartPropertySet::UndefinedID ){
+ // we can safely call the following functions and ignore their
+ // return values since they will touch the parameters' values
+ // if the propSet *contains* corresponding own values only.
+ int iDummy;
+ curPropSet.hasOwnAreaBrush( iDummy, theAreaBrush );
+ }
+ painter->setBrush( theAreaBrush );
+
+ }
+ QPointArray segment( 4 );
+ segment.setPoint( 0, points[0]->point( value ) );
+ segment.setPoint( 1, points[0]->point( value+1 ) );
+ segment.setPoint( 2, points[0]->point( value+1 ).x(), yCoord );
+ segment.setPoint( 3, points[0]->point( value ).x(), yCoord );
+ painter->drawPolygon( segment );
+ }
+
+ // old: draw the complete area in on go:
+ /*
+ // remove the last two points added
+ point -= 2;
+ */
+ //qDebug("\n111");
+ } // if ( mode == Normal || dataset == (int)datasetEnd )
+ else {
+ // don't mess around with the original array; we'll need
+ // that for the next time through.
+
+ //qDebug("222");
+ // no 3d handling for areas yet
+ QPointArray thisSection = points[0]->copy();
+
+ thisSection.resize( point + previousPoints.size() );
+ // append the previous array (there is guaranteed to be
+ // one because we are at least the second time through
+ // here) in reverse order
+ for ( unsigned int i = 0; i < previousPoints.size(); ++i ) {
+ thisSection.setPoint( point + i,
+ previousPoints.point( previousPoints.size() - i - 1 ) );
+ //qDebug("\nx: %i",previousPoints.point( previousPoints.size() - i - 1 ).x());
+ //qDebug("y: %i",previousPoints.point( previousPoints.size() - i - 1 ).y());
+ }
+ painter->drawPolygon( thisSection );
+ }
+ // draw the line with no brush and outline color
+ painter->setBrush( Qt::NoBrush );
+ painter->setPen( QPen( params()->outlineDataColor(),
+ params()->outlineDataLineWidth() ) );
+ } else {
+ // line
+ if( showThreeDLines ) {
+ // This is a 3D line:
+ // We draw the line with the data color brush
+ // and the outline data pen.
+ painter->setBrush( params()->dataColor( dataset ) );
+ painter->setPen( QPen( params()->outlineDataColor(),
+ params()->outlineDataLineWidth() ) );
+ } else {
+ // This is a 2D line:
+ // We draw the line with the no brush
+ // and the data color if no special line color was specified.
+ painter->setBrush( Qt::NoBrush );
+ painter->setPen( default2DPen );
+ }
+ }
+
+ // Neither draw the contour line if this is a pure Point chart
+ // nor draw it for the last row of a percent area chart.
+
+ if( bDrawLines &&
+ ( (mode != Percent) || !mIsArea || (dataset != (int)datasetEnd) ) ){
+ if( showThreeDLines ) {
+
+ // A 3D line needs to be drawn piece-wise
+ for ( int value = 0; value < point-1; ++value ) {
+ // if( data->cell( dataset, value ).hasValue() &&
+ // data->cell( dataset, value+1 ).hasValue() ) {
+ // qDebug( "Draw a segment in dataset %d from %d to %d", dataset, value, value+1 );
+
+ //store the rotated points ( see project() )
+ QPointArray rotatedSegment( 4 );
+ rotatedSegment.setPoint( 0, points[0]->point( value ));
+ rotatedSegment.setPoint( 1, points[0]->point( value+1 ) );
+ rotatedSegment.setPoint( 2, points[1]->point( value+1 ) );
+ rotatedSegment.setPoint( 3, points[1]->point( value ) );
+
+ //store the true points without rotation
+ QPointArray trueSegment( 4 );
+ trueSegment.setPoint( 0, oripoints[0]->point( value ));
+ trueSegment.setPoint( 1, oripoints[0]->point( value+1 ) );
+ trueSegment.setPoint( 2, oripoints[1]->point( value+1 ) );
+ trueSegment.setPoint( 3, oripoints[1]->point( value ) );
+
+ // calculate the rotated points position relative to each other
+ // we will then be able to keep the rotation ( see: project () )
+ // by reporting this position relative to the true segment line
+ //left side pt3 and pt0
+ int dx30 = rotatedSegment.point(3).x() - rotatedSegment.point(0).x();
+ int dy30 = rotatedSegment.point(3).y() - rotatedSegment.point(0).y();
+ //right side pt1 and pt2
+ int dx12 = rotatedSegment.point(2).x() - rotatedSegment.point(1).x();
+ int dy12 = rotatedSegment.point(2).y() - rotatedSegment.point(1).y();
+
+ // store and paint the "3D" segment
+ QPointArray segment( 4 );
+ segment.setPoint( 0, trueSegment.point(0) );
+ segment.setPoint( 1, trueSegment.point(1) );
+ segment.setPoint( 2, trueSegment.point(1).x() + dx12, trueSegment.point(1).y() + dy12 );
+ segment.setPoint( 3, trueSegment.point(0).x() + dx30, trueSegment.point(0).y() + dy30);
+
+
+ //PENDING Michel 3dlines drawing a segment with showThreeDLines
+ painter->drawPolygon( segment );
+
+
+ // } else
+ // qDebug( "Can't draw a segment in dataset %d from %d to %d", dataset, value, value+1 );
+ }
+ } else {
+ QPoint p1, p2;
+ // Note: If markers are drawn very near to each other
+ // and tiny markers are used
+ // we don't draw the connecting lines.
+ bool b4PMarkers = KDChartParams::LineMarker4Pixels == markerStyle;
+ bool bTinyMarkers =
+ KDChartParams::LineMarker1Pixel == markerStyle || b4PMarkers;
+ curPropSetId = KDChartPropertySet::UndefinedID;
+ painter->setPen( default2DPen );
+ for ( int value = 0; value < point-1; ++value ) {
+ p1 = points[0]->point( value );
+ p2 = points[0]->point( value+1 );
+
+ // Determine properties assigned to this cell
+ // and change the painter if necessarry:
+ currentDrawMarkers = defaultDrawMarkers;
+ int cellPropID;
+ if( data->cellProp( dataset, value, cellPropID ) &&
+ cellPropID != curPropSetId ){
+ if( cellPropID != KDChartPropertySet::UndefinedID &&
+ params()->calculateProperties( cellPropID,
+ curPropSet ) ){
+ curPropSetId = cellPropID;
+ }else{
+ curPropSetId = KDChartPropertySet::UndefinedID;
+ }
+ // preset with default values
+ int theLineWidth = default2DPen.width();
+ QColor theLineColor = default2DPen.color();
+ Qt::PenStyle theLineStyle = default2DPen.style();
+ if( curPropSetId != KDChartPropertySet::UndefinedID ){
+ // we can safely call the following functions and ignore their
+ // return values since they will touch the parameters' values
+ // if the propSet *contains* corresponding own values only.
+ int iDummy;
+ curPropSet.hasOwnLineWidth ( iDummy, theLineWidth );
+ curPropSet.hasOwnLineColor ( iDummy, theLineColor );
+ curPropSet.hasOwnLineStyle ( iDummy, theLineStyle );
+ curPropSet.hasOwnShowMarker( iDummy, currentDrawMarkers );
+ }
+ painter->setPen( QPen( theLineColor,
+ theLineWidth,
+ theLineStyle ) );
+ }
+
+ if( !currentDrawMarkers ){
+ //PENDING Michel: drawing a line - not currentMarkers
+ painter->drawLine( p1, p2 );
+ }else{
+ int dx = p2.x() - p1.x();
+ int dy = p2.y() - p1.y();
+ if( !bTinyMarkers || (abs(dx) > 4) || (abs(dy) > 4) ){
+ if( bTinyMarkers ) {
+ double m = !dx ? 100.0
+ : !dy ? 0.01
+ : ((double)dy / (double)dx);
+ double am = fabs(m);
+ int dxx;
+ int dyy;
+ if( 0.25 > am ){
+ dxx = 3;
+ dyy = 0;
+ }else if( 0.67 > am ){
+ dxx = 3;
+ dyy = 1;
+ }else if( 1.33 > am ){
+ dxx = 2;
+ dyy = 2;
+ }else if( 4.0 > am ){
+ dxx = 1;
+ dyy = 3;
+ }else{
+ dxx = 0;
+ dyy = 3;
+ }
+ if( 0 > dx )
+ dxx *= -1;
+ if( 0 > dy )
+ dyy *= -1;
+ if( b4PMarkers ){
+ if( 0 < dx )
+ ++p1.rx();
+ else if( 0 > dx )
+ ++p2.rx();
+ if( 0 < dy )
+ ++p1.ry();
+ else if( 0 > dy )
+ ++p2.ry();
+ }
+ p1.rx() += dxx; p1.ry() += dyy;
+ p2.rx() -= dxx; p2.ry() -= dyy;
+ }
+ //PENDING Michel: drawing a line - currentMarkers
+ painter->drawLine( p1, p2 );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Save point array for next way through (needed for e.g. stacked
+ // areas), not for 3D currently
+ points[0]->resize( point );
+ previousPoints = points[0]->copy();
+ }
+
+
+ // Now draw any extra lines (and/or their markers, resp.) that
+ // are to be printed IN FRONT of the normal lines:
+ if( mChartType == KDChartParams::Line ){
+ for( dataset = datasetEnd; ( dataset >= (int)datasetStart && dataset >= 0 ); --dataset ) {
+
+ const KDChartParams::LineMarkerStyle
+ defaultMarkerStyle = params()->lineMarkerStyle( dataset );
+ const QPen default2DPen( params()->lineColor().isValid()
+ ? params()->lineColor()
+ : params()->dataColor( dataset ),
+ params()->lineWidth(),
+ params()->lineStyle( dataset ) );
+
+ if( ai.bAbscissaHasTrueAxisDtValues )
+ ai.numValues = data->cols();
+
+ for ( int value = 0; value < ai.numValues; ++value ) {
+ int iVec = static_cast < int > ( datasetEnd-dataset ) * arrayNumValues + value;
+ if( allPoints[ iVec ].bValid ){
+ const MyPoint& mp = allPoints[iVec];
+ //qDebug("\np.x() %i p.y() %i", p.x(), p.y() );
+
+ // --------------------------------------------------------
+ // determine any 'extra' properties assigned to this cell
+ // by traversing the property set chain (if necessary)
+ // --------------------------------------------------------
+ int cellPropID;
+ if( data->cellProp( dataset, value, cellPropID ) &&
+ cellPropID != curPropSetId ){
+ if( cellPropID != KDChartPropertySet::UndefinedID &&
+ params()->calculateProperties( cellPropID,
+ curPropSet ) )
+ curPropSetId = cellPropID;
+ else
+ curPropSetId = KDChartPropertySet::UndefinedID;
+ }
+ if( curPropSetId != KDChartPropertySet::UndefinedID ){
+ drawExtraLinesAndMarkers(
+ curPropSet,
+ default2DPen,
+ defaultMarkerStyle,
+ mp.p.x(), mp.p.y(),
+ painter,
+ ai.abscissaPara,
+ ordinatePara,
+ logWidth/1000.0,
+ logHeight/1000.0,
+ true );
+ }
+ }
+ }
+ }
+ }
+ //qDebug(const_cast < KDChartParams* > ( params() )->properties( KDCHART_PROPSET_NORMAL_DATA )->name().latin1());
+ //qDebug(const_cast < KDChartParams* > ( params() )->properties( KDCHART_PROPSET_TRANSPARENT_DATA )->name().latin1());
+ //qDebug(const_cast < KDChartParams* > ( params() )->properties( KDCHART_PROPSET_HORI_LINE )->name().latin1());
+ //qDebug(const_cast < KDChartParams* > ( params() )->properties( KDCHART_PROPSET_VERT_LINE )->name().latin1());
+ //qDebug("--");
+ }