/* 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 info@klaralvdalens-datakonsult.se if any conditions of this ** licensing are not clear to you. ** **********************************************************************/ #include "KDChartLinesPainter.h" #include #include #include #if COMPAT_TQT_VERSION >= 0x030000 #include #else #include #endif #include /** \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 TQPainter 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( TQPainter* 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 TQPainter 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( TQPainter* 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. */ TQPoint KDChartLinesPainter::project( int x, int y, int z ) { double xrad = DEGTORAD( params()->threeDLineXRotation() ); double yrad = DEGTORAD( params()->threeDLineYRotation() ); TQPoint ret( static_cast( x*cos( yrad ) + z * sin( yrad ) ), static_cast( 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; } TQPoint p; bool bValid; bool bSkipThis; double cellValue; }; void KDChartLinesPainter::specificPaintData( TQPainter* painter, const TQRect& /*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;; //tqDebug("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 tqDebug( "Internal error in KDChartLinesPainter::paintDataInternal(): Unknown subtype" ); TQMap < 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; } TQMap < 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_TQT_VERSION >= 0x030000 TQValueVector allPoints( #else TQArray 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 TQPen default2DPen( params()->lineColor().isValid() ? params()->lineColor() : params()->dataColor( dataset ), params()->lineWidth(), params()->lineStyle( dataset ) ); if( ai.bAbscissaHasTrueAxisDtValues ) ai.numValues = data->cols(); TQVariant vValY; TQVariant 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 ) && TQVariant::Double == vValY.type() ) valueTotal += vValY.toDouble(); } } if( data->cellContent( dataset, value, vValY, vValX, cellPropID ) && TQVariant::Double == vValY.type() && ( !ai.bCellsHaveSeveralCoordinates || TQVariant::Invalid != vValX.type() ) ){ //tqDebug("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; //tqDebug("\nlogarithmic calc - cellValue: %f drawValue: %f", // cellValue, drawValue ); }else{ drawValue = cellValue * ordinatePixelsPerUnit * (bOrdinateDecreasing ? -1.0 : 1.0); //tqDebug("\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 = TQMIN( zeroXAxisI - drawValue, (logHeight + axisYOffset) * 3 ); pY = TQMAX( 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; //tqDebug("skipped"); }else{ // use typecast to make it compile on windows using qt232 allPoints[ static_cast < int > ( datasetEnd-dataset ) * arrayNumValues + value ].set( myPointX, myPointY, cellValue ); //tqDebug("ok"); } if( !skipMe ){ // -------------------------------------------------------- // determine any 'extra' properties assigned to this cell // by traversing the property set chain (if necessary) // -------------------------------------------------------- if( cellPropID != curPropSetId ){ //tqDebug("b. ( curPropSetId: %i )",curPropSetId); //tqDebug("b. cellPropID: %i",cellPropID); //tqDebug(curPropSet.name().latin1()); if( cellPropID != KDChartPropertySet::UndefinedID && params()->calculateProperties( cellPropID, curPropSet ) ){ curPropSetId = cellPropID; //tqDebug("c. curPropSetId: %i",curPropSetId); //tqDebug(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; } } } } TQPointArray 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 TQPen 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) TQPtrVector< TQPointArray > points( 2 ); points.setAutoDelete( true ); /* Pending Michel - we need to keep track of the * non rotated points for 3D lines */ TQPtrVector< TQPointArray > oripoints( 2 ); oripoints.setAutoDelete( true ); int i = 0; for( i = 0; i < 2; ++i ) { points.insert( i, new TQPointArray( ai.numValues + 2 ) ); oripoints.insert( i, new TQPointArray( 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]; //tqDebug("\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 = TQMAX(TQMIN(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 = TQt::AlignCenter; bool hasOwnSize = false; int theWidth = 0; int theHeight = 0; TQColor 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 ); TQSize 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, TQPoint(x,y), dataset, value, chart, regions, hasOwnSize ? &theWidth : 0, hasOwnSize ? &theHeight : 0, theAlignment ); } // store the region else if( regions ) { TQRect rect( TQPoint( x-1, y-1 ), TQPoint( x+1, y+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( TQPen( TQt::NoPen ) ); const TQBrush datasetBrush( params()->dataColor( dataset ), TQt::SolidPattern ); painter->setBrush( datasetBrush ); TQBrush 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 TQPoint 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(zeroXAxisI); else yCoord = static_cast(axisYOffset); // old: draw the complete area in on go: /* // no 3d handling for areas yet points[0]->setPoint( point, lastPoint.x(), yCoord ); point++; TQPoint 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 TQBrush 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 ); } TQPointArray 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; */ //tqDebug("\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. //tqDebug("222"); // no 3d handling for areas yet TQPointArray 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 ) ); //tqDebug("\nx: %i",previousPoints.point( previousPoints.size() - i - 1 ).x()); //tqDebug("y: %i",previousPoints.point( previousPoints.size() - i - 1 ).y()); } painter->drawPolygon( thisSection ); } // draw the line with no brush and outline color painter->setBrush( TQt::NoBrush ); painter->setPen( TQPen( 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( TQPen( 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( TQt::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() ) { // tqDebug( "Draw a segment in dataset %d from %d to %d", dataset, value, value+1 ); //store the rotated points ( see project() ) TQPointArray 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 TQPointArray 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 TQPointArray 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 // tqDebug( "Can't draw a segment in dataset %d from %d to %d", dataset, value, value+1 ); } } else { TQPoint 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(); TQColor 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( TQPen( 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 TQPen 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]; //tqDebug("\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 ); } } } } } //tqDebug(const_cast < KDChartParams* > ( params() )->properties( KDCHART_PROPSET_NORMAL_DATA )->name().latin1()); //tqDebug(const_cast < KDChartParams* > ( params() )->properties( KDCHART_PROPSET_TRANSPARENT_DATA )->name().latin1()); //tqDebug(const_cast < KDChartParams* > ( params() )->properties( KDCHART_PROPSET_HORI_LINE )->name().latin1()); //tqDebug(const_cast < KDChartParams* > ( params() )->properties( KDCHART_PROPSET_VERT_LINE )->name().latin1()); //tqDebug("--"); }