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.
//
/////////////////////////////////////////////////////////////////////////////////
2015-11-06 01:32:58 -06:00
# include "RiuLineSegmentQwtPlotCurve.h"
2015-09-17 11:42:35 -05:00
2015-11-06 03:08:35 -06:00
# include "qwt_symbol.h"
2018-06-08 08:10:45 -05:00
# include "RiaCurveDataTools.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
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2015-11-06 03:08:35 -06:00
RiuLineSegmentQwtPlotCurve : : RiuLineSegmentQwtPlotCurve ( const QString & title )
: 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-05-25 04:10:03 -05:00
m_showErrorBars = true ;
m_attachedToPlot = nullptr ;
2015-09-17 11:42:35 -05:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2015-11-06 01:32:58 -06:00
RiuLineSegmentQwtPlotCurve : : ~ RiuLineSegmentQwtPlotCurve ( )
2015-09-17 11:42:35 -05:00
{
}
2016-05-24 03:30:50 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-05-25 04:10:03 -05:00
void RiuLineSegmentQwtPlotCurve : : 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 ) ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuLineSegmentQwtPlotCurve : : setSamplesFromXValuesAndYValues ( const std : : vector < double > & xValues , const std : : vector < double > & yValues , bool keepOnlyPositiveValues )
{
setSamplesFromXValuesAndYValues ( xValues , yValues , std : : vector < double > ( ) , keepOnlyPositiveValues ) ;
2016-11-22 04:13:34 -06:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-12-04 13:00:56 -06:00
void RiuLineSegmentQwtPlotCurve : : setSamplesFromDatesAndYValues ( const std : : vector < QDateTime > & dateTimes , const std : : vector < double > & yValues , bool keepOnlyPositiveValues )
2016-11-22 04:13:34 -06:00
{
2018-05-25 04:10:03 -05:00
setSamplesFromXValuesAndYValues ( RiuLineSegmentQwtPlotCurve : : 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
///
//--------------------------------------------------------------------------------------------------
2017-12-04 13:00:56 -06:00
void RiuLineSegmentQwtPlotCurve : : setSamplesFromTimeTAndYValues ( const std : : vector < time_t > & dateTimes , const std : : vector < double > & yValues , bool keepOnlyPositiveValues )
2016-11-22 04:13:34 -06:00
{
2018-05-25 04:10:03 -05:00
setSamplesFromXValuesAndYValues ( RiuLineSegmentQwtPlotCurve : : fromTime_t ( dateTimes ) , yValues , std : : vector < double > ( ) , keepOnlyPositiveValues ) ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuLineSegmentQwtPlotCurve : : setSamplesFromTimeTAndYValues ( const std : : vector < time_t > & dateTimes , const std : : vector < double > & yValues , const std : : vector < double > & yErrorValues , bool keepOnlyPositiveValues )
{
setSamplesFromXValuesAndYValues ( RiuLineSegmentQwtPlotCurve : : fromTime_t ( dateTimes ) , yValues , yErrorValues , keepOnlyPositiveValues ) ;
2016-11-22 04:13:34 -06:00
}
//--------------------------------------------------------------------------------------------------
2015-09-17 11:42:35 -05:00
///
//--------------------------------------------------------------------------------------------------
2015-11-06 01:32:58 -06:00
void RiuLineSegmentQwtPlotCurve : : 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
//--------------------------------------------------------------------------------------------------
void RiuLineSegmentQwtPlotCurve : : drawSymbols ( QPainter * painter , const QwtSymbol & symbol ,
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 ) ;
const RiuCurveQwtSymbol * sym = dynamic_cast < const RiuCurveQwtSymbol * > ( & 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 )
{
int width = painter - > fontMetrics ( ) . width ( sym - > label ( ) ) ;
painter - > drawText ( pt . x ( ) - width / 2 , pt . y ( ) - 5 , sym - > label ( ) ) ;
}
}
}
2016-06-22 03:38:04 -05:00
}
2015-10-15 04:27:12 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2015-11-06 03:08:35 -06:00
void RiuLineSegmentQwtPlotCurve : : 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
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuLineSegmentQwtPlotCurve : : setSymbolSkipPixelDistance ( float distance )
{
m_symbolSkipPixelDistance = distance > = 0.0f ? distance : 0.0f ;
}
2017-11-08 00:38:25 -06:00
2018-05-25 04:10:03 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuLineSegmentQwtPlotCurve : : attach ( QwtPlot * plot )
{
QwtPlotItem : : attach ( plot ) ;
if ( m_showErrorBars ) m_errorBars - > attach ( plot ) ;
m_attachedToPlot = plot ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuLineSegmentQwtPlotCurve : : detach ( )
{
QwtPlotItem : : detach ( ) ;
m_errorBars - > detach ( ) ;
m_attachedToPlot = nullptr ;
}
2018-07-09 02:09:51 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuLineSegmentQwtPlotCurve : : clearErrorBars ( )
{
m_errorBars - > setSamples ( nullptr ) ;
}
2018-05-25 04:10:03 -05:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuLineSegmentQwtPlotCurve : : showErrorBars ( bool show )
{
m_showErrorBars = show ;
if ( m_showErrorBars & & m_attachedToPlot ) m_errorBars - > attach ( m_attachedToPlot ) ;
else m_errorBars - > detach ( ) ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuLineSegmentQwtPlotCurve : : setErrorBarsColor ( QColor color )
{
QwtIntervalSymbol * newSymbol = new QwtIntervalSymbol ( QwtIntervalSymbol : : Bar ) ;
newSymbol - > setPen ( QPen ( color ) ) ;
m_errorBars - > setSymbol ( newSymbol ) ;
}
2017-11-08 00:38:25 -06:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std : : vector < double > RiuLineSegmentQwtPlotCurve : : fromQDateTime ( const std : : vector < QDateTime > & dateTimes )
{
std : : vector < double > doubleValues ;
if ( ! dateTimes . empty ( ) )
{
doubleValues . reserve ( dateTimes . size ( ) ) ;
for ( const auto & dt : dateTimes )
{
doubleValues . push_back ( QwtDate : : toDouble ( dt ) ) ;
}
}
return doubleValues ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std : : vector < double > RiuLineSegmentQwtPlotCurve : : fromTime_t ( const std : : vector < time_t > & timeSteps )
{
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 ;
}
2018-06-14 07:10:37 -05:00
//--------------------------------------------------------------------------------------------------
/// Internal class to support labels on symbols
//--------------------------------------------------------------------------------------------------
RiuCurveQwtSymbol : : RiuCurveQwtSymbol ( QwtSymbol : : Style style , const QString & label ) : QwtSymbol ( style ) , m_label ( label )
{
}
void RiuCurveQwtSymbol : : renderSymbols ( QPainter * painter , const QPointF * points , int numPoints ) const
{
QwtSymbol : : renderSymbols ( painter , points , numPoints ) ;
if ( ! m_label . isEmpty ( ) )
{
for ( int i = 0 ; i < numPoints ; i + + )
{
auto pt = points [ i ] ;
int width = painter - > fontMetrics ( ) . width ( m_label ) ;
painter - > drawText ( pt . x ( ) - width / 2 , pt . y ( ) - 5 , m_label ) ;
}
}
}