ResInsight/ApplicationLibCode/UserInterface/RiuQtChartsPlotCurve.cpp
Magne Sjaastad 952e766c2f
Update clang-format.yml (#10068)
* Update to clang-format-15
Removed two custom .clang-format files in subfolders of AppFwk

* Fixes by clang-format
2023-04-13 07:05:53 +02:00

638 lines
22 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2021- Equinor ASA
//
// 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.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RiuQtChartsPlotCurve.h"
#include "RiaPlotDefines.h"
#include "RiuQtChartsPlotCurveSymbol.h"
#include "RiuQtChartsPlotWidget.h"
#include "cvfBoundingBox.h"
#include <QLegend>
#include <QLegendMarker>
#include <QtCharts/QChartView>
#include <QtCharts/QDateTimeAxis>
#include <QtCharts/QValueAxis>
#include <limits>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuQtChartsPlotCurve::RiuQtChartsPlotCurve( RimPlotCurve* ownerRimCurve, const QString& title )
: RiuPlotCurve( ownerRimCurve, title )
{
m_plotWidget = nullptr;
m_lineSeries = new QtCharts::QLineSeries();
m_lineSeries->setName( title );
m_areaSeries = new QtCharts::QAreaSeries();
m_areaSeries->setName( title );
m_scatterSeries = new QtCharts::QScatterSeries();
m_scatterSeries->setName( title );
m_axisX = RiuPlotAxis::defaultBottom();
m_axisY = RiuPlotAxis::defaultLeft();
m_isVisibleInLegend = false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuQtChartsPlotCurve::~RiuQtChartsPlotCurve()
{
if ( m_plotWidget && m_plotWidget->qtChart() )
{
m_plotWidget->detach( this );
auto* line = lineSeries();
if ( line )
{
m_plotWidget->qtChart()->removeSeries( line );
// removeSeries() releases chart ownership of the data, delete data to avoid memory leak
delete line;
}
auto* area = areaSeries();
if ( area )
{
m_plotWidget->qtChart()->removeSeries( area );
// removeSeries() releases chart ownership of the data, delete data to avoid memory leak
delete area;
}
auto* scatter = scatterSeries();
if ( scatter )
{
m_plotWidget->qtChart()->removeSeries( scatter );
// removeSeries() releases chart ownership of the data, delete data to avoid memory leak
delete scatter;
}
}
// Delete if it is still owned by by plot curve
delete m_lineSeries;
m_lineSeries = nullptr;
delete m_areaSeries;
m_areaSeries = nullptr;
delete m_scatterSeries;
m_scatterSeries = nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::setTitle( const QString& title )
{
if ( !isQtChartObjectsPresent() ) return;
lineSeries()->setName( title );
areaSeries()->setName( title );
scatterSeries()->setName( title );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::setAppearance( RiuQwtPlotCurveDefines::LineStyleEnum lineStyle,
RiuQwtPlotCurveDefines::CurveInterpolationEnum interpolationType,
int requestedCurveThickness,
const QColor& curveColor,
const QBrush& fillBrush /* = QBrush( Qt::NoBrush )*/ )
{
if ( !isQtChartObjectsPresent() ) return;
Qt::PenStyle penStyle = RiuQwtPlotCurveDefines::convertToPenStyle( lineStyle );
QPen curvePen( curveColor );
curvePen.setWidth( requestedCurveThickness );
curvePen.setStyle( penStyle );
lineSeries()->setPen( curvePen );
lineSeries()->setBrush( fillBrush );
areaSeries()->setPen( curvePen );
areaSeries()->setBrush( fillBrush );
if ( fillBrush.style() == Qt::NoBrush )
{
lineSeries()->show();
areaSeries()->hide();
setVisibleInLegend( m_isVisibleInLegend );
}
else
{
lineSeries()->hide();
areaSeries()->show();
setVisibleInLegend( m_isVisibleInLegend );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::setBrush( const QBrush& brush )
{
if ( !isQtChartObjectsPresent() ) return;
lineSeries()->setBrush( brush );
areaSeries()->setBrush( brush );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::setColor( const QColor& color )
{
QPen curvePen = lineSeries()->pen();
curvePen.setColor( color );
lineSeries()->setPen( curvePen );
curvePen = areaSeries()->pen();
curvePen.setColor( color );
areaSeries()->setPen( curvePen );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::attachToPlot( RiuPlotWidget* plotWidget )
{
m_plotWidget = dynamic_cast<RiuQtChartsPlotWidget*>( plotWidget );
CAF_ASSERT( m_plotWidget );
if ( m_plotWidget->getLineSeries( this ) && m_plotWidget->getScatterSeries( this ) )
{
m_plotWidget->qtChart()->legend()->setMarkerShape( QtCharts::QLegend::MarkerShape::MarkerShapeFromSeries );
setVisibleInLegend( true );
lineSeries()->show();
}
else
{
if ( !m_lineSeries ) m_lineSeries = new QtCharts::QLineSeries();
if ( !m_areaSeries ) m_areaSeries = new QtCharts::QAreaSeries();
if ( !m_scatterSeries ) m_scatterSeries = new QtCharts::QScatterSeries();
m_plotWidget->attach( this, m_lineSeries, m_areaSeries, m_scatterSeries, m_axisX, m_axisY );
// Plot widget takes ownership.
m_lineSeries = nullptr;
m_areaSeries = nullptr;
m_scatterSeries = nullptr;
}
if ( scatterSeries() )
{
if ( m_symbol )
{
scatterSeries()->show();
auto qtChartsSymbol = dynamic_cast<RiuQtChartsPlotCurveSymbol*>( m_symbol.get() );
CAF_ASSERT( qtChartsSymbol );
qtChartsSymbol->applyToScatterSeries( scatterSeries() );
}
else
{
scatterSeries()->hide();
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::detach()
{
QtCharts::QLineSeries* line = lineSeries();
if ( line )
{
line->hide();
}
QtCharts::QAreaSeries* area = areaSeries();
if ( area )
{
area->hide();
}
if ( scatterSeries() )
{
scatterSeries()->hide();
}
if ( m_plotWidget )
{
m_plotWidget->detach( this );
setVisibleInLegend( false );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::showInPlot()
{
// show();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::setSamplesInPlot( const std::vector<double>& xValues, const std::vector<double>& yValues )
{
if ( !isQtChartObjectsPresent() ) return;
CAF_ASSERT( xValues.size() == yValues.size() );
QVector<QPointF> values( static_cast<int>( xValues.size() ) );
for ( int i = 0; i < static_cast<int>( xValues.size() ); i++ )
{
values[i] = QPointF( xValues[i], yValues[i] );
}
QtCharts::QLineSeries* line = lineSeries();
line->replace( values );
QtCharts::QLineSeries* upper = new QtCharts::QLineSeries;
upper->replace( values );
areaSeries()->setUpperSeries( upper );
updateScatterSeries();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::updateScatterSeries()
{
if ( !scatterSeries() ) return;
double minX = std::numeric_limits<double>::max();
double maxX = -std::numeric_limits<double>::max();
QVector<QPointF> points;
if ( lineSeries() )
{
points = lineSeries()->pointsVector();
bool foundAxis = false;
auto axes = lineSeries()->attachedAxes();
for ( auto axis : axes )
{
if ( axis->orientation() == Qt::Orientation::Horizontal )
{
QtCharts::QValueAxis* valueAxis = dynamic_cast<QtCharts::QValueAxis*>( axis );
QtCharts::QDateTimeAxis* dateTimeAxis = dynamic_cast<QtCharts::QDateTimeAxis*>( axis );
if ( valueAxis )
{
minX = valueAxis->min();
maxX = valueAxis->max();
foundAxis = true;
}
else if ( dateTimeAxis )
{
minX = dateTimeAxis->min().toMSecsSinceEpoch();
maxX = dateTimeAxis->max().toMSecsSinceEpoch();
foundAxis = true;
}
}
}
if ( !foundAxis )
{
for ( auto p : points )
{
minX = std::min( minX, p.x() );
maxX = std::max( maxX, p.x() );
}
}
}
QVector<QPointF> scatterValues;
if ( !points.empty() )
{
double range = maxX - minX;
double displaySize = 1400;
if ( m_plotWidget && m_plotWidget->qtChart() )
{
// Use the max size since plot area can be small before the widget is shown
displaySize = std::max( displaySize, m_plotWidget->qtChart()->plotArea().width() );
}
double rangePerPixel = range / displaySize;
double skipDistance = rangePerPixel * m_symbolSkipPixelDistance;
// Always have symbol on first point
scatterValues << points[0];
int lastDrawnIndex = 0;
for ( int i = 1; i < static_cast<int>( points.size() ); i++ )
{
// Skip points until skip distance is reached
double diff = points[i].x() - points[lastDrawnIndex].x();
// Always add last point.
bool isLastPoint = i == points.size() - 1;
if ( diff > skipDistance || isLastPoint )
{
scatterValues << points[i];
lastDrawnIndex = i;
}
}
}
scatterSeries()->replace( scatterValues );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiuQtChartsPlotCurve::isQtChartObjectsPresent() const
{
if ( !lineSeries() ) return false;
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::setZ( int z )
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::clearErrorBars()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::updateErrorBarsAppearance( bool showErrorBars, const QColor& curveColor )
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::setXAxis( RiuPlotAxis axis )
{
m_axisX = axis;
if ( m_plotWidget )
{
m_plotWidget->setXAxis( axis, lineSeries(), this );
m_plotWidget->setXAxis( axis, areaSeries(), this );
m_plotWidget->setXAxis( axis, scatterSeries(), this );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::setYAxis( RiuPlotAxis axis )
{
m_axisY = axis;
if ( m_plotWidget )
{
m_plotWidget->setYAxis( axis, lineSeries(), this );
m_plotWidget->setYAxis( axis, areaSeries(), this );
m_plotWidget->setYAxis( axis, scatterSeries(), this );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int RiuQtChartsPlotCurve::numSamples() const
{
if ( !lineSeries() ) return 0;
return lineSeries()->count();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<double, double> RiuQtChartsPlotCurve::sample( int index ) const
{
CAF_ASSERT( index >= 0 && index <= numSamples() );
auto p = lineSeries()->at( index );
return std::make_pair( p.x(), p.y() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<double, double> RiuQtChartsPlotCurve::xDataRange() const
{
cvf::BoundingBox bb = computeBoundingBox();
return std::make_pair( bb.min().x(), bb.max().x() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<double, double> RiuQtChartsPlotCurve::yDataRange() const
{
cvf::BoundingBox bb = computeBoundingBox();
return std::make_pair( bb.min().y(), bb.max().y() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::BoundingBox RiuQtChartsPlotCurve::computeBoundingBox() const
{
auto points = lineSeries()->pointsVector();
cvf::BoundingBox bb;
for ( auto p : points )
bb.add( cvf::Vec3d( p.x(), p.y(), 0.0 ) );
return bb;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::setVisibleInLegend( bool isVisibleInLegend )
{
if ( !m_plotWidget ) return;
CAF_ASSERT( m_plotWidget->qtChart() );
CAF_ASSERT( m_plotWidget->qtChart()->legend() );
// The markers can be set visible independent to the visibility state of the containing legend. Use the
// visibility state of the legend to override the visibility flag
if ( !m_plotWidget->qtChart()->legend()->isAttachedToChart() ) isVisibleInLegend = false;
if ( !m_plotWidget->qtChart()->legend()->isVisible() ) isVisibleInLegend = false;
bool showScatterMarker = isVisibleInLegend && m_symbol;
bool showLineMarker = isVisibleInLegend && !m_symbol;
auto setLegendVisibility = [this]( auto series, bool isVisible )
{
if ( series )
{
auto markers = m_plotWidget->qtChart()->legend()->markers( series );
if ( !markers.isEmpty() ) markers[0]->setVisible( isVisible );
}
};
m_isVisibleInLegend = showLineMarker || showScatterMarker;
setLegendVisibility( lineSeries(), showLineMarker );
setLegendVisibility( areaSeries(), false );
setLegendVisibility( scatterSeries(), showScatterMarker );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QtCharts::QLineSeries* RiuQtChartsPlotCurve::lineSeries() const
{
if ( m_lineSeries ) return m_lineSeries;
if ( m_plotWidget ) return dynamic_cast<QtCharts::QLineSeries*>( m_plotWidget->getLineSeries( this ) );
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QtCharts::QScatterSeries* RiuQtChartsPlotCurve::scatterSeries() const
{
if ( m_scatterSeries ) return m_scatterSeries;
if ( m_plotWidget ) return dynamic_cast<QtCharts::QScatterSeries*>( m_plotWidget->getScatterSeries( this ) );
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QtCharts::QAreaSeries* RiuQtChartsPlotCurve::areaSeries() const
{
if ( m_areaSeries ) return m_areaSeries;
if ( m_plotWidget ) return dynamic_cast<QtCharts::QAreaSeries*>( m_plotWidget->getAreaSeries( this ) );
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::setSymbol( RiuPlotCurveSymbol* symbol )
{
if ( symbol )
{
auto qtChartsSymbol = dynamic_cast<RiuQtChartsPlotCurveSymbol*>( symbol );
CAF_ASSERT( qtChartsSymbol );
m_symbol.reset( symbol );
if ( scatterSeries() )
{
qtChartsSymbol->applyToScatterSeries( scatterSeries() );
updateScatterSeries();
updateLineAndAreaSeries();
}
}
else
{
m_symbol.reset();
if ( scatterSeries() ) scatterSeries()->hide();
updateLineAndAreaSeries();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::updateLineAndAreaSeries()
{
bool isFilled = areaSeries() && areaSeries()->brush().style() != Qt::NoBrush;
bool isLine = lineSeries() && lineSeries()->pen().style() != Qt::PenStyle::NoPen;
if ( areaSeries() ) areaSeries()->setVisible( isFilled );
if ( lineSeries() ) lineSeries()->setVisible( isLine );
setVisibleInLegend( m_isVisibleInLegend );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiuPlotCurveSymbol* RiuQtChartsPlotCurve::createSymbol( RiuPlotCurveSymbol::PointSymbolEnum symbol ) const
{
return new RiuQtChartsPlotCurveSymbol( symbol );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::setCurveFittingTolerance( double tolerance )
{
// Not supported
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::setLegendIconSize( const QSize& iconSize )
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QSize RiuQtChartsPlotCurve::legendIconSize() const
{
// Default from Qwt
return QSize( 8, 8 );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QPixmap RiuQtChartsPlotCurve::legendIcon( const QSizeF& iconSize ) const
{
return QPixmap();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiuQtChartsPlotCurve::axisRangeChanged()
{
updateScatterSeries();
}