6#include "QskGraduation.h"
7#include "QskFunctions.h"
8#include "QskIntervalF.h"
9#include "QskTickmarks.h"
20 inline int fuzzyCompare(
double value1,
double value2,
double intervalSize )
22 const double eps = std::abs( 1.0e-6 * intervalSize );
24 if ( value2 - value1 > eps )
27 if ( value1 - value2 > eps )
33 inline bool fuzzyContains(
const QskIntervalF& interval,
double value )
35 if ( !interval.isValid() )
38 if ( fuzzyCompare( value, interval.lowerBound(), interval.length() ) < 0 )
41 if ( fuzzyCompare( value, interval.upperBound(), interval.length() ) > 0 )
47 double minorStepSize(
double intervalSize,
int maxSteps )
49 const double minStep = QskGraduation::stepSize( intervalSize, maxSteps );
54 const int numTicks = qCeil( qAbs( intervalSize / minStep ) ) - 1;
57 if ( fuzzyCompare( ( numTicks + 1 ) * qAbs( minStep ),
58 qAbs( intervalSize ), intervalSize ) > 0 )
61 return 0.5 * intervalSize;
68 QVector< qreal > strip(
69 const QVector< qreal >& ticks,
const QskIntervalF& interval )
71 if ( !interval.isValid() || ticks.count() == 0 )
72 return QVector< qreal >();
74 if ( fuzzyContains( interval, ticks.first() )
75 && fuzzyContains( interval, ticks.last() ) )
80 QVector< qreal > strippedTicks;
81 strippedTicks.reserve( ticks.count() );
83 for (
const auto tick : ticks )
85 if ( fuzzyContains( interval, tick ) )
86 strippedTicks += tick;
92 QVector< qreal > buildMajorTicks(
95 int numTicks = qRound( interval.length() / stepSize ) + 1;
96 if ( numTicks > 10000 )
99 QVector< qreal > ticks;
100 ticks.reserve( numTicks );
102 ticks += interval.lowerBound();
103 for (
int i = 1; i < numTicks - 1; i++ )
104 ticks += interval.lowerBound() + i * stepSize;
105 ticks += interval.upperBound();
110 void buildMinorTicks(
111 const QVector< qreal >& majorTicks,
int maxMinorSteps, qreal stepSize,
112 QVector< qreal >& minorTicks, QVector< qreal >& mediumTicks )
114 auto minStep = minorStepSize( stepSize, maxMinorSteps );
115 if ( minStep == 0.0 )
119 const int numTicks = qCeil( qAbs( stepSize / minStep ) ) - 1;
123 medIndex = numTicks / 2;
127 for (
int i = 0; i < majorTicks.count(); i++ )
129 auto val = majorTicks[i];
130 for (
int k = 0; k < numTicks; k++ )
134 double alignedValue = val;
135 if ( fuzzyCompare( val, 0.0, stepSize ) == 0 )
139 mediumTicks += alignedValue;
141 minorTicks += alignedValue;
147 const QskIntervalF& interval, qreal stepSize,
int maxMinorSteps )
151 const auto boundingInterval = interval.fuzzyAligned( stepSize );
153 QVector< qreal > ticks[3];
154 ticks[T::MajorTick] = buildMajorTicks( boundingInterval, stepSize );
156 if ( maxMinorSteps > 0 )
158 buildMinorTicks( ticks[T::MajorTick], maxMinorSteps, stepSize,
159 ticks[T::MinorTick], ticks[T::MediumTick] );
162 for (
auto& t : ticks )
164 t = strip( t, interval );
169 for (
int i = 0; i < t.count(); i++ )
171 if ( fuzzyCompare( t[i], 0.0, stepSize ) == 0 )
176 return { ticks[T::MinorTick], ticks[T::MediumTick], ticks[T::MajorTick] };
182 qreal x1, qreal x2,
int maxMajorSteps,
int maxMinorSteps, qreal stepSize )
186 const auto interval = QskIntervalF::normalized( x1, x2 );
188 if ( interval.length() > std::numeric_limits< qreal >::max() )
190 qWarning() <<
"QskGraduation::divideInterval: overflow";
194 if ( interval.length() <= 0.0 || stepSize < 0.0 )
197 if ( stepSize == 0.0 )
199 if ( maxMajorSteps < 1 )
202 stepSize = QskGraduation::stepSize( interval.length(), maxMajorSteps );
205 if ( stepSize != 0.0 )
206 tickmarks = Engine::buildTicks( interval, stepSize, maxMinorSteps );
211qreal QskGraduation::stepSize(
double length,
int numSteps )
216 const auto v = length / numSteps;
217 if ( qFuzzyIsNull( v ) )
220 constexpr double base = 10.0;
223 const double lx = std::log( std::fabs( v ) ) / std::log( base );
224 const double p = std::floor( lx );
226 const double fraction = std::pow( base, lx - p );
228 double stepSize = std::pow( base, p );
230 stepSize = -stepSize;
232 for (
const double f : { 2.0, 2.5, 5.0, 10.0 } )
234 if ( fraction <= f || qFuzzyCompare( fraction, f ) )