Loading...
Searching...
No Matches
qwt_date_scale_engine.cpp
/******************************************************************************
* Qwt Widget Library
* Copyright (C) 1997 Josef Wilgen
* Copyright (C) 2002 Uwe Rathmann
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the Qwt License, Version 1.0
*****************************************************************************/
#include "qwt_date_scale_engine.h"
#include "qwt_math.h"
#include "qwt_interval.h"
#include <qdatetime.h>
#include <limits>
static inline double qwtMsecsForType( int type )
{
static const double msecs[] =
{
1.0,
1000.0,
60.0 * 1000.0,
3600.0 * 1000.0,
24.0 * 3600.0 * 1000.0,
7.0 * 24.0 * 3600.0 * 1000.0,
30.0 * 24.0 * 3600.0 * 1000.0,
365.0 * 24.0 * 3600.0 * 1000.0,
};
if ( type < 0 || type >= static_cast< int >( sizeof( msecs ) / sizeof( msecs[0] ) ) )
return 1.0;
return msecs[ type ];
}
static inline int qwtAlignValue(
double value, double stepSize, bool up )
{
double d = value / stepSize;
d = up ? std::ceil( d ) : std::floor( d );
return static_cast< int >( d * stepSize );
}
static double qwtIntervalWidth( const QDateTime& minDate,
const QDateTime& maxDate, QwtDate::IntervalType intervalType )
{
switch( intervalType )
{
case QwtDate::Millisecond:
{
return minDate.msecsTo( maxDate );
}
case QwtDate::Second:
{
return minDate.secsTo( maxDate );
}
case QwtDate::Minute:
{
const double secsTo = minDate.secsTo( maxDate );
return std::floor( secsTo / 60 );
}
case QwtDate::Hour:
{
const double secsTo = minDate.secsTo( maxDate );
return std::floor( secsTo / 3600 );
}
case QwtDate::Day:
{
return minDate.daysTo( maxDate );
}
case QwtDate::Week:
{
return std::floor( minDate.daysTo( maxDate ) / 7.0 );
}
case QwtDate::Month:
{
const double years =
double( maxDate.date().year() ) - minDate.date().year();
int months = maxDate.date().month() - minDate.date().month();
if ( maxDate.date().day() < minDate.date().day() )
months--;
return years * 12 + months;
}
case QwtDate::Year:
{
double years =
double( maxDate.date().year() ) - minDate.date().year();
if ( maxDate.date().month() < minDate.date().month() )
years -= 1.0;
return years;
}
}
return 0.0;
}
static double qwtRoundedIntervalWidth(
const QDateTime& minDate, const QDateTime& maxDate,
QwtDate::IntervalType intervalType )
{
const QDateTime minD = QwtDate::floor( minDate, intervalType );
const QDateTime maxD = QwtDate::ceil( maxDate, intervalType );
return qwtIntervalWidth( minD, maxD, intervalType );
}
static inline int qwtStepCount( int intervalSize, int maxSteps,
const int limits[], size_t numLimits )
{
for ( uint i = 0; i < numLimits; i++ )
{
const int numSteps = intervalSize / limits[ i ];
if ( numSteps > 1 && numSteps <= maxSteps &&
numSteps * limits[ i ] == intervalSize )
{
return numSteps;
}
}
return 0;
}
static int qwtStepSize( int intervalSize, int maxSteps, uint base )
{
if ( maxSteps <= 0 )
return 0;
if ( maxSteps > 2 )
{
for ( int numSteps = maxSteps; numSteps > 1; numSteps-- )
{
const double stepSize = double( intervalSize ) / numSteps;
const double p = std::floor( std::log( stepSize ) / std::log( double( base ) ) );
const double fraction = std::pow( base, p );
for ( uint n = base; n >= 1; n /= 2 )
{
if ( qFuzzyCompare( stepSize, n * fraction ) )
return qRound( stepSize );
if ( n == 3 && ( base % 2 ) == 0 )
{
if ( qFuzzyCompare( stepSize, 2 * fraction ) )
return qRound( stepSize );
}
}
}
}
return 0;
}
static int qwtDivideInterval( double intervalSize, int numSteps,
const int limits[], size_t numLimits )
{
const int v = qwtCeil( intervalSize / double( numSteps ) );
for ( uint i = 0; i < numLimits - 1; i++ )
{
if ( v <= limits[i] )
return limits[i];
}
return limits[ numLimits - 1 ];
}
static double qwtDivideScale( double intervalSize, int numSteps,
QwtDate::IntervalType intervalType )
{
if ( intervalType != QwtDate::Day )
{
if ( ( intervalSize > numSteps ) &&
( intervalSize <= 2 * numSteps ) )
{
return 2.0;
}
}
double stepSize;
switch( intervalType )
{
case QwtDate::Second:
case QwtDate::Minute:
{
static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
stepSize = qwtDivideInterval( intervalSize, numSteps,
limits, sizeof( limits ) / sizeof( int ) );
break;
}
case QwtDate::Hour:
{
static int limits[] = { 1, 2, 3, 4, 6, 12, 24 };
stepSize = qwtDivideInterval( intervalSize, numSteps,
limits, sizeof( limits ) / sizeof( int ) );
break;
}
case QwtDate::Day:
{
const double v = intervalSize / double( numSteps );
if ( v <= 5.0 )
stepSize = std::ceil( v );
else
stepSize = std::ceil( v / 7 ) * 7;
break;
}
case QwtDate::Week:
{
static int limits[] = { 1, 2, 4, 8, 12, 26, 52 };
stepSize = qwtDivideInterval( intervalSize, numSteps,
limits, sizeof( limits ) / sizeof( int ) );
break;
}
case QwtDate::Month:
{
static int limits[] = { 1, 2, 3, 4, 6, 12 };
stepSize = qwtDivideInterval( intervalSize, numSteps,
limits, sizeof( limits ) / sizeof( int ) );
break;
}
case QwtDate::Year:
case QwtDate::Millisecond:
default:
{
stepSize = QwtScaleArithmetic::divideInterval(
intervalSize, numSteps, 10 );
}
}
return stepSize;
}
static double qwtDivideMajorStep( double stepSize, int maxMinSteps,
QwtDate::IntervalType intervalType )
{
double minStepSize = 0.0;
switch( intervalType )
{
case QwtDate::Second:
{
minStepSize = qwtStepSize( stepSize, maxMinSteps, 10 );
if ( minStepSize == 0.0 )
minStepSize = 0.5 * stepSize;
break;
}
case QwtDate::Minute:
{
static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
int numSteps;
if ( stepSize > maxMinSteps )
{
numSteps = qwtStepCount( stepSize, maxMinSteps,
limits, sizeof( limits ) / sizeof( int ) );
}
else
{
numSteps = qwtStepCount( stepSize * 60, maxMinSteps,
limits, sizeof( limits ) / sizeof( int ) );
}
if ( numSteps > 0 )
minStepSize = stepSize / numSteps;
break;
}
case QwtDate::Hour:
{
int numSteps = 0;
if ( stepSize > maxMinSteps )
{
static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
numSteps = qwtStepCount( stepSize, maxMinSteps,
limits, sizeof( limits ) / sizeof( int ) );
}
else
{
static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
numSteps = qwtStepCount( stepSize * 60, maxMinSteps,
limits, sizeof( limits ) / sizeof( int ) );
}
if ( numSteps > 0 )
minStepSize = stepSize / numSteps;
break;
}
case QwtDate::Day:
{
int numSteps = 0;
if ( stepSize > maxMinSteps )
{
static int limits[] = { 1, 2, 3, 7, 14, 28 };
numSteps = qwtStepCount( stepSize, maxMinSteps,
limits, sizeof( limits ) / sizeof( int ) );
}
else
{
static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
numSteps = qwtStepCount( stepSize * 24, maxMinSteps,
limits, sizeof( limits ) / sizeof( int ) );
}
if ( numSteps > 0 )
minStepSize = stepSize / numSteps;
break;
}
case QwtDate::Week:
{
const int daysInStep = stepSize * 7;
if ( maxMinSteps >= daysInStep )
{
// we want to have one tick per day
minStepSize = 1.0 / 7.0;
}
else
{
// when the stepSize is more than a week we want to
// have a tick for each week
const int stepSizeInWeeks = stepSize;
if ( stepSizeInWeeks <= maxMinSteps )
{
minStepSize = 1;
}
else
{
minStepSize = QwtScaleArithmetic::divideInterval(
stepSizeInWeeks, maxMinSteps, 10 );
}
}
break;
}
case QwtDate::Month:
{
// fractions of months doesn't make any sense
if ( stepSize < maxMinSteps )
maxMinSteps = static_cast< int >( stepSize );
static int limits[] = { 1, 2, 3, 4, 6, 12 };
int numSteps = qwtStepCount( stepSize, maxMinSteps,
limits, sizeof( limits ) / sizeof( int ) );
if ( numSteps > 0 )
minStepSize = stepSize / numSteps;
break;
}
case QwtDate::Year:
{
if ( stepSize >= maxMinSteps )
{
minStepSize = QwtScaleArithmetic::divideInterval(
stepSize, maxMinSteps, 10 );
}
else
{
// something in months
static int limits[] = { 1, 2, 3, 4, 6, 12 };
int numSteps = qwtStepCount( 12 * stepSize, maxMinSteps,
limits, sizeof( limits ) / sizeof( int ) );
if ( numSteps > 0 )
minStepSize = stepSize / numSteps;
}
break;
}
default:
break;
}
if ( intervalType != QwtDate::Month
&& minStepSize == 0.0 )
{
minStepSize = 0.5 * stepSize;
}
return minStepSize;
}
static QList< double > qwtDstTicks( const QDateTime& dateTime,
int secondsMajor, int secondsMinor )
{
if ( secondsMinor <= 0 )
return QList< double >();
QDateTime minDate = dateTime.addSecs( -secondsMajor );
minDate = QwtDate::floor( minDate, QwtDate::Hour );
const double utcOffset = QwtDate::utcOffset( dateTime );
// find the hours where daylight saving time happens
double dstMin = QwtDate::toDouble( minDate );
while ( minDate < dateTime &&
QwtDate::utcOffset( minDate ) != utcOffset )
{
minDate = minDate.addSecs( 3600 );
dstMin += 3600 * 1000.0;
}
QList< double > ticks;
ticks.reserve( 3600 / secondsMinor);
for ( int i = 0; i < 3600; i += secondsMinor )
ticks += dstMin + i * 1000.0;
return ticks;
}
static QwtScaleDiv qwtDivideToSeconds(
const QDateTime& minDate, const QDateTime& maxDate,
double stepSize, int maxMinSteps,
QwtDate::IntervalType intervalType )
{
// calculate the min step size
double minStepSize = 0;
if ( maxMinSteps > 1 )
{
minStepSize = qwtDivideMajorStep( stepSize,
maxMinSteps, intervalType );
}
bool daylightSaving = false;
if ( minDate.timeSpec() == Qt::LocalTime )
{
daylightSaving = intervalType > QwtDate::Hour;
if ( intervalType == QwtDate::Hour )
{
daylightSaving = stepSize > 1;
}
}
const double s = qwtMsecsForType( intervalType ) / 1000;
const int secondsMajor = static_cast< int >( stepSize * s );
const double secondsMinor = minStepSize * s;
// UTC excludes daylight savings. So from the difference
// of a date and its UTC counterpart we can find out
// the daylight saving hours
const double utcOffset = QwtDate::utcOffset( minDate );
double dstOff = 0;
QList< double > majorTicks;
QList< double > mediumTicks;
QList< double > minorTicks;
for ( QDateTime dt = minDate; dt <= maxDate;
dt = dt.addSecs( secondsMajor ) )
{
if ( !dt.isValid() )
break;
double majorValue = QwtDate::toDouble( dt );
if ( daylightSaving )
{
const double offset = utcOffset - QwtDate::utcOffset( dt );
majorValue += offset * 1000.0;
if ( offset > dstOff )
{
// we add some minor ticks for the DST hour,
// otherwise the ticks will be unaligned: 0, 2, 3, 5 ...
minorTicks += qwtDstTicks(
dt, secondsMajor, qRound( secondsMinor ) );
}
dstOff = offset;
}
if ( majorTicks.isEmpty() || majorTicks.last() != majorValue )
majorTicks += majorValue;
if ( secondsMinor > 0.0 )
{
const int numMinorSteps = qwtFloor( secondsMajor / secondsMinor );
for ( int i = 1; i < numMinorSteps; i++ )
{
const QDateTime mt = dt.addMSecs(
qRound64( i * secondsMinor * 1000 ) );
double minorValue = QwtDate::toDouble( mt );
if ( daylightSaving )
{
const double offset = utcOffset - QwtDate::utcOffset( mt );
minorValue += offset * 1000.0;
}
if ( minorTicks.isEmpty() || minorTicks.last() != minorValue )
{
const bool isMedium = ( numMinorSteps % 2 == 0 )
&& ( i != 1 ) && ( i == numMinorSteps / 2 );
if ( isMedium )
mediumTicks += minorValue;
else
minorTicks += minorValue;
}
}
}
}
QwtScaleDiv scaleDiv;
scaleDiv.setInterval( QwtDate::toDouble( minDate ),
QwtDate::toDouble( maxDate ) );
scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
return scaleDiv;
}
static QwtScaleDiv qwtDivideToMonths(
QDateTime& minDate, const QDateTime& maxDate,
double stepSize, int maxMinSteps )
{
// months are intervals with non
// equidistant ( in ms ) steps: we have to build the
// scale division manually
int minStepDays = 0;
int minStepSize = 0.0;
if ( maxMinSteps > 1 )
{
if ( stepSize == 1 )
{
if ( maxMinSteps >= 30 )
minStepDays = 1;
else if ( maxMinSteps >= 6 )
minStepDays = 5;
else if ( maxMinSteps >= 3 )
minStepDays = 10;
else
minStepDays = 15;
}
else
{
minStepSize = qwtDivideMajorStep(
stepSize, maxMinSteps, QwtDate::Month );
}
}
QList< double > majorTicks;
QList< double > mediumTicks;
QList< double > minorTicks;
for ( QDateTime dt = minDate;
dt <= maxDate; dt = dt.addMonths( stepSize ) )
{
if ( !dt.isValid() )
break;
majorTicks += QwtDate::toDouble( dt );
if ( minStepDays > 0 )
{
for ( int days = minStepDays;
days < 30; days += minStepDays )
{
const double tick = QwtDate::toDouble( dt.addDays( days ) );
if ( days == 15 && minStepDays != 15 )
mediumTicks += tick;
else
minorTicks += tick;
}
}
else if ( minStepSize > 0.0 )
{
const int numMinorSteps = qRound( stepSize / (double) minStepSize );
for ( int i = 1; i < numMinorSteps; i++ )
{
const double minorValue =
QwtDate::toDouble( dt.addMonths( i * minStepSize ) );
if ( ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 ) )
mediumTicks += minorValue;
else
minorTicks += minorValue;
}
}
}
QwtScaleDiv scaleDiv;
scaleDiv.setInterval( QwtDate::toDouble( minDate ),
QwtDate::toDouble( maxDate ) );
scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
return scaleDiv;
}
static QwtScaleDiv qwtDivideToYears(
const QDateTime& minDate, const QDateTime& maxDate,
double stepSize, int maxMinSteps )
{
QList< double > majorTicks;
QList< double > mediumTicks;
QList< double > minorTicks;
double minStepSize = 0.0;
if ( maxMinSteps > 1 )
{
minStepSize = qwtDivideMajorStep(
stepSize, maxMinSteps, QwtDate::Year );
}
int numMinorSteps = 0;
if ( minStepSize > 0.0 )
numMinorSteps = qwtFloor( stepSize / minStepSize );
bool dateBC = minDate.date().year() < -1;
for ( QDateTime dt = minDate; dt <= maxDate;
dt = dt.addYears( stepSize ) )
{
if ( dateBC && dt.date().year() > 1 )
{
// there is no year 0 in the Julian calendar
dt = dt.addYears( -1 );
dateBC = false;
}
if ( !dt.isValid() )
break;
majorTicks += QwtDate::toDouble( dt );
for ( int i = 1; i < numMinorSteps; i++ )
{
QDateTime tickDate;
const double years = qRound( i * minStepSize );
if ( years >= std::numeric_limits< int >::max() / 12 )
{
tickDate = dt.addYears( years );
}
else
{
tickDate = dt.addMonths( qRound( years * 12 ) );
}
const bool isMedium = ( numMinorSteps > 2 ) &&
( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 );
const double minorValue = QwtDate::toDouble( tickDate );
if ( isMedium )
mediumTicks += minorValue;
else
minorTicks += minorValue;
}
if ( QwtDate::maxDate().addYears( -stepSize ) < dt.date() )
{
break;
}
}
QwtScaleDiv scaleDiv;
scaleDiv.setInterval( QwtDate::toDouble( minDate ),
QwtDate::toDouble( maxDate ) );
scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
return scaleDiv;
}
class QwtDateScaleEngine::PrivateData
{
public:
explicit PrivateData( Qt::TimeSpec spec )
: timeSpec( spec )
, utcOffset( 0 )
, week0Type( QwtDate::FirstThursday )
, maxWeeks( 4 )
{
}
Qt::TimeSpec timeSpec;
int utcOffset;
QwtDate::Week0Type week0Type;
int maxWeeks;
};
QwtDateScaleEngine::QwtDateScaleEngine( Qt::TimeSpec timeSpec )
: QwtLinearScaleEngine( 10 )
{
m_data = new PrivateData( timeSpec );
}
QwtDateScaleEngine::~QwtDateScaleEngine()
{
delete m_data;
}
void QwtDateScaleEngine::setTimeSpec( Qt::TimeSpec timeSpec )
{
m_data->timeSpec = timeSpec;
}
Qt::TimeSpec QwtDateScaleEngine::timeSpec() const
{
return m_data->timeSpec;
}
void QwtDateScaleEngine::setUtcOffset( int seconds )
{
m_data->utcOffset = seconds;
}
int QwtDateScaleEngine::utcOffset() const
{
return m_data->utcOffset;
}
void QwtDateScaleEngine::setWeek0Type( QwtDate::Week0Type week0Type )
{
m_data->week0Type = week0Type;
}
QwtDate::Week0Type QwtDateScaleEngine::week0Type() const
{
return m_data->week0Type;
}
void QwtDateScaleEngine::setMaxWeeks( int weeks )
{
m_data->maxWeeks = qMax( weeks, 0 );
}
int QwtDateScaleEngine::maxWeeks() const
{
return m_data->maxWeeks;
}
QwtDate::IntervalType QwtDateScaleEngine::intervalType(
const QDateTime& minDate, const QDateTime& maxDate,
int maxSteps ) const
{
const double jdMin = minDate.date().toJulianDay();
const double jdMax = maxDate.date().toJulianDay();
if ( ( jdMax - jdMin ) / 365 > maxSteps )
return QwtDate::Year;
const int months = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Month );
if ( months > maxSteps * 6 )
return QwtDate::Year;
const int days = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Day );
const int weeks = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Week );
if ( weeks > m_data->maxWeeks )
{
if ( days > 4 * maxSteps * 7 )
return QwtDate::Month;
}
if ( days > maxSteps * 7 )
return QwtDate::Week;
const int hours = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Hour );
if ( hours > maxSteps * 24 )
return QwtDate::Day;
const int seconds = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Second );
if ( seconds >= maxSteps * 3600 )
return QwtDate::Hour;
if ( seconds >= maxSteps * 60 )
return QwtDate::Minute;
if ( seconds >= maxSteps )
return QwtDate::Second;
return QwtDate::Millisecond;
}
void QwtDateScaleEngine::autoScale( int maxNumSteps,
double& x1, double& x2, double& stepSize ) const
{
stepSize = 0.0;
QwtInterval interval( x1, x2 );
interval = interval.normalized();
interval.setMinValue( interval.minValue() - lowerMargin() );
interval.setMaxValue( interval.maxValue() + upperMargin() );
if ( testAttribute( QwtScaleEngine::Symmetric ) )
interval = interval.symmetrize( reference() );
if ( testAttribute( QwtScaleEngine::IncludeReference ) )
interval = interval.extend( reference() );
if ( interval.width() == 0.0 )
interval = buildInterval( interval.minValue() );
const QDateTime from = toDateTime( interval.minValue() );
const QDateTime to = toDateTime( interval.maxValue() );
if ( from.isValid() && to.isValid() )
{
if ( maxNumSteps < 1 )
maxNumSteps = 1;
const QwtDate::IntervalType intvType =
intervalType( from, to, maxNumSteps );
const double width = qwtIntervalWidth( from, to, intvType );
const double stepWidth = qwtDivideScale( width, maxNumSteps, intvType );
if ( stepWidth != 0.0 && !testAttribute( QwtScaleEngine::Floating ) )
{
const QDateTime d1 = alignDate( from, stepWidth, intvType, false );
const QDateTime d2 = alignDate( to, stepWidth, intvType, true );
interval.setMinValue( QwtDate::toDouble( d1 ) );
interval.setMaxValue( QwtDate::toDouble( d2 ) );
}
stepSize = stepWidth * qwtMsecsForType( intvType );
}
x1 = interval.minValue();
x2 = interval.maxValue();
if ( testAttribute( QwtScaleEngine::Inverted ) )
{
qSwap( x1, x2 );
stepSize = -stepSize;
}
}
QwtScaleDiv QwtDateScaleEngine::divideScale( double x1, double x2,
int maxMajorSteps, int maxMinorSteps, double stepSize ) const
{
if ( maxMajorSteps < 1 )
maxMajorSteps = 1;
const double min = qwtMinF( x1, x2 );
const double max = qwtMaxF( x1, x2 );
const QDateTime from = toDateTime( min );
const QDateTime to = toDateTime( max );
if ( from == to )
return QwtScaleDiv();
stepSize = qAbs( stepSize );
if ( stepSize > 0.0 )
{
// as interval types above hours are not equidistant
// ( even days might have 23/25 hours because of daylight saving )
// the stepSize is used as a hint only
maxMajorSteps = qwtCeil( ( max - min ) / stepSize );
}
const QwtDate::IntervalType intvType =
intervalType( from, to, maxMajorSteps );
QwtScaleDiv scaleDiv;
if ( intvType == QwtDate::Millisecond )
{
// for milliseconds and below we can use the decimal system
scaleDiv = QwtLinearScaleEngine::divideScale( min, max,
maxMajorSteps, maxMinorSteps, stepSize );
}
else
{
const QDateTime minDate = QwtDate::floor( from, intvType );
const QDateTime maxDate = QwtDate::ceil( to, intvType );
scaleDiv = buildScaleDiv( minDate, maxDate,
maxMajorSteps, maxMinorSteps, intvType );
// scaleDiv has been calculated from an extended interval
// adjusted to the step size. We have to shrink it again.
scaleDiv = scaleDiv.bounded( min, max );
}
if ( x1 > x2 )
scaleDiv.invert();
return scaleDiv;
}
QwtScaleDiv QwtDateScaleEngine::buildScaleDiv(
const QDateTime& minDate, const QDateTime& maxDate,
int maxMajorSteps, int maxMinorSteps,
QwtDate::IntervalType intervalType ) const
{
// calculate the step size
const double stepSize = qwtDivideScale(
qwtIntervalWidth( minDate, maxDate, intervalType ),
maxMajorSteps, intervalType );
// align minDate to the step size
QDateTime dt0 = alignDate( minDate, stepSize, intervalType, false );
if ( !dt0.isValid() )
{
// the floored date is out of the range of a
// QDateTime - we ceil instead.
dt0 = alignDate( minDate, stepSize, intervalType, true );
}
QwtScaleDiv scaleDiv;
if ( intervalType <= QwtDate::Week )
{
scaleDiv = qwtDivideToSeconds( dt0, maxDate,
stepSize, maxMinorSteps, intervalType );
}
else
{
if( intervalType == QwtDate::Month )
{
scaleDiv = qwtDivideToMonths( dt0, maxDate,
stepSize, maxMinorSteps );
}
else if ( intervalType == QwtDate::Year )
{
scaleDiv = qwtDivideToYears( dt0, maxDate,
stepSize, maxMinorSteps );
}
}
return scaleDiv;
}
QDateTime QwtDateScaleEngine::alignDate(
const QDateTime& dateTime, double stepSize,
QwtDate::IntervalType intervalType, bool up ) const
{
// what about: (year == 1582 && month == 10 && day > 4 && day < 15) ??
QDateTime dt = dateTime;
if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
{
#if QT_VERSION >= 0x050200
dt.setOffsetFromUtc( 0 );
#else
dt.setUtcOffset( 0 );
#endif
}
switch( intervalType )
{
case QwtDate::Millisecond:
{
const int ms = qwtAlignValue(
dt.time().msec(), stepSize, up );
dt = QwtDate::floor( dateTime, QwtDate::Second );
dt = dt.addMSecs( ms );
break;
}
case QwtDate::Second:
{
int second = dt.time().second();
if ( up )
{
if ( dt.time().msec() > 0 )
second++;
}
const int s = qwtAlignValue( second, stepSize, up );
dt = QwtDate::floor( dt, QwtDate::Minute );
dt = dt.addSecs( s );
break;
}
case QwtDate::Minute:
{
int minute = dt.time().minute();
if ( up )
{
if ( dt.time().msec() > 0 || dt.time().second() > 0 )
minute++;
}
const int m = qwtAlignValue( minute, stepSize, up );
dt = QwtDate::floor( dt, QwtDate::Hour );
dt = dt.addSecs( m * 60 );
break;
}
case QwtDate::Hour:
{
int hour = dt.time().hour();
if ( up )
{
if ( dt.time().msec() > 0 || dt.time().second() > 0
|| dt.time().minute() > 0 )
{
hour++;
}
}
const int h = qwtAlignValue( hour, stepSize, up );
dt = QwtDate::floor( dt, QwtDate::Day );
dt = dt.addSecs( h * 3600 );
break;
}
case QwtDate::Day:
{
// What date do we expect f.e. from an alignment of 5 days ??
// Aligning them to the beginning of the year avoids at least
// jumping major ticks when panning
int day = dt.date().dayOfYear();
if ( up )
{
if ( dt.time() > QTime( 0, 0 ) )
day++;
}
const int d = qwtAlignValue( day, stepSize, up );
dt = QwtDate::floor( dt, QwtDate::Year );
dt = dt.addDays( d - 1 );
break;
}
case QwtDate::Week:
{
const QDate date = QwtDate::dateOfWeek0(
dt.date().year(), m_data->week0Type );
int numWeeks = date.daysTo( dt.date() ) / 7;
if ( up )
{
if ( dt.time() > QTime( 0, 0 ) ||
date.daysTo( dt.date() ) % 7 )
{
numWeeks++;
}
}
const int d = qwtAlignValue( numWeeks, stepSize, up ) * 7;
dt = QwtDate::floor( dt, QwtDate::Day );
dt.setDate( date );
dt = dt.addDays( d );
break;
}
case QwtDate::Month:
{
int month = dt.date().month();
if ( up )
{
if ( dt.date().day() > 1 ||
dt.time() > QTime( 0, 0 ) )
{
month++;
}
}
const int m = qwtAlignValue( month - 1, stepSize, up );
dt = QwtDate::floor( dt, QwtDate::Year );
dt = dt.addMonths( m );
break;
}
case QwtDate::Year:
{
int year = dateTime.date().year();
if ( up )
{
if ( dateTime.date().dayOfYear() > 1 ||
dt.time() > QTime( 0, 0 ) )
{
year++;
}
}
const int y = qwtAlignValue( year, stepSize, up );
dt = QwtDate::floor( dt, QwtDate::Day );
if ( y == 0 )
{
// there is no year 0 in the Julian calendar
dt.setDate( QDate( stepSize, 1, 1 ).addYears( -stepSize ) );
}
else
{
dt.setDate( QDate( y, 1, 1 ) );
}
break;
}
}
if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
{
#if QT_VERSION >= 0x050200
dt.setOffsetFromUtc( dateTime.offsetFromUtc() );
#else
dt.setUtcOffset( dateTime.utcOffset() );
#endif
}
return dt;
}
QDateTime QwtDateScaleEngine::toDateTime( double value ) const
{
QDateTime dt = QwtDate::toDateTime( value, m_data->timeSpec );
if ( !dt.isValid() )
{
const QDate date = ( value <= 0.0 )
? QwtDate::minDate() : QwtDate::maxDate();
dt = QDateTime( date, QTime( 0, 0 ), m_data->timeSpec );
}
if ( m_data->timeSpec == Qt::OffsetFromUTC )
{
dt = dt.addSecs( m_data->utcOffset );
#if QT_VERSION >= 0x050200
dt.setOffsetFromUtc( m_data->utcOffset );
#else
dt.setUtcOffset( m_data->utcOffset );
#endif
}
return dt;
}
QList< double >
QwtDate
A collection of methods around date/time values.
Definition qwt_date.h:43
QwtDate::floor
static QDateTime floor(const QDateTime &, IntervalType)
Definition qwt_date.cpp:425
QwtDate::toDateTime
static QDateTime toDateTime(double value, Qt::TimeSpec=Qt::UTC)
Definition qwt_date.cpp:261
QwtDate::minDate
static QDate minDate()
Definition qwt_date.cpp:499
QwtDate::ceil
static QDateTime ceil(const QDateTime &, IntervalType)
Definition qwt_date.cpp:323
QwtDate::Week0Type
Week0Type
Definition qwt_date.h:50
QwtDate::dateOfWeek0
static QDate dateOfWeek0(int year, Week0Type)
Date of the first day of the first week for a year.
Definition qwt_date.cpp:542
QwtDate::IntervalType
IntervalType
Definition qwt_date.h:76
QwtDate::Month
@ Month
The interval is related to months.
Definition qwt_date.h:96
QwtDate::Day
@ Day
The interval is related to days.
Definition qwt_date.h:90
QwtDate::Millisecond
@ Millisecond
The interval is related to milliseconds.
Definition qwt_date.h:78
QwtDate::Minute
@ Minute
The interval is related to minutes.
Definition qwt_date.h:84
QwtDate::Hour
@ Hour
The interval is related to hours.
Definition qwt_date.h:87
QwtDate::Second
@ Second
The interval is related to seconds.
Definition qwt_date.h:81
QwtDate::Week
@ Week
The interval is related to weeks.
Definition qwt_date.h:93
QwtDate::Year
@ Year
The interval is related to years.
Definition qwt_date.h:99
QwtDate::utcOffset
static int utcOffset(const QDateTime &)
Definition qwt_date.cpp:635
QwtDate::maxDate
static QDate maxDate()
Definition qwt_date.cpp:521
QwtDate::toDouble
static double toDouble(const QDateTime &)
Definition qwt_date.cpp:298
QwtDateScaleEngine::setMaxWeeks
void setMaxWeeks(int)
Definition qwt_date_scale_engine.cpp:841
QwtDateScaleEngine::week0Type
QwtDate::Week0Type week0Type() const
Definition qwt_date_scale_engine.cpp:824
QwtDateScaleEngine::~QwtDateScaleEngine
virtual ~QwtDateScaleEngine()
Destructor.
Definition qwt_date_scale_engine.cpp:754
QwtDateScaleEngine::alignDate
virtual QDateTime alignDate(const QDateTime &, double stepSize, QwtDate::IntervalType, bool up) const
Definition qwt_date_scale_engine.cpp:1108
QwtDateScaleEngine::toDateTime
QDateTime toDateTime(double) const
Definition qwt_date_scale_engine.cpp:1298
QwtDateScaleEngine::divideScale
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.
Definition qwt_date_scale_engine.cpp:992
QwtDateScaleEngine::setUtcOffset
void setUtcOffset(int seconds)
Definition qwt_date_scale_engine.cpp:789
QwtDateScaleEngine::setWeek0Type
void setWeek0Type(QwtDate::Week0Type)
Definition qwt_date_scale_engine.cpp:815
QwtDateScaleEngine::intervalType
virtual QwtDate::IntervalType intervalType(const QDateTime &, const QDateTime &, int maxSteps) const
Definition qwt_date_scale_engine.cpp:865
QwtDateScaleEngine::timeSpec
Qt::TimeSpec timeSpec() const
Definition qwt_date_scale_engine.cpp:774
QwtDateScaleEngine::QwtDateScaleEngine
QwtDateScaleEngine(Qt::TimeSpec=Qt::LocalTime)
Constructor.
Definition qwt_date_scale_engine.cpp:747
QwtDateScaleEngine::autoScale
virtual void autoScale(int maxNumSteps, double &x1, double &x2, double &stepSize) const override
Definition qwt_date_scale_engine.cpp:925
QwtDateScaleEngine::utcOffset
int utcOffset() const
Definition qwt_date_scale_engine.cpp:801
QwtDateScaleEngine::setTimeSpec
void setTimeSpec(Qt::TimeSpec)
Definition qwt_date_scale_engine.cpp:765
QwtDateScaleEngine::maxWeeks
int maxWeeks() const
Definition qwt_date_scale_engine.cpp:851
QwtInterval
A class representing an interval.
Definition qwt_interval.h:23
QwtInterval::minValue
double minValue() const
Definition qwt_interval.h:192
QwtInterval::normalized
QwtInterval normalized() const
Normalize the limits of the interval.
Definition qwt_interval.cpp:29
QwtInterval::width
double width() const
Return the width of an interval.
Definition qwt_interval.h:227
QwtInterval::setMaxValue
void setMaxValue(double)
Definition qwt_interval.h:186
QwtInterval::maxValue
double maxValue() const
Definition qwt_interval.h:198
QwtInterval::extend
QwtInterval extend(double value) const
Extend the interval.
Definition qwt_interval.cpp:363
QwtInterval::setMinValue
void setMinValue(double)
Definition qwt_interval.h:176
QwtInterval::symmetrize
QwtInterval symmetrize(double value) const
Definition qwt_interval.cpp:317
QwtLinearScaleEngine
A scale engine for linear scales.
Definition qwt_scale_engine.h:152
QwtLinearScaleEngine::divideScale
virtual QwtScaleDiv divideScale(double x1, double x2, int maxMajorSteps, int maxMinorSteps, double stepSize=0.0) const override
Calculate a scale division for an interval.
Definition qwt_scale_engine.cpp:577
QwtScaleArithmetic::divideInterval
static double divideInterval(double intervalSize, int numSteps, uint base)
Definition qwt_scale_engine.cpp:160
QwtScaleDiv
A class representing a scale division.
Definition qwt_scale_div.h:34
QwtScaleDiv::bounded
QwtScaleDiv bounded(double lowerBound, double upperBound) const
Definition qwt_scale_div.cpp:263
QwtScaleDiv::invert
void invert()
Definition qwt_scale_div.cpp:224
QwtScaleDiv::setInterval
void setInterval(double lowerBound, double upperBound)
Definition qwt_scale_div.cpp:91
QwtScaleDiv::MediumTick
@ MediumTick
Medium ticks.
Definition qwt_scale_div.h:46
QwtScaleDiv::MinorTick
@ MinorTick
Minor ticks.
Definition qwt_scale_div.h:43
QwtScaleDiv::MajorTick
@ MajorTick
Major ticks.
Definition qwt_scale_div.h:49
QwtScaleDiv::setTicks
void setTicks(int tickType, const QList< double > &)
Definition qwt_scale_div.cpp:297
QwtScaleEngine::reference
double reference() const
Definition qwt_scale_engine.cpp:474
QwtScaleEngine::Floating
@ Floating
Definition qwt_scale_engine.h:72
QwtScaleEngine::Inverted
@ Inverted
Turn the scale upside down.
Definition qwt_scale_engine.h:75
QwtScaleEngine::Symmetric
@ Symmetric
Build a scale which is symmetric to the reference() value.
Definition qwt_scale_engine.h:63
QwtScaleEngine::IncludeReference
@ IncludeReference
Build a scale which includes the reference() value.
Definition qwt_scale_engine.h:60
QwtScaleEngine::upperMargin
double upperMargin() const
Definition qwt_scale_engine.cpp:291
QwtScaleEngine::testAttribute
bool testAttribute(Attribute) const
Definition qwt_scale_engine.cpp:431
QwtScaleEngine::buildInterval
QwtInterval buildInterval(double value) const
Build an interval around a value.
Definition qwt_scale_engine.cpp:395
QwtScaleEngine::lowerMargin
double lowerMargin() const
Definition qwt_scale_engine.cpp:280