2015-09-17 18:42:35 +02: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 09:08:29 +02:00
#include "RiuQwtPlotCurve.h"
2015-09-17 18:42:35 +02:00
2018-06-08 15:10:45 +02:00
#include "RiaCurveDataTools.h"
2018-09-04 09:08:29 +02:00
#include "RiuQwtSymbol.h"
#include "qwt_symbol.h"
2016-05-24 10:30:50 +02:00
#include "qwt_date.h"
2016-06-22 10:38:04 +02:00
#include "qwt_point_mapper.h"
#include "qwt_painter.h"
2018-05-25 11:10:03 +02:00
#include "qwt_plot_intervalcurve.h"
#include "qwt_scale_map.h"
#include "qwt_interval_symbol.h"
2015-10-15 11:27:12 +02:00
2018-07-04 09:53:00 +02:00
#include <limits>
2018-07-04 09:34:54 +02:00
//--------------------------------------------------------------------------------------------------
/// Internal constants
//--------------------------------------------------------------------------------------------------
#define DOUBLE_INF std::numeric_limits<double>::infinity()
2015-09-17 18:42:35 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 09:08:29 +02:00
RiuQwtPlotCurve :: RiuQwtPlotCurve ( const QString & title )
2015-11-06 10:08:35 +01:00
: QwtPlotCurve ( title )
2015-09-17 18:42:35 +02:00
{
2016-06-22 10:38:04 +02: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 11:10:03 +02:00
m_errorBars = new QwtPlotIntervalCurve ();
m_errorBars -> setStyle ( QwtPlotIntervalCurve :: CurveStyle :: NoCurve );
m_errorBars -> setSymbol ( new QwtIntervalSymbol ( QwtIntervalSymbol :: Bar ));
2018-05-28 13:45:30 +02:00
m_errorBars -> setItemAttribute ( QwtPlotItem :: Legend , false );
2018-12-05 09:35:05 +01:00
m_errorBars -> setZ ( Z_ERROR_BARS );
2018-05-25 11:10:03 +02:00
m_showErrorBars = true ;
m_attachedToPlot = nullptr ;
2015-09-17 18:42:35 +02:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 09:08:29 +02:00
RiuQwtPlotCurve ::~ RiuQwtPlotCurve ()
2015-09-17 18:42:35 +02:00
{
}
2016-05-24 10:30:50 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 09:08:29 +02:00
void RiuQwtPlotCurve :: setSamplesFromXValuesAndYValues ( const std :: vector < double >& xValues , const std :: vector < double >& yValues , const std :: vector < double >& yErrorValues , bool keepOnlyPositiveValues )
2016-05-24 10:30:50 +02:00
{
2017-11-08 07:38:25 +01:00
CVF_ASSERT ( xValues . size () == yValues . size ());
2018-05-25 11:10:03 +02:00
CVF_ASSERT ( yErrorValues . empty () || yErrorValues . size () == xValues . size ());
2016-05-24 10:30:50 +02:00
2018-05-25 11:10:03 +02:00
bool showErrorBars = m_showErrorBars && ! yErrorValues . empty ();
2016-05-24 10:30:50 +02:00
QPolygonF points ;
2018-05-25 11:10:03 +02:00
QVector < QwtIntervalSample > errorIntervals ;
2016-05-24 10:30:50 +02:00
std :: vector < std :: pair < size_t , size_t > > filteredIntervals ;
{
2017-11-08 07:38:25 +01:00
std :: vector < double > filteredYValues ;
std :: vector < double > filteredXValues ;
2018-05-25 11:10:03 +02:00
std :: vector < double > filteredYErrorValues ;
2016-05-24 10:30:50 +02:00
{
2018-06-08 15:10:45 +02:00
auto intervalsOfValidValues = RiaCurveDataTools :: calculateIntervalsOfValidValues ( yValues , keepOnlyPositiveValues );
2016-05-24 10:30:50 +02:00
2018-06-08 15:10:45 +02:00
RiaCurveDataTools :: getValuesByIntervals ( yValues , intervalsOfValidValues , & filteredYValues );
RiaCurveDataTools :: getValuesByIntervals ( xValues , intervalsOfValidValues , & filteredXValues );
2016-05-24 10:30:50 +02:00
2018-06-08 15:10:45 +02:00
if ( showErrorBars ) RiaCurveDataTools :: getValuesByIntervals ( yErrorValues , intervalsOfValidValues , & filteredYErrorValues );
2018-05-25 11:10:03 +02:00
2018-06-08 15:10:45 +02:00
filteredIntervals = RiaCurveDataTools :: computePolyLineStartStopIndices ( intervalsOfValidValues );
2016-05-24 10:30:50 +02:00
}
2017-11-08 07:38:25 +01:00
points . reserve ( static_cast < int > ( filteredXValues . size ()));
2018-05-25 11:10:03 +02:00
errorIntervals . reserve ( static_cast < int > ( filteredXValues . size ()));
2017-11-08 07:38:25 +01:00
for ( size_t i = 0 ; i < filteredXValues . size (); i ++ )
2016-11-22 11:13:34 +01:00
{
2017-11-08 07:38:25 +01:00
points << QPointF ( filteredXValues [ i ], filteredYValues [ i ]);
2018-05-25 11:10:03 +02:00
2018-07-04 09:34:54 +02:00
if ( showErrorBars && filteredYValues [ i ] != DOUBLE_INF && filteredYErrorValues [ i ] != DOUBLE_INF )
2018-05-25 11:10:03 +02:00
{
errorIntervals << QwtIntervalSample ( filteredXValues [ i ], filteredYValues [ i ] - filteredYErrorValues [ i ], filteredYValues [ i ] + filteredYErrorValues [ i ]);
}
2016-11-22 11:13:34 +01:00
}
}
this -> setSamples ( points );
this -> setLineSegmentStartStopIndices ( filteredIntervals );
2018-05-25 11:10:03 +02:00
if ( showErrorBars ) m_errorBars -> setSamples ( errorIntervals );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 09:08:29 +02:00
void RiuQwtPlotCurve :: setSamplesFromXValuesAndYValues ( const std :: vector < double >& xValues , const std :: vector < double >& yValues , bool keepOnlyPositiveValues )
2018-05-25 11:10:03 +02:00
{
setSamplesFromXValuesAndYValues ( xValues , yValues , std :: vector < double > (), keepOnlyPositiveValues );
2016-11-22 11:13:34 +01:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 09:08:29 +02:00
void RiuQwtPlotCurve :: setSamplesFromDatesAndYValues ( const std :: vector < QDateTime >& dateTimes , const std :: vector < double >& yValues , bool keepOnlyPositiveValues )
2016-11-22 11:13:34 +01:00
{
2018-09-04 09:08:29 +02:00
setSamplesFromXValuesAndYValues ( RiuQwtPlotCurve :: fromQDateTime ( dateTimes ), yValues , std :: vector < double > (), keepOnlyPositiveValues );
2016-05-24 10:30:50 +02:00
}
2015-09-17 18:42:35 +02:00
//--------------------------------------------------------------------------------------------------
2016-11-22 11:13:34 +01:00
///
//--------------------------------------------------------------------------------------------------
2018-09-04 09:08:29 +02:00
void RiuQwtPlotCurve :: setSamplesFromTimeTAndYValues ( const std :: vector < time_t >& dateTimes , const std :: vector < double >& yValues , bool keepOnlyPositiveValues )
2016-11-22 11:13:34 +01:00
{
2018-09-04 09:08:29 +02:00
setSamplesFromXValuesAndYValues ( RiuQwtPlotCurve :: fromTime_t ( dateTimes ), yValues , std :: vector < double > (), keepOnlyPositiveValues );
2018-05-25 11:10:03 +02:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 09:08:29 +02: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 11:10:03 +02:00
{
2018-09-04 09:08:29 +02:00
setSamplesFromXValuesAndYValues ( RiuQwtPlotCurve :: fromTime_t ( dateTimes ), yValues , yErrorValues , keepOnlyPositiveValues );
2016-11-22 11:13:34 +01:00
}
//--------------------------------------------------------------------------------------------------
2015-09-17 18:42:35 +02:00
///
//--------------------------------------------------------------------------------------------------
2018-09-04 09:08:29 +02:00
void RiuQwtPlotCurve :: drawCurve ( QPainter * p , int style ,
2015-09-17 18:42:35 +02:00
const QwtScaleMap & xMap , const QwtScaleMap & yMap ,
const QRectF & canvasRect , int from , int to ) const
{
2015-10-15 23:12:18 +02:00
size_t intervalCount = m_polyLineStartStopIndices . size ();
2015-09-17 18:42:35 +02:00
if ( intervalCount > 0 )
{
for ( size_t intIdx = 0 ; intIdx < intervalCount ; intIdx ++ )
{
2015-11-06 10:08:35 +01: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 18:42:35 +02:00
}
}
2015-11-06 10:08:35 +01:00
else
{
QwtPlotCurve :: drawCurve ( p , style , xMap , yMap , canvasRect , from , to );
}
2015-09-17 18:42:35 +02:00
};
2015-10-15 11:27:12 +02:00
2016-06-22 10:38:04 +02:00
//--------------------------------------------------------------------------------------------------
/// Drawing symbols but skipping if they are to close to the previous one
//--------------------------------------------------------------------------------------------------
2018-09-04 09:08:29 +02:00
void RiuQwtPlotCurve :: drawSymbols ( QPainter * painter , const QwtSymbol & symbol ,
2016-06-22 10:38:04 +02:00
const QwtScaleMap & xMap , const QwtScaleMap & yMap ,
const QRectF & canvasRect , int from , int to ) const
{
2018-06-14 14:10:37 +02:00
QwtPointMapper mapper ;
bool filterSymbols = m_symbolSkipPixelDistance > 0 ;
if ( filterSymbols )
2016-06-22 10:38:04 +02:00
{
2018-06-14 14:10:37 +02:00
mapper . setFlag ( QwtPointMapper :: RoundPoints ,
QwtPainter :: roundingAlignment ( painter ));
mapper . setFlag ( QwtPointMapper :: WeedOutPoints ,
testPaintAttribute ( QwtPlotCurve :: FilterPoints ));
mapper . setBoundingRect ( canvasRect );
2016-06-22 10:38:04 +02:00
}
2018-06-14 14:10:37 +02:00
const QPolygonF points = mapper . toPointsF ( xMap , yMap , data (), from , to );
2016-06-22 10:38:04 +02:00
int pointCount = points . size ();
2018-06-14 14:10:37 +02:00
QPolygonF pointsToDisplay ;
2016-06-22 10:38:04 +02:00
2018-06-14 14:10:37 +02:00
if ( filterSymbols )
2016-06-22 10:38:04 +02:00
{
2018-06-14 14:10:37 +02:00
QPointF lastDrawnSymbolPos ;
2016-06-22 10:38:04 +02:00
2018-06-14 14:10:37 +02:00
if ( pointCount > 0 )
{
pointsToDisplay . push_back ( points [ 0 ]);
lastDrawnSymbolPos = points [ 0 ];
}
2016-06-22 10:38:04 +02:00
2018-06-14 14:10:37 +02:00
float sqSkipDist = m_symbolSkipPixelDistance * m_symbolSkipPixelDistance ;
2016-06-22 10:38:04 +02:00
2018-06-14 14:10:37 +02:00
for ( int pIdx = 1 ; pIdx < pointCount - 1 ; ++ pIdx )
2016-06-22 10:38:04 +02:00
{
2018-06-14 14:10:37 +02: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 10:38:04 +02:00
}
2018-06-14 14:10:37 +02:00
if ( pointCount > 1 ) pointsToDisplay . push_back ( points . back ());
}
else
{
pointsToDisplay = points ;
2016-06-22 10:38:04 +02:00
}
2018-06-14 14:10:37 +02:00
if ( pointsToDisplay . size () > 0 )
{
symbol . drawSymbols ( painter , pointsToDisplay );
2018-09-04 09:08:29 +02:00
const RiuQwtSymbol * sym = dynamic_cast < const RiuQwtSymbol *> ( & symbol );
2016-06-22 10:38:04 +02:00
2018-06-14 14:10:37 +02:00
if ( sym && ! sym -> label (). isEmpty ())
{
for ( auto & pt : pointsToDisplay )
{
2018-08-30 14:03:58 +02:00
sym -> renderSymbolLabel ( painter , pt );
2018-06-14 14:10:37 +02:00
}
}
}
2016-06-22 10:38:04 +02:00
}
2015-10-15 11:27:12 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 09:08:29 +02:00
void RiuQwtPlotCurve :: setLineSegmentStartStopIndices ( const std :: vector < std :: pair < size_t , size_t > >& lineSegmentStartStopIndices )
2015-11-06 08:32:58 +01:00
{
m_polyLineStartStopIndices = lineSegmentStartStopIndices ;
2015-10-15 11:27:12 +02:00
}
2016-06-22 10:38:04 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 09:08:29 +02:00
void RiuQwtPlotCurve :: setSymbolSkipPixelDistance ( float distance )
2016-06-22 10:38:04 +02:00
{
m_symbolSkipPixelDistance = distance >= 0.0f ? distance : 0.0f ;
}
2017-11-08 07:38:25 +01:00
2018-05-25 11:10:03 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 09:08:29 +02:00
void RiuQwtPlotCurve :: attach ( QwtPlot * plot )
2018-05-25 11:10:03 +02:00
{
QwtPlotItem :: attach ( plot );
if ( m_showErrorBars ) m_errorBars -> attach ( plot );
m_attachedToPlot = plot ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 09:08:29 +02:00
void RiuQwtPlotCurve :: detach ()
2018-05-25 11:10:03 +02:00
{
QwtPlotItem :: detach ();
m_errorBars -> detach ();
m_attachedToPlot = nullptr ;
}
2018-07-09 09:09:51 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 09:08:29 +02:00
void RiuQwtPlotCurve :: clearErrorBars ()
2018-07-09 09:09:51 +02:00
{
m_errorBars -> setSamples ( nullptr );
}
2018-05-25 11:10:03 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 09:08:29 +02:00
void RiuQwtPlotCurve :: showErrorBars ( bool show )
2018-05-25 11:10:03 +02:00
{
m_showErrorBars = show ;
if ( m_showErrorBars && m_attachedToPlot ) m_errorBars -> attach ( m_attachedToPlot );
else m_errorBars -> detach ();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 09:08:29 +02:00
void RiuQwtPlotCurve :: setErrorBarsColor ( QColor color )
2018-05-25 11:10:03 +02:00
{
QwtIntervalSymbol * newSymbol = new QwtIntervalSymbol ( QwtIntervalSymbol :: Bar );
newSymbol -> setPen ( QPen ( color ));
m_errorBars -> setSymbol ( newSymbol );
}
2018-09-04 09:08:29 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQwtPlotCurve :: setAppearance ( LineStyleEnum lineStyle ,
CurveInterpolationEnum interpolationType ,
int curveThickness ,
const QColor & curveColor )
{
QwtPlotCurve :: CurveStyle curveStyle = QwtPlotCurve :: NoCurve ;
Qt :: PenStyle penStyle = Qt :: SolidLine ;
if ( lineStyle != STYLE_NONE )
{
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 );
}
2017-11-08 07:38:25 +01:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-09-04 09:08:29 +02:00
std :: vector < double > RiuQwtPlotCurve :: fromQDateTime ( const std :: vector < QDateTime >& dateTimes )
2017-11-08 07:38:25 +01: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 09:08:29 +02:00
std :: vector < double > RiuQwtPlotCurve :: fromTime_t ( const std :: vector < time_t >& timeSteps )
2017-11-08 07:38:25 +01: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 ;
}