]> Git trees. - libqmvoc.git/commitdiff
Cleanup KPlotWidget
authorJason Harris <kstars@30doradus.org>
Wed, 6 Sep 2006 14:17:11 +0000 (14:17 +0000)
committerJason Harris <kstars@30doradus.org>
Wed, 6 Sep 2006 14:17:11 +0000 (14:17 +0000)
KPlotWidget:

- added setAntialias(bool), set to true for smoother (but slower)
plots

- removed explicit double-buffering (Qt4 does this automatically)

- added top and right axes (by default, they show the same tickmarks
as bottom and left)

- added optional secondary data limits for showing alternate
set of tickmarks on top and right axes.

- removed setShowTickMarks() / setShowTickLabels()...each axis now
handles its own visibility logic.

KPlotAxis:

- each axis now calculates its own tickmark positions (rather
than KPlotWidget)

- rather than setting the number of tickmarks and distance between
them, the tickmark positions are now stored in a QList<double>

- simplified visibility logic:
  + isVisible() : draw axis and tickmarks ?
  + showTickLabels() : obvious
  + the axis label is drawn if it is not empty

- added new ticklabel format 't', which indicates that the ticklabel
values are decimal hours, and should be rendered with a clock-time
format ("hh:mm").  Other special format flags are possible.

I didn't have to modify kalzium or ktouch, so the API hasn't
changed too much.  Most of the changes are "under the hood".

I also didn't look at kmathtool, since they have their own copy
of KPlotWidget in the playground.  I'll let them decide if and when
to use this version.

CCMAIL: kde-edu@kde.org

svn path=/trunk/KDE/kdeedu/libkdeedu/; revision=581495

kdeeduplot/kplotaxis.cpp
kdeeduplot/kplotaxis.h
kdeeduplot/kplotwidget.cpp
kdeeduplot/kplotwidget.h

index b81552fd955cc56e9579a2c7591bdee7f2dc62a1..fdaf83d0ee3f27eff08230de574e6bd6a46700b0 100644 (file)
  *                                                                         *
  ***************************************************************************/
 
+#include <math.h> //for log10(), pow(), modf()
+#include <kdebug.h>
+
 #include "kplotaxis.h"
 
-KPlotAxis::KPlotAxis() : m_visible(true), m_labelFieldWidth(0), m_labelFmt('g'),
-       m_labelPrec(2)
+KPlotAxis::KPlotAxis() : m_visible(true), m_showTickLabels(false), m_label(QString()),
+       m_labelFieldWidth(0), m_labelFmt('g'), m_labelPrec(-1)
 {
 }
 
 KPlotAxis::KPlotAxis(const QString& label) : m_visible(true), m_label(label), 
-       m_labelFieldWidth(0), m_labelFmt('g'), m_labelPrec(2)
+       m_labelFieldWidth(0), m_labelFmt('g'), m_labelPrec(-1)
 {
 }
 
+void KPlotAxis::setTickMarks( double x0, double length ) {
+       m_MajorTickMarks.clear();
+       m_MinorTickMarks.clear();
+
+       //s is the power-of-ten factor of length:
+       //length = t * s; s = 10^(pwr).  e.g., length=350.0 then t=3.5, s = 100.0; pwr = 2.0
+       double pwr = 0.0;
+       modf( log10( length ), &pwr );
+       double s = pow( 10.0, pwr );
+       double t = length / s;
+
+       double TickDistance = 0.0; //The distance between major tickmarks
+       int NumMajorTicks = 0; //will be between 3 and 5
+       int NumMinorTicks = 0; //The number of minor ticks between major ticks (will be 4 or 5)
+
+       //adjust s and t such that t is between 3 and 5:
+       if ( t < 3.0 ) {
+               t *= 10.0;
+               s /= 10.0;
+               // t is now between 3 and 30
+       }
+
+       if ( t < 6.0 ) { //accept current values
+               TickDistance = s;
+               NumMajorTicks = int( t );
+               NumMinorTicks = 5;
+       } else if ( t < 10.0 ) { // adjust by a factor of 2
+               TickDistance = s * 2.0;
+               NumMajorTicks = int( t / 2.0 );
+               NumMinorTicks = 4;
+       } else if ( t < 20.0 ) { //adjust by a factor of 4
+               TickDistance = s * 4.0;
+               NumMajorTicks = int( t / 4.0 );
+               NumMinorTicks = 4;
+       } else { //adjust by a factor of 5
+               TickDistance = s * 5.0;
+               NumMajorTicks = int( t / 5.0 );
+               NumMinorTicks = 5;
+       }
+
+       //We have determined the number of tickmarks and their separation
+       //Now we determine their positions in the Data space.
+
+       //Tick0 is the position of a "virtual" tickmark; the first major tickmark 
+       //position beyond the "minimum" edge of the data range.
+       double Tick0 = x0 - fmod( x0, TickDistance );
+       if ( x0 < 0.0 ) {
+               Tick0 -= TickDistance;
+               NumMajorTicks++;
+       }
+
+       for ( int i=0; i<NumMajorTicks+1; i++ ) {
+               double xmaj = Tick0 + i*TickDistance;
+               if ( xmaj >= x0 && xmaj <= x0 + length ) {
+                       m_MajorTickMarks.append( xmaj );
+               }
+
+               for ( int j=1; j<NumMinorTicks; j++ ) {
+                       double xmin = xmaj + TickDistance*j/NumMinorTicks;
+                       if ( xmin >= x0 && xmin <= x0 + length ) 
+                               m_MinorTickMarks.append( xmin );
+               }
+       }
+}
+
+QString KPlotAxis::tickLabel( double val ) const {
+       if ( tickLabelFmt() == 't' ) {
+               while ( val <   0.0 ) val += 24.0;
+               while ( val >= 24.0 ) val -= 24.0;
+
+               int h = int(val);
+               int m = int( 60.*(val - h) );
+               return QString( "%1:%2" ).arg( h, 2, 10, QLatin1Char('0') ).arg( m, 2, 10, QLatin1Char('0') );
+       }
+
+       return QString( "%1" ).arg( val, tickLabelWidth(), tickLabelFmt(), tickLabelPrec() );
+}
index df2228b9b6f9ef20387c798de5a095a5ea581910..bbcffccea2654f7db0be4cf02c5ce7c6cd848344 100644 (file)
@@ -19,6 +19,7 @@
 #define KPLOTAXIS_H
 
 #include <QString>
+#include <QList>
 
 #include <libkdeedu_plot_export.h>
 
@@ -50,63 +51,101 @@ public:
        /**
         * @return whether the axis is visible or not
         */
-       bool isVisible() const { return m_visible; }
+       inline bool isVisible() const { return m_visible; }
 
        /**
         * Sets the "visible" property of the axis.
         */
-       void setVisible(bool visible) { m_visible = visible; }
+       inline void setVisible(bool visible) { m_visible = visible; }
 
        /**
-        * Shows the axis (axis will be shown at next update of plot widget).
+        * @return whether tick labels will be drawn for this axis
         */
-       void show() { m_visible = true; }
+       inline bool showTickLabels() const { return m_showTickLabels; }
 
        /**
-        * Hides the axis (axis will be hidden at next update of plot widget).
+        * Determine whether tick labels will be drawn for this axis.
         */
-       void hide() { m_visible = false; }
+       inline void setShowTickLabels( bool b ) { m_showTickLabels = b; }
 
        /**
         * Sets the axis label.
         * Set the label to an empty string to omit the axis label.
         * @param label a string describing the data plotted on the axis.
         */
-       void setLabel( const QString& label ) { m_label = label; }
+       inline void setLabel( const QString& label ) { m_label = label; }
 
        /**
         * @return the axis label
         */
-       QString label() const { return m_label; }
+       inline QString label() const { return m_label; }
 
        /**
-        * Set the number format for the tick labels, see QString::arg() for
-        * description of arguments.
-         */
-       void setLabelFormat(int fieldWidth, char fmt = 'g', int prec=-1) {
+        * @return the ticklabel string for the given value, rendered according
+        * to the current format specification.
+        * @param the value to be rendered as a tick label.
+        * @sa setTickLabelFormat()
+        */
+       QString tickLabel( double value ) const;
+
+       /**
+        * Set the display format for converting the double value of the 
+        * tick's position to the QString for the tick label.
+        *
+        * Normally, the format character is one of 'e', 'E', 'f', 'g', or 'G'
+        * (see the documentation for QString::arg(double) for details).
+        *
+        * In addition, it is possible to set the format character to 't';
+        * in this case the tickmark value is interpreted as a time in hours,
+        * and the ticklabel string will be in "hh:mm" clock format.
+        * Note that when the format character is 't', the fieldWidth and prec
+        * values are ignored.
+        *
+        * @param fmt the format specification character 
+        * @param fieldWidth the number of characters in the output string.
+        * If set to 0, the string will be as wide as it needs to be to fully 
+        * render the value.
+        * @param prec the number of characters following the decimal point.
+        */
+       inline void setTickLabelFormat( char fmt = 'g', int fieldWidth = 0, int prec=-1) {
                m_labelFieldWidth = fieldWidth; m_labelFmt = fmt; m_labelPrec = prec; }
 
        /**
         * @return the field width of the tick labels
         */
-       int labelFieldWidth() const { return m_labelFieldWidth; }
+       inline int tickLabelWidth() const { return m_labelFieldWidth; }
 
        /**
         * @return the number format of the tick labels
         */
-       char labelFmt() const { return m_labelFmt; }
+       inline char tickLabelFmt() const { return m_labelFmt; }
 
        /**
         * @return the number precision of the tick labels
         */
-       int labelPrec() const { return m_labelPrec; }
+       inline int tickLabelPrec() const { return m_labelPrec; }
+
+       /**
+        * Determine the positions of major and minor tickmarks for this axis.
+        * @param x0 the minimum data coordinate of the axis.
+        * @param length the range covered by the axis, in data units.
+        * @sa majorTickMarks()
+        * @sa minorTickMarks()
+        */
+       void setTickMarks( double x0, double length );
+
+       inline QList<double>& majorTickMarks() { return m_MajorTickMarks; }
+       inline QList<double>& minorTickMarks() { return m_MinorTickMarks; }
 
 private:
        bool            m_visible;                      ///< Property "visible" defines if Axis is drawn or not.
+       bool            m_showTickLabels;
        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().
+       QList<double> m_MajorTickMarks, m_MinorTickMarks;
+
 };
 
 #endif // KPLOTAXIS_H
index 1579201fbb57fe2d78e33c3d8e656de352cd2493..b28a95e07f2379d43d9fb9426a1e2f34006c21ea 100644 (file)
@@ -15,8 +15,6 @@
  *                                                                         *
  ***************************************************************************/
 
-#include <math.h> //for log10(), pow(), modf()
-
 #include <kdebug.h>
 
 #include <qevent.h>
@@ -25,6 +23,7 @@
 #include <QPolygon>
 #include <QToolTip>
 #include <QtAlgorithms>
+#include <klocale.h>
 
 #include "kplotwidget.h"
 #include "kplotwidget.moc"
 #define YPADDING 20
 
 KPlotWidget::KPlotWidget( double x1, double x2, double y1, double y2, QWidget *parent )
- : QFrame( parent ),
-   dXtick(0.0), dYtick(0.0),
-   nmajX(0), nminX(0), nmajY(0), nminY(0),
-   ShowTickMarks( true ), ShowTickLabels( true ), ShowGrid( false ), ShowObjectToolTips( true )
+ : QFrame( parent ), ShowGrid( false ), ShowObjectToolTips( true ), UseAntialias( false )
 {
        setAttribute( Qt::WA_NoBackground, true );
 
-       // creating the axes
+       // create the axes
        mAxes[LeftAxis] = new KPlotAxis();
        mAxes[BottomAxis] = new KPlotAxis();
+       mAxes[RightAxis] = new KPlotAxis();
+       mAxes[TopAxis] = new KPlotAxis();
 
        //set DataRect
        setLimits( x1, x2, y1, y2 );
-       setDefaultPaddings();
+       SecondDataRect = QRect(); //default: no secondary data rect
 
-       //Set PixRect
-       recalcPixRect();
+       //By default, the left and bottom axes have tickmark labels
+       axis(LeftAxis)->setShowTickLabels( true );
+       axis(BottomAxis)->setShowTickLabels( true );
 
-       buffer = new QPixmap();
+       setDefaultPaddings();
 
        //default colors:
        setBackgroundColor( Qt::black );
@@ -68,7 +67,6 @@ KPlotWidget::KPlotWidget( double x1, double x2, double y1, double y2, QWidget *p
 
 KPlotWidget::~KPlotWidget()
 {
-       delete (buffer);
        qDeleteAll( ObjectList );
        ObjectList.clear();
        qDeleteAll( mAxes );
@@ -87,66 +85,52 @@ void KPlotWidget::setLimits( double x1, double x2, double y1, double y2 ) {
        if ( y2<y1) { YA1=y2; YA2=y1; }
        else { YA1=y1; YA2=y2; }
 
+       if ( XA2 == XA1 ) {
+               kWarning() << k_funcinfo << ": " <<
+                       i18n("x1 and x2 cannot be equal.  Setting x2 = x1 + 1.0") << endl;
+               XA2 = XA1 + 1.0;
+       }
+       if ( YA2 == YA1 ) {
+               kWarning() << k_funcinfo << ": " << 
+                       i18n("y1 and y2 cannot be equal.  Setting y2 = y1 + 1.0") << endl;
+               YA2 = YA1 + 1.0;
+       }
        DataRect = QRectF( XA1, YA1, XA2-XA1, YA2-YA1 );
-       updateTickmarks();
-       update();
-}
 
-void KPlotWidget::updateTickmarks() {
-       // Determine the number and spacing of tickmarks for the current plot limits.
-       if ( dataWidth() == 0.0 ) {
-               kWarning() << "KPlotWidget::updateTickmarks(): X range [" << x() << ", " << x2() << "] invalid!" << endl;
-               DataRect.setWidth( 1.0 );
-               return;
-       }
-       if ( dataHeight() == 0.0 ) {
-               kWarning() << "KPlotWidget::updateTickmarks(): Y range [" << y() << ", " << y2() << "] invalid!" << endl;
-               DataRect.setHeight( 1.0 );
-               return;
+       axis(LeftAxis)->setTickMarks( y(), dataHeight() );
+       axis(BottomAxis)->setTickMarks( x(), dataWidth() );
+
+       if ( secondaryDataRect().isNull() ) {
+               axis(RightAxis)->setTickMarks( y(), dataHeight() );
+               axis(TopAxis)->setTickMarks( x(), dataWidth() );
        }
 
-       calcTickMarks( DataRect.width(), dXtick, nmajX, nminX );
-       calcTickMarks( DataRect.height(), dYtick, nmajY, nminY );
+       update();
 }
 
-void KPlotWidget::calcTickMarks( double length, double& dTick, int& nmajor, int& nminor ) {
-       //s is the power-of-ten factor of length:
-       //length = t * s; s = 10^(pwr).  e.g., length=350.0 then t=3.5, s = 100.0; pwr = 2.0
-       double pwr = 0.0;
-       modf( log10( length ), &pwr );
-       double s = pow( 10.0, pwr );
-       double t = length / 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 between 3 and 30
+void KPlotWidget::setSecondaryLimits( 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; }
+
+       if ( XA2 == XA1 ) {
+               kWarning() << k_funcinfo << ": " <<
+                       i18n("x1 and x2 cannot be equal.  Setting x2 = x1 + 1.0") << endl;
+               XA2 = XA1 + 1.0;
        }
-       double _dTick = 0.0;
-       int _nmajor = 0;
-       int _nminor = 0;
-       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 ( YA2 == YA1 ) {
+               kWarning() << k_funcinfo << ": " << 
+                       i18n("y1 and y2 cannot be equal.  Setting y2 = y1 + 1.0") << endl;
+               YA2 = YA1 + 1.0;
        }
+       SecondDataRect = QRectF( XA1, YA1, XA2-XA1, YA2-YA1 );
 
-       nmajor = _nmajor;
-       nminor = _nminor;
-       dTick = _dTick;
+       axis(RightAxis)->setTickMarks( SecondDataRect.y(), SecondDataRect.height() );
+       axis(TopAxis)->setTickMarks( SecondDataRect.x(), SecondDataRect.width() );
+
+       update();
 }
 
 void KPlotWidget::addObject( KPlotObject *o ) {
@@ -183,26 +167,6 @@ void KPlotWidget::setBackgroundColor( const QColor &bg ) {
        update();
 }
 
-void KPlotWidget::setShowAxes( bool show ) {
-       QHash<Axis, KPlotAxis*>::iterator itEnd = mAxes.end();
-       for ( QHash<Axis, KPlotAxis*>::iterator it = mAxes.begin(); it != itEnd; ++it ) {
-               (*it)->setVisible(show);
-       }
-       update();
-}
-
-
-void KPlotWidget::setShowTickMarks( bool show ) {
-       ShowTickMarks = show;
-       update();
-}
-
-void KPlotWidget::setShowTickLabels( bool show ) {
-       ShowTickLabels = show;
-       recalcPixRect();
-       update();
-}
-
 void KPlotWidget::setShowGrid( bool show ) {
        ShowGrid = show;
        update();
@@ -217,13 +181,6 @@ KPlotAxis* KPlotWidget::axis( Axis a ) {
        return mAxes.contains( a ) ? mAxes[a] : 0;
 }
 
-void KPlotWidget::recalcPixRect() {
-       int newWidth = contentsRect().width() - leftPadding() - rightPadding();
-       int newHeight = contentsRect().height() - topPadding() - bottomPadding();
-       // PixRect starts at (0,0) because we will translate by leftPadding(), topPadding()
-       PixRect = QRect( 0, 0, newWidth, newHeight );
-}
-
 QList<KPlotObject*> KPlotWidget::pointsUnderPoint( const QPoint& p ) const {
        QList<KPlotObject*> pts;
        for ( QList<KPlotObject*>::ConstIterator it = ObjectList.begin(); it != ObjectList.constEnd(); ++it ) {
@@ -259,12 +216,14 @@ bool KPlotWidget::event( QEvent* e ) {
 }
 
 void KPlotWidget::resizeEvent( QResizeEvent* /* e */ ) {
-       recalcPixRect();
+       update();
+}
 
-       QPixmap *tmp = new QPixmap( contentsRect().size() );
-       delete buffer;
-       buffer = tmp;
-       tmp = 0;
+void KPlotWidget::setPixRect() {
+       int newWidth = contentsRect().width() - leftPadding() - rightPadding();
+       int newHeight = contentsRect().height() - topPadding() - bottomPadding();
+       // PixRect starts at (0,0) because we will translate by leftPadding(), topPadding()
+       PixRect = QRect( 0, 0, newWidth, newHeight );
 }
 
 void KPlotWidget::paintEvent( QPaintEvent *e ) {
@@ -272,20 +231,19 @@ void KPlotWidget::paintEvent( QPaintEvent *e ) {
        QFrame::paintEvent( e );
        QPainter p;
 
-       p.begin( buffer );
-       p.fillRect( buffer->rect(), backgroundColor() );
+       p.begin( this );
+       p.setRenderHint( QPainter::Antialiasing, UseAntialias );
+       p.fillRect( rect(), backgroundColor() );
        p.translate( leftPadding(), topPadding() );
 
+       setPixRect();
        p.setClipRect( PixRect );
        p.setClipping( true );
        drawObjects( &p );
        p.setClipping( false );
-       drawBox( &p );
+       drawAxes( &p );
 
        p.end();
-       p.begin( this );
-       p.drawPixmap( contentsRect().topLeft(), *buffer );
-       p.end();
 }
 
 void KPlotWidget::drawObjects( QPainter *p ) {
@@ -357,168 +315,227 @@ void KPlotWidget::drawObjects( QPainter *p ) {
        }
 }
 
-void KPlotWidget::drawBox( QPainter *p ) {
+void KPlotWidget::drawAxes( QPainter *p ) {
        if ( ShowGrid ) {
-               //Grid lines are placed at locations of primary axes' major tickmarks
                p->setPen( gridColor() );
 
+               //Grid lines are placed at locations of primary axes' major tickmarks
                //vertical grid lines
-               double x0 = x() - fmod( x(), dXtick ); //zeropoint; x(i) is this plus i*dXtick1
-               for ( int ix = 0; ix <= nmajX; ix++ ) {
-                       int px = int( PixRect.width() * ( (x0 + ix*dXtick - x())/dataWidth() ) );
-                       p->drawLine( px, 0, px, PixRect.height() );
+               foreach ( double xx, axis(BottomAxis)->majorTickMarks() ) {
+                       double px = PixRect.width() * (xx - x()) / dataWidth();
+                       p->drawLine( QPointF( px, 0.0 ), QPointF( px, double(PixRect.height()) ) );
                }
-
                //horizontal grid lines
-               double y0 = y() - fmod( y(), dYtick ); //zeropoint; y(i) is this plus i*mX
-               for ( int iy = 0; iy <= nmajY; iy++ ) {
-                       int py = PixRect.height() - int( PixRect.height() * ( (y0 + iy*dYtick - y())/dataHeight() ) );
-                       p->drawLine( 0, py, PixRect.width(), py );
+               foreach( double yy, axis(LeftAxis)->majorTickMarks() ) {
+                       double py = PixRect.height() * (yy - y()) / dataHeight();
+                       p->drawLine( QPointF( 0.0, py ), QPointF( double(PixRect.width()), py ) );
                }
        }
 
        p->setPen( foregroundColor() );
        p->setBrush( Qt::NoBrush );
 
-       if (mAxes[BottomAxis]->isVisible() || mAxes[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 (mAxes[BottomAxis]->isVisible()) {
-                       // Draw X tickmarks
-                       double x0 = x() - fmod( 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 );
+       //set small font for tick labels
+       QFont f = p->font();
+       int s = f.pointSize();
+       f.setPointSize( s - 2 );
+       p->setFont( f );
+
+       /*** BottomAxis ***/
+       KPlotAxis *a = axis(BottomAxis);
+       if (a->isVisible()) {
+               //Draw axis line
+               p->drawLine( 0, PixRect.height(), PixRect.width(), PixRect.height() );
+
+               // Draw major tickmarks
+               foreach( double xx, a->majorTickMarks() ) {
+                       double px = PixRect.width() * (xx - x()) / dataWidth();
+                       if ( px > 0 && px < PixRect.width() ) {
+                               p->drawLine( QPointF( px, double(PixRect.height() - 2.0)), 
+                                               QPointF( px, double(PixRect.height() - BIGTICKSIZE - 2.0)) );
+
+                               //Draw ticklabel
+                               if ( a->showTickLabels() ) {
+                                       QRect r( int(px) - BIGTICKSIZE, PixRect.height()+BIGTICKSIZE, 2*BIGTICKSIZE, BIGTICKSIZE );
+                                       p->drawText( r, Qt::AlignCenter | Qt::TextDontClip, a->tickLabel( xx ) );
                                }
-               
-                               //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, mAxes[BottomAxis]->labelFieldWidth(), mAxes[BottomAxis]->labelFmt(), mAxes[BottomAxis]->labelPrec() );
-                                       if ( px > 0 && px < PixRect.width() ) {
-                                               QRect r( px - BIGTICKSIZE, PixRect.height()+BIGTICKSIZE, 2*BIGTICKSIZE, BIGTICKSIZE );
-                                               p->drawText( r, Qt::AlignCenter | Qt::TextDontClip, 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 ( ! mAxes[BottomAxis]->label().isEmpty() ) {
-                               QRect r( 0, PixRect.height() + 2*YPADDING, PixRect.width(), YPADDING );
-                               p->drawText( r, Qt::AlignCenter, mAxes[BottomAxis]->label() );
                        }
+               }
 
+               // Draw minor tickmarks
+               foreach ( double xx, a->minorTickMarks() ) {
+                       double px = PixRect.width() * (xx - x()) / dataWidth();
+                       if ( px > 0 && px < PixRect.width() ) {
+                               p->drawLine( QPointF( px, double(PixRect.height() - 2.0)), 
+                                               QPointF( px, double(PixRect.height() - SMALLTICKSIZE - 2.0)) );
+                       }
                }
 
-               //--- Draw left Y Axis ---//
-               if (mAxes[LeftAxis]->isVisible()) {
-                       // Draw Y tickmarks
-                       double y0 = y() - fmod( 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 );
+               // Draw BottomAxis Label
+               if ( ! a->label().isEmpty() ) {
+                       QRect r( 0, PixRect.height() + 2*YPADDING, PixRect.width(), YPADDING );
+                       p->drawText( r, Qt::AlignCenter, a->label() );
+               }
+       }  //End of BottomAxis
+
+       /*** LeftAxis ***/
+       a = axis(LeftAxis);
+       if (a->isVisible()) {
+               //Draw axis line
+               p->drawLine( 0, 0, 0, PixRect.height() );
+
+               // Draw major tickmarks
+               foreach( double yy, a->majorTickMarks() ) {
+                       double py = PixRect.height() * ( 1.0 - (yy - y()) / dataHeight() );
+                       if ( py > 0 && py < PixRect.height() ) {
+                               p->drawLine( QPointF( 2.0, py ), QPointF( double(2.0 + BIGTICKSIZE), py ) );
+
+                               //Draw ticklabel
+                               if ( a->showTickLabels() ) {
+                                       QRect r( -2*BIGTICKSIZE-SMALLTICKSIZE, int(py)-SMALLTICKSIZE, 2*BIGTICKSIZE, 2*SMALLTICKSIZE );
+                                       p->drawText( r, Qt::AlignRight | Qt::AlignVCenter | Qt::TextDontClip, a->tickLabel( yy ) );
                                }
+                       }
+               }
+
+               // Draw minor tickmarks
+               foreach ( double yy, a->minorTickMarks() ) {
+                       double py = PixRect.height() * ( 1.0 - (yy - y()) / dataHeight() );
+                       if ( py > 0 && py < PixRect.height() ) {
+                               p->drawLine( QPointF( 2.0, py ), QPointF( double(2.0 + SMALLTICKSIZE), py ) );
+                       }
+               }
+
+               //Draw LeftAxis Label.  We need to draw the text sideways.
+               if ( ! a->label().isEmpty() ) {
+                       //store current painter translation/rotation state
+                       p->save();
        
-                               //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
+                       //translate coord sys to left corner of axis label rectangle, then rotate 90 degrees.
+                       p->translate( -3*XPADDING, PixRect.height() );
+                       p->rotate( -90.0 );
        
-                                       QString str = QString( "%1" ).arg( lab, mAxes[LeftAxis]->labelFieldWidth(), mAxes[LeftAxis]->labelFmt(), mAxes[LeftAxis]->labelPrec() );
-                                       if ( py > 0 && py < PixRect.height() ) {
-                                               QRect r( -2*BIGTICKSIZE, py-SMALLTICKSIZE, 2*BIGTICKSIZE, 2*SMALLTICKSIZE );
-                                               p->drawText( r, Qt::AlignCenter | Qt::TextDontClip, str );
-                                       }
-                               }
+                       QRect r( 0, 0, PixRect.height(), XPADDING );
+                       p->drawText( r, Qt::AlignCenter, a->label() ); //draw the label, now that we are sideways
        
-                               //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 );
-                                       }
+                       p->restore();  //restore translation/rotation state
+               }
+       }  //End of LeftAxis
+
+       /*** TopAxis ***/
+       a = axis(TopAxis);
+       if (a->isVisible()) {
+               //Draw axis line
+               p->drawLine( 0, 0, PixRect.width(), 0 );
+
+               // Draw major tickmarks
+               foreach( double xx, a->majorTickMarks() ) {
+                       double px = PixRect.width() * (xx - x()) / dataWidth();
+                       if ( px > 0 && px < PixRect.width() ) {
+                               p->drawLine( QPointF( px, 2.0 ), QPointF( px, double(BIGTICKSIZE + 2.0)) );
+
+                               //Draw ticklabel
+                               if ( a->showTickLabels() ) {
+                                       QRect r( int(px) - BIGTICKSIZE, -1*BIGTICKSIZE, 2*BIGTICKSIZE, BIGTICKSIZE );
+                                       p->drawText( r, Qt::AlignCenter | Qt::TextDontClip, a->tickLabel( xx ) );
                                }
-                       } // end draw Y tickmarks
-
-                       //Draw Y Axis Label.  We need to draw the text sideways.
-                       if ( ! mAxes[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, mAxes[LeftAxis]->label() ); //draw the label, now that we are sideways
-               
-                               p->restore();  //restore translation/rotation state
                        }
                }
 
-       } //end if ( ShowTickMarks )
+               // Draw minor tickmarks
+               foreach ( double xx, a->minorTickMarks() ) {
+                       double px = PixRect.width() * (xx - x()) / dataWidth();
+                       if ( px > 0 && px < PixRect.width() ) {
+                               p->drawLine( QPointF( px, 2.0 ), QPointF( px, double(SMALLTICKSIZE + 2.0)) );
+                       }
+               }
 
+               // Draw TopAxis Label
+               if ( ! a->label().isEmpty() ) {
+                       QRect r( 0, 0 - 3*YPADDING, PixRect.width(), YPADDING );
+                       p->drawText( r, Qt::AlignCenter, a->label() );
+               }
+       }  //End of TopAxis
+
+       /*** RightAxis ***/
+       a = axis(RightAxis);
+       if (a->isVisible()) {
+               //Draw axis line
+               p->drawLine( PixRect.width(), 0, PixRect.width(), PixRect.height() );
+
+               // Draw major tickmarks
+               foreach( double yy, a->majorTickMarks() ) {
+                       double py = PixRect.height() * ( 1.0 - (yy - y()) / dataHeight() );
+                       if ( py > 0 && py < PixRect.height() ) {
+                               p->drawLine( QPointF( double(PixRect.width() - 2.0), py ), 
+                                               QPointF( double(PixRect.width() - 2.0 - BIGTICKSIZE), py ) );
+
+                               //Draw ticklabel
+                               if ( a->showTickLabels() ) {
+                                       QRect r( PixRect.width() + SMALLTICKSIZE, int(py)-SMALLTICKSIZE, 2*BIGTICKSIZE, 2*SMALLTICKSIZE );
+                                       p->drawText( r, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip, a->tickLabel( yy ) );
+                               }
+                       }
+               }
 
+               // Draw minor tickmarks
+               foreach ( double yy, a->minorTickMarks() ) {
+                       double py = PixRect.height() * ( 1.0 - (yy - y()) / dataHeight() );
+                       if ( py > 0 && py < PixRect.height() ) {
+                               p->drawLine( QPointF( double(PixRect.width() - 2.0), py ), 
+                                               QPointF( double(PixRect.width() - 2.0 - SMALLTICKSIZE), py ) );
+                       }
+               }
+
+               //Draw RightAxis Label.  We need to draw the text sideways.
+               if ( ! a->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( PixRect.width() + 2*XPADDING, PixRect.height() );
+                       p->rotate( -90.0 );
+       
+                       QRect r( 0, 0, PixRect.height(), XPADDING );
+                       p->drawText( r, Qt::AlignCenter, a->label() ); //draw the label, now that we are sideways
+       
+                       p->restore();  //restore translation/rotation state
+               }
+       }  //End of RightAxis
 }
 
-int KPlotWidget::leftPadding() const {
+int KPlotWidget::leftPadding() {
        if ( LeftPadding >= 0 ) return LeftPadding;
-       if ( ! mAxes[LeftAxis]->label().isEmpty() && ShowTickLabels ) return 3*XPADDING;
-       if ( ! mAxes[LeftAxis]->label().isEmpty() || ShowTickLabels ) return 2*XPADDING;
+       if ( axis(LeftAxis)->isVisible() && axis(LeftAxis)->showTickLabels() ) {
+               if ( ! axis(LeftAxis)->label().isEmpty() ) return 3*XPADDING;
+               else return 2*XPADDING;
+       }
        return XPADDING;
 }
 
-int KPlotWidget::rightPadding() const {
+int KPlotWidget::rightPadding() {
        if ( RightPadding >= 0 ) return RightPadding;
+       if ( axis(RightAxis)->isVisible() && axis(RightAxis)->showTickLabels() ) {
+               if ( ! axis(RightAxis)->label().isEmpty() ) return 3*XPADDING;
+               else return 2*XPADDING;
+       }
        return XPADDING;
 }
 
-int KPlotWidget::topPadding() const {
+int KPlotWidget::topPadding() {
        if ( TopPadding >= 0 ) return TopPadding;
+       if ( axis(TopAxis)->isVisible() && axis(TopAxis)->showTickLabels() ) {
+               if ( ! axis(TopAxis)->label().isEmpty() ) return 3*YPADDING;
+               else return 2*YPADDING;
+       }
        return YPADDING;
 }
 
-int KPlotWidget::bottomPadding() const {
+int KPlotWidget::bottomPadding() {
        if ( BottomPadding >= 0 ) return BottomPadding;
-       if ( ! mAxes[BottomAxis]->label().isEmpty() && ShowTickLabels ) return 3*YPADDING;
-       if ( ! mAxes[BottomAxis]->label().isEmpty() || ShowTickLabels ) return 2*YPADDING;
+       if ( axis(BottomAxis)->isVisible() && axis(BottomAxis)->showTickLabels() ) {
+               if ( ! axis(BottomAxis)->label().isEmpty() ) return 3*YPADDING;
+               else return 2*YPADDING;
+       }
        return YPADDING;
 }
-
index 2358708070038fd897efa440eb07716ae1889999..13d54109d952bfeab0760ab106a08e56fe0c9047 100644 (file)
@@ -80,8 +80,6 @@ class EDUPLOT_EXPORT KPlotWidget : public QFrame {
        Q_PROPERTY(QColor foregroundColor READ foregroundColor WRITE setForegroundColor)
        Q_PROPERTY(QColor gridColor READ gridColor WRITE setGridColor)
        Q_PROPERTY(bool grid READ isGridShown WRITE setShowGrid)
-       Q_PROPERTY(bool tickMarks READ areTickMarksShown WRITE setShowTickMarks)
-       Q_PROPERTY(bool tickLabels READ areTickLabelsShown WRITE setShowTickLabels)
        Q_PROPERTY(bool objectToolTip READ areObjectToolTipsShown WRITE setShowObjectToolTips)
 public:
        /**
@@ -102,11 +100,7 @@ public:
        /**
         *@enum Axis The kinds of axes we have
         */
-       enum Axis
-       {
-               LeftAxis = 0,
-               BottomAxis
-       };
+       enum Axis { LeftAxis = 0, BottomAxis, RightAxis, TopAxis };
 
        /**
         *@return suggested size for widget
@@ -114,12 +108,6 @@ public:
         */
        virtual QSize minimumSizeHint() const;
 
-       /**
-        * Determine the placement of major and minor tickmarks, based on the
-        * current Limit settings
-        */
-       virtual void updateTickmarks();
-
        /**
         * Reset the data limits.
         * @param x1 the minimum X value in data units
@@ -129,6 +117,19 @@ public:
         */
        virtual void setLimits( double x1, double x2, double y1, double y2 );
 
+       /**
+        * Reset the secondary data limits, which control the top and right axes.
+        * Note that all data points are plotted using the coordinates defined by 
+        * the primary data limits, so this function is only useful for plotting 
+        * alternate axes along the top and right edges.
+        * @param x1 the minimum X value in secondary data units
+        * @param x2 the maximum X value in secondary data units
+        * @param y1 the minimum Y value in secondary data units
+        * @param y2 the maximum Y value in secondary data units
+        * @sa setLimits()
+        */
+       virtual void setSecondaryLimits( double x1, double x2, double y1, double y2 );
+
        /**
         * @return the minimum X value in data units
         */
@@ -159,6 +160,8 @@ public:
         */
        virtual double dataHeight() const { return DataRect.height(); }
 
+       const QRectF& secondaryDataRect() const { return SecondDataRect; }
+
        /**
         * Add an item to the list of KPlotObjects to be plotted.
         * @param o pointer to the KPlotObject to be added
@@ -221,23 +224,6 @@ public:
         */
        void setGridColor( const QColor &gc ) { cGrid = gc; }
 
-       /**
-        * Toggle whether plot axes are drawn.
-        * @param show if true, axes will be drawn.
-        * The axes are just a box outline around the plot.
-        */
-       void setShowAxes( bool show );
-
-       /**
-        * @return whether the tick marks are shown
-        */
-       bool areTickMarksShown() const { return ShowTickMarks; }
-
-       /**
-        * @return whether the tick labels are shown
-        */
-       bool areTickLabelsShown() const { return ShowTickLabels; }
-
        /**
         * @return whether the grid lines are shown
         */
@@ -248,30 +234,32 @@ public:
         */
        bool areObjectToolTipsShown() const { return ShowObjectToolTips; }
 
+       inline void setAntialias( bool b ) { UseAntialias = b; }
+
        /**
         * @return 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;
+       virtual int leftPadding();
        /**
         * @return 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;
+       virtual int rightPadding();
        /**
         * @return 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;
+       virtual int topPadding();
        /**
         * @return 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;
+       virtual int bottomPadding();
 
        /**
         * Set the number of pixels to the left of the plot area.
@@ -318,18 +306,6 @@ public:
        KPlotAxis* axis( Axis a );
 
 public slots:
-       /**
-        * Toggle whether tick marks are drawn along the axes.
-        * @param show if true, tick marks will be drawn.
-        */
-       void setShowTickMarks( bool show );
-
-       /**
-        * Toggle whether tick labels are drawn at major tickmarks.
-        * @param show if true, tick labels will be drawn.
-        */
-       void setShowTickLabels( bool show );
-
        /**
         * Toggle whether grid lines are drawn at major tickmarks.
         * @param show if true, grid lines will be drawn.
@@ -372,25 +348,12 @@ protected:
         * 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 );
+       virtual void drawAxes( QPainter *p );
 
-       virtual void recalcPixRect();
+       void setPixRect();
 
        QList<KPlotObject*> pointsUnderPoint( const QPoint& p ) const;
 
-       /**
-        * Recalc the ticks for the specified @p length.
-        * @p dTick , @p nmajor and @p nminor will contain respectively the
-        * distance between every major tick, the number of number of major
-        * ticks, and the number of minor ticks.
-        */
-       void calcTickMarks( double length, double& dTick, int& nmajor, int& nminor );
-
-       //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
         */
@@ -398,7 +361,7 @@ protected:
        /**
         * Limits of the plot area in data units
         */
-       QRectF DataRect;
+       QRectF DataRect, SecondDataRect;
        /**
         * List of KPlotObjects
         */
@@ -412,11 +375,9 @@ protected:
        //Colors
        QColor cBackground, cForeground, cGrid;
        //draw options
-       bool ShowTickMarks, ShowTickLabels, ShowGrid, ShowObjectToolTips;
+       bool ShowGrid, ShowObjectToolTips, UseAntialias;
        //padding
        int LeftPadding, RightPadding, TopPadding, BottomPadding;
-
-       QPixmap *buffer;
 };
 
 #endif