diff options
Diffstat (limited to 'libkdeedu/kdeeduplot')
-rw-r--r-- | libkdeedu/kdeeduplot/Makefile.am | 15 | ||||
-rw-r--r-- | libkdeedu/kdeeduplot/README | 23 | ||||
-rw-r--r-- | libkdeedu/kdeeduplot/kplotaxis.cpp | 32 | ||||
-rw-r--r-- | libkdeedu/kdeeduplot/kplotaxis.h | 86 | ||||
-rw-r--r-- | libkdeedu/kdeeduplot/kplotobject.cpp | 49 | ||||
-rw-r--r-- | libkdeedu/kdeeduplot/kplotobject.h | 205 | ||||
-rw-r--r-- | libkdeedu/kdeeduplot/kplotwidget.cpp | 405 | ||||
-rw-r--r-- | libkdeedu/kdeeduplot/kplotwidget.h | 263 |
8 files changed, 1078 insertions, 0 deletions
diff --git a/libkdeedu/kdeeduplot/Makefile.am b/libkdeedu/kdeeduplot/Makefile.am new file mode 100644 index 00000000..639ea788 --- /dev/null +++ b/libkdeedu/kdeeduplot/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES= $(all_includes) +SUBDIRS = . + +lib_LTLIBRARIES = libkdeeduplot.la + +libkdeeduplotincludedir = $(includedir)/libkdeedu +libkdeeduplotinclude_HEADERS = kplotobject.h kplotaxis.h kplotwidget.h + +libkdeeduplot_la_SOURCES = kplotobject.cpp kplotaxis.cpp kplotwidget.cpp + +libkdeeduplot_la_LDFLAGS = $(all_libraries) -no-undefined -version-info 3:0:2 +libkdeeduplot_la_LIBADD = $(LIB_KDECORE) $(LIB_QT) + +METASOURCES = AUTO + diff --git a/libkdeedu/kdeeduplot/README b/libkdeedu/kdeeduplot/README new file mode 100644 index 00000000..ab695f9a --- /dev/null +++ b/libkdeedu/kdeeduplot/README @@ -0,0 +1,23 @@ +This library provides KPlotWidget and KPlotObject classes. + +KPlotWidget is a QWidget-derived class that provides a virtual baseclass +for easy data-plotting. The idea behind KPlotWidget is that you only have +to specify information in "data units"; i.e., the natural units of the +data being plotted. KPlotWidget automatically converts everything +to screen pixel units. + +KPlotWidget draws X and Y axes with tickmarks and tick labels. It +automatically determines how many tickmarks to use and where they should +be, based on the data limits specified for the plot. You change the limits +by calling setLimits( double x1, double x2, double y1, double y2 ), and +then calling updateTickmarks() to recompute the positions of tickmarks +and ticklabels. + +Data to be plotted are stored using the KPlotObject class. KPlotObject +consists of a QPtrList of DPoints, each specifying the X,Y coordinates +of a data point (DPoint is like QPoint, but the X and Y values are doubles +instead of ints). KPlotObject also specifies the "type" of data to be +plotted (POINTS or CURVE or POLYGON or LABEL). + +Jason Harris +kstars@30doradus.org diff --git a/libkdeedu/kdeeduplot/kplotaxis.cpp b/libkdeedu/kdeeduplot/kplotaxis.cpp new file mode 100644 index 00000000..0099e0c2 --- /dev/null +++ b/libkdeedu/kdeeduplot/kplotaxis.cpp @@ -0,0 +1,32 @@ +/*************************************************************************** + kplotaxis.cpp - An axis for the plot widget + ------------------- + begin : 16 June 2005 + copyright : (C) 2005 by Andreas Nicolai + email : Andreas.Nicolai@gmx.net + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include <qpainter.h> +#include <kdebug.h> +#include <klocale.h> +#include "kplotaxis.h" + +KPlotAxis::KPlotAxis() : m_visible(true), m_labelFieldWidth(0), m_labelFmt('g'), + m_labelPrec(2) +{ +} + +KPlotAxis::KPlotAxis(const QString& label) : m_visible(true), m_label(label), + m_labelFieldWidth(0), m_labelFmt('g'), m_labelPrec(2) +{ +} + diff --git a/libkdeedu/kdeeduplot/kplotaxis.h b/libkdeedu/kdeeduplot/kplotaxis.h new file mode 100644 index 00000000..3a82417c --- /dev/null +++ b/libkdeedu/kdeeduplot/kplotaxis.h @@ -0,0 +1,86 @@ +/*************************************************************************** + kplotaxis.h - An axis for the plot widget + ------------------- + begin : 16 June 2005 + copyright : (C) 2005 by Andreas Nicolai + email : Andreas.Nicolai@gmx.net + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KPLOTAXIS_H +#define KPLOTAXIS_H + +#include <qstring.h> + +#include <kdemacros.h> + +/**class KPlotAxis + *@short Contains all data for drawing an axis including format specification axis labels. + *@author Andreas Nicolai + *@version 1.0 + */ +class KDE_EXPORT KPlotAxis { +public: + + /**@short Default constructor, creates a default axis. */ + KPlotAxis(); + + /**@short Constructor, constructs a labeled axis. */ + KPlotAxis(const QString& label); + + /**@short Destructor. */ + virtual ~KPlotAxis() {} + + /**@short Returns whether the axis is visible or not. */ + virtual bool isVisible() const { return m_visible; } + + /**@short Sets the "visible" property of the axis. */ + virtual void setVisible(bool visible) { m_visible = visible; } + + /**@short Shows the axis (axis will be shown at next update of plot widget). */ + virtual void show() { m_visible = true; } + + /**@short Hides the axis (axis will be hidden at next update of plot widget). */ + virtual void hide() { m_visible = false; } + + /**@short Sets the axis label. + *@param label A short string describing the data plotted on the axis. + *Set the label to an empty string to omit the axis label. + */ + virtual void setLabel( const QString& label ) { m_label = label; } + + /**@short Returns the axis label. */ + virtual QString label() const { return m_label; } + + /**@short Set the number format for the tick labels, see QString::arg() for + description of arguments. + */ + virtual void setLabelFormat(int fieldWidth, char fmt = 'g', int prec=-1) { + m_labelFieldWidth = fieldWidth; m_labelFmt = fmt; m_labelPrec = prec; } + + /**@short Returns the field width of the tick labels. */ + virtual int labelFieldWidth() const { return m_labelFieldWidth; } + + /**@short Returns the number format of the tick labels. */ + virtual char labelFmt() const { return m_labelFmt; } + + /**@short Returns the number precision of the tick labels. */ + virtual int labelPrec() const { return m_labelPrec; } + +private: + bool m_visible; ///< Property "visible" defines if Axis is drawn or not. + QString m_label; ///< The label of the axis. + int m_labelFieldWidth; ///< Field width for number labels, see QString::arg(). + char m_labelFmt; ///< Number format for number labels, see QString::arg(). + int m_labelPrec; ///< Number precision for number labels, see QString::arg(). +}; + +#endif // KPLOTAXIS_H diff --git a/libkdeedu/kdeeduplot/kplotobject.cpp b/libkdeedu/kdeeduplot/kplotobject.cpp new file mode 100644 index 00000000..145f12c3 --- /dev/null +++ b/libkdeedu/kdeeduplot/kplotobject.cpp @@ -0,0 +1,49 @@ +/*************************************************************************** + kplotobject.cpp - A list of points to be plotted + ------------------- + begin : Sun 18 May 2003 + copyright : (C) 2003 by Jason Harris + email : kstars@30doradus.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include <qpainter.h> +#include <kdebug.h> +#include "kplotobject.h" + +KPlotObject::KPlotObject() { + KPlotObject( "", "white", POINTS ); +} + +KPlotObject::KPlotObject( const QString &n, const QString &c, PTYPE t, unsigned int s, unsigned int p ) { + //We use the set functions because they may include data validation + setName( n ); + setColor( c ); + setType( t ); + setSize( s ); + setParam( p ); + + pList.setAutoDelete( true ); +} + +KPlotObject::~KPlotObject() +{ +} + +void KPlotObject::removePoint( unsigned int index ) { + if ( index > pList.count() - 1 ) { + kdWarning() << "Ignoring attempt to remove non-existent plot object" << endl; + return; + } + + pList.remove( index ); +} + diff --git a/libkdeedu/kdeeduplot/kplotobject.h b/libkdeedu/kdeeduplot/kplotobject.h new file mode 100644 index 00000000..52d79540 --- /dev/null +++ b/libkdeedu/kdeeduplot/kplotobject.h @@ -0,0 +1,205 @@ +/*************************************************************************** + kplotobject.h - A list of points to be plotted + ------------------- + begin : Sun 18 May 2003 + copyright : (C) 2003 by Jason Harris + email : kstars@30doradus.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KPLOTOBJECT_H +#define KPLOTOBJECT_H + + +class QString; +class QPainter; + +#include <kdemacros.h> + +/**class DRect + *@short equivalent of QRect with double x,y coordinates + *@author Jason Harris + *@version 1.0 + */ +class KDE_EXPORT DRect { +public: + DRect() { DRect( 0.0, 0.0, 1.0, 1.0 ); } + DRect( double x, double y, double w, double h ) { X = x; Y = y; W = w; H = h; } + ~DRect() {} + + double x() const { return X; } + double y() const { return Y; } + double x2() const { return X + W; } + double y2() const { return Y + H; } + double width() const { return W; } + double height() const { return H; } + + void setX( double x ) { X = x; } + void setY( double y ) { Y = y; } + void setWidth( double w ) { W = w; } + void setHeight( double h ) { H = h; } + +private: + double X,Y,W,H; +}; + +/**class DPoint + *@short equivalent of QPoint with double x,y coordinates + *@author Jason Harris + *@version 1.0 + */ +class KDE_EXPORT DPoint { +public: + DPoint() { DPoint( 0.0, 0.0 ); } + DPoint( double x, double y ) { setX( x ); setY( y ); } + ~DPoint() {} + + double x() const { return X; } + double y() const { return Y; } + + QPoint qpoint( QRect pb, DRect db ) { + int px = pb.left() + int( pb.width()*( x() - db.x() )/db.width() ); + int py = pb.top() + int( pb.height()*( db.y2() - y() )/db.height() ); + return QPoint( px, py ); + } + + void setX( double x ) { X = x; } + void setY( double y ) { Y = y; } + +private: + double X, Y; +}; + +/**@class KPlotObject + *@short Encapsulates an object to be plotted in a KPlotWidget. + *@author Jason Harris + *@version 1.0 + *Each KPlotObject consists of a list of QPoints, an object type, a color, a size, + *and a QString name. An additional integer (param) specifies something further + *about the object's appearance, depending on its type. There is a draw function + *for plotting the object on a KPlotWidget's QPainter. + */ +class KDE_EXPORT KPlotObject{ +public: +/**@enum PTYPE + *The Type classification of the KPlotObject + */ + enum PTYPE { POINTS=0, CURVE=1, LABEL=2, POLYGON=3, UNKNOWN_TYPE }; + +/**@enum PPARAM + *Parameter specifying the kind of points + */ + enum PPARAM { DOT=0, CIRCLE=1, SQUARE=2, LETTER=3, UNKNOWN_POINT }; + +/**@enum CPARAM + *Parameter specifying the kind of line. These are numerically equal to + *the Qt::PenStyle enum values. + */ + enum CPARAM { NO_LINE=0, SOLID=1, DASHED=2, DOTTED=3, DASHDOTTED=4, DASHDOTDOTTED=5, UNKNOWN_CURVE }; + +/**Default constructor. Create a POINTS-type object with an empty list of points. + */ + KPlotObject(); + +/**Constructor. Create a KPlotObject according to the arguments. + */ + KPlotObject( const QString &name, const QString &color, PTYPE otype, unsigned int size=2, unsigned int param=0 ); + +/**Destructor (empty) + */ + ~KPlotObject(); + +/**@return the KPlotObject's Name + */ + QString name() const { return Name; } + +/**@short set the KPlotObject's Name + *@param n the new name + */ + void setName( const QString &n ) { Name = n; } + +/**@return the KPlotObject's Color + */ + QString color() const { return Color; } + +/**@short set the KPlotObject's Color + *@param c the new color + */ + void setColor( const QString &c ) { Color = c; } + +/**@return the KPlotObject's Type + */ + PTYPE type() const { return Type; } + +/**@short set the KPlotObject's Type + *@param t the new type + */ + void setType( PTYPE t ) { Type = t; } + +/**@return the KPlotObject's Size + */ + unsigned int size() const { return Size; } + +/**@short set the KPlotObject's Size + *@param s the new size + */ + void setSize( unsigned int s ) { Size = s; } + +/**@return the KPlotObject's type-specific Parameter + *Parameter is an unsigned int because it can either be a PPARAM or a CPARAM enum. + */ + unsigned int param() const { return Parameter; } + +/**@short set the KPlotObject's type-specific Parameter + *@param p the new parameter + *Parameter is an unsigned int because it can either be a PPARAM or a CPARAM enum. + */ + void setParam( unsigned int p ) { Parameter = p; } + +/**@return a pointer to the DPoint at position i + *@param i the index of the desired point. + */ + DPoint* point( unsigned int i ) { return pList.at(i); } + + QPtrList<DPoint>* points() { return &pList; } + +/**@short Add a point to the object's list. + *@param p the DPoint to add. + */ + void addPoint( const DPoint &p ) { pList.append( new DPoint( p.x(), p.y() ) ); } + +/**@short Add a point to the object's list. This is an overloaded function, + *provided for convenience. It behaves essentially like the above function. + *@param p pointer to the DPoint to add. + */ + void addPoint( DPoint *p ) { pList.append( p ); } + +/**@short remove the QPoint at position index from the list of points + *@param index the index of the point to be removed. + */ + void removePoint( unsigned int index ); + +/**@return the number of QPoints currently in the list + */ + unsigned int count() const { return pList.count(); } + +/**@short clear the Object's points list + */ + void clearPoints() { pList.clear(); } + +private: + QPtrList<DPoint> pList; + PTYPE Type; + unsigned int Size, Parameter; + QString Color, Name; +}; + +#endif diff --git a/libkdeedu/kdeeduplot/kplotwidget.cpp b/libkdeedu/kdeeduplot/kplotwidget.cpp new file mode 100644 index 00000000..e2103509 --- /dev/null +++ b/libkdeedu/kdeeduplot/kplotwidget.cpp @@ -0,0 +1,405 @@ +/*************************************************************************** + kplotwidget.cpp - A widget for plotting in KStars + ------------------- + begin : Sun 18 May 2003 + copyright : (C) 2003 by Jason Harris + email : kstars@30doradus.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include <math.h> //for log10(), pow(), modf() +#include <kdebug.h> +#include <qpainter.h> +#include <qpixmap.h> + +#include "kplotwidget.h" +#include "kplotwidget.moc" + +KPlotWidget::KPlotWidget( double x1, double x2, double y1, double y2, QWidget *parent, const char* name ) + : QWidget( parent, name, WNoAutoErase ), + dXtick(0.0), dYtick(0.0), + nmajX(0), nminX(0), nmajY(0), nminY(0), + ShowTickMarks( true ), ShowTickLabels( true ), ShowGrid( false ) + { + setBackgroundMode( QWidget::NoBackground ); + + //set DataRect + setLimits( x1, x2, y1, y2 ); + setDefaultPadding(); + + //Set PixRect (starts at (0,0) because we will translate by leftPadding(), topPadding() ) + PixRect = QRect( 0, 0, width() - leftPadding() - rightPadding(), + height() - topPadding() - bottomPadding() ); + + buffer = new QPixmap(); + + //default colors: + setBGColor( QColor( "black" ) ); + setFGColor( QColor( "white" ) ); + setGridColor( QColor( "grey" ) ); + + ObjectList.setAutoDelete( true ); +} + +KPlotWidget::~KPlotWidget() +{ + delete (buffer); +} + +void KPlotWidget::setLimits( double x1, double x2, double y1, double y2 ) { + double XA1, XA2, YA1, YA2; + if (x2<x1) { XA1=x2; XA2=x1; } + else { XA1=x1; XA2=x2; } + if ( y2<y1) { YA1=y2; YA2=y1; } + else { YA1=y1; YA2=y2; } + + DataRect = DRect( XA1, YA1, XA2-XA1, YA2-YA1 ); + updateTickmarks(); +} + +void KPlotWidget::updateTickmarks() { + // Determine the number and spacing of tickmarks for the current plot limits. + if ( dataWidth() == 0.0 ) { + kdWarning() << "X range invalid! " << x() << " to " << x2() << endl; + DataRect.setWidth( 1.0 ); + return; + } + if ( dataHeight() == 0.0 ) { + kdWarning() << "Y range invalid! " << y() << " to " << y2() << endl; + DataRect.setHeight( 1.0 ); + return; + } + + int nmajor(0), nminor(0); + double z(0.0), z2(0.0); + double Range(0.0), s(0.0), t(0.0), pwr(0.0), dTick(0.0); + + //loop over X and Y axes...the z variables substitute for either X or Y + for ( unsigned int iaxis=0; iaxis<2; ++iaxis ) { + if ( iaxis == 1 ) { + z = x(); z2 = x2(); + } else { + z = y(); z2 = y2(); + } + + //determine size of region to be drawn, in draw units + Range = z2 - z; + + //s is the power-of-ten factor of Range: + //Range = t * s; s = 10^(pwr). e.g., Range=350.0 then t=3.5, s = 100.0; pwr = 2.0 + modf( log10(Range), &pwr ); + s = pow( 10.0, pwr ); + t = Range/s; + + //adjust s and t such that t is between 3 and 5: + if ( t < 3.0 ) { t *= 10.0; s /= 10.0; } //t now btwn 3 and 30 + if ( t < 6.0 ) { //accept current values + dTick = s; + nmajor = int(t); + nminor = 5; + } else if ( t < 10.0 ) { //factor of 2 + dTick = s*2.0; + nmajor = int(t/2.0); + nminor = 4; + } else if ( t < 20.0 ) { //factor of 4 + dTick = s*4.0; + nmajor = int(t/4.0); + nminor = 4; + } else { //factor of 5 + dTick = s*5.0; + nmajor = int(t/5.0); + nminor = 5; + } + + if ( iaxis==1 ) { //X axis + nmajX = nmajor; + nminX = nminor; + dXtick = dTick; + } else { //Y axis + nmajY = nmajor; + nminY = nminor; + dYtick = dTick; + } + } //end for iaxis +} + +void KPlotWidget::resizeEvent( QResizeEvent* /* e */ ) { + int newWidth = width() - leftPadding() - rightPadding(); + int newHeight = height() - topPadding() - bottomPadding(); + PixRect = QRect( 0, 0, newWidth, newHeight ); + + buffer->resize( width(), height() ); +} + +void KPlotWidget::paintEvent( QPaintEvent* /* e */ ) { + QPainter p; + + p.begin( buffer ); + p.fillRect( 0, 0, width(), height(), bgColor() ); + p.translate( leftPadding(), topPadding() ); + + drawObjects( &p ); + drawBox( &p ); + + p.end(); + bitBlt( this, 0, 0, buffer ); +} + +void KPlotWidget::drawObjects( QPainter *p ) { + for ( KPlotObject *po = ObjectList.first(); po; po = ObjectList.next() ) { + + if ( po->points()->count() ) { + //draw the plot object + p->setPen( QColor( po->color() ) ); + + switch ( po->type() ) { + case KPlotObject::POINTS : + { + p->setBrush( QColor( po->color() ) ); + + for ( DPoint *dp = po->points()->first(); dp; dp = po->points()->next() ) { + QPoint q = dp->qpoint( PixRect, DataRect ); + int x1 = q.x() - po->size()/2; + int y1 = q.y() - po->size()/2; + + switch( po->param() ) { + case KPlotObject::CIRCLE : p->drawEllipse( x1, y1, po->size(), po->size() ); break; + case KPlotObject::SQUARE : p->drawRect( x1, y1, po->size(), po->size() ); break; + case KPlotObject::LETTER : p->drawText( q, po->name().left(1) ); break; + default: p->drawPoint( q ); + } + } + + p->setBrush( Qt::NoBrush ); + break; + } + + case KPlotObject::CURVE : + { + p->setPen( QPen( QColor( po->color() ), po->size(), (QPen::PenStyle)po->param() ) ); + DPoint *dp = po->points()->first(); + p->moveTo( dp->qpoint( PixRect, DataRect ) ); + for ( dp = po->points()->next(); dp; dp = po->points()->next() ) + p->lineTo( dp->qpoint( PixRect, DataRect ) ); + break; + } + + case KPlotObject::LABEL : //draw label centered at point in x, and slightly below point in y. + { + QPoint q = po->points()->first()->qpoint( PixRect, DataRect ); + p->drawText( q.x()-20, q.y()+6, 40, 10, Qt::AlignCenter | Qt::DontClip, po->name() ); + break; + } + + case KPlotObject::POLYGON : + { + p->setPen( QPen( QColor( po->color() ), po->size(), (QPen::PenStyle)po->param() ) ); + p->setBrush( po->color() ); + + QPointArray a( po->count() ); + + unsigned int i=0; + for ( DPoint *dp = po->points()->first(); dp; dp = po->points()->next() ) + a.setPoint( i++, dp->qpoint( PixRect, DataRect ) ); + + p->drawPolygon( a ); + break; + } + + case KPlotObject::UNKNOWN_TYPE : break; + } + } + } +} + +double KPlotWidget::dmod( double a, double b ) { return ( b * ( ( a / b ) - int( a / b ) ) ); } + +void KPlotWidget::drawBox( QPainter *p ) { + //First, fill in padding region with bgColor() to mask out-of-bounds plot data + p->setPen( bgColor() ); + p->setBrush( bgColor() ); + + //left padding ( don't forget: we have translated by XPADDING, YPADDING ) + p->drawRect( -leftPadding(), -topPadding(), leftPadding(), height() ); + + //right padding + p->drawRect( PixRect.width(), -topPadding(), rightPadding(), height() ); + + //top padding + p->drawRect( 0, -topPadding(), PixRect.width(), topPadding() ); + + //bottom padding + p->drawRect( 0, PixRect.height(), PixRect.width(), bottomPadding() ); + + if ( ShowGrid ) { + //Grid lines are placed at locations of primary axes' major tickmarks + p->setPen( gridColor() ); + + //vertical grid lines + double x0 = x() - dmod( x(), dXtick ); //zeropoint; x(i) is this plus i*dXtick1 + for ( int ix = 0; ix <= nmajX+1; ix++ ) { + int px = int( PixRect.width() * ( (x0 + ix*dXtick - x())/dataWidth() ) ); + p->drawLine( px, 0, px, PixRect.height() ); + } + + //horizontal grid lines + double y0 = y() - dmod( y(), dYtick ); //zeropoint; y(i) is this plus i*mX + for ( int iy = 0; iy <= nmajY+1; iy++ ) { + int py = PixRect.height() - int( PixRect.height() * ( (y0 + iy*dYtick - y())/dataHeight() ) ); + p->drawLine( 0, py, PixRect.width(), py ); + } + } + + p->setPen( fgColor() ); + p->setBrush( Qt::NoBrush ); + + if (BottomAxis.isVisible() || LeftAxis.isVisible()) p->drawRect( PixRect ); //box outline + + if ( ShowTickMarks ) { + //spacing between minor tickmarks (in data units) + double dminX = dXtick/nminX; + double dminY = dYtick/nminY; + + //set small font for tick labels + QFont f = p->font(); + int s = f.pointSize(); + f.setPointSize( s - 2 ); + p->setFont( f ); + + //--- Draw bottom X Axis ---// + if (BottomAxis.isVisible()) { + // Draw X tickmarks + double x0 = x() - dmod( x(), dXtick ); //zeropoint; tickmark i is this plus i*dXtick (in data units) + if ( x() < 0.0 ) x0 -= dXtick; + + for ( int ix = 0; ix <= nmajX+1; ix++ ) { + //position of tickmark i (in screen units) + int px = int( PixRect.width() * ( (x0 + ix*dXtick - x() )/dataWidth() ) ); + + if ( px > 0 && px < PixRect.width() ) { + p->drawLine( px, PixRect.height() - 2, px, PixRect.height() - BIGTICKSIZE - 2 ); + p->drawLine( px, 0, px, BIGTICKSIZE ); + } + + //tick label + if ( ShowTickLabels ) { + double lab = x0 + ix*dXtick; + if ( fabs(lab)/dXtick < 0.00001 ) lab = 0.0; //fix occassional roundoff error with "0.0" label + + QString str = QString( "%1" ).arg( lab, BottomAxis.labelFieldWidth(), BottomAxis.labelFmt(), BottomAxis.labelPrec() ); + if ( px > 0 && px < PixRect.width() ) { + QRect r( px - BIGTICKSIZE, PixRect.height()+BIGTICKSIZE, 2*BIGTICKSIZE, BIGTICKSIZE ); + p->drawText( r, Qt::AlignCenter | Qt::DontClip, str ); + } + } + + //draw minor ticks + for ( int j=0; j < nminX; j++ ) { + //position of minor tickmark j (in screen units) + int pmin = int( px + PixRect.width()*j*dminX/dataWidth() ); + + if ( pmin > 0 && pmin < PixRect.width() ) { + p->drawLine( pmin, PixRect.height() - 2, pmin, PixRect.height() - SMALLTICKSIZE - 2 ); + p->drawLine( pmin, 0, pmin, SMALLTICKSIZE ); + } + } + } // end draw X tickmarks + + // Draw X Axis Label + if ( ! BottomAxis.label().isEmpty() ) { + QRect r( 0, PixRect.height() + 2*YPADDING, PixRect.width(), YPADDING ); + p->drawText( r, Qt::AlignCenter, BottomAxis.label() ); + } + + } + + //--- Draw left Y Axis ---// + if (LeftAxis.isVisible()) { + // Draw Y tickmarks + double y0 = y() - dmod( y(), dYtick ); //zeropoint; tickmark i is this plus i*dYtick1 (in data units) + if ( y() < 0.0 ) y0 -= dYtick; + + for ( int iy = 0; iy <= nmajY+1; iy++ ) { + //position of tickmark i (in screen units) + int py = PixRect.height() - int( PixRect.height() * ( (y0 + iy*dYtick - y())/dataHeight() ) ); + if ( py > 0 && py < PixRect.height() ) { + p->drawLine( 0, py, BIGTICKSIZE, py ); + p->drawLine( PixRect.width()-2, py, PixRect.width()-BIGTICKSIZE-2, py ); + } + + //tick label + if ( ShowTickLabels ) { + double lab = y0 + iy*dYtick; + if ( fabs(lab)/dYtick < 0.00001 ) lab = 0.0; //fix occassional roundoff error with "0.0" label + + QString str = QString( "%1" ).arg( lab, LeftAxis.labelFieldWidth(), LeftAxis.labelFmt(), LeftAxis.labelPrec() ); + if ( py > 0 && py < PixRect.height() ) { + QRect r( -2*BIGTICKSIZE, py-SMALLTICKSIZE, 2*BIGTICKSIZE, 2*SMALLTICKSIZE ); + p->drawText( r, Qt::AlignCenter | Qt::DontClip, str ); + } + } + + //minor ticks + for ( int j=0; j < nminY; j++ ) { + //position of minor tickmark j (in screen units) + int pmin = int( py - PixRect.height()*j*dminY/dataHeight() ); + if ( pmin > 0 && pmin < PixRect.height() ) { + p->drawLine( 0, pmin, SMALLTICKSIZE, pmin ); + p->drawLine( PixRect.width()-2, pmin, PixRect.width()-SMALLTICKSIZE-2, pmin ); + } + } + } // end draw Y tickmarks + + //Draw Y Axis Label. We need to draw the text sideways. + if ( ! LeftAxis.label().isEmpty() ) { + //store current painter translation/rotation state + p->save(); + + //translate coord sys to left corner of axis label rectangle, then rotate 90 degrees. + p->translate( -3*XPADDING, PixRect.height() ); + p->rotate( -90.0 ); + + QRect r( 0, 0, PixRect.height(), XPADDING ); + p->drawText( r, Qt::AlignCenter, LeftAxis.label() ); //draw the label, now that we are sideways + + p->restore(); //restore translation/rotation state + } + } + + } //end if ( ShowTickMarks ) + + +} + +int KPlotWidget::leftPadding() const { + if ( LeftPadding >= 0 ) return LeftPadding; + if ( ! LeftAxis.label().isEmpty() && ShowTickLabels ) return 3*XPADDING; + if ( ! LeftAxis.label().isEmpty() || ShowTickLabels ) return 2*XPADDING; + return XPADDING; +} + +int KPlotWidget::rightPadding() const { + if ( RightPadding >= 0 ) return RightPadding; + return XPADDING; +} + +int KPlotWidget::topPadding() const { + if ( TopPadding >= 0 ) return TopPadding; + return YPADDING; +} + +int KPlotWidget::bottomPadding() const { + if ( BottomPadding >= 0 ) return BottomPadding; + if ( ! BottomAxis.label().isEmpty() && ShowTickLabels ) return 3*YPADDING; + if ( ! BottomAxis.label().isEmpty() || ShowTickLabels ) return 2*YPADDING; + return YPADDING; +} + diff --git a/libkdeedu/kdeeduplot/kplotwidget.h b/libkdeedu/kdeeduplot/kplotwidget.h new file mode 100644 index 00000000..b24e31bf --- /dev/null +++ b/libkdeedu/kdeeduplot/kplotwidget.h @@ -0,0 +1,263 @@ +/*************************************************************************** + kplotwidget.h - A generic data-plotting widget + ------------------- + begin : Sun 18 May 2003 + copyright : (C) 2003 by Jason Harris + email : kstars@30doradus.org + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef _KPLOTWIDGET_H_ +#define _KPLOTWIDGET_H_ + +#include <qwidget.h> +#include "kplotobject.h" +#include "kplotaxis.h" + +#define BIGTICKSIZE 10 +#define SMALLTICKSIZE 4 +#define XPADDING 20 +#define YPADDING 20 + +class QColor; +class QPixmap; + +/**@class KPlotWidget + *@short Genric data plotting widget. + *@author Jason Harris + *@version 1.1 + *Widget for drawing plots. Includes adjustable axes (KPlotAxis) with + *tickmarks and labels and a list of KPlotObjects to be drawn. + */ + +class KDE_EXPORT KPlotWidget : public QWidget { + Q_OBJECT +public: + /**@short Constructor. Sets the primary x and y limits in data units. + *@param x1 the minimum X value in data units + *@param x2 the maximum X value in data units + *@param y1 the minimum Y value in data units + *@param y2 the maximum Y value in data units + *@param parent the parent widget + *@param name name label for the KPlotWidget + */ + KPlotWidget( double x1=0.0, double x2=1.0, double y1=0.0, double y2=1.0, QWidget *parent=0, const char* name=0 ); + + /**Destructor (empty) + */ + virtual ~KPlotWidget(); + + /**@short Determine the placement of major and minor tickmarks, + *based on the current Limit settings + */ + virtual void updateTickmarks(); + + /**@short Reset the data limits. + *@param x1 the minimum X value in data units + *@param x2 the maximum X value in data units + *@param y1 the minimum Y value in data units + *@param y2 the maximum Y value in data units + */ + virtual void setLimits( double x1, double x2, double y1, double y2 ); + + /**@return the minimum X value in data units*/ + virtual double x() const { return DataRect.x(); } + + /**@return the maximum X value in data units*/ + virtual double x2() const { return DataRect.x2(); } + + /**@return the minimum Y value in data units*/ + virtual double y() const { return DataRect.y(); } + + /**@return the maximum Y value in data units*/ + virtual double y2() const { return DataRect.y2(); } + + /**@return the width in data units*/ + virtual double dataWidth() const { return DataRect.width(); } + + /**@return the height in data units*/ + virtual double dataHeight() const { return DataRect.height(); } + + /**@short Add an item to the list of KPlotObjects to be plotted. + *@param o pointer to the KPlotObject to be added + */ + virtual void addObject( KPlotObject *o ) { ObjectList.append( o ); } + + /**@short Remove all items from the list of KPlotObjects + */ + virtual void clearObjectList() { ObjectList.clear(); update(); } + + /**@short replace an item in the KPlotObject list. + *@param i the index of th item to be replaced + *@param o pointer to the replacement KPlotObject + */ + virtual void replaceObject( int i, KPlotObject *o ) { ObjectList.replace( i, o ); } + + /**@return the number of KPlotObjects in the list + */ + virtual int objectCount() const { return ObjectList.count(); } + + /**@return a pointer to a specific KPlotObject in the list + *@param i the index of the desired KPlotObject + */ + virtual KPlotObject *object( int i ) { return ObjectList.at(i); } + + /**@return the background color */ + virtual QColor bgColor() const { return cBackground; } + + /**@return the foreground color */ + virtual QColor fgColor() const { return cForeground; } + + /**@return the grid color */ + virtual QColor gridColor() const { return cGrid; } + + /**@short set the background color + *@param bg the new background color + */ + virtual void setBGColor( const QColor &bg ) { cBackground = bg; setBackgroundColor( bg ); } + + /**@short set the foreground color + *@param fg the new foreground color + */ + virtual void setFGColor( const QColor &fg ) { cForeground = fg; } + + /**@short set the grid color + *@param gc the new grid color + */ + virtual void setGridColor( const QColor &gc ) { cGrid = gc; } + + /**@short toggle whether plot axes are drawn. + *@param show if true, axes will be drawn. + *The axes are just a box outline around the plot. + */ + virtual void setShowAxes( bool show ) { BottomAxis.setVisible(show); LeftAxis.setVisible(show); } + /**@short toggle whether tick marks are drawn along the axes. + *@param show if true, tick marks will be drawn. + */ + virtual void setShowTickMarks( bool show ) { ShowTickMarks = show; } + /**@short toggle whether tick labels are drawn at major tickmarks. + *@param show if true, tick labels will be drawn. + */ + virtual void setShowTickLabels( bool show ) { ShowTickLabels = show; } + /**@short toggle whether grid lines are drawn at major tickmarks. + *@param show if true, grid lines will be drawn. + */ + virtual void setShowGrid( bool show ) { ShowGrid = show; } + + /**@short (Deprecated) Sets the X-axis label + *@param xlabel a short string describing the data plotted on the x-axis. + *Set the label to an empty string to omit the axis label. + *This function is deprecated, set the label property in the BottomAxis directly. + */ + virtual void setXAxisLabel( QString xlabel ) { BottomAxis.setLabel(xlabel); } + /**@short (Deprecated) Sets the Y-axis label + *@param ylabel a short string describing the data plotted on the y-axis. + *Set the label to an empty string to omit the axis label. + *This function is deprecated, set the label property in the LeftAxis directly. + */ + virtual void setYAxisLabel( QString ylabel ) { LeftAxis.setLabel(ylabel); } + + /**@returns the number of pixels to the left of the plot area. Padding values + *are set to -1 by default; if unchanged, this function will try to guess + *a good value, based on whether ticklabels and/or axis labels are to be drawn. + */ + virtual int leftPadding() const; + /**@returns the number of pixels to the right of the plot area. + *Padding values are set to -1 by default; if unchanged, this function will try to guess + *a good value, based on whether ticklabels and/or axis labels are to be drawn. + */ + virtual int rightPadding() const; + /**@returns the number of pixels above the plot area. + *Padding values are set to -1 by default; if unchanged, this function will try to guess + *a good value, based on whether ticklabels and/or axis labels are to be drawn. + */ + virtual int topPadding() const; + /**@returns the number of pixels below the plot area. + *Padding values are set to -1 by default; if unchanged, this function will try to guess + *a good value, based on whether ticklabels and/or axis labels are to be drawn. + */ + virtual int bottomPadding() const; + + /**@short set the number of pixels to the left of the plot area. + *Set this to -1 to revert to automatic determination of padding values. + */ + virtual void setLeftPadding( int pad ) { LeftPadding = pad; } + /**@short set the number of pixels to the right of the plot area. + *Set this to -1 to revert to automatic determination of padding values. + */ + virtual void setRightPadding( int pad ) { RightPadding = pad; } + /**@short set the number of pixels above the plot area. + *Set this to -1 to revert to automatic determination of padding values. + */ + virtual void setTopPadding( int pad ) { TopPadding = pad; } + /**@short set the number of pixels below the plot area. + *Set this to -1 to revert to automatic determination of padding values. + */ + virtual void setBottomPadding( int pad ) { BottomPadding = pad; } + + /**@short revert all four padding values to be automatically determined. */ + virtual void setDefaultPadding() { LeftPadding = -1; RightPadding = -1; TopPadding = -1; BottomPadding = -1; } + + /**@short The bottom X axis. */ + KPlotAxis BottomAxis; + /**@short The left Y axis. */ + KPlotAxis LeftAxis; + +protected: + /**@short the paint event handler, executed when update() or repaint() is called. + */ + virtual void paintEvent( QPaintEvent* /* e */ ); + + /**@short the resize event handler, called when the widget is resized. + */ + virtual void resizeEvent( QResizeEvent* /* e */ ); + + /**@short draws all of the objects onto the widget. Internal use only; one should simply call update() + *to draw the widget with axes and all objects. + *@param p pointer to the painter on which we are drawing + */ + virtual void drawObjects( QPainter *p ); + + /**@short draws the plot axes and axis labels. Internal use only; one should simply call update() + *to draw the widget with axes and all objects. + *@param p pointer to the painter on which we are drawing + */ + virtual void drawBox( QPainter *p ); + + /**@short modulus function for double variables. + *For example, dmod( 17.0, 7.0 ) returns 3.0 + *@return the remainder after dividing b into a. + */ + double dmod( double a, double b ); + + //The distance between major tickmarks in data units + double dXtick, dYtick; + //The number of major and minor tickmarks to be plotted in X and Y + int nmajX, nminX, nmajY, nminY; + + //Limits of the plot area in pixel units + QRect PixRect; + //Limits of the plot area in data units + DRect DataRect; + //List of KPlotObjects + QPtrList<KPlotObject> ObjectList; + + //Colors + QColor cBackground, cForeground, cGrid; + //draw options + bool ShowTickMarks, ShowTickLabels, ShowGrid; + //padding + int LeftPadding, RightPadding, TopPadding, BottomPadding; + + QPixmap *buffer; +}; + +#endif |