/*************************************************************************** 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 //for log10(), pow(), modf() #include #include #include #include "kplotwidget.h" #include "kplotwidget.moc" KPlotWidget::KPlotWidget( double x1, double x2, double y1, double y2, TQWidget *tqparent, const char* name ) : TQWidget( tqparent, name, WNoAutoErase ), dXtick(0.0), dYtick(0.0), nmajX(0), nminX(0), nmajY(0), nminY(0), ShowTickMarks( true ), ShowTickLabels( true ), ShowGrid( false ) { setBackgroundMode( TQWidget::NoBackground ); //set DataRect setLimits( x1, x2, y1, y2 ); setDefaultPadding(); //Set PixRect (starts at (0,0) because we will translate by leftPadding(), topPadding() ) PixRect = TQRect( 0, 0, width() - leftPadding() - rightPadding(), height() - topPadding() - bottomPadding() ); buffer = new TQPixmap(); //default colors: setBGColor( TQColor( "black" ) ); setFGColor( TQColor( "white" ) ); setGridColor( TQColor( "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 (x2resize( width(), height() ); } void KPlotWidget::paintEvent( TQPaintEvent* /* e */ ) { TQPainter 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( TQPainter *p ) { for ( KPlotObject *po = ObjectList.first(); po; po = ObjectList.next() ) { if ( po->points()->count() ) { //draw the plot object p->setPen( TQColor( po->color() ) ); switch ( po->type() ) { case KPlotObject::POINTS : { p->setBrush( TQColor( po->color() ) ); for ( DPoint *dp = po->points()->first(); dp; dp = po->points()->next() ) { TQPoint 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( TQt::NoBrush ); break; } case KPlotObject::CURVE : { p->setPen( TQPen( TQColor( po->color() ), po->size(), (TQPen::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. { TQPoint q = po->points()->first()->qpoint( PixRect, DataRect ); p->drawText( q.x()-20, q.y()+6, 40, 10, TQt::AlignCenter | TQt::DontClip, po->name() ); break; } case KPlotObject::POLYGON : { p->setPen( TQPen( TQColor( po->color() ), po->size(), (TQPen::PenStyle)po->param() ) ); p->setBrush( TQColor( po->color() ) ); TQPointArray 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( TQPainter *p ) { //First, fill in padding region with bgColor() to tqmask 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( TQt::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 TQFont 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 TQString str = TQString( "%1" ).tqarg( lab, BottomAxis.labelFieldWidth(), BottomAxis.labelFmt(), BottomAxis.labelPrec() ); if ( px > 0 && px < PixRect.width() ) { TQRect r( px - BIGTICKSIZE, PixRect.height()+BIGTICKSIZE, 2*BIGTICKSIZE, BIGTICKSIZE ); p->drawText( r, TQt::AlignCenter | TQt::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() ) { TQRect r( 0, PixRect.height() + 2*YPADDING, PixRect.width(), YPADDING ); p->drawText( r, TQt::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 TQString str = TQString( "%1" ).tqarg( lab, LeftAxis.labelFieldWidth(), LeftAxis.labelFmt(), LeftAxis.labelPrec() ); if ( py > 0 && py < PixRect.height() ) { TQRect r( -2*BIGTICKSIZE, py-SMALLTICKSIZE, 2*BIGTICKSIZE, 2*SMALLTICKSIZE ); p->drawText( r, TQt::AlignCenter | TQt::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 ); TQRect r( 0, 0, PixRect.height(), XPADDING ); p->drawText( r, TQt::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; }