working on QskGraduation

This commit is contained in:
Uwe Rathmann 2023-11-28 13:07:21 +01:00
parent 54761e5a64
commit 7197c89533
6 changed files with 76 additions and 143 deletions

View File

@ -13,7 +13,7 @@
#include <cmath>
namespace
namespace Engine
{
// What about using qskFuzzyCompare and friends ???
@ -46,61 +46,9 @@ namespace
return true;
}
double ceilEps( double value, double intervalSize )
{
const double eps = _eps * intervalSize;
value = ( value - eps ) / intervalSize;
return std::ceil( value ) * intervalSize;
}
double floorEps( double value, double intervalSize )
{
const double eps = _eps * intervalSize;
value = ( value + eps ) / intervalSize;
return std::floor( value ) * intervalSize;
}
double suggestedStepSize( double intervalSize, int numSteps )
{
if ( numSteps <= 0 )
return 0.0;
const auto v = intervalSize / numSteps;
if ( qFuzzyIsNull( v ) )
return 0.0;
constexpr double base = 10.0;
// the same as std::log10( std::fabs( v ) );
const double lx = std::log( std::fabs( v ) ) / std::log( base );
const double p = std::floor( lx );
const double fraction = std::pow( base, lx - p );
double stepSize = std::pow( base, p );
if ( v < 0 )
stepSize = -stepSize;
for ( const double f : { 2.0, 2.5, 5.0, 10.0 } )
{
if ( fraction <= f || qFuzzyCompare( fraction, f ) )
{
stepSize *= f;
break;
}
}
return stepSize;
}
}
namespace Engine
{
double minorStepSize( double intervalSize, int maxSteps )
{
const double minStep = suggestedStepSize( intervalSize, maxSteps );
const double minStep = QskGraduation::stepSize( intervalSize, maxSteps );
if ( minStep != 0.0 )
{
@ -132,42 +80,17 @@ namespace Engine
}
QVector< qreal > strippedTicks;
for ( int i = 0; i < ticks.count(); i++ )
strippedTicks.reserve( ticks.count() );
for ( const auto tick : ticks )
{
if ( fuzzyContains( interval, ticks[i] ) )
strippedTicks += ticks[i];
if ( fuzzyContains( interval, tick ) )
strippedTicks += tick;
}
return strippedTicks;
}
QskIntervalF align( const QskIntervalF& interval, qreal stepSize )
{
auto x1 = interval.lowerBound();
auto x2 = interval.upperBound();
// when there is no rounding beside some effect, when
// calculating with doubles, we keep the original value
const auto max = std::numeric_limits< qreal >::max();
if ( -max + stepSize <= x1 )
{
const auto x = floorEps( x1, stepSize );
if ( qFuzzyIsNull( x ) || !qFuzzyCompare( x1, x ) )
x1 = x;
}
if ( max - stepSize >= x2 )
{
const auto x = ceilEps( x2, stepSize );
if ( qFuzzyIsNull( x ) || !qFuzzyCompare( x2, x ) )
x2 = x;
}
return QskIntervalF( x1, x2 );
}
QVector< qreal > buildMajorTicks(
const QskIntervalF& interval, qreal stepSize )
{
@ -227,7 +150,7 @@ namespace Engine
{
using T = QskTickmarks;
const auto boundingInterval = align( interval, stepSize );
const auto boundingInterval = interval.fuzzyAligned( stepSize );
QVector< qreal > ticks[3];
ticks[T::MajorTick] = buildMajorTicks( boundingInterval, stepSize );
@ -252,18 +175,13 @@ namespace Engine
}
}
QskTickmarks tickmarks;
tickmarks.setMinorTicks( ticks[T::MinorTick] );
tickmarks.setMediumTicks( ticks[T::MediumTick] );
tickmarks.setMajorTicks( ticks[T::MajorTick] );
return tickmarks;
return { ticks[T::MinorTick], ticks[T::MediumTick], ticks[T::MajorTick] };
}
}
QskTickmarks QskGraduation::divideInterval(
qreal x1, qreal x2, int maxMajorSteps, int maxMinorSteps, qreal stepSize)
qreal x1, qreal x2, int maxMajorSteps, int maxMinorSteps, qreal stepSize )
{
QskTickmarks tickmarks;
@ -275,58 +193,52 @@ QskTickmarks QskGraduation::divideInterval(
return tickmarks;
}
if ( interval.width() <= 0 )
if ( interval.width() <= 0.0 || stepSize < 0.0 )
return tickmarks;
stepSize = qAbs( stepSize );
if ( stepSize == 0.0 )
{
if ( maxMajorSteps < 1 )
maxMajorSteps = 1;
stepSize = suggestedStepSize( interval.width(), maxMajorSteps );
stepSize = QskGraduation::stepSize( interval.width(), maxMajorSteps );
}
if ( stepSize != 0.0 )
{
tickmarks = Engine::buildTicks( interval, stepSize, maxMinorSteps );
}
if ( x1 > x2 )
tickmarks.invert();
return tickmarks;
}
void QskGraduation::calculate( Attributes attributes, int maxNumSteps,
qreal& x1, qreal& x2, qreal& stepSize)
qreal QskGraduation::stepSize( double length, int numSteps )
{
auto interval = QskIntervalF::normalized( x1, x2 );
interval.setLowerBound( interval.lowerBound() );
interval.setUpperBound( interval.upperBound() );
stepSize = suggestedStepSize( interval.width(), qMax( maxNumSteps, 1 ) );
if ( !( attributes & Floating ) )
interval = Engine::align( interval, stepSize );
x1 = interval.lowerBound();
x2 = interval.upperBound();
if ( attributes & Inverted )
{
qSwap( x1, x2 );
stepSize = -stepSize;
}
}
qreal QskGraduation::alignedStepSize( double intervalSize, int numSteps )
{
if ( intervalSize <= 0.0 )
if ( numSteps <= 0 )
return 0.0;
return suggestedStepSize( intervalSize, numSteps );
}
const auto v = length / numSteps;
if ( qFuzzyIsNull( v ) )
return 0.0;
#include "moc_QskGraduation.cpp"
constexpr double base = 10.0;
// the same as std::log10( std::fabs( v ) );
const double lx = std::log( std::fabs( v ) ) / std::log( base );
const double p = std::floor( lx );
const double fraction = std::pow( base, lx - p );
double stepSize = std::pow( base, p );
if ( v < 0 )
stepSize = -stepSize;
for ( const double f : { 2.0, 2.5, 5.0, 10.0 } )
{
if ( fraction <= f || qFuzzyCompare( fraction, f ) )
{
stepSize *= f;
break;
}
}
return stepSize;
}

View File

@ -15,24 +15,10 @@ namespace QskGraduation
{
Q_NAMESPACE_EXPORT( QSK_EXPORT )
enum Attribute
{
Inverted = 1 << 0,
Floating = 1 << 1
};
Q_ENUM_NS( Attribute )
Q_DECLARE_FLAGS( Attributes, Attribute )
Q_DECLARE_OPERATORS_FOR_FLAGS( Attributes )
QskTickmarks divideInterval( qreal x1, qreal x2,
int maxMajorSteps, int maxMinorSteps, qreal stepSize = 0.0 );
void calculate( Attributes, int maxNumSteps,
qreal& x1, qreal& x2, qreal& stepSize );
qreal alignedStepSize( double intervalSize, int numSteps );
qreal stepSize( double length, int numSteps );
}
#endif

View File

@ -187,6 +187,30 @@ void QskIntervalF::spanFromUpperBound( qreal value ) noexcept
}
}
QskIntervalF QskIntervalF::fuzzyAligned( qreal stepSize ) const
{
auto v1 = m_lowerBound;
auto v2 = m_upperBound;
const auto max = std::numeric_limits< qreal >::max();
if ( -max + stepSize <= v1 )
{
const auto v = qskFuzzyFloor( v1, stepSize );
if ( !qskFuzzyCompare( v1, v ) )
v1 = v;
}
if ( max - stepSize >= v2 )
{
const auto v = qskFuzzyCeil( v2, stepSize );
if ( !qskFuzzyCompare( v2, v ) )
v2 = v;
}
return QskIntervalF( v1, v2 );
}
bool QskIntervalF::fuzzyContains( qreal value ) const
{
if ( !isValid() )

View File

@ -75,6 +75,8 @@ class QSK_EXPORT QskIntervalF
QskIntervalF operator|( qreal ) const noexcept;
QskIntervalF& operator|=( qreal ) noexcept;
QskIntervalF fuzzyAligned( qreal stepSize ) const;
constexpr bool isValid() const noexcept;
constexpr bool isNull() const noexcept;
constexpr bool isEmpty() const noexcept;

View File

@ -21,6 +21,12 @@ QskTickmarks::QskTickmarks()
{
}
QskTickmarks::QskTickmarks( const QVector< qreal >& minorTicks,
const QVector< qreal >& mediumTicks, const QVector< qreal >& majorTicks )
: m_ticks{ minorTicks, mediumTicks, majorTicks }
{
}
QskTickmarks::~QskTickmarks()
{
}

View File

@ -28,6 +28,9 @@ class QSK_EXPORT QskTickmarks
Q_ENUM( TickType )
QskTickmarks();
QskTickmarks( const QVector< qreal >& minorTicks,
const QVector< qreal >& mediumTicks, const QVector< qreal >& majorTicks );
~QskTickmarks();
bool operator==( const QskTickmarks& ) const noexcept;