10#include "qwt_date_scale_engine.h"
12#include "qwt_interval.h"
18static inline double qwtMsecsForType(
int type )
20 static const double msecs[] =
26 24.0 * 3600.0 * 1000.0,
27 7.0 * 24.0 * 3600.0 * 1000.0,
28 30.0 * 24.0 * 3600.0 * 1000.0,
29 365.0 * 24.0 * 3600.0 * 1000.0,
32 if ( type < 0 || type >=
static_cast< int >(
sizeof( msecs ) /
sizeof( msecs[0] ) ) )
38static inline int qwtAlignValue(
39 double value,
double stepSize,
bool up )
41 double d = value / stepSize;
42 d = up ? std::ceil( d ) : std::floor( d );
44 return static_cast< int >( d * stepSize );
47static double qwtIntervalWidth(
const QDateTime& minDate,
50 switch( intervalType )
54 return minDate.msecsTo( maxDate );
58 return minDate.secsTo( maxDate );
62 const double secsTo = minDate.secsTo( maxDate );
63 return std::floor( secsTo / 60 );
67 const double secsTo = minDate.secsTo( maxDate );
68 return std::floor( secsTo / 3600 );
72 return minDate.daysTo( maxDate );
76 return std::floor( minDate.daysTo( maxDate ) / 7.0 );
81 double( maxDate.date().year() ) - minDate.date().year();
83 int months = maxDate.date().month() - minDate.date().month();
84 if ( maxDate.date().day() < minDate.date().day() )
87 return years * 12 + months;
92 double( maxDate.date().year() ) - minDate.date().year();
94 if ( maxDate.date().month() < minDate.date().month() )
104static double qwtRoundedIntervalWidth(
105 const QDateTime& minDate,
const QDateTime& maxDate,
109 const QDateTime maxD =
QwtDate::ceil( maxDate, intervalType );
111 return qwtIntervalWidth( minD, maxD, intervalType );
114static inline int qwtStepCount(
int intervalSize,
int maxSteps,
115 const int limits[],
size_t numLimits )
117 for ( uint i = 0; i < numLimits; i++ )
119 const int numSteps = intervalSize / limits[ i ];
121 if ( numSteps > 1 && numSteps <= maxSteps &&
122 numSteps * limits[ i ] == intervalSize )
131static int qwtStepSize(
int intervalSize,
int maxSteps, uint base )
138 for (
int numSteps = maxSteps; numSteps > 1; numSteps-- )
140 const double stepSize = double( intervalSize ) / numSteps;
142 const double p = std::floor( std::log( stepSize ) / std::log(
double( base ) ) );
143 const double fraction = std::pow( base, p );
145 for ( uint n = base; n >= 1; n /= 2 )
147 if ( qFuzzyCompare( stepSize, n * fraction ) )
148 return qRound( stepSize );
150 if ( n == 3 && ( base % 2 ) == 0 )
152 if ( qFuzzyCompare( stepSize, 2 * fraction ) )
153 return qRound( stepSize );
162static int qwtDivideInterval(
double intervalSize,
int numSteps,
163 const int limits[],
size_t numLimits )
165 const int v = qwtCeil( intervalSize /
double( numSteps ) );
167 for ( uint i = 0; i < numLimits - 1; i++ )
169 if ( v <= limits[i] )
173 return limits[ numLimits - 1 ];
176static double qwtDivideScale(
double intervalSize,
int numSteps,
181 if ( ( intervalSize > numSteps ) &&
182 ( intervalSize <= 2 * numSteps ) )
190 switch( intervalType )
195 static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
197 stepSize = qwtDivideInterval( intervalSize, numSteps,
198 limits,
sizeof( limits ) /
sizeof(
int ) );
204 static int limits[] = { 1, 2, 3, 4, 6, 12, 24 };
206 stepSize = qwtDivideInterval( intervalSize, numSteps,
207 limits,
sizeof( limits ) /
sizeof(
int ) );
213 const double v = intervalSize / double( numSteps );
215 stepSize = std::ceil( v );
217 stepSize = std::ceil( v / 7 ) * 7;
223 static int limits[] = { 1, 2, 4, 8, 12, 26, 52 };
225 stepSize = qwtDivideInterval( intervalSize, numSteps,
226 limits,
sizeof( limits ) /
sizeof(
int ) );
232 static int limits[] = { 1, 2, 3, 4, 6, 12 };
234 stepSize = qwtDivideInterval( intervalSize, numSteps,
235 limits,
sizeof( limits ) /
sizeof(
int ) );
244 intervalSize, numSteps, 10 );
251static double qwtDivideMajorStep(
double stepSize,
int maxMinSteps,
254 double minStepSize = 0.0;
256 switch( intervalType )
260 minStepSize = qwtStepSize( stepSize, maxMinSteps, 10 );
261 if ( minStepSize == 0.0 )
262 minStepSize = 0.5 * stepSize;
268 static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
272 if ( stepSize > maxMinSteps )
274 numSteps = qwtStepCount( stepSize, maxMinSteps,
275 limits,
sizeof( limits ) /
sizeof(
int ) );
280 numSteps = qwtStepCount( stepSize * 60, maxMinSteps,
281 limits,
sizeof( limits ) /
sizeof(
int ) );
285 minStepSize = stepSize / numSteps;
293 if ( stepSize > maxMinSteps )
295 static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
297 numSteps = qwtStepCount( stepSize, maxMinSteps,
298 limits,
sizeof( limits ) /
sizeof(
int ) );
302 static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
304 numSteps = qwtStepCount( stepSize * 60, maxMinSteps,
305 limits,
sizeof( limits ) /
sizeof(
int ) );
309 minStepSize = stepSize / numSteps;
317 if ( stepSize > maxMinSteps )
319 static int limits[] = { 1, 2, 3, 7, 14, 28 };
321 numSteps = qwtStepCount( stepSize, maxMinSteps,
322 limits,
sizeof( limits ) /
sizeof(
int ) );
326 static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
328 numSteps = qwtStepCount( stepSize * 24, maxMinSteps,
329 limits,
sizeof( limits ) /
sizeof(
int ) );
333 minStepSize = stepSize / numSteps;
339 const int daysInStep = stepSize * 7;
341 if ( maxMinSteps >= daysInStep )
344 minStepSize = 1.0 / 7.0;
351 const int stepSizeInWeeks = stepSize;
353 if ( stepSizeInWeeks <= maxMinSteps )
360 stepSizeInWeeks, maxMinSteps, 10 );
369 if ( stepSize < maxMinSteps )
370 maxMinSteps =
static_cast< int >( stepSize );
372 static int limits[] = { 1, 2, 3, 4, 6, 12 };
374 int numSteps = qwtStepCount( stepSize, maxMinSteps,
375 limits,
sizeof( limits ) /
sizeof(
int ) );
378 minStepSize = stepSize / numSteps;
384 if ( stepSize >= maxMinSteps )
387 stepSize, maxMinSteps, 10 );
393 static int limits[] = { 1, 2, 3, 4, 6, 12 };
395 int numSteps = qwtStepCount( 12 * stepSize, maxMinSteps,
396 limits,
sizeof( limits ) /
sizeof(
int ) );
399 minStepSize = stepSize / numSteps;
409 && minStepSize == 0.0 )
411 minStepSize = 0.5 * stepSize;
418 int secondsMajor,
int secondsMinor )
420 if ( secondsMinor <= 0 )
423 QDateTime minDate = dateTime.addSecs( -secondsMajor );
431 while ( minDate < dateTime &&
434 minDate = minDate.addSecs( 3600 );
435 dstMin += 3600 * 1000.0;
439 ticks.reserve( 3600 / secondsMinor);
441 for (
int i = 0; i < 3600; i += secondsMinor )
442 ticks += dstMin + i * 1000.0;
448 const QDateTime& minDate,
const QDateTime& maxDate,
449 double stepSize,
int maxMinSteps,
453 double minStepSize = 0;
455 if ( maxMinSteps > 1 )
457 minStepSize = qwtDivideMajorStep( stepSize,
458 maxMinSteps, intervalType );
461 bool daylightSaving =
false;
462 if ( minDate.timeSpec() == Qt::LocalTime )
467 daylightSaving = stepSize > 1;
471 const double s = qwtMsecsForType( intervalType ) / 1000;
472 const int secondsMajor =
static_cast< int >( stepSize * s );
473 const double secondsMinor = minStepSize * s;
486 for ( QDateTime dt = minDate; dt <= maxDate;
487 dt = dt.addSecs( secondsMajor ) )
494 if ( daylightSaving )
497 majorValue += offset * 1000.0;
499 if ( offset > dstOff )
503 minorTicks += qwtDstTicks(
504 dt, secondsMajor, qRound( secondsMinor ) );
510 if ( majorTicks.isEmpty() || majorTicks.last() != majorValue )
511 majorTicks += majorValue;
513 if ( secondsMinor > 0.0 )
515 const int numMinorSteps = qwtFloor( secondsMajor / secondsMinor );
517 for (
int i = 1; i < numMinorSteps; i++ )
519 const QDateTime mt = dt.addMSecs(
520 qRound64( i * secondsMinor * 1000 ) );
523 if ( daylightSaving )
526 minorValue += offset * 1000.0;
529 if ( minorTicks.isEmpty() || minorTicks.last() != minorValue )
531 const bool isMedium = ( numMinorSteps % 2 == 0 )
532 && ( i != 1 ) && ( i == numMinorSteps / 2 );
535 mediumTicks += minorValue;
537 minorTicks += minorValue;
556 QDateTime& minDate,
const QDateTime& maxDate,
557 double stepSize,
int maxMinSteps )
564 int minStepSize = 0.0;
566 if ( maxMinSteps > 1 )
570 if ( maxMinSteps >= 30 )
572 else if ( maxMinSteps >= 6 )
574 else if ( maxMinSteps >= 3 )
581 minStepSize = qwtDivideMajorStep(
590 for ( QDateTime dt = minDate;
591 dt <= maxDate; dt = dt.addMonths( stepSize ) )
598 if ( minStepDays > 0 )
600 for (
int days = minStepDays;
601 days < 30; days += minStepDays )
605 if ( days == 15 && minStepDays != 15 )
611 else if ( minStepSize > 0.0 )
613 const int numMinorSteps = qRound( stepSize / (
double) minStepSize );
615 for (
int i = 1; i < numMinorSteps; i++ )
617 const double minorValue =
620 if ( ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 ) )
621 mediumTicks += minorValue;
623 minorTicks += minorValue;
640 const QDateTime& minDate,
const QDateTime& maxDate,
641 double stepSize,
int maxMinSteps )
647 double minStepSize = 0.0;
649 if ( maxMinSteps > 1 )
651 minStepSize = qwtDivideMajorStep(
655 int numMinorSteps = 0;
656 if ( minStepSize > 0.0 )
657 numMinorSteps = qwtFloor( stepSize / minStepSize );
659 bool dateBC = minDate.date().year() < -1;
661 for ( QDateTime dt = minDate; dt <= maxDate;
662 dt = dt.addYears( stepSize ) )
664 if ( dateBC && dt.date().year() > 1 )
667 dt = dt.addYears( -1 );
676 for (
int i = 1; i < numMinorSteps; i++ )
680 const double years = qRound( i * minStepSize );
681 if ( years >= std::numeric_limits< int >::max() / 12 )
683 tickDate = dt.addYears( years );
687 tickDate = dt.addMonths( qRound( years * 12 ) );
690 const bool isMedium = ( numMinorSteps > 2 ) &&
691 ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 );
695 mediumTicks += minorValue;
697 minorTicks += minorValue;
717class QwtDateScaleEngine::PrivateData
720 explicit PrivateData( Qt::TimeSpec spec )
723 , week0Type(
QwtDate::FirstThursday )
728 Qt::TimeSpec timeSpec;
750 m_data =
new PrivateData(
timeSpec );
776 return m_data->timeSpec;
791 m_data->utcOffset = seconds;
803 return m_data->utcOffset;
826 return m_data->week0Type;
843 m_data->maxWeeks = qMax( weeks, 0 );
853 return m_data->maxWeeks;
866 const QDateTime& minDate,
const QDateTime& maxDate,
869 const double jdMin = minDate.date().toJulianDay();
870 const double jdMax = maxDate.date().toJulianDay();
872 if ( ( jdMax - jdMin ) / 365 > maxSteps )
875 const int months = qwtRoundedIntervalWidth( minDate, maxDate,
QwtDate::Month );
876 if ( months > maxSteps * 6 )
879 const int days = qwtRoundedIntervalWidth( minDate, maxDate,
QwtDate::Day );
880 const int weeks = qwtRoundedIntervalWidth( minDate, maxDate,
QwtDate::Week );
882 if ( weeks > m_data->maxWeeks )
884 if ( days > 4 * maxSteps * 7 )
888 if ( days > maxSteps * 7 )
891 const int hours = qwtRoundedIntervalWidth( minDate, maxDate,
QwtDate::Hour );
892 if ( hours > maxSteps * 24 )
895 const int seconds = qwtRoundedIntervalWidth( minDate, maxDate,
QwtDate::Second );
897 if ( seconds >= maxSteps * 3600 )
900 if ( seconds >= maxSteps * 60 )
903 if ( seconds >= maxSteps )
926 double& x1,
double& x2,
double& stepSize )
const
942 if ( interval.
width() == 0.0 )
948 if ( from.isValid() && to.isValid() )
950 if ( maxNumSteps < 1 )
956 const double width = qwtIntervalWidth( from, to, intvType );
958 const double stepWidth = qwtDivideScale( width, maxNumSteps, intvType );
961 const QDateTime d1 =
alignDate( from, stepWidth, intvType,
false );
962 const QDateTime d2 =
alignDate( to, stepWidth, intvType,
true );
968 stepSize = stepWidth * qwtMsecsForType( intvType );
977 stepSize = -stepSize;
993 int maxMajorSteps,
int maxMinorSteps,
double stepSize )
const
995 if ( maxMajorSteps < 1 )
998 const double min = qwtMinF( x1, x2 );
999 const double max = qwtMaxF( x1, x2 );
1007 stepSize = qAbs( stepSize );
1008 if ( stepSize > 0.0 )
1014 maxMajorSteps = qwtCeil( ( max - min ) / stepSize );
1026 maxMajorSteps, maxMinorSteps, stepSize );
1033 scaleDiv = buildScaleDiv( minDate, maxDate,
1034 maxMajorSteps, maxMinorSteps, intvType );
1039 scaleDiv = scaleDiv.
bounded( min, max );
1049 const QDateTime& minDate,
const QDateTime& maxDate,
1050 int maxMajorSteps,
int maxMinorSteps,
1054 const double stepSize = qwtDivideScale(
1060 if ( !dt0.isValid() )
1071 scaleDiv = qwtDivideToSeconds( dt0, maxDate,
1078 scaleDiv = qwtDivideToMonths( dt0, maxDate,
1079 stepSize, maxMinorSteps );
1083 scaleDiv = qwtDivideToYears( dt0, maxDate,
1084 stepSize, maxMinorSteps );
1109 const QDateTime& dateTime,
double stepSize,
1114 QDateTime dt = dateTime;
1116 if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
1118#if QT_VERSION >= 0x050200
1119 dt.setOffsetFromUtc( 0 );
1121 dt.setUtcOffset( 0 );
1129 const int ms = qwtAlignValue(
1130 dt.time().msec(), stepSize, up );
1133 dt = dt.addMSecs( ms );
1139 int second = dt.time().second();
1142 if ( dt.time().msec() > 0 )
1146 const int s = qwtAlignValue( second, stepSize, up );
1149 dt = dt.addSecs( s );
1155 int minute = dt.time().minute();
1158 if ( dt.time().msec() > 0 || dt.time().second() > 0 )
1162 const int m = qwtAlignValue( minute, stepSize, up );
1165 dt = dt.addSecs( m * 60 );
1171 int hour = dt.time().hour();
1174 if ( dt.time().msec() > 0 || dt.time().second() > 0
1175 || dt.time().minute() > 0 )
1180 const int h = qwtAlignValue( hour, stepSize, up );
1183 dt = dt.addSecs( h * 3600 );
1193 int day = dt.date().dayOfYear();
1196 if ( dt.time() > QTime( 0, 0 ) )
1200 const int d = qwtAlignValue( day, stepSize, up );
1203 dt = dt.addDays( d - 1 );
1210 dt.date().year(), m_data->week0Type );
1212 int numWeeks = date.daysTo( dt.date() ) / 7;
1215 if ( dt.time() > QTime( 0, 0 ) ||
1216 date.daysTo( dt.date() ) % 7 )
1222 const int d = qwtAlignValue( numWeeks, stepSize, up ) * 7;
1226 dt = dt.addDays( d );
1232 int month = dt.date().month();
1235 if ( dt.date().day() > 1 ||
1236 dt.time() > QTime( 0, 0 ) )
1242 const int m = qwtAlignValue( month - 1, stepSize, up );
1245 dt = dt.addMonths( m );
1251 int year = dateTime.date().year();
1254 if ( dateTime.date().dayOfYear() > 1 ||
1255 dt.time() > QTime( 0, 0 ) )
1261 const int y = qwtAlignValue( year, stepSize, up );
1267 dt.setDate( QDate( stepSize, 1, 1 ).addYears( -stepSize ) );
1271 dt.setDate( QDate( y, 1, 1 ) );
1278 if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
1280#if QT_VERSION >= 0x050200
1281 dt.setOffsetFromUtc( dateTime.offsetFromUtc() );
1283 dt.setUtcOffset( dateTime.utcOffset() );
1301 if ( !dt.isValid() )
1303 const QDate date = ( value <= 0.0 )
1306 dt = QDateTime( date, QTime( 0, 0 ), m_data->timeSpec );
1309 if ( m_data->timeSpec == Qt::OffsetFromUTC )
1311 dt = dt.addSecs( m_data->utcOffset );
1312#if QT_VERSION >= 0x050200
1313 dt.setOffsetFromUtc( m_data->utcOffset );
1315 dt.setUtcOffset( m_data->utcOffset );
A collection of methods around date/time values.
static QDateTime floor(const QDateTime &, IntervalType)
static QDateTime toDateTime(double value, Qt::TimeSpec=Qt::UTC)
static QDateTime ceil(const QDateTime &, IntervalType)
static QDate dateOfWeek0(int year, Week0Type)
Date of the first day of the first week for a year.
@ Month
The interval is related to months.
@ Day
The interval is related to days.
@ Millisecond
The interval is related to milliseconds.
@ Minute
The interval is related to minutes.
@ Hour
The interval is related to hours.
@ Second
The interval is related to seconds.
@ Week
The interval is related to weeks.
@ Year
The interval is related to years.
static int utcOffset(const QDateTime &)
static double toDouble(const QDateTime &)
QwtDate::Week0Type week0Type() const
virtual ~QwtDateScaleEngine()
Destructor.
virtual QDateTime alignDate(const QDateTime &, double stepSize, QwtDate::IntervalType, bool up) const
QDateTime toDateTime(double) const
virtual QwtScaleDiv divideScale(double x1, double x2, int maxMajorSteps, int maxMinorSteps, double stepSize=0.0) const override
Calculate a scale division for a date/time interval.
void setUtcOffset(int seconds)
void setWeek0Type(QwtDate::Week0Type)
virtual QwtDate::IntervalType intervalType(const QDateTime &, const QDateTime &, int maxSteps) const
Qt::TimeSpec timeSpec() const
QwtDateScaleEngine(Qt::TimeSpec=Qt::LocalTime)
Constructor.
virtual void autoScale(int maxNumSteps, double &x1, double &x2, double &stepSize) const override
void setTimeSpec(Qt::TimeSpec)
A class representing an interval.
QwtInterval normalized() const
Normalize the limits of the interval.
double width() const
Return the width of an interval.
QwtInterval extend(double value) const
Extend the interval.
QwtInterval symmetrize(double value) const
A scale engine for linear scales.
virtual QwtScaleDiv divideScale(double x1, double x2, int maxMajorSteps, int maxMinorSteps, double stepSize=0.0) const override
Calculate a scale division for an interval.
static double divideInterval(double intervalSize, int numSteps, uint base)
A class representing a scale division.
QwtScaleDiv bounded(double lowerBound, double upperBound) const
void setInterval(double lowerBound, double upperBound)
@ MediumTick
Medium ticks.
void setTicks(int tickType, const QList< double > &)
@ Inverted
Turn the scale upside down.
@ Symmetric
Build a scale which is symmetric to the reference() value.
@ IncludeReference
Build a scale which includes the reference() value.
double upperMargin() const
bool testAttribute(Attribute) const
QwtInterval buildInterval(double value) const
Build an interval around a value.
double lowerMargin() const