qskinny/playground/plots/QskPlotCurveData.cpp

286 lines
6.4 KiB
C++
Raw Normal View History

2023-11-28 13:36:47 +01:00
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#include "QskPlotCurveData.h"
#include <qdebug.h>
#include <algorithm>
#include <cstddef>
namespace
{
struct compareX
{
inline bool operator()( const double x, const QPointF& pos ) const
{
return ( x < pos.x() );
}
};
struct compareY
{
inline bool operator()( const double y, const QPointF& pos ) const
{
return ( y < pos.y() );
}
};
template< typename LessThan >
inline int upperIndex(
const QskPlotCurveData* data, qreal value, LessThan lessThan )
{
const int indexMax = data->count() - 1;
if ( indexMax < 0 || !lessThan( value, data->pointAt( indexMax ) ) )
return -1;
int indexMin = 0;
int n = indexMax;
while ( n > 0 )
{
const int half = n >> 1;
const int indexMid = indexMin + half;
if ( lessThan( value, data->pointAt( indexMid ) ) )
{
n = half;
}
else
{
indexMin = indexMid + 1;
n -= half + 1;
}
}
return indexMin;
}
}
namespace
{
QRectF boundingRect( const QskPlotCurveData* data )
{
const auto count = data->count();
if ( count <= 0 )
return QRectF();
const auto hints = data->hints();
const bool montonicX = hints & QskPlotCurveData::MonotonicX;
const bool montonicY = hints & QskPlotCurveData::MonotonicY;
if ( montonicX && montonicY )
{
const auto p1 = data->pointAt( 0 );
const auto p2 = data->pointAt( count - 1 );
return QRectF( p1, p2 ).normalized();
}
if ( montonicX )
{
const auto p1 = data->pointAt( 0 );
const auto p2 = data->pointAt( count - 1 );
qreal yMin, yMax;
yMin = yMax = p1.y();
for ( int i = 1; i < count; i++ )
{
const auto p = data->pointAt( i );
if ( p.y() < yMin )
yMin = p.y();
else if ( p.y() > yMax )
yMax = p.y();
}
return QRectF( p1.x(), yMin, p2.x() - p1.x(), yMax - yMin ).normalized();
}
if ( montonicY )
{
const auto p1 = data->pointAt( 0 );
const auto p2 = data->pointAt( count - 1 );
qreal xMin, xMax;
xMin = xMax = p1.x();
for ( int i = 1; i < count; i++ )
{
const auto p = data->pointAt( i );
if ( p.x() < xMin )
xMin = p.x();
else if ( p.x() > xMax )
xMax = p.x();
}
return QRectF( xMin, p1.y(), xMax - xMin, p2.y() - p1.y() ).normalized();
}
{
const auto p1 = data->pointAt( 0 );
qreal xMin, xMax;
qreal yMin, yMax;
xMin = xMax = p1.x();
yMin = yMax = p1.y();
for ( int i = 1; i < count; i++ )
{
const auto p = data->pointAt( i );
if ( p.x() < xMin )
xMin = p.x();
else if ( p.x() > xMax )
xMax = p.x();
if ( p.y() < yMin )
yMin = p.y();
else if ( p.y() > yMax )
yMax = p.y();
}
return QRectF( xMin, yMin, xMax - xMin, yMax - yMin );
}
}
}
QskPlotCurveData::QskPlotCurveData( QObject* parent )
: QObject( parent )
{
}
QskPlotCurveData::~QskPlotCurveData()
{
}
void QskPlotCurveData::setHints( Hints hints )
{
if ( m_hints != hints )
{
m_hints = hints;
Q_EMIT changed();
}
}
void QskPlotCurveData::setHint( Hint hint, bool on )
{
if ( on )
setHints( m_hints | hint );
else
setHints( m_hints & ~hint );
}
QRectF QskPlotCurveData::boundingRect() const
{
if ( m_boundingRect.isNull() )
m_boundingRect = ::boundingRect( this );
return m_boundingRect;
}
int QskPlotCurveData::upperIndex( Qt::Orientation orientation, qreal value ) const
{
const int n = count();
if ( n == 0 )
return -1;
int index;
if ( orientation == Qt::Horizontal )
{
index = ::upperIndex( this, value, compareX() );
if ( ( index == -1 ) && ( value == pointAt( n - 1 ).x() ) )
index = n - 1;
}
else
{
index = ::upperIndex( this, value, compareY() );
if ( ( index == -1 ) && ( value == pointAt( n - 1 ).y() ) )
index = n - 1;
}
return index;
}
QPointF QskPlotCurveData::interpolatedPoint(
Qt::Orientation orientation, qreal value ) const
{
const int n = count();
if ( n == 0 )
return QPointF();
if ( n == 1 )
{
if ( orientation == Qt::Horizontal )
return QPointF( value, 0.0 );
else
return QPointF( 0.0, value );
}
int index = 0;
if ( n > 2 )
{
index = upperIndex( orientation, value );
if ( index > 0 )
index--;
}
const auto p1 = pointAt( index );
const auto p2 = pointAt( index + 1 );
if ( orientation == Qt::Horizontal )
{
const auto dx = p2.x() - p1.x();
if ( dx == 0.0 )
return p2;
const auto t = ( value - p1.x() ) / dx;
const auto y = p1.y() + t * ( p2.y() - p1.y() );
return QPointF( value, y );
}
else
{
const auto dy = p2.y() - p1.y();
if ( dy == 0.0 )
return p2;
const auto t = ( value - p1.y() ) / dy;
const auto x = p1.x() + t * ( p2.x() - p1.x() );
return QPointF( x, value );
}
}
QskPlotCurvePoints::QskPlotCurvePoints( QObject* parent )
: QskPlotCurveData( parent )
{
}
QskPlotCurvePoints::QskPlotCurvePoints(
const QVector< QPointF >& points, QObject* parent )
: QskPlotCurveData( parent )
, m_points( points )
{
}
void QskPlotCurvePoints::setPoints( const QVector< QPointF >& points )
{
m_points = points;
m_boundingRect = QRectF(); // invalidating
Q_EMIT changed();
}
#include "moc_QskPlotCurveData.cpp"