2015-09-17 11:42:35 -05:00
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015- Statoil ASA
// Copyright (C) 2015- Ceetron Solutions AS
//
// ResInsight 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 3 of the License, or
// (at your option) any later version.
//
// ResInsight 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 General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
2018-09-04 02:08:29 -05:00
# include "RiuQwtPlotCurve.h"
2015-09-17 11:42:35 -05:00
2018-06-08 08:10:45 -05:00
# include "RiaCurveDataTools.h"
2019-03-28 14:09:25 -05:00
# include "RiaImageTools.h"
2018-09-04 02:08:29 -05:00
# include "RiuQwtSymbol.h"
# include "qwt_symbol.h"
2016-05-24 03:30:50 -05:00
# include "qwt_date.h"
2016-06-22 03:38:04 -05:00
# include "qwt_point_mapper.h"
# include "qwt_painter.h"
2018-05-25 04:10:03 -05:00
# include "qwt_plot_intervalcurve.h"
# include "qwt_scale_map.h"
# include "qwt_interval_symbol.h"
2015-10-15 04:27:12 -05:00
2018-07-04 02:53:00 -05:00
# include <limits>
2018-07-04 02:34:54 -05:00
//--------------------------------------------------------------------------------------------------
/// Internal constants
//--------------------------------------------------------------------------------------------------
# define DOUBLE_INF std::numeric_limits<double>::infinity()
2015-09-17 11:42:35 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
RiuQwtPlotCurve : : RiuQwtPlotCurve ( const QString & title )
2015-11-06 03:08:35 -06:00
: QwtPlotCurve ( title )
2015-09-17 11:42:35 -05:00
{
2016-06-22 03:38:04 -05:00
this - > setLegendAttribute ( QwtPlotCurve : : LegendShowLine , true ) ;
this - > setLegendAttribute ( QwtPlotCurve : : LegendShowSymbol , true ) ;
this - > setLegendAttribute ( QwtPlotCurve : : LegendShowBrush , true ) ;
this - > setRenderHint ( QwtPlotItem : : RenderAntialiased , true ) ;
m_symbolSkipPixelDistance = 10.0f ;
2018-05-25 04:10:03 -05:00
m_errorBars = new QwtPlotIntervalCurve ( ) ;
m_errorBars - > setStyle ( QwtPlotIntervalCurve : : CurveStyle : : NoCurve ) ;
m_errorBars - > setSymbol ( new QwtIntervalSymbol ( QwtIntervalSymbol : : Bar ) ) ;
2018-05-28 06:45:30 -05:00
m_errorBars - > setItemAttribute ( QwtPlotItem : : Legend , false ) ;
2018-12-05 02:35:05 -06:00
m_errorBars - > setZ ( Z_ERROR_BARS ) ;
2018-05-25 04:10:03 -05:00
m_showErrorBars = true ;
m_attachedToPlot = nullptr ;
2019-03-28 14:09:25 -05:00
m_blackAndWhiteLegendIcon = false ;
2015-09-17 11:42:35 -05:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
RiuQwtPlotCurve : : ~ RiuQwtPlotCurve ( )
2015-09-17 11:42:35 -05:00
{
}
2016-05-24 03:30:50 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
void RiuQwtPlotCurve : : setSamplesFromXValuesAndYValues ( const std : : vector < double > & xValues , const std : : vector < double > & yValues , const std : : vector < double > & yErrorValues , bool keepOnlyPositiveValues )
2016-05-24 03:30:50 -05:00
{
2017-11-08 00:38:25 -06:00
CVF_ASSERT ( xValues . size ( ) = = yValues . size ( ) ) ;
2018-05-25 04:10:03 -05:00
CVF_ASSERT ( yErrorValues . empty ( ) | | yErrorValues . size ( ) = = xValues . size ( ) ) ;
2016-05-24 03:30:50 -05:00
2018-05-25 04:10:03 -05:00
bool showErrorBars = m_showErrorBars & & ! yErrorValues . empty ( ) ;
2016-05-24 03:30:50 -05:00
QPolygonF points ;
2018-05-25 04:10:03 -05:00
QVector < QwtIntervalSample > errorIntervals ;
2016-05-24 03:30:50 -05:00
std : : vector < std : : pair < size_t , size_t > > filteredIntervals ;
{
2017-11-08 00:38:25 -06:00
std : : vector < double > filteredYValues ;
std : : vector < double > filteredXValues ;
2018-05-25 04:10:03 -05:00
std : : vector < double > filteredYErrorValues ;
2016-05-24 03:30:50 -05:00
{
2018-06-08 08:10:45 -05:00
auto intervalsOfValidValues = RiaCurveDataTools : : calculateIntervalsOfValidValues ( yValues , keepOnlyPositiveValues ) ;
2016-05-24 03:30:50 -05:00
2018-06-08 08:10:45 -05:00
RiaCurveDataTools : : getValuesByIntervals ( yValues , intervalsOfValidValues , & filteredYValues ) ;
RiaCurveDataTools : : getValuesByIntervals ( xValues , intervalsOfValidValues , & filteredXValues ) ;
2016-05-24 03:30:50 -05:00
2018-06-08 08:10:45 -05:00
if ( showErrorBars ) RiaCurveDataTools : : getValuesByIntervals ( yErrorValues , intervalsOfValidValues , & filteredYErrorValues ) ;
2018-05-25 04:10:03 -05:00
2018-06-08 08:10:45 -05:00
filteredIntervals = RiaCurveDataTools : : computePolyLineStartStopIndices ( intervalsOfValidValues ) ;
2016-05-24 03:30:50 -05:00
}
2017-11-08 00:38:25 -06:00
points . reserve ( static_cast < int > ( filteredXValues . size ( ) ) ) ;
2018-05-25 04:10:03 -05:00
errorIntervals . reserve ( static_cast < int > ( filteredXValues . size ( ) ) ) ;
2017-11-08 00:38:25 -06:00
for ( size_t i = 0 ; i < filteredXValues . size ( ) ; i + + )
2016-11-22 04:13:34 -06:00
{
2017-11-08 00:38:25 -06:00
points < < QPointF ( filteredXValues [ i ] , filteredYValues [ i ] ) ;
2018-05-25 04:10:03 -05:00
2018-07-04 02:34:54 -05:00
if ( showErrorBars & & filteredYValues [ i ] ! = DOUBLE_INF & & filteredYErrorValues [ i ] ! = DOUBLE_INF )
2018-05-25 04:10:03 -05:00
{
errorIntervals < < QwtIntervalSample ( filteredXValues [ i ] , filteredYValues [ i ] - filteredYErrorValues [ i ] , filteredYValues [ i ] + filteredYErrorValues [ i ] ) ;
}
2016-11-22 04:13:34 -06:00
}
}
this - > setSamples ( points ) ;
this - > setLineSegmentStartStopIndices ( filteredIntervals ) ;
2018-05-25 04:10:03 -05:00
if ( showErrorBars ) m_errorBars - > setSamples ( errorIntervals ) ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
void RiuQwtPlotCurve : : setSamplesFromXValuesAndYValues ( const std : : vector < double > & xValues , const std : : vector < double > & yValues , bool keepOnlyPositiveValues )
2018-05-25 04:10:03 -05:00
{
setSamplesFromXValuesAndYValues ( xValues , yValues , std : : vector < double > ( ) , keepOnlyPositiveValues ) ;
2016-11-22 04:13:34 -06:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
void RiuQwtPlotCurve : : setSamplesFromDatesAndYValues ( const std : : vector < QDateTime > & dateTimes , const std : : vector < double > & yValues , bool keepOnlyPositiveValues )
2016-11-22 04:13:34 -06:00
{
2018-09-04 02:08:29 -05:00
setSamplesFromXValuesAndYValues ( RiuQwtPlotCurve : : fromQDateTime ( dateTimes ) , yValues , std : : vector < double > ( ) , keepOnlyPositiveValues ) ;
2016-05-24 03:30:50 -05:00
}
2015-09-17 11:42:35 -05:00
//--------------------------------------------------------------------------------------------------
2016-11-22 04:13:34 -06:00
///
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
void RiuQwtPlotCurve : : setSamplesFromTimeTAndYValues ( const std : : vector < time_t > & dateTimes , const std : : vector < double > & yValues , bool keepOnlyPositiveValues )
2016-11-22 04:13:34 -06:00
{
2018-09-04 02:08:29 -05:00
setSamplesFromXValuesAndYValues ( RiuQwtPlotCurve : : fromTime_t ( dateTimes ) , yValues , std : : vector < double > ( ) , keepOnlyPositiveValues ) ;
2018-05-25 04:10:03 -05:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
void RiuQwtPlotCurve : : setSamplesFromTimeTAndYValues ( const std : : vector < time_t > & dateTimes , const std : : vector < double > & yValues , const std : : vector < double > & yErrorValues , bool keepOnlyPositiveValues )
2018-05-25 04:10:03 -05:00
{
2018-09-04 02:08:29 -05:00
setSamplesFromXValuesAndYValues ( RiuQwtPlotCurve : : fromTime_t ( dateTimes ) , yValues , yErrorValues , keepOnlyPositiveValues ) ;
2016-11-22 04:13:34 -06:00
}
//--------------------------------------------------------------------------------------------------
2015-09-17 11:42:35 -05:00
///
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
void RiuQwtPlotCurve : : drawCurve ( QPainter * p , int style ,
2015-09-17 11:42:35 -05:00
const QwtScaleMap & xMap , const QwtScaleMap & yMap ,
const QRectF & canvasRect , int from , int to ) const
{
2015-10-15 16:12:18 -05:00
size_t intervalCount = m_polyLineStartStopIndices . size ( ) ;
2015-09-17 11:42:35 -05:00
if ( intervalCount > 0 )
{
for ( size_t intIdx = 0 ; intIdx < intervalCount ; intIdx + + )
{
2015-11-06 03:08:35 -06:00
if ( m_polyLineStartStopIndices [ intIdx ] . first = = m_polyLineStartStopIndices [ intIdx ] . second )
{
// Use a symbol to draw a single value, as a single value will not be visible
// when using QwtPlotCurve::drawCurve without symbols activated
QwtSymbol symbol ( QwtSymbol : : XCross ) ;
symbol . setSize ( 10 , 10 ) ;
QwtPlotCurve : : drawSymbols ( p , symbol , xMap , yMap , canvasRect , ( int ) m_polyLineStartStopIndices [ intIdx ] . first , ( int ) m_polyLineStartStopIndices [ intIdx ] . second ) ;
}
else
{
QwtPlotCurve : : drawCurve ( p , style , xMap , yMap , canvasRect , ( int ) m_polyLineStartStopIndices [ intIdx ] . first , ( int ) m_polyLineStartStopIndices [ intIdx ] . second ) ;
}
2015-09-17 11:42:35 -05:00
}
}
2015-11-06 03:08:35 -06:00
else
{
QwtPlotCurve : : drawCurve ( p , style , xMap , yMap , canvasRect , from , to ) ;
}
2015-09-17 11:42:35 -05:00
} ;
2015-10-15 04:27:12 -05:00
2016-06-22 03:38:04 -05:00
//--------------------------------------------------------------------------------------------------
/// Drawing symbols but skipping if they are to close to the previous one
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
void RiuQwtPlotCurve : : drawSymbols ( QPainter * painter , const QwtSymbol & symbol ,
2016-06-22 03:38:04 -05:00
const QwtScaleMap & xMap , const QwtScaleMap & yMap ,
const QRectF & canvasRect , int from , int to ) const
{
2018-06-14 07:10:37 -05:00
QwtPointMapper mapper ;
bool filterSymbols = m_symbolSkipPixelDistance > 0 ;
if ( filterSymbols )
2016-06-22 03:38:04 -05:00
{
2018-06-14 07:10:37 -05:00
mapper . setFlag ( QwtPointMapper : : RoundPoints ,
QwtPainter : : roundingAlignment ( painter ) ) ;
mapper . setFlag ( QwtPointMapper : : WeedOutPoints ,
testPaintAttribute ( QwtPlotCurve : : FilterPoints ) ) ;
mapper . setBoundingRect ( canvasRect ) ;
2016-06-22 03:38:04 -05:00
}
2018-06-14 07:10:37 -05:00
const QPolygonF points = mapper . toPointsF ( xMap , yMap , data ( ) , from , to ) ;
2016-06-22 03:38:04 -05:00
int pointCount = points . size ( ) ;
2018-06-14 07:10:37 -05:00
QPolygonF pointsToDisplay ;
2016-06-22 03:38:04 -05:00
2018-06-14 07:10:37 -05:00
if ( filterSymbols )
2016-06-22 03:38:04 -05:00
{
2018-06-14 07:10:37 -05:00
QPointF lastDrawnSymbolPos ;
2016-06-22 03:38:04 -05:00
2018-06-14 07:10:37 -05:00
if ( pointCount > 0 )
{
pointsToDisplay . push_back ( points [ 0 ] ) ;
lastDrawnSymbolPos = points [ 0 ] ;
}
2016-06-22 03:38:04 -05:00
2018-06-14 07:10:37 -05:00
float sqSkipDist = m_symbolSkipPixelDistance * m_symbolSkipPixelDistance ;
2016-06-22 03:38:04 -05:00
2018-06-14 07:10:37 -05:00
for ( int pIdx = 1 ; pIdx < pointCount - 1 ; + + pIdx )
2016-06-22 03:38:04 -05:00
{
2018-06-14 07:10:37 -05:00
QPointF diff = points [ pIdx ] - lastDrawnSymbolPos ;
float sqDistBetweenSymbols = diff . x ( ) * diff . x ( ) + diff . y ( ) * diff . y ( ) ;
if ( sqDistBetweenSymbols > sqSkipDist )
{
pointsToDisplay . push_back ( points [ pIdx ] ) ;
lastDrawnSymbolPos = points [ pIdx ] ;
}
2016-06-22 03:38:04 -05:00
}
2018-06-14 07:10:37 -05:00
if ( pointCount > 1 ) pointsToDisplay . push_back ( points . back ( ) ) ;
}
else
{
pointsToDisplay = points ;
2016-06-22 03:38:04 -05:00
}
2018-06-14 07:10:37 -05:00
if ( pointsToDisplay . size ( ) > 0 )
{
symbol . drawSymbols ( painter , pointsToDisplay ) ;
2018-09-04 02:08:29 -05:00
const RiuQwtSymbol * sym = dynamic_cast < const RiuQwtSymbol * > ( & symbol ) ;
2016-06-22 03:38:04 -05:00
2018-06-14 07:10:37 -05:00
if ( sym & & ! sym - > label ( ) . isEmpty ( ) )
{
for ( auto & pt : pointsToDisplay )
{
2018-08-30 07:03:58 -05:00
sym - > renderSymbolLabel ( painter , pt ) ;
2018-06-14 07:10:37 -05:00
}
}
}
2016-06-22 03:38:04 -05:00
}
2015-10-15 04:27:12 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
void RiuQwtPlotCurve : : setLineSegmentStartStopIndices ( const std : : vector < std : : pair < size_t , size_t > > & lineSegmentStartStopIndices )
2015-11-06 01:32:58 -06:00
{
m_polyLineStartStopIndices = lineSegmentStartStopIndices ;
2015-10-15 04:27:12 -05:00
}
2016-06-22 03:38:04 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
void RiuQwtPlotCurve : : setSymbolSkipPixelDistance ( float distance )
2016-06-22 03:38:04 -05:00
{
m_symbolSkipPixelDistance = distance > = 0.0f ? distance : 0.0f ;
}
2017-11-08 00:38:25 -06:00
2018-05-25 04:10:03 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
void RiuQwtPlotCurve : : attach ( QwtPlot * plot )
2018-05-25 04:10:03 -05:00
{
QwtPlotItem : : attach ( plot ) ;
if ( m_showErrorBars ) m_errorBars - > attach ( plot ) ;
m_attachedToPlot = plot ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
void RiuQwtPlotCurve : : detach ( )
2018-05-25 04:10:03 -05:00
{
QwtPlotItem : : detach ( ) ;
m_errorBars - > detach ( ) ;
m_attachedToPlot = nullptr ;
}
2018-07-09 02:09:51 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
void RiuQwtPlotCurve : : clearErrorBars ( )
2018-07-09 02:09:51 -05:00
{
m_errorBars - > setSamples ( nullptr ) ;
}
2018-05-25 04:10:03 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
void RiuQwtPlotCurve : : showErrorBars ( bool show )
2018-05-25 04:10:03 -05:00
{
m_showErrorBars = show ;
if ( m_showErrorBars & & m_attachedToPlot ) m_errorBars - > attach ( m_attachedToPlot ) ;
else m_errorBars - > detach ( ) ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
void RiuQwtPlotCurve : : setErrorBarsColor ( QColor color )
2018-05-25 04:10:03 -05:00
{
QwtIntervalSymbol * newSymbol = new QwtIntervalSymbol ( QwtIntervalSymbol : : Bar ) ;
newSymbol - > setPen ( QPen ( color ) ) ;
m_errorBars - > setSymbol ( newSymbol ) ;
}
2018-09-04 02:08:29 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotCurve : : setAppearance ( LineStyleEnum lineStyle ,
2019-02-25 01:29:16 -06:00
CurveInterpolationEnum interpolationType ,
int requestedCurveThickness ,
const QColor & curveColor )
2018-09-04 02:08:29 -05:00
{
QwtPlotCurve : : CurveStyle curveStyle = QwtPlotCurve : : NoCurve ;
2019-02-21 05:52:23 -06:00
Qt : : PenStyle penStyle = Qt : : NoPen ;
2018-09-04 02:08:29 -05:00
2019-02-25 01:29:16 -06:00
// Qwt bug workaround (#4135): need to set 0 curve thickness for STYLE_NONE
int curveThickness = 0 ;
2018-09-04 02:08:29 -05:00
if ( lineStyle ! = STYLE_NONE )
{
2019-02-25 01:29:16 -06:00
curveThickness = requestedCurveThickness ;
2018-09-04 02:08:29 -05:00
switch ( interpolationType )
{
case INTERPOLATION_STEP_LEFT :
curveStyle = QwtPlotCurve : : Steps ;
setCurveAttribute ( QwtPlotCurve : : Inverted , false ) ;
break ;
case INTERPOLATION_POINT_TO_POINT : // Fall through
default :
curveStyle = QwtPlotCurve : : Lines ;
break ;
}
switch ( lineStyle )
{
case STYLE_SOLID :
penStyle = Qt : : SolidLine ;
break ;
case STYLE_DASH :
penStyle = Qt : : DashLine ;
break ;
case STYLE_DOT :
penStyle = Qt : : DotLine ;
break ;
case STYLE_DASH_DOT :
penStyle = Qt : : DashDotLine ;
break ;
default :
break ;
}
}
QPen curvePen ( curveColor ) ;
curvePen . setWidth ( curveThickness ) ;
curvePen . setStyle ( penStyle ) ;
setPen ( curvePen ) ;
setStyle ( curveStyle ) ;
}
2019-03-28 14:09:25 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotCurve : : setBlackAndWhiteLegendIcon ( bool blackAndWhite )
{
m_blackAndWhiteLegendIcon = blackAndWhite ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QwtGraphic RiuQwtPlotCurve : : legendIcon ( int index , const QSizeF & size ) const
{
QwtGraphic icon = QwtPlotCurve : : legendIcon ( index , size ) ;
if ( m_blackAndWhiteLegendIcon )
{
QImage image = icon . toImage ( ) ;
RiaImageTools : : makeGrayScale ( image ) ;
QPainter painter ( & icon ) ;
painter . drawImage ( QPoint ( 0 , 0 ) , image ) ;
}
return icon ;
}
2017-11-08 00:38:25 -06:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
std : : vector < double > RiuQwtPlotCurve : : fromQDateTime ( const std : : vector < QDateTime > & dateTimes )
2017-11-08 00:38:25 -06:00
{
std : : vector < double > doubleValues ;
if ( ! dateTimes . empty ( ) )
{
doubleValues . reserve ( dateTimes . size ( ) ) ;
for ( const auto & dt : dateTimes )
{
doubleValues . push_back ( QwtDate : : toDouble ( dt ) ) ;
}
}
return doubleValues ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 02:08:29 -05:00
std : : vector < double > RiuQwtPlotCurve : : fromTime_t ( const std : : vector < time_t > & timeSteps )
2017-11-08 00:38:25 -06:00
{
std : : vector < double > doubleValues ;
if ( ! timeSteps . empty ( ) )
{
doubleValues . reserve ( timeSteps . size ( ) ) ;
for ( const auto & time : timeSteps )
{
double milliSecSinceEpoch = time * 1000 ; // This is kind of hack, as the c++ standard does not state what time_t is. "Almost always" secs since epoch according to cppreference.com
doubleValues . push_back ( milliSecSinceEpoch ) ;
}
}
return doubleValues ;
}