2018-11-11 14:41:06 -05:00
/*******************************************************************************
* gui / common / RSGraphWidget . cpp *
* *
* Copyright ( C ) 2014 RetroShare Team *
* Copyright ( c ) 2006 - 2007 , crypton *
* Copyright ( c ) 2006 , Matt Edman , Justin Hipple *
* *
* This program is free software : you can redistribute it and / or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation , either version 3 of the *
* License , or ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU Affero General Public License for more details . *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program . If not , see < https : //www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-10-12 17:12:58 -04:00
2014-10-12 17:38:27 -04:00
# ifndef WINDOWS_SYS
2014-10-12 17:12:58 -04:00
# include <sys/times.h>
2014-10-12 17:38:27 -04:00
# endif
2014-10-12 17:12:58 -04:00
# include <iostream>
2014-10-13 18:05:50 -04:00
# include <math.h>
2014-10-12 17:12:58 -04:00
# include <QtGlobal>
# include <QPainter>
# include <QDateTime>
2015-03-21 14:40:21 -04:00
# include <QWheelEvent>
2014-10-12 17:12:58 -04:00
# include <QTimer>
2015-02-04 05:56:19 -05:00
# include <retroshare-gui/RsAutoUpdatePage.h>
2014-10-12 17:12:58 -04:00
# include "RSGraphWidget.h"
2015-02-13 14:34:38 -05:00
# if QT_VERSION < 0x040700
# include <sys/time.h>
static qint64 getCurrentMSecsSinceEpoch ( )
{
timeval tv ;
gettimeofday ( & tv , NULL ) ;
return ( qint64 ) tv . tv_sec + ( qint64 ) tv . tv_usec / 1000 ;
}
# endif
2014-10-12 17:12:58 -04:00
RSGraphSource : : RSGraphSource ( )
{
_time_limit_msecs = 10 * 1000 ;
_update_period_msecs = 1 * 1000 ;
2015-02-13 14:34:38 -05:00
# if QT_VERSION < 0x040700
_time_orig_msecs = getCurrentMSecsSinceEpoch ( ) ;
# else
2014-10-12 17:12:58 -04:00
_time_orig_msecs = QDateTime : : currentMSecsSinceEpoch ( ) ;
2015-02-13 14:34:38 -05:00
# endif
2014-10-12 17:12:58 -04:00
_timer = new QTimer ;
2014-10-16 15:17:17 -04:00
_digits = 2 ;
2017-05-11 08:42:06 -04:00
_filtering_enabled = true ;
2014-10-13 18:05:50 -04:00
2015-02-04 05:56:19 -05:00
QObject : : connect ( _timer , SIGNAL ( timeout ( ) ) , this , SLOT ( updateIfPossible ( ) ) ) ;
2014-10-12 17:12:58 -04:00
}
RSGraphSource : : ~ RSGraphSource ( )
{
stop ( ) ;
delete _timer ;
}
void RSGraphSource : : clear ( )
{
_points . clear ( ) ;
}
void RSGraphSource : : stop ( )
{
_timer - > stop ( ) ;
}
void RSGraphSource : : start ( )
{
_timer - > stop ( ) ;
_timer - > start ( ( int ) ( _update_period_msecs ) ) ;
}
2014-10-13 18:05:50 -04:00
int RSGraphSource : : n_values ( ) const { return _points . size ( ) ; }
2014-10-15 18:00:49 -04:00
QString RSGraphSource : : displayName ( int i ) const
{
int n = 0 ;
2015-06-14 08:48:12 -04:00
for ( std : : map < std : : string , std : : list < std : : pair < qint64 , float > > > : : const_iterator it = _points . begin ( ) ; it ! = _points . end ( ) ; + + it )
if ( n + + = = i )
return QString : : fromStdString ( it - > first ) ;
2014-10-15 18:00:49 -04:00
2015-06-14 08:48:12 -04:00
return QString ( " [error] " ) ;
2014-10-15 18:00:49 -04:00
}
QString RSGraphSource : : displayValue ( float v ) const
{
2014-10-16 15:17:17 -04:00
return QString : : number ( v , ' f ' , _digits ) + " " + unitName ( ) ;
2014-10-15 18:00:49 -04:00
}
2017-04-20 14:54:51 -04:00
void RSGraphSource : : getCumulatedValues ( std : : vector < float > & vals ) const
{
2017-09-27 16:26:14 -04:00
for ( std : : map < std : : string , ZeroInitFloat > : : const_iterator it = _totals . begin ( ) ; it ! = _totals . end ( ) ; + + it )
vals . push_back ( it - > second . v ) ;
2017-04-20 14:54:51 -04:00
}
2015-05-15 09:12:46 -04:00
void RSGraphSource : : getCurrentValues ( std : : vector < QPointF > & vals ) const
2014-10-15 18:00:49 -04:00
{
std : : map < std : : string , std : : list < std : : pair < qint64 , float > > > : : const_iterator it = _points . begin ( ) ;
2015-05-15 09:12:46 -04:00
qint64 now = getTime ( ) ;
2014-10-15 18:00:49 -04:00
for ( it = _points . begin ( ) ; it ! = _points . end ( ) ; + + it )
2015-05-15 09:12:46 -04:00
vals . push_back ( QPointF ( ( now - it - > second . back ( ) . first ) / 1000.0f , it - > second . back ( ) . second ) ) ;
2014-10-15 18:00:49 -04:00
}
2017-04-20 14:54:51 -04:00
QString RSGraphSource : : legend ( int i , float v , bool show_value ) const
2014-10-16 15:17:17 -04:00
{
2017-04-20 14:54:51 -04:00
return displayName ( i ) + ( show_value ? ( " ( " + displayValue ( v ) + " ) " ) : " " ) ;
2014-10-16 15:17:17 -04:00
}
2015-12-27 17:00:54 -05:00
void RSGraphSource : : getDataPoints ( int index , std : : vector < QPointF > & pts , float filter_factor ) const
2014-10-12 17:12:58 -04:00
{
pts . clear ( ) ;
qint64 now = getTime ( ) ;
2017-05-11 08:42:06 -04:00
if ( ! _filtering_enabled )
filter_factor = 0 ;
2014-10-13 18:05:50 -04:00
std : : map < std : : string , std : : list < std : : pair < qint64 , float > > > : : const_iterator it = _points . begin ( ) ;
int n = 0 ;
for ( it = _points . begin ( ) ; it ! = _points . end ( ) & & n < index ; + + it , + + n ) ;
if ( n ! = index )
return ;
2015-12-27 17:00:54 -05:00
float last_value = it - > second . empty ( ) ? 0.0f : ( it - > second . begin ( ) - > second ) ;
2014-10-13 18:05:50 -04:00
for ( std : : list < std : : pair < qint64 , float > > : : const_iterator it2 = it - > second . begin ( ) ; it2 ! = it - > second . end ( ) ; + + it2 )
2015-12-27 17:00:54 -05:00
{
float val = ( 1 - filter_factor ) * ( * it2 ) . second + filter_factor * last_value ;
last_value = val ;
pts . push_back ( QPointF ( ( now - ( * it2 ) . first ) / 1000.0f , val ) ) ;
}
2014-10-12 17:12:58 -04:00
}
2014-10-17 15:47:05 -04:00
void RSGraphWidget : : setShowEntry ( uint32_t entry , bool b )
{
if ( b )
{
std : : set < std : : string > : : iterator it = _masked_entries . find ( _source - > displayName ( entry ) . toStdString ( ) ) ;
if ( it ! = _masked_entries . end ( ) )
_masked_entries . erase ( it ) ;
}
else
_masked_entries . insert ( _source - > displayName ( entry ) . toStdString ( ) ) ;
}
2015-07-09 23:24:39 -04:00
void RSGraphWidget : : setSource ( RSGraphSource * gs )
2014-10-12 17:12:58 -04:00
{
2016-10-22 17:48:19 -04:00
if ( _source ! = NULL )
delete _source ;
2014-10-13 18:05:50 -04:00
2016-10-22 17:48:19 -04:00
_source = gs ;
2014-10-12 17:12:58 -04:00
}
qint64 RSGraphSource : : getTime ( ) const
{
2015-02-13 14:34:38 -05:00
# if QT_VERSION < 0x040700
return getCurrentMSecsSinceEpoch ( ) - _time_orig_msecs ;
# else
2014-10-12 17:12:58 -04:00
return QDateTime : : currentMSecsSinceEpoch ( ) - _time_orig_msecs ;
2015-02-13 14:34:38 -05:00
# endif
2014-10-12 17:12:58 -04:00
}
2015-02-04 05:56:19 -05:00
void RSGraphSource : : updateIfPossible ( )
{
if ( RsAutoUpdatePage : : eventsLocked ( ) )
return ;
update ( ) ;
}
2014-10-12 17:12:58 -04:00
void RSGraphSource : : update ( )
{
2014-10-13 18:05:50 -04:00
std : : map < std : : string , float > vals ;
2014-10-12 17:12:58 -04:00
getValues ( vals ) ;
qint64 ms = getTime ( ) ;
2014-10-13 18:05:50 -04:00
for ( std : : map < std : : string , float > : : iterator it = vals . begin ( ) ; it ! = vals . end ( ) ; + + it )
{
std : : list < std : : pair < qint64 , float > > & lst ( _points [ it - > first ] ) ;
lst . push_back ( std : : make_pair ( ms , it - > second ) ) ;
2017-09-27 16:26:14 -04:00
float & total ( _totals [ it - > first ] . v ) ;
total + = it - > second ;
for ( std : : list < std : : pair < qint64 , float > > : : iterator it2 = lst . begin ( ) ; it2 ! = lst . end ( ) ; ) // This loop should be very fast, since we only remove the first elements, if applicable.
2014-10-13 18:05:50 -04:00
if ( ms - ( * it2 ) . first > _time_limit_msecs )
{
//std::cerr << " removing old value with time " << (*it).first/1000.0f << std::endl;
2017-09-27 16:26:14 -04:00
total - = ( * it2 ) . second ;
2014-10-13 18:05:50 -04:00
it2 = lst . erase ( it2 ) ;
}
else
break ;
}
2014-10-12 17:12:58 -04:00
2014-10-13 18:05:50 -04:00
// remove empty lists
2014-10-12 17:12:58 -04:00
2014-10-13 18:05:50 -04:00
for ( std : : map < std : : string , std : : list < std : : pair < qint64 , float > > > : : iterator it = _points . begin ( ) ; it ! = _points . end ( ) ; )
if ( it - > second . empty ( ) )
2017-09-27 16:26:14 -04:00
{
std : : map < std : : string , std : : list < std : : pair < qint64 , float > > > : : iterator tmp ( it ) ;
+ + tmp ;
_totals . erase ( it - > first ) ;
_points . erase ( it ) ;
it = tmp ;
}
2014-10-12 17:12:58 -04:00
else
2014-10-13 18:05:50 -04:00
+ + it ;
2017-04-20 14:54:51 -04:00
}
2017-09-27 16:26:14 -04:00
# ifdef TO_REMOVE
2017-04-20 14:54:51 -04:00
void RSGraphSource : : updateTotals ( )
{
2017-09-27 16:26:14 -04:00
std : : cerr < < " RsGraphSource::updateTotals() for " < < _points . size ( ) < < " values " < < std : : endl ;
2017-04-20 14:54:51 -04:00
// now compute totals
_totals . clear ( ) ;
for ( std : : map < std : : string , std : : list < std : : pair < qint64 , float > > > : : const_iterator it ( _points . begin ( ) ) ; it ! = _points . end ( ) ; + + it )
{
2017-09-27 16:26:14 -04:00
float & f = _totals [ it - > first ] . v ;
2017-04-20 14:54:51 -04:00
for ( std : : list < std : : pair < qint64 , float > > : : const_iterator it2 = it - > second . begin ( ) ; it2 ! = it - > second . end ( ) ; + + it2 )
f + = ( * it2 ) . second ;
}
2014-10-12 17:12:58 -04:00
}
2017-09-27 16:26:14 -04:00
# endif
2015-07-12 00:04:18 -04:00
2014-10-17 15:47:05 -04:00
void RSGraphSource : : reset ( )
{
2017-04-20 14:54:51 -04:00
_points . clear ( ) ;
_totals . clear ( ) ;
2014-10-17 15:47:05 -04:00
}
2014-10-12 17:12:58 -04:00
void RSGraphSource : : setCollectionTimeLimit ( qint64 s ) { _time_limit_msecs = s ; }
void RSGraphSource : : setCollectionTimePeriod ( qint64 s ) { _update_period_msecs = s ; }
void RSGraphWidget : : setTimeScale ( float pixels_per_second )
{
_time_scale = pixels_per_second ;
}
/** Default contructor */
RSGraphWidget : : RSGraphWidget ( QWidget * parent )
: QFrame ( parent )
{
2014-10-13 18:05:50 -04:00
_source = NULL ;
2014-10-12 17:12:58 -04:00
_painter = new QPainter ( ) ;
/* Initialize graph values */
_maxPoints = getNumPoints ( ) ;
_maxValue = MINUSER_SCALE ;
2017-05-04 15:19:23 -04:00
_linewidthscale = 1.0f ;
2014-10-17 15:47:05 -04:00
_opacity = 0.6 ;
2014-10-13 18:05:50 -04:00
_flags = 0 ;
2014-10-12 17:12:58 -04:00
_time_scale = 5.0f ; // in pixels per second.
2015-12-27 17:00:54 -05:00
_time_filter = 1.0f ;
2014-10-12 17:12:58 -04:00
_timer = new QTimer ;
2015-02-04 05:56:19 -05:00
QObject : : connect ( _timer , SIGNAL ( timeout ( ) ) , this , SLOT ( updateIfPossible ( ) ) ) ;
2014-10-12 17:12:58 -04:00
2017-05-11 08:42:06 -04:00
2014-10-13 18:05:50 -04:00
_y_scale = 1.0f ;
2014-10-12 17:12:58 -04:00
_timer - > start ( 1000 ) ;
2015-06-25 16:11:39 -04:00
float FS = QFontMetricsF ( font ( ) ) . height ( ) ;
setMinimumHeight ( 12 * FS ) ;
2017-05-11 08:42:06 -04:00
_graph_base = FS * GRAPH_BASE ;
2014-10-12 17:12:58 -04:00
}
2015-02-04 05:56:19 -05:00
void RSGraphWidget : : updateIfPossible ( )
{
if ( RsAutoUpdatePage : : eventsLocked ( ) )
return ;
if ( ! isVisible ( ) )
return ;
update ( ) ;
}
2014-10-12 17:12:58 -04:00
/** Default destructor */
RSGraphWidget : : ~ RSGraphWidget ( )
{
2014-10-13 18:05:50 -04:00
delete _painter ;
delete _source ;
2014-10-12 17:12:58 -04:00
}
2017-05-11 08:42:06 -04:00
void RSGraphWidget : : setFiltering ( bool b )
{
if ( _source ! = NULL )
_source - > setFiltering ( b ) ;
}
/** Gets the width of the desktop, which is the maximum number of points
2014-10-12 17:12:58 -04:00
* we can plot in the graph . */
int
RSGraphWidget : : getNumPoints ( )
{
QDesktopWidget * desktop = QApplication : : desktop ( ) ;
int width = desktop - > width ( ) ;
return width ;
}
/** Clears the graph. */
void
RSGraphWidget : : resetGraph ( )
{
2014-10-17 15:47:05 -04:00
_source - > reset ( ) ;
2014-10-12 17:12:58 -04:00
_maxValue = MINUSER_SCALE ;
2015-02-04 05:56:19 -05:00
updateIfPossible ( ) ;
2014-10-12 17:12:58 -04:00
}
/** Overloads default QWidget::paintEvent. Draws the actual
* bandwidth graph . */
void RSGraphWidget : : paintEvent ( QPaintEvent * )
{
//std::cerr << "In paint event!" << std::endl;
/* Set current graph dimensions */
_rec = this - > frameRect ( ) ;
/* Start the painter */
_painter - > begin ( this ) ;
/* We want antialiased lines and text */
_painter - > setRenderHint ( QPainter : : Antialiasing ) ;
_painter - > setRenderHint ( QPainter : : TextAntialiasing ) ;
/* Fill in the background */
_painter - > fillRect ( _rec , QBrush ( BACK_COLOR ) ) ;
_painter - > drawRect ( _rec ) ;
/* Paint the scale */
2015-05-15 09:35:51 -04:00
paintScale1 ( ) ;
2017-07-01 10:21:22 -04:00
/* Plot the data */
2014-10-12 17:12:58 -04:00
paintData ( ) ;
2017-07-01 10:21:22 -04:00
/* Paint the totals */
2014-10-12 17:12:58 -04:00
paintTotals ( ) ;
2015-05-15 09:35:51 -04:00
// part of the scale that needs to write over the data curves.
paintScale2 ( ) ;
2014-10-15 18:00:49 -04:00
if ( _flags & RSGRAPH_FLAGS_SHOW_LEGEND )
paintLegend ( ) ;
2014-10-12 17:12:58 -04:00
/* Stop the painter */
_painter - > end ( ) ;
}
2017-07-15 12:32:53 -04:00
//QSizeF RSGraphWidget::sizeHint(Qt::SizeHint which, const QSizeF& /* constraint */) const
//{
// float FS = QFontMetricsF(font()).height();
// //float fact = FS/14.0 ;
//
// switch(which)
// {
//default:
// case Qt::MinimumSize:
// case Qt::PreferredSize:
// return QSizeF(70*FS,12*FS);
// case Qt::MaximumSize:
// return QSizeF(700*FS,120*FS);
// }
//}
2015-06-25 16:11:39 -04:00
2017-05-11 12:46:40 -04:00
QColor RSGraphWidget : : getColor ( const std : : string & name )
2014-10-14 16:25:23 -04:00
{
2017-05-11 12:46:40 -04:00
uint32_t r = 57 ;
for ( uint32_t i = 0 ; i < name . length ( ) ; + + i )
r = ( 113 * name [ i ] + r ) ^ 0x93859aeb ;
2014-10-14 16:25:23 -04:00
// shuffle the colors a little bit
2017-05-11 12:46:40 -04:00
int h = ( r * 86243 ) % 359 ;
2014-10-14 16:25:23 -04:00
2017-05-11 12:46:40 -04:00
return QColor : : fromHsv ( h , 255 , 255 ) ;
2014-10-14 16:25:23 -04:00
}
2014-10-17 15:47:05 -04:00
void RSGraphWidget : : setCurvesOpacity ( float f )
{
_opacity = f ;
}
2014-10-12 17:12:58 -04:00
/** Paints an integral and an outline of that integral for each data set (rsdht
* and / or alldht ) that is to be displayed . The integrals will be drawn first ,
* followed by the outlines , since we want the area of overlapping integrals
* to blend , but not the outlines of those integrals . */
void RSGraphWidget : : paintData ( )
{
/* Convert the bandwidth data points to graph points */
2014-10-13 18:05:50 -04:00
if ( _source = = NULL )
return ;
const RSGraphSource & source ( * _source ) ;
_maxValue = 0.0f ;
2014-10-12 17:12:58 -04:00
2014-10-13 18:05:50 -04:00
for ( int i = 0 ; i < source . n_values ( ) ; + + i )
2014-10-17 15:47:05 -04:00
if ( _masked_entries . find ( source . displayName ( i ) . toStdString ( ) ) = = _masked_entries . end ( ) )
{
std : : vector < QPointF > values ;
2015-12-27 17:00:54 -05:00
//std::cerr << "time filter = " << _time_filter << ", factor=" << 1./_time_scale*_time_filter/(1+_time_filter/_time_scale) << std::endl;
source . getDataPoints ( i , values , 1. / _time_scale * _time_filter / ( 1.0f + _time_filter / _time_scale ) ) ;
2014-10-13 18:05:50 -04:00
2014-10-17 15:47:05 -04:00
QVector < QPointF > points ;
pointsFromData ( values , points ) ;
2014-10-13 18:05:50 -04:00
2017-05-11 12:46:40 -04:00
QColor pcolor = getColor ( source . displayName ( i ) . toStdString ( ) ) ;
2017-07-01 10:21:22 -04:00
/* Plot the bandwidth as solid lines. If the graph style is currently an area graph, we end up outlining the integrals. */
2017-05-04 15:19:23 -04:00
if ( _flags & RSGRAPH_FLAGS_PAINT_STYLE_DOTS )
2017-05-11 12:46:40 -04:00
paintDots ( points , pcolor ) ;
2017-05-04 15:19:23 -04:00
else
2017-05-11 12:46:40 -04:00
paintLine ( points , pcolor ) ;
2017-07-01 10:21:22 -04:00
/* Plot the data as area graphs */
points . push_front ( QPointF ( _rec . width ( ) , _rec . height ( ) - _graph_base ) ) ; // add a point in the lower right corner, to close the path.
if ( _flags & RSGRAPH_FLAGS_PAINT_STYLE_PLAIN )
paintIntegral ( points , pcolor , _opacity ) ;
2014-10-17 15:47:05 -04:00
}
2014-10-13 18:05:50 -04:00
if ( _maxValue > 0.0f )
2016-10-19 09:41:25 -04:00
{
2014-10-13 18:05:50 -04:00
if ( _flags & RSGRAPH_FLAGS_LOG_SCALE_Y )
2017-07-15 16:00:35 -04:00
_y_scale = _rec . height ( ) * 0.8 / log ( std : : max ( ( qreal ) 2.0 , ( qreal ) _maxValue ) ) ;
2014-10-13 18:05:50 -04:00
else
_y_scale = _rec . height ( ) * 0.8 / _maxValue ;
2016-10-19 09:41:25 -04:00
}
2014-10-12 17:12:58 -04:00
}
/** Returns a list of points on the bandwidth graph based on the supplied set
* of rsdht or alldht values . */
2014-10-13 18:05:50 -04:00
void RSGraphWidget : : pointsFromData ( const std : : vector < QPointF > & values , QVector < QPointF > & points )
2014-10-12 17:12:58 -04:00
{
2015-12-27 17:00:54 -05:00
points . clear ( ) ;
2014-10-12 17:12:58 -04:00
2015-12-27 17:00:54 -05:00
int x = _rec . width ( ) ;
int y = _rec . height ( ) ;
2014-10-12 17:12:58 -04:00
2016-06-05 11:05:52 -04:00
//float time_step = 1.0f ; // number of seconds per pixel
2014-10-12 17:12:58 -04:00
2015-12-27 17:00:54 -05:00
/* Translate all data points to points on the graph frame */
2014-10-12 17:12:58 -04:00
2015-12-27 17:00:54 -05:00
// take 0 as the origin, otherwise the different curves are not aligned properly
float last = 0 ; //values.back().x();
2014-10-12 17:12:58 -04:00
2015-12-27 17:00:54 -05:00
//std::cerr << "Got " << values.size() << " values for index 0" << std::endl;
2014-10-12 17:12:58 -04:00
2015-12-27 17:00:54 -05:00
float FS = QFontMetricsF ( font ( ) ) . height ( ) ;
float fact = FS / 14.0 ;
2015-06-24 16:55:09 -04:00
2015-12-27 17:00:54 -05:00
float last_px = SCALE_WIDTH * fact ;
float last_py = 0.0f ;
2014-10-12 17:12:58 -04:00
2015-12-27 17:00:54 -05:00
// float min_x_no_data_threshold = 1.5 ; // 1.5 sec.
2015-07-17 16:13:31 -04:00
2015-12-27 17:00:54 -05:00
for ( uint i = 0 ; i < values . size ( ) ; + + i )
{
//std::cerr << "Value: (" << values[i].x() << " , " << values[i].y() << ")" << std::endl;
2014-10-12 17:12:58 -04:00
2015-12-27 17:00:54 -05:00
// compute point in pixels
2014-10-12 17:12:58 -04:00
2015-12-27 17:00:54 -05:00
qreal px = x - ( values [ i ] . x ( ) - last ) * _time_scale ;
qreal py = y - valueToPixels ( values [ i ] . y ( ) ) ;
2014-10-12 17:12:58 -04:00
2017-05-11 08:42:06 -04:00
if ( ! ( _flags & RSGRAPH_FLAGS_PAINT_STYLE_DOTS ) )
2015-12-27 17:00:54 -05:00
{
2017-05-11 08:42:06 -04:00
if ( px > = SCALE_WIDTH * fact & & last_px < SCALE_WIDTH * fact )
{
float alpha = ( SCALE_WIDTH * fact - last_px ) / ( px - last_px ) ;
float ipx = SCALE_WIDTH * fact ;
float ipy = ( 1 - alpha ) * last_py + alpha * py ;
points < < QPointF ( ipx , y - _graph_base ) ;
points < < QPointF ( ipx , ipy ) ;
}
else if ( i = = 0 )
{
if ( px < SCALE_WIDTH * fact )
points < < QPointF ( SCALE_WIDTH * fact , py ) ;
else
points < < QPointF ( px , y - _graph_base ) ;
}
2015-12-27 17:00:54 -05:00
}
2014-10-12 17:12:58 -04:00
2015-12-27 17:00:54 -05:00
if ( px < SCALE_WIDTH * fact )
continue ;
2014-10-12 17:12:58 -04:00
2017-05-11 08:42:06 -04:00
if ( ( _flags & RSGRAPH_FLAGS_PAINT_STYLE_DOTS ) & & values [ i ] . y ( ) = = 0 )
continue ;
2015-12-27 17:00:54 -05:00
_maxValue = std : : max ( _maxValue , values [ i ] . y ( ) ) ;
2014-10-17 15:47:05 -04:00
2015-12-27 17:00:54 -05:00
// remove midle point when 3 consecutive points have the same value.
2014-10-12 17:12:58 -04:00
2017-05-11 08:42:06 -04:00
if ( ! ( _flags & RSGRAPH_FLAGS_PAINT_STYLE_DOTS ) )
if ( points . size ( ) > 1 & & points [ points . size ( ) - 2 ] . y ( ) = = points . back ( ) . y ( ) & & points . back ( ) . y ( ) = = py )
points . pop_back ( ) ;
2015-07-17 16:13:31 -04:00
2015-12-27 17:00:54 -05:00
points < < QPointF ( px , py ) ;
2014-10-12 17:12:58 -04:00
2017-05-11 08:42:06 -04:00
if ( ! ( _flags & RSGRAPH_FLAGS_PAINT_STYLE_DOTS ) & & ( i = = values . size ( ) - 1 ) )
2017-07-01 10:21:22 -04:00
points < < QPointF ( px , py ) ;
2015-07-17 16:13:31 -04:00
2015-12-27 17:00:54 -05:00
last_px = px ;
last_py = py ;
2015-07-17 16:13:31 -04:00
2015-12-27 17:00:54 -05:00
}
2014-10-13 18:05:50 -04:00
}
2014-10-12 17:12:58 -04:00
2014-10-13 18:05:50 -04:00
qreal RSGraphWidget : : valueToPixels ( qreal val )
{
if ( _flags & RSGRAPH_FLAGS_LOG_SCALE_Y )
2017-05-11 08:42:06 -04:00
return _graph_base + log ( std : : max ( ( qreal ) 1.0 , val ) ) * _y_scale ;
2014-10-13 18:05:50 -04:00
else
2017-05-11 08:42:06 -04:00
return _graph_base + val * _y_scale ;
2014-10-13 18:05:50 -04:00
}
qreal RSGraphWidget : : pixelsToValue ( qreal val )
{
if ( _flags & RSGRAPH_FLAGS_LOG_SCALE_Y )
2017-05-11 08:42:06 -04:00
return exp ( ( val - _graph_base ) / _y_scale ) ;
2014-10-13 18:05:50 -04:00
else
2017-05-11 08:42:06 -04:00
return ( val - _graph_base ) / _y_scale ;
2014-10-12 17:12:58 -04:00
}
/** Plots an integral using the data points in <b>points</b>. The area will be
* filled in using < b > color < / b > and an alpha - blending level of < b > alpha < / b >
* ( default is opaque ) . */
void RSGraphWidget : : paintIntegral ( const QVector < QPointF > & points , QColor color , qreal alpha )
{
/* Save the current brush, plot the integral, and restore the old brush */
QBrush oldBrush = _painter - > brush ( ) ;
color . setAlphaF ( alpha ) ;
_painter - > setBrush ( QBrush ( color ) ) ;
_painter - > drawPolygon ( points . data ( ) , points . size ( ) ) ;
_painter - > setBrush ( oldBrush ) ;
}
/** Iterates the input list and draws a line on the graph in the appropriate
* color . */
void RSGraphWidget : : paintLine ( const QVector < QPointF > & points , QColor color , Qt : : PenStyle lineStyle )
{
/* Save the current brush, plot the line, and restore the old brush */
QPen oldPen = _painter - > pen ( ) ;
2017-05-04 15:19:23 -04:00
QPen newPen ( color , lineStyle ) ;
newPen . setWidth ( 2.0f * _linewidthscale ) ;
_painter - > setPen ( newPen ) ;
2014-10-12 17:12:58 -04:00
_painter - > drawPolyline ( points . data ( ) , points . size ( ) ) ;
_painter - > setPen ( oldPen ) ;
}
2017-05-04 15:19:23 -04:00
void RSGraphWidget : : paintDots ( const QVector < QPointF > & points , QColor color )
{
/* Save the current brush, plot the line, and restore the old brush */
QPen oldPen = _painter - > pen ( ) ;
_painter - > setPen ( QPen ( color , oldPen . style ( ) ) ) ;
QBrush oldBrush = _painter - > brush ( ) ;
_painter - > setBrush ( QBrush ( color ) ) ;
for ( int i = 0 ; i < points . size ( ) ; + + i )
2017-05-11 08:42:06 -04:00
_painter - > drawEllipse ( QRect ( points [ i ] . x ( ) - 2.5 * _linewidthscale , points [ i ] . y ( ) - 2.5 * _linewidthscale , 5 * _linewidthscale , 5 * _linewidthscale ) ) ;
2014-10-12 17:12:58 -04:00
2017-05-04 15:19:23 -04:00
_painter - > setPen ( oldPen ) ;
_painter - > setBrush ( oldBrush ) ;
}
2014-10-12 17:12:58 -04:00
/** Paints selected total indicators on the graph. */
void RSGraphWidget : : paintTotals ( )
{
2015-06-24 16:55:09 -04:00
float FS = QFontMetricsF ( font ( ) ) . height ( ) ;
2016-06-05 11:05:52 -04:00
//float fact = FS/14.0 ;
2015-06-24 16:55:09 -04:00
2016-06-05 10:43:57 -04:00
//int x = SCALE_WIDTH*fact + FS, y = 0;
2015-06-24 16:55:09 -04:00
int rowHeight = FS ;
2014-10-12 17:12:58 -04:00
2015-08-29 15:31:01 -04:00
# if !defined(Q_OS_MAC)
2014-10-12 17:12:58 -04:00
/* On Mac, we don't need vertical spacing between the text rows. */
rowHeight + = 5 ;
# endif
}
/** Returns a formatted string with the correct size suffix. */
QString RSGraphWidget : : totalToStr ( qreal total )
{
/* Determine the correct size suffix */
if ( total < 1024 ) {
/* Use KB suffix */
return tr ( " %1 KB " ) . arg ( total , 0 , ' f ' , 2 ) ;
} else if ( total < 1048576 ) {
/* Use MB suffix */
return tr ( " %1 MB " ) . arg ( total / 1024.0 , 0 , ' f ' , 2 ) ;
} else {
/* Use GB suffix */
return tr ( " %1 GB " ) . arg ( total / 1048576.0 , 0 , ' f ' , 2 ) ;
}
}
/** Paints the scale on the graph. */
2015-05-15 09:35:51 -04:00
void RSGraphWidget : : paintScale1 ( )
2014-10-12 17:12:58 -04:00
{
2015-06-24 16:55:09 -04:00
float FS = QFontMetricsF ( font ( ) ) . height ( ) ;
float fact = FS / 14.0 ;
int top = _rec . y ( ) ;
2017-05-11 08:42:06 -04:00
int bottom = _rec . height ( ) - _graph_base ;
qreal paintStep = ( bottom - top ) / 5 ;
2014-10-13 18:05:50 -04:00
2015-03-21 14:40:21 -04:00
/* Draw the other marks in their correctly scaled locations */
qreal scale ;
qreal pos ;
2014-10-13 18:05:50 -04:00
2015-03-21 14:40:21 -04:00
if ( _source = = NULL )
return ;
2014-10-13 18:05:50 -04:00
2015-03-21 14:40:21 -04:00
QString unit_name = _source - > unitName ( ) ;
2014-10-13 18:05:50 -04:00
2017-05-11 08:42:06 -04:00
for ( int i = 0 ; i < 5 ; i + + )
2015-03-21 14:40:21 -04:00
{
2017-05-11 08:42:06 -04:00
pos = bottom - ( i * paintStep ) ;
2014-10-13 18:05:50 -04:00
2017-05-11 08:42:06 -04:00
scale = pixelsToValue ( _graph_base + i * paintStep ) ;
// If legend contains integers only the value should be rounded to the nearest integer
if ( _flags & RSGRAPH_FLAGS_LEGEND_INTEGER )
{
scale = ( int ) scale ;
pos = bottom - ( valueToPixels ( scale ) - _graph_base ) ;
}
2014-10-15 18:00:49 -04:00
2015-03-21 14:40:21 -04:00
QString text = _source - > displayValue ( scale ) ;
_painter - > setPen ( SCALE_COLOR ) ;
2017-05-11 08:52:26 -04:00
_painter - > drawText ( QPointF ( SCALE_WIDTH * fact - QFontMetricsF ( font ( ) ) . width ( text ) - 4 * fact , pos + 0.4 * FS ) , text ) ;
2015-03-21 14:40:21 -04:00
_painter - > setPen ( GRID_COLOR ) ;
2015-06-24 16:55:09 -04:00
_painter - > drawLine ( QPointF ( SCALE_WIDTH * fact , pos ) , QPointF ( _rec . width ( ) , pos ) ) ;
2015-03-21 14:40:21 -04:00
}
/* Draw vertical separator */
2015-06-24 16:55:09 -04:00
_painter - > drawLine ( SCALE_WIDTH * fact , top , SCALE_WIDTH * fact , bottom ) ;
2015-05-15 09:35:51 -04:00
}
2015-03-21 14:40:21 -04:00
2015-05-15 09:35:51 -04:00
void RSGraphWidget : : paintScale2 ( )
{
// draw time below the graph
2015-03-21 14:40:21 -04:00
2015-06-24 16:55:09 -04:00
float FS = QFontMetricsF ( font ( ) ) . height ( ) ;
float fact = FS / 14.0 ;
2016-06-05 11:05:52 -04:00
//int bottom = _rec.height();
2015-06-24 16:55:09 -04:00
static const int npix = 100 * fact ;
2015-03-21 14:40:21 -04:00
2015-06-24 16:55:09 -04:00
for ( int i = _rec . width ( ) ; i > SCALE_WIDTH * fact ; i - = npix )
2015-05-15 09:35:51 -04:00
{
2016-06-05 11:05:52 -04:00
//qreal pos = bottom - FS;
2015-03-21 14:40:21 -04:00
2015-05-15 09:35:51 -04:00
int seconds = ( _rec . width ( ) - i ) / _time_scale ; // pixels / (pixels per second) => seconds
QString text = QString : : number ( seconds ) + " secs " ;
2015-03-21 14:40:21 -04:00
2015-05-15 09:35:51 -04:00
_painter - > setPen ( SCALE_COLOR ) ;
2015-06-24 16:55:09 -04:00
_painter - > drawText ( QPointF ( i , _rec . height ( ) - 0.5 * FS ) , text ) ;
2015-05-15 09:35:51 -04:00
}
2015-03-21 14:40:21 -04:00
}
void RSGraphWidget : : wheelEvent ( QWheelEvent * e )
{
2015-12-27 17:00:54 -05:00
if ( e - > modifiers ( ) & Qt : : ShiftModifier )
if ( e - > delta ( ) > 0 )
_time_filter * = 1.1 ;
else
_time_filter / = 1.1 ;
2017-05-04 15:19:23 -04:00
else if ( e - > modifiers ( ) & Qt : : ControlModifier )
if ( e - > delta ( ) > 0 )
_linewidthscale * = 1.2 ;
else
_linewidthscale / = 1.2 ;
2015-12-27 17:00:54 -05:00
else
if ( e - > delta ( ) > 0 )
_time_scale * = 1.1 ;
else
_time_scale / = 1.1 ;
2015-03-21 14:40:21 -04:00
2015-12-27 17:00:54 -05:00
update ( ) ;
2014-10-12 17:12:58 -04:00
}
2014-10-15 18:00:49 -04:00
void RSGraphWidget : : paintLegend ( )
{
2016-06-05 10:43:57 -04:00
//int bottom = _rec.height();
2014-10-15 18:00:49 -04:00
2017-04-20 14:54:51 -04:00
std : : vector < float > vals ;
if ( _flags & RSGRAPH_FLAGS_LEGEND_CUMULATED )
_source - > getCumulatedValues ( vals ) ;
else
{
std : : vector < QPointF > cvals ;
_source - > getCurrentValues ( cvals ) ;
for ( uint32_t i = 0 ; i < cvals . size ( ) ; + + i )
vals . push_back ( cvals [ i ] . y ( ) ) ;
}
2015-05-15 09:12:46 -04:00
int j = 0 ;
2014-10-15 18:00:49 -04:00
2015-06-24 16:55:09 -04:00
float FS = QFontMetricsF ( font ( ) ) . height ( ) ;
float fact = FS / 14.0 ;
2014-10-15 18:00:49 -04:00
for ( uint i = 0 ; i < vals . size ( ) ; + + i )
2015-05-15 08:40:47 -04:00
if ( _masked_entries . find ( _source - > displayName ( i ) . toStdString ( ) ) = = _masked_entries . end ( ) )
2015-05-15 09:12:46 -04:00
{
2017-04-20 14:54:51 -04:00
// if( _rec.width() - (vals[i].x()-0)*_time_scale < SCALE_WIDTH*fact )
// continue ;
2014-10-15 18:00:49 -04:00
2015-06-24 16:55:09 -04:00
qreal paintStep = 4 * fact + FS ;
qreal pos = 15 * fact + j * paintStep ;
2017-04-20 14:54:51 -04:00
QString text = _source - > legend ( i , vals [ i ] ) ;
2014-10-15 18:00:49 -04:00
2015-05-15 09:12:46 -04:00
QPen oldPen = _painter - > pen ( ) ;
2017-05-11 08:42:06 -04:00
2017-05-11 12:46:40 -04:00
QPen pen ( getColor ( _source - > displayName ( i ) . toStdString ( ) ) , Qt : : SolidLine ) ;
2017-05-11 08:42:06 -04:00
pen . setWidth ( _linewidthscale ) ;
_painter - > setPen ( pen ) ;
2015-06-25 16:11:39 -04:00
_painter - > drawLine ( QPointF ( SCALE_WIDTH * fact + 10.0 * fact , pos + FS / 3 ) , QPointF ( SCALE_WIDTH * fact + 30.0 * fact , pos + FS / 3 ) ) ;
2015-05-15 09:12:46 -04:00
_painter - > setPen ( oldPen ) ;
_painter - > setPen ( SCALE_COLOR ) ;
2015-06-24 16:55:09 -04:00
_painter - > drawText ( QPointF ( SCALE_WIDTH * fact + 40 * fact , pos + 0.5 * FS ) , text ) ;
2015-05-15 09:12:46 -04:00
+ + j ;
}
2014-10-15 18:00:49 -04:00
}