diff --git a/src/common/QskGraduation.cpp b/src/common/QskGraduation.cpp index bb17c34e..2ac1a433 100644 --- a/src/common/QskGraduation.cpp +++ b/src/common/QskGraduation.cpp @@ -13,7 +13,7 @@ #include -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; +} diff --git a/src/common/QskGraduation.h b/src/common/QskGraduation.h index c7a5442a..3fa3731a 100644 --- a/src/common/QskGraduation.h +++ b/src/common/QskGraduation.h @@ -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 diff --git a/src/common/QskIntervalF.cpp b/src/common/QskIntervalF.cpp index e94f7769..fce9ce2f 100644 --- a/src/common/QskIntervalF.cpp +++ b/src/common/QskIntervalF.cpp @@ -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() ) diff --git a/src/common/QskIntervalF.h b/src/common/QskIntervalF.h index 7c44b0ea..54fb2963 100644 --- a/src/common/QskIntervalF.h +++ b/src/common/QskIntervalF.h @@ -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; diff --git a/src/common/QskTickmarks.cpp b/src/common/QskTickmarks.cpp index 07358297..d9f744fa 100644 --- a/src/common/QskTickmarks.cpp +++ b/src/common/QskTickmarks.cpp @@ -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() { } diff --git a/src/common/QskTickmarks.h b/src/common/QskTickmarks.h index c742eb70..a90915cd 100644 --- a/src/common/QskTickmarks.h +++ b/src/common/QskTickmarks.h @@ -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;