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.
//
/////////////////////////////////////////////////////////////////////////////////
2015-11-06 08:32:58 +01:00
#include "RiuLineSegmentQwtPlotCurve.h"
2015-09-17 18:42:35 +02:00
2015-11-06 10:08:35 +01:00
#include "qwt_symbol.h"
2018-06-08 15:10:45 +02:00
#include "RiaCurveDataTools.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
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2015-11-06 10:08:35 +01:00
RiuLineSegmentQwtPlotCurve :: RiuLineSegmentQwtPlotCurve ( const QString & title )
: 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-05-25 11:10:03 +02:00
m_showErrorBars = true ;
m_attachedToPlot = nullptr ;
2015-09-17 18:42:35 +02:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2015-11-06 08:32:58 +01:00
RiuLineSegmentQwtPlotCurve ::~ RiuLineSegmentQwtPlotCurve ()
2015-09-17 18:42:35 +02:00
{
}
2016-05-24 10:30:50 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2018-05-25 11:10:03 +02:00
void RiuLineSegmentQwtPlotCurve :: 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 );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
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 11:13:34 +01:00
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-12-04 20:00:56 +01:00
void RiuLineSegmentQwtPlotCurve :: setSamplesFromDatesAndYValues ( const std :: vector < QDateTime >& dateTimes , const std :: vector < double >& yValues , bool keepOnlyPositiveValues )
2016-11-22 11:13:34 +01:00
{
2018-05-25 11:10:03 +02:00
setSamplesFromXValuesAndYValues ( RiuLineSegmentQwtPlotCurve :: 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
///
//--------------------------------------------------------------------------------------------------
2017-12-04 20:00:56 +01:00
void RiuLineSegmentQwtPlotCurve :: setSamplesFromTimeTAndYValues ( const std :: vector < time_t >& dateTimes , const std :: vector < double >& yValues , bool keepOnlyPositiveValues )
2016-11-22 11:13:34 +01:00
{
2018-05-25 11:10:03 +02: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 11:13:34 +01:00
}
//--------------------------------------------------------------------------------------------------
2015-09-17 18:42:35 +02:00
///
//--------------------------------------------------------------------------------------------------
2015-11-06 08:32:58 +01:00
void RiuLineSegmentQwtPlotCurve :: 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
//--------------------------------------------------------------------------------------------------
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 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 );
const RiuCurveQwtSymbol * sym = dynamic_cast < const RiuCurveQwtSymbol *> ( & 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 )
{
int width = painter -> fontMetrics (). width ( sym -> label ());
painter -> drawText ( pt . x () - width / 2 , pt . y () - 5 , sym -> label ());
}
}
}
2016-06-22 10:38:04 +02:00
}
2015-10-15 11:27:12 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2015-11-06 10:08:35 +01:00
void RiuLineSegmentQwtPlotCurve :: 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
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuLineSegmentQwtPlotCurve :: setSymbolSkipPixelDistance ( float distance )
{
m_symbolSkipPixelDistance = distance >= 0.0f ? distance : 0.0f ;
}
2017-11-08 07:38:25 +01:00
2018-05-25 11:10:03 +02: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 09:09:51 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuLineSegmentQwtPlotCurve :: clearErrorBars ()
{
m_errorBars -> setSamples ( nullptr );
}
2018-05-25 11:10:03 +02: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 07:38:25 +01: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 14:10:37 +02: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 );
}
}
}