You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/kchart/kdchart/KDChartLinesPainter.cpp

945 lines
44 KiB

/* -*- 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 info@klaralvdalens-datakonsult.se if any conditions of this
** licensing are not clear to you.
**
**********************************************************************/
#include "KDChartLinesPainter.h"
#include <KDChartParams.h>
#include <KDChartPropertySet.h>
#include <tqpainter.h>
#if COMPAT_TQT_VERSION >= 0x030000
#include <tqvaluevector.h>
#else
#include <tqarray.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
// tqgeometry 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<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;
}
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;;
//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" );
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<MyPoint> allPoints(
#else
TQArray<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 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() ) ){
//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 = 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;
//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;
}
}
}
}
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 *tqcontains* 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 = 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 *tqcontains* 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<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++;
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 *tqcontains* 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;
*/
//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
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 ) );
//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( 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() ) {
// qDebug( "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
// qDebug( "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 *tqcontains* 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];
//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("--");
}