Commit 1c4fdc2e authored by Gus Grubba's avatar Gus Grubba
Browse files

First shot at it

parent 2f5324fd
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* 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_color_map.h"
#include "qwt_math.h"
#include "qwt_interval.h"
#include <qnumeric.h>
class QwtLinearColorMap::ColorStops
{
public:
ColorStops()
{
_stops.reserve( 256 );
}
void insert( double pos, const QColor &color );
QRgb rgb( QwtLinearColorMap::Mode, double pos ) const;
QVector<double> stops() const;
private:
class ColorStop
{
public:
ColorStop():
pos( 0.0 ),
rgb( 0 )
{
};
ColorStop( double p, const QColor &c ):
pos( p ),
rgb( c.rgb() )
{
r = qRed( rgb );
g = qGreen( rgb );
b = qBlue( rgb );
}
double pos;
QRgb rgb;
int r, g, b;
};
inline int findUpper( double pos ) const;
QVector<ColorStop> _stops;
};
void QwtLinearColorMap::ColorStops::insert( double pos, const QColor &color )
{
// Lookups need to be very fast, insertions are not so important.
// Anyway, a balanced tree is what we need here. TODO ...
if ( pos < 0.0 || pos > 1.0 )
return;
int index;
if ( _stops.size() == 0 )
{
index = 0;
_stops.resize( 1 );
}
else
{
index = findUpper( pos );
if ( index == _stops.size() ||
qAbs( _stops[index].pos - pos ) >= 0.001 )
{
_stops.resize( _stops.size() + 1 );
for ( int i = _stops.size() - 1; i > index; i-- )
_stops[i] = _stops[i-1];
}
}
_stops[index] = ColorStop( pos, color );
}
inline QVector<double> QwtLinearColorMap::ColorStops::stops() const
{
QVector<double> positions( _stops.size() );
for ( int i = 0; i < _stops.size(); i++ )
positions[i] = _stops[i].pos;
return positions;
}
inline int QwtLinearColorMap::ColorStops::findUpper( double pos ) const
{
int index = 0;
int n = _stops.size();
const ColorStop *stops = _stops.data();
while ( n > 0 )
{
const int half = n >> 1;
const int middle = index + half;
if ( stops[middle].pos <= pos )
{
index = middle + 1;
n -= half + 1;
}
else
n = half;
}
return index;
}
inline QRgb QwtLinearColorMap::ColorStops::rgb(
QwtLinearColorMap::Mode mode, double pos ) const
{
if ( pos <= 0.0 )
return _stops[0].rgb;
if ( pos >= 1.0 )
return _stops[ _stops.size() - 1 ].rgb;
const int index = findUpper( pos );
if ( mode == FixedColors )
{
return _stops[index-1].rgb;
}
else
{
const ColorStop &s1 = _stops[index-1];
const ColorStop &s2 = _stops[index];
const double ratio = ( pos - s1.pos ) / ( s2.pos - s1.pos );
const int r = s1.r + qRound( ratio * ( s2.r - s1.r ) );
const int g = s1.g + qRound( ratio * ( s2.g - s1.g ) );
const int b = s1.b + qRound( ratio * ( s2.b - s1.b ) );
return qRgb( r, g, b );
}
}
//! Constructor
QwtColorMap::QwtColorMap( Format format ):
d_format( format )
{
}
//! Destructor
QwtColorMap::~QwtColorMap()
{
}
/*!
Build and return a color map of 256 colors
The color table is needed for rendering indexed images in combination
with using colorIndex().
\param interval Range for the values
\return A color table, that can be used for a QImage
*/
QVector<QRgb> QwtColorMap::colorTable( const QwtInterval &interval ) const
{
QVector<QRgb> table( 256 );
if ( interval.isValid() )
{
const double step = interval.width() / ( table.size() - 1 );
for ( int i = 0; i < table.size(); i++ )
table[i] = rgb( interval, interval.minValue() + step * i );
}
return table;
}
class QwtLinearColorMap::PrivateData
{
public:
ColorStops colorStops;
QwtLinearColorMap::Mode mode;
};
/*!
Build a color map with two stops at 0.0 and 1.0. The color
at 0.0 is Qt::blue, at 1.0 it is Qt::yellow.
\param format Preferred format of the color map
*/
QwtLinearColorMap::QwtLinearColorMap( QwtColorMap::Format format ):
QwtColorMap( format )
{
d_data = new PrivateData;
d_data->mode = ScaledColors;
setColorInterval( Qt::blue, Qt::yellow );
}
/*!
Build a color map with two stops at 0.0 and 1.0.
\param color1 Color used for the minimum value of the value interval
\param color2 Color used for the maximum value of the value interval
\param format Preferred format for the color map
*/
QwtLinearColorMap::QwtLinearColorMap( const QColor &color1,
const QColor &color2, QwtColorMap::Format format ):
QwtColorMap( format )
{
d_data = new PrivateData;
d_data->mode = ScaledColors;
setColorInterval( color1, color2 );
}
//! Destructor
QwtLinearColorMap::~QwtLinearColorMap()
{
delete d_data;
}
/*!
\brief Set the mode of the color map
FixedColors means the color is calculated from the next lower
color stop. ScaledColors means the color is calculated
by interpolating the colors of the adjacent stops.
\sa mode()
*/
void QwtLinearColorMap::setMode( Mode mode )
{
d_data->mode = mode;
}
/*!
\return Mode of the color map
\sa setMode()
*/
QwtLinearColorMap::Mode QwtLinearColorMap::mode() const
{
return d_data->mode;
}
/*!
Set the color range
Add stops at 0.0 and 1.0.
\param color1 Color used for the minimum value of the value interval
\param color2 Color used for the maximum value of the value interval
\sa color1(), color2()
*/
void QwtLinearColorMap::setColorInterval(
const QColor &color1, const QColor &color2 )
{
d_data->colorStops = ColorStops();
d_data->colorStops.insert( 0.0, color1 );
d_data->colorStops.insert( 1.0, color2 );
}
/*!
Add a color stop
The value has to be in the range [0.0, 1.0].
F.e. a stop at position 17.0 for a range [10.0,20.0] must be
passed as: (17.0 - 10.0) / (20.0 - 10.0)
\param value Value between [0.0, 1.0]
\param color Color stop
*/
void QwtLinearColorMap::addColorStop( double value, const QColor& color )
{
if ( value >= 0.0 && value <= 1.0 )
d_data->colorStops.insert( value, color );
}
/*!
\return Positions of color stops in increasing order
*/
QVector<double> QwtLinearColorMap::colorStops() const
{
return d_data->colorStops.stops();
}
/*!
\return the first color of the color range
\sa setColorInterval()
*/
QColor QwtLinearColorMap::color1() const
{
return QColor( d_data->colorStops.rgb( d_data->mode, 0.0 ) );
}
/*!
\return the second color of the color range
\sa setColorInterval()
*/
QColor QwtLinearColorMap::color2() const
{
return QColor( d_data->colorStops.rgb( d_data->mode, 1.0 ) );
}
/*!
Map a value of a given interval into a RGB value
\param interval Range for all values
\param value Value to map into a RGB value
\return RGB value for value
*/
QRgb QwtLinearColorMap::rgb(
const QwtInterval &interval, double value ) const
{
if ( qIsNaN(value) )
return qRgba(0, 0, 0, 0);
const double width = interval.width();
double ratio = 0.0;
if ( width > 0.0 )
ratio = ( value - interval.minValue() ) / width;
return d_data->colorStops.rgb( d_data->mode, ratio );
}
/*!
\brief Map a value of a given interval into a color index
\param interval Range for all values
\param value Value to map into a color index
\return Index, between 0 and 255
*/
unsigned char QwtLinearColorMap::colorIndex(
const QwtInterval &interval, double value ) const
{
const double width = interval.width();
if ( qIsNaN(value) || width <= 0.0 || value <= interval.minValue() )
return 0;
if ( value >= interval.maxValue() )
return 255;
const double ratio = ( value - interval.minValue() ) / width;
unsigned char index;
if ( d_data->mode == FixedColors )
index = static_cast<unsigned char>( ratio * 255 ); // always floor
else
index = static_cast<unsigned char>( qRound( ratio * 255 ) );
return index;
}
class QwtAlphaColorMap::PrivateData
{
public:
QColor color;
QRgb rgb;
};
/*!
Constructor
\param color Color of the map
*/
QwtAlphaColorMap::QwtAlphaColorMap( const QColor &color ):
QwtColorMap( QwtColorMap::RGB )
{
d_data = new PrivateData;
d_data->color = color;
d_data->rgb = color.rgb() & qRgba( 255, 255, 255, 0 );
}
//! Destructor
QwtAlphaColorMap::~QwtAlphaColorMap()
{
delete d_data;
}
/*!
Set the color
\param color Color
\sa color()
*/
void QwtAlphaColorMap::setColor( const QColor &color )
{
d_data->color = color;
d_data->rgb = color.rgb();
}
/*!
\return the color
\sa setColor()
*/
QColor QwtAlphaColorMap::color() const
{
return d_data->color;
}
/*!
\brief Map a value of a given interval into a alpha value
alpha := (value - interval.minValue()) / interval.width();
\param interval Range for all values
\param value Value to map into a RGB value
\return RGB value, with an alpha value
*/
QRgb QwtAlphaColorMap::rgb( const QwtInterval &interval, double value ) const
{
const double width = interval.width();
if ( !qIsNaN(value) && width >= 0.0 )
{
const double ratio = ( value - interval.minValue() ) / width;
int alpha = qRound( 255 * ratio );
if ( alpha < 0 )
alpha = 0;
if ( alpha > 255 )
alpha = 255;
return d_data->rgb | ( alpha << 24 );
}
return d_data->rgb;
}
/*!
Dummy function, needed to be implemented as it is pure virtual
in QwtColorMap. Color indices make no sense in combination with
an alpha channel.
\return Always 0
*/
unsigned char QwtAlphaColorMap::colorIndex(
const QwtInterval &, double ) const
{
return 0;
}
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* 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
*****************************************************************************/
#ifndef QWT_COLOR_MAP_H
#define QWT_COLOR_MAP_H
#include "qwt_global.h"
#include "qwt_interval.h"
#include <qcolor.h>
#include <qvector.h>
/*!
\brief QwtColorMap is used to map values into colors.
For displaying 3D data on a 2D plane the 3rd dimension is often
displayed using colors, like f.e in a spectrogram.
Each color map is optimized to return colors for only one of the
following image formats:
- QImage::Format_Indexed8\n
- QImage::Format_ARGB32\n
\sa QwtPlotSpectrogram, QwtScaleWidget
*/
class QWT_EXPORT QwtColorMap
{
public:
/*!
Format for color mapping
\sa rgb(), colorIndex(), colorTable()
*/
enum Format
{
//! The map is intended to map into RGB values.
RGB,
/*!
The map is intended to map into 8 bit values, that
are indices into the color table.
*/
Indexed
};
QwtColorMap( Format = QwtColorMap::RGB );
virtual ~QwtColorMap();
Format format() const;
/*!
Map a value of a given interval into a RGB value.
\param interval Range for the values
\param value Value
\return RGB value, corresponding to value
*/
virtual QRgb rgb( const QwtInterval &interval,
double value ) const = 0;
/*!
Map a value of a given interval into a color index
\param interval Range for the values
\param value Value
\return color index, corresponding to value
*/
virtual unsigned char colorIndex(
const QwtInterval &interval, double value ) const = 0;
QColor color( const QwtInterval &, double value ) const;
virtual QVector<QRgb> colorTable( const QwtInterval & ) const;
private:
Format d_format;
};
/*!
\brief QwtLinearColorMap builds a color map from color stops.
A color stop is a color at a specific position. The valid
range for the positions is [0.0, 1.0]. When mapping a value
into a color it is translated into this interval according to mode().
*/
class QWT_EXPORT QwtLinearColorMap: public QwtColorMap
{
public:
/*!
Mode of color map
\sa setMode(), mode()
*/
enum Mode
{
//! Return the color from the next lower color stop
FixedColors,
//! Interpolating the colors of the adjacent stops.
ScaledColors
};
QwtLinearColorMap( QwtColorMap::Format = QwtColorMap::RGB );
QwtLinearColorMap( const QColor &from, const QColor &to,
QwtColorMap::Format = QwtColorMap::RGB );
virtual ~QwtLinearColorMap();
void setMode( Mode );
Mode mode() const;
void setColorInterval( const QColor &color1, const QColor &color2 );
void addColorStop( double value, const QColor& );
QVector<double> colorStops() const;
QColor color1() const;
QColor color2() const;
virtual QRgb rgb( const QwtInterval &, double value ) const;
virtual unsigned char colorIndex(
const QwtInterval &, double value ) const;
class ColorStops;
private:
// Disabled copy constructor and operator=
QwtLinearColorMap( const QwtLinearColorMap & );
QwtLinearColorMap &operator=( const QwtLinearColorMap & );
class PrivateData;
PrivateData *d_data;
};
/*!
\brief QwtAlphaColorMap varies the alpha value of a color
*/
class QWT_EXPORT QwtAlphaColorMap: public QwtColorMap
{
public:
QwtAlphaColorMap( const QColor & = QColor( Qt::gray ) );
virtual ~QwtAlphaColorMap();
void setColor( const QColor & );
QColor color() const;
virtual QRgb rgb( const QwtInterval &, double value ) const;
private:
QwtAlphaColorMap( const QwtAlphaColorMap & );
QwtAlphaColorMap &operator=( const QwtAlphaColorMap & );
virtual unsigned char colorIndex(
const QwtInterval &, double value ) const;
class PrivateData;
PrivateData *d_data;
};
/*!
Map a value into a color
\param interval Valid interval for values
\param value Value
\return Color corresponding to value
\warning This method is slow for Indexed color maps. If it is
necessary to map many values, its better to get the
color table once and find the color using colorIndex().
*/
inline QColor QwtColorMap::color(
const QwtInterval &interval, double value ) const
{
if ( d_format == RGB )
{
return QColor( rgb( interval, value ) );
}
else
{
const unsigned int index = colorIndex( interval, value );
return colorTable( interval )[index]; // slow
}
}
/*!
\return Intended format of the color map
\sa Format
*/
inline QwtColorMap::Format QwtColorMap::format() const
{
return d_format;
}
#endif
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* 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_column_symbol.h"
#include "qwt_math.h"
#include "qwt_painter.h"
#include <qpainter.h>
#include <qpalette.h>
static void qwtDrawBox( QPainter *p, const QRectF &rect,
const QPalette &pal, double lw )
{
if ( lw > 0.0 )
{
if ( rect.width() == 0.0 )
{
p->setPen( pal.dark().color() );
p->drawLine( rect.topLeft(), rect.bottomLeft() );
return;
}
if ( rect.height() == 0.0 )
{
p->setPen( pal.dark().color() );
p->drawLine( rect.topLeft(), rect.topRight() );
return;
}
lw = qMin( lw, rect.height() / 2.0 - 1.0 );
lw = qMin( lw, rect.width() / 2.0 - 1.0 );
const QRectF outerRect = rect.adjusted( 0, 0, 1, 1 );
QPolygonF polygon( outerRect );
if ( outerRect.width() > 2 * lw &&
outerRect.height() > 2 * lw )
{
const QRectF innerRect = outerRect.adjusted( lw, lw, -lw, -lw );
polygon = polygon.subtracted( innerRect );
}
p->setPen( Qt::NoPen );
p->setBrush( pal.dark() );
p->drawPolygon( polygon );
}
const QRectF windowRect = rect.adjusted( lw, lw, -lw + 1, -lw + 1 );
if ( windowRect.isValid() )
p->fillRect( windowRect, pal.window() );
}
static void qwtDrawPanel( QPainter *painter, const QRectF &rect,
const QPalette &pal, double lw )
{
if ( lw > 0.0 )
{
if ( rect.width() == 0.0 )
{
painter->setPen( pal.window().color() );
painter->drawLine( rect.topLeft(), rect.bottomLeft() );
return;
}
if ( rect.height() == 0.0 )
{
painter->setPen( pal.window().color() );
painter->drawLine( rect.topLeft(), rect.topRight() );
return;
}
lw = qMin( lw, rect.height() / 2.0 - 1.0 );
lw = qMin( lw, rect.width() / 2.0 - 1.0 );
const QRectF outerRect = rect.adjusted( 0, 0, 1, 1 );
const QRectF innerRect = outerRect.adjusted( lw, lw, -lw, -lw );
QPolygonF lines[2];
lines[0] += outerRect.bottomLeft();
lines[0] += outerRect.topLeft();
lines[0] += outerRect.topRight();
lines[0] += innerRect.topRight();
lines[0] += innerRect.topLeft();
lines[0] += innerRect.bottomLeft();
lines[1] += outerRect.topRight();
lines[1] += outerRect.bottomRight();
lines[1] += outerRect.bottomLeft();
lines[1] += innerRect.bottomLeft();
lines[1] += innerRect.bottomRight();
lines[1] += innerRect.topRight();
painter->setPen( Qt::NoPen );
painter->setBrush( pal.light() );
painter->drawPolygon( lines[0] );
painter->setBrush( pal.dark() );
painter->drawPolygon( lines[1] );
}
painter->fillRect( rect.adjusted( lw, lw, -lw + 1, -lw + 1 ), pal.window() );
}
class QwtColumnSymbol::PrivateData
{
public:
PrivateData():
style( QwtColumnSymbol::Box ),
frameStyle( QwtColumnSymbol::Raised ),
lineWidth( 2 )
{
palette = QPalette( Qt::gray );
}
QwtColumnSymbol::Style style;
QwtColumnSymbol::FrameStyle frameStyle;
QPalette palette;
int lineWidth;
};
/*!
Constructor
\param style Style of the symbol
\sa setStyle(), style(), Style
*/
QwtColumnSymbol::QwtColumnSymbol( Style style )
{
d_data = new PrivateData();
d_data->style = style;
}
//! Destructor
QwtColumnSymbol::~QwtColumnSymbol()
{
delete d_data;
}
/*!
Specify the symbol style
\param style Style
\sa style(), setPalette()
*/
void QwtColumnSymbol::setStyle( Style style )
{
d_data->style = style;
}
/*!
\return Current symbol style
\sa setStyle()
*/
QwtColumnSymbol::Style QwtColumnSymbol::style() const
{
return d_data->style;
}
/*!
Assign a palette for the symbol
\param palette Palette
\sa palette(), setStyle()
*/
void QwtColumnSymbol::setPalette( const QPalette &palette )
{
d_data->palette = palette;
}
/*!
\return Current palette
\sa setPalette()
*/
const QPalette& QwtColumnSymbol::palette() const
{
return d_data->palette;
}
/*!
Set the frame, that is used for the Box style.
\param frameStyle Frame style
\sa frameStyle(), setLineWidth(), setStyle()
*/
void QwtColumnSymbol::setFrameStyle( FrameStyle frameStyle )
{
d_data->frameStyle = frameStyle;
}
/*!
\return Current frame style, that is used for the Box style.
\sa setFrameStyle(), lineWidth(), setStyle()
*/
QwtColumnSymbol::FrameStyle QwtColumnSymbol::frameStyle() const
{
return d_data->frameStyle;
}
/*!
Set the line width of the frame, that is used for the Box style.
\param width Width
\sa lineWidth(), setFrameStyle()
*/
void QwtColumnSymbol::setLineWidth( int width )
{
if ( width < 0 )
width = 0;
d_data->lineWidth = width;
}
/*!
\return Line width of the frame, that is used for the Box style.
\sa setLineWidth(), frameStyle(), setStyle()
*/
int QwtColumnSymbol::lineWidth() const
{
return d_data->lineWidth;
}
/*!
Draw the symbol depending on its style.
\param painter Painter
\param rect Directed rectangle
\sa drawBox()
*/
void QwtColumnSymbol::draw( QPainter *painter,
const QwtColumnRect &rect ) const
{
painter->save();
switch ( d_data->style )
{
case QwtColumnSymbol::Box:
{
drawBox( painter, rect );
break;
}
default:;
}
painter->restore();
}
/*!
Draw the symbol when it is in Box style.
\param painter Painter
\param rect Directed rectangle
\sa draw()
*/
void QwtColumnSymbol::drawBox( QPainter *painter,
const QwtColumnRect &rect ) const
{
QRectF r = rect.toRect();
if ( QwtPainter::roundingAlignment( painter ) )
{
r.setLeft( qRound( r.left() ) );
r.setRight( qRound( r.right() ) );
r.setTop( qRound( r.top() ) );
r.setBottom( qRound( r.bottom() ) );
}
switch ( d_data->frameStyle )
{
case QwtColumnSymbol::Raised:
{
qwtDrawPanel( painter, r, d_data->palette, d_data->lineWidth );
break;
}
case QwtColumnSymbol::Plain:
{
qwtDrawBox( painter, r, d_data->palette, d_data->lineWidth );
break;
}
default:
{
painter->fillRect( r, d_data->palette.window() );
}
}
}
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* 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
*****************************************************************************/
#ifndef QWT_COLUMN_SYMBOL_H
#define QWT_COLUMN_SYMBOL_H
#include "qwt_global.h"
#include "qwt_interval.h"
#include <qpen.h>
#include <qsize.h>
#include <qrect.h>
class QPainter;
class QPalette;
class QRect;
class QwtText;
/*!
\brief Directed rectangle representing bounding rectangle and orientation
of a column.
*/
class QWT_EXPORT QwtColumnRect
{
public:
//! Direction of the column
enum Direction
{
//! From left to right
LeftToRight,
//! From right to left
RightToLeft,
//! From bottom to top
BottomToTop,
//! From top to bottom
TopToBottom
};
//! Build an rectangle with invalid intervals directed BottomToTop.
QwtColumnRect():
direction( BottomToTop )
{
}
//! \return A normalized QRect built from the intervals
QRectF toRect() const
{
QRectF r( hInterval.minValue(), vInterval.minValue(),
hInterval.maxValue() - hInterval.minValue(),
vInterval.maxValue() - vInterval.minValue() );
r = r.normalized();
if ( hInterval.borderFlags() & QwtInterval::ExcludeMinimum )
r.adjust( 1, 0, 0, 0 );
if ( hInterval.borderFlags() & QwtInterval::ExcludeMaximum )
r.adjust( 0, 0, -1, 0 );
if ( vInterval.borderFlags() & QwtInterval::ExcludeMinimum )
r.adjust( 0, 1, 0, 0 );
if ( vInterval.borderFlags() & QwtInterval::ExcludeMaximum )
r.adjust( 0, 0, 0, -1 );
return r;
}
//! \return Orientation
Qt::Orientation orientation() const
{
if ( direction == LeftToRight || direction == RightToLeft )
return Qt::Horizontal;
return Qt::Vertical;
}
//! Interval for the horizontal coordinates
QwtInterval hInterval;
//! Interval for the vertical coordinates
QwtInterval vInterval;
//! Direction
Direction direction;
};
//! A drawing primitive for columns
class QWT_EXPORT QwtColumnSymbol
{
public:
/*!
Style
\sa setStyle(), style()
*/
enum Style
{
//! No Style, the symbol draws nothing
NoStyle = -1,
/*!
The column is painted with a frame depending on the frameStyle()
and lineWidth() using the palette().
*/
Box,
/*!
Styles >= QwtColumnSymbol::UserStyle are reserved for derived
classes of QwtColumnSymbol that overload draw() with
additional application specific symbol types.
*/
UserStyle = 1000
};
/*!
Frame Style used in Box style().
\sa Style, setFrameStyle(), frameStyle(), setStyle(), setPalette()
*/
enum FrameStyle
{
//! No frame
NoFrame,
//! A plain frame style
Plain,
//! A raised frame style
Raised
};
public:
QwtColumnSymbol( Style = NoStyle );
virtual ~QwtColumnSymbol();
void setFrameStyle( FrameStyle style );
FrameStyle frameStyle() const;
void setLineWidth( int width );
int lineWidth() const;
void setPalette( const QPalette & );
const QPalette &palette() const;
void setStyle( Style );
Style style() const;
virtual void draw( QPainter *, const QwtColumnRect & ) const;
protected:
void drawBox( QPainter *, const QwtColumnRect & ) const;
private:
class PrivateData;
PrivateData* d_data;
};
#endif
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* 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_compass.h"
#include "qwt_compass_rose.h"
#include "qwt_math.h"
#include "qwt_scale_draw.h"
#include "qwt_painter.h"
#include "qwt_dial_needle.h"
#include <qpainter.h>
#include <qpixmap.h>
#include <qevent.h>
/*!
\brief Constructor
Initializes a label map for multiples of 45 degrees
*/
QwtCompassScaleDraw::QwtCompassScaleDraw()
{
enableComponent( QwtAbstractScaleDraw::Backbone, false );
enableComponent( QwtAbstractScaleDraw::Ticks, false );
d_labelMap.insert( 0.0, QString::fromLatin1( "N" ) );
d_labelMap.insert( 45.0, QString::fromLatin1( "NE" ) );
d_labelMap.insert( 90.0, QString::fromLatin1( "E" ) );
d_labelMap.insert( 135.0, QString::fromLatin1( "SE" ) );
d_labelMap.insert( 180.0, QString::fromLatin1( "S" ) );
d_labelMap.insert( 225.0, QString::fromLatin1( "SW" ) );
d_labelMap.insert( 270.0, QString::fromLatin1( "W" ) );
d_labelMap.insert( 315.0, QString::fromLatin1( "NW" ) );
#if 0
d_labelMap.insert( 22.5, QString::fromLatin1( "NNE" ) );
d_labelMap.insert( 67.5, QString::fromLatin1( "NEE" ) );
d_labelMap.insert( 112.5, QString::fromLatin1( "SEE" ) );
d_labelMap.insert( 157.5, QString::fromLatin1( "SSE" ) );
d_labelMap.insert( 202.5, QString::fromLatin1( "SSW" ) );
d_labelMap.insert( 247.5, QString::fromLatin1( "SWW" ) );
d_labelMap.insert( 292.5, QString::fromLatin1( "NWW" ) );
d_labelMap.insert( 337.5, QString::fromLatin1( "NNW" ) );
#endif
}
/*!
\brief Constructor
\param map Value to label map
*/
QwtCompassScaleDraw::QwtCompassScaleDraw( const QMap<double, QString> &map ):
d_labelMap( map )
{
enableComponent( QwtAbstractScaleDraw::Backbone, false );
enableComponent( QwtAbstractScaleDraw::Ticks, false );
}
/*!
\brief Set a map, mapping values to labels
\param map Value to label map
The values of the major ticks are found by looking into this
map. The default map consists of the labels N, NE, E, SE, S, SW, W, NW.
\warning The map will have no effect for values that are no major
tick values. Major ticks can be changed by QwtScaleDraw::setScale
\sa labelMap(), scaleDraw(), setScale()
*/
void QwtCompassScaleDraw::setLabelMap( const QMap<double, QString> &map )
{
d_labelMap = map;
}
/*!
\return map, mapping values to labels
\sa setLabelMap()
*/
QMap<double, QString> QwtCompassScaleDraw::labelMap() const
{
return d_labelMap;
}
/*!
Map a value to a corresponding label
\param value Value that will be mapped
label() looks in the labelMap() for a corresponding label for value
or returns an null text.
\return Label, or QString::null
\sa labelMap(), setLabelMap()
*/
QwtText QwtCompassScaleDraw::label( double value ) const
{
if ( qFuzzyCompare( value + 1.0, 1.0 ) )
value = 0.0;
if ( value < 0.0 )
value += 360.0;
if ( d_labelMap.contains( value ) )
return d_labelMap[value];
return QwtText();
}
class QwtCompass::PrivateData
{
public:
PrivateData():
rose( NULL )
{
}
~PrivateData()
{
delete rose;
}
QwtCompassRose *rose;
};
/*!
\brief Constructor
\param parent Parent widget
Create a compass widget with a scale, no needle and no rose.
The default origin is 270.0 with no valid value. It accepts
mouse and keyboard inputs and has no step size. The default mode
is QwtDial::RotateNeedle.
*/
QwtCompass::QwtCompass( QWidget* parent ):
QwtDial( parent )
{
d_data = new PrivateData;
setScaleDraw( new QwtCompassScaleDraw() );
setOrigin( 270.0 );
setWrapping( true );
setScaleMaxMajor( 36 );
setScaleMaxMinor( 10 );
setScale( 0.0, 360.0 ); // degrees as default
setTotalSteps( 360 );
}
//! Destructor
QwtCompass::~QwtCompass()
{
delete d_data;
}
/*!
Draw the contents of the scale
\param painter Painter
\param center Center of the content circle
\param radius Radius of the content circle
*/
void QwtCompass::drawScaleContents( QPainter *painter,
const QPointF &center, double radius ) const
{
QPalette::ColorGroup cg;
if ( isEnabled() )
cg = hasFocus() ? QPalette::Active : QPalette::Inactive;
else
cg = QPalette::Disabled;
double north = origin();
if ( isValid() )
{
if ( mode() == RotateScale )
north -= value();
}
const int margin = 4;
drawRose( painter, center, radius - margin, 360.0 - north, cg );
}
/*!
Draw the compass rose
\param painter Painter
\param center Center of the compass
\param radius of the circle, where to paint the rose
\param north Direction pointing north, in degrees counter clockwise
\param cg Color group
*/
void QwtCompass::drawRose( QPainter *painter, const QPointF &center,
double radius, double north, QPalette::ColorGroup cg ) const
{
if ( d_data->rose )
d_data->rose->draw( painter, center, radius, north, cg );
}
/*!
Set a rose for the compass
\param rose Compass rose
\warning The rose will be deleted, when a different rose is
set or in ~QwtCompass
\sa rose()
*/
void QwtCompass::setRose( QwtCompassRose *rose )
{
if ( rose != d_data->rose )
{
if ( d_data->rose )
delete d_data->rose;
d_data->rose = rose;
update();
}
}
/*!
\return rose
\sa setRose()
*/
const QwtCompassRose *QwtCompass::rose() const
{
return d_data->rose;
}
/*!
\return rose
\sa setRose()
*/
QwtCompassRose *QwtCompass::rose()
{
return d_data->rose;
}
/*!
Handles key events
Beside the keys described in QwtDial::keyPressEvent numbers
from 1-9 (without 5) set the direction according to their
position on the num pad.
\sa isReadOnly()
*/
void QwtCompass::keyPressEvent( QKeyEvent *kev )
{
if ( isReadOnly() )
return;
#if 0
if ( kev->key() == Key_5 )
{
invalidate(); // signal ???
return;
}
#endif
double newValue = value();
if ( kev->key() >= Qt::Key_1 && kev->key() <= Qt::Key_9 )
{
if ( mode() != RotateNeedle || kev->key() == Qt::Key_5 )
return;
switch ( kev->key() )
{
case Qt::Key_6:
newValue = 180.0 * 0.0;
break;
case Qt::Key_3:
newValue = 180.0 * 0.25;
break;
case Qt::Key_2:
newValue = 180.0 * 0.5;
break;
case Qt::Key_1:
newValue = 180.0 * 0.75;
break;
case Qt::Key_4:
newValue = 180.0 * 1.0;
break;
case Qt::Key_7:
newValue = 180.0 * 1.25;
break;
case Qt::Key_8:
newValue = 180.0 * 1.5;
break;
case Qt::Key_9:
newValue = 180.0 * 1.75;
break;
}
newValue -= origin();
setValue( newValue );
}
else
{
QwtDial::keyPressEvent( kev );
}
}
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* 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
*****************************************************************************/
#ifndef QWT_COMPASS_H
#define QWT_COMPASS_H 1
#include "qwt_global.h"
#include "qwt_dial.h"
#include "qwt_round_scale_draw.h"
#include <qstring.h>
#include <qmap.h>
class QwtCompassRose;
/*!
\brief A special scale draw made for QwtCompass
QwtCompassScaleDraw maps values to strings using
a special map, that can be modified by the application
The default map consists of the labels N, NE, E, SE, S, SW, W, NW.
\sa QwtCompass
*/
class QWT_EXPORT QwtCompassScaleDraw: public QwtRoundScaleDraw
{
public:
explicit QwtCompassScaleDraw();
explicit QwtCompassScaleDraw( const QMap<double, QString> &map );
void setLabelMap( const QMap<double, QString> &map );
QMap<double, QString> labelMap() const;
virtual QwtText label( double value ) const;
private:
QMap<double, QString> d_labelMap;
};
/*!
\brief A Compass Widget
QwtCompass is a widget to display and enter directions. It consists
of a scale, an optional needle and rose.
\image html dials1.png
\note The examples/dials example shows how to use QwtCompass.
*/
class QWT_EXPORT QwtCompass: public QwtDial
{
Q_OBJECT
public:
explicit QwtCompass( QWidget* parent = NULL );
virtual ~QwtCompass();
void setRose( QwtCompassRose *rose );
const QwtCompassRose *rose() const;
QwtCompassRose *rose();
protected:
virtual void drawRose( QPainter *, const QPointF &center,
double radius, double north, QPalette::ColorGroup ) const;
virtual void drawScaleContents( QPainter *,
const QPointF &center, double radius ) const;
virtual void keyPressEvent( QKeyEvent * );
private:
class PrivateData;
PrivateData *d_data;
};
#endif
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* 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_compass_rose.h"
#include "qwt_point_polar.h"
#include "qwt_painter.h"
#include <qpainter.h>
static QPointF qwtIntersection(
QPointF p11, QPointF p12, QPointF p21, QPointF p22 )
{
const QLineF line1( p11, p12 );
const QLineF line2( p21, p22 );
QPointF pos;
if ( line1.intersect( line2, &pos ) == QLineF::NoIntersection )
return QPointF();
return pos;
}
class QwtSimpleCompassRose::PrivateData
{
public:
PrivateData():
width( 0.2 ),
numThorns( 8 ),
numThornLevels( -1 ),
shrinkFactor( 0.9 )
{
}
double width;
int numThorns;
int numThornLevels;
double shrinkFactor;
};
/*!
Constructor
\param numThorns Number of thorns
\param numThornLevels Number of thorn levels
*/
QwtSimpleCompassRose::QwtSimpleCompassRose(
int numThorns, int numThornLevels )
{
d_data = new PrivateData();
d_data->numThorns = numThorns;
d_data->numThornLevels = numThornLevels;
const QColor dark( 128, 128, 255 );
const QColor light( 192, 255, 255 );
QPalette palette;
palette.setColor( QPalette::Dark, dark );
palette.setColor( QPalette::Light, light );
setPalette( palette );
}
//! Destructor
QwtSimpleCompassRose::~QwtSimpleCompassRose()
{
delete d_data;
}
/*!
Set the Factor how to shrink the thorns with each level
The default value is 0.9.
\param factor Shrink factor
\sa shrinkFactor()
*/
void QwtSimpleCompassRose::setShrinkFactor( double factor )
{
d_data->shrinkFactor = factor;
}
/*!
\return Factor how to shrink the thorns with each level
\sa setShrinkFactor()
*/
double QwtSimpleCompassRose::shrinkFactor() const
{
return d_data->shrinkFactor;
}
/*!
Draw the rose
\param painter Painter
\param center Center point
\param radius Radius of the rose
\param north Position
\param cg Color group
*/
void QwtSimpleCompassRose::draw( QPainter *painter, const QPointF &center,
double radius, double north, QPalette::ColorGroup cg ) const
{
QPalette pal = palette();
pal.setCurrentColorGroup( cg );
drawRose( painter, pal, center, radius, north, d_data->width,
d_data->numThorns, d_data->numThornLevels, d_data->shrinkFactor );
}
/*!
Draw the rose
\param painter Painter
\param palette Palette
\param center Center of the rose
\param radius Radius of the rose
\param north Position pointing to north
\param width Width of the rose
\param numThorns Number of thorns
\param numThornLevels Number of thorn levels
\param shrinkFactor Factor to shrink the thorns with each level
*/
void QwtSimpleCompassRose::drawRose(
QPainter *painter,
const QPalette &palette,
const QPointF &center, double radius, double north, double width,
int numThorns, int numThornLevels, double shrinkFactor )
{
if ( numThorns < 4 )
numThorns = 4;
if ( numThorns % 4 )
numThorns += 4 - numThorns % 4;
if ( numThornLevels <= 0 )
numThornLevels = numThorns / 4;
if ( shrinkFactor >= 1.0 )
shrinkFactor = 1.0;
if ( shrinkFactor <= 0.5 )
shrinkFactor = 0.5;
painter->save();
painter->setPen( Qt::NoPen );
for ( int j = 1; j <= numThornLevels; j++ )
{
double step = qPow( 2.0, j ) * M_PI / numThorns;
if ( step > M_PI_2 )
break;
double r = radius;
for ( int k = 0; k < 3; k++ )
{
if ( j + k < numThornLevels )
r *= shrinkFactor;
}
double leafWidth = r * width;
if ( 2.0 * M_PI / step > 32 )
leafWidth = 16;
const double origin = qwtRadians( north );
for ( double angle = origin;
angle < 2.0 * M_PI + origin; angle += step )
{
const QPointF p = qwtPolar2Pos( center, r, angle );
const QPointF p1 = qwtPolar2Pos( center, leafWidth, angle + M_PI_2 );
const QPointF p2 = qwtPolar2Pos( center, leafWidth, angle - M_PI_2 );
const QPointF p3 = qwtPolar2Pos( center, r, angle + step / 2.0 );
const QPointF p4 = qwtPolar2Pos( center, r, angle - step / 2.0 );
QPainterPath darkPath;
darkPath.moveTo( center );
darkPath.lineTo( p );
darkPath.lineTo( qwtIntersection( center, p3, p1, p ) );
painter->setBrush( palette.brush( QPalette::Dark ) );
painter->drawPath( darkPath );
QPainterPath lightPath;
lightPath.moveTo( center );
lightPath.lineTo( p );
lightPath.lineTo( qwtIntersection( center, p4, p2, p ) );
painter->setBrush( palette.brush( QPalette::Light ) );
painter->drawPath( lightPath );
}
}
painter->restore();
}
/*!
Set the width of the rose heads. Lower value make thinner heads.
The range is limited from 0.03 to 0.4.
\param width Width
*/
void QwtSimpleCompassRose::setWidth( double width )
{
d_data->width = width;
if ( d_data->width < 0.03 )
d_data->width = 0.03;
if ( d_data->width > 0.4 )
d_data->width = 0.4;
}
/*!
\return Width of the rose
\sa setWidth()
*/
double QwtSimpleCompassRose::width() const
{
return d_data->width;
}
/*!
Set the number of thorns on one level
The number is aligned to a multiple of 4, with a minimum of 4
\param numThorns Number of thorns
\sa numThorns(), setNumThornLevels()
*/
void QwtSimpleCompassRose::setNumThorns( int numThorns )
{
if ( numThorns < 4 )
numThorns = 4;
if ( numThorns % 4 )
numThorns += 4 - numThorns % 4;
d_data->numThorns = numThorns;
}
/*!
\return Number of thorns
\sa setNumThorns(), setNumThornLevels()
*/
int QwtSimpleCompassRose::numThorns() const
{
return d_data->numThorns;
}
/*!
Set the of thorns levels
\param numThornLevels Number of thorns levels
\sa setNumThorns(), numThornLevels()
*/
void QwtSimpleCompassRose::setNumThornLevels( int numThornLevels )
{
d_data->numThornLevels = numThornLevels;
}
/*!
\return Number of thorn levels
\sa setNumThorns(), setNumThornLevels()
*/
int QwtSimpleCompassRose::numThornLevels() const
{
return d_data->numThornLevels;
}
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* 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
*****************************************************************************/
#ifndef QWT_COMPASS_ROSE_H
#define QWT_COMPASS_ROSE_H 1
#include "qwt_global.h"
#include <qpalette.h>
class QPainter;
/*!
\brief Abstract base class for a compass rose
*/
class QWT_EXPORT QwtCompassRose
{
public:
//! Destructor
virtual ~QwtCompassRose() {};
//! Assign a palette
virtual void setPalette( const QPalette &p )
{
d_palette = p;
}
//! \return Current palette
const QPalette &palette() const
{
return d_palette;
}
/*!
Draw the rose
\param painter Painter
\param center Center point
\param radius Radius of the rose
\param north Position
\param colorGroup Color group
*/
virtual void draw( QPainter *painter,
const QPointF &center, double radius, double north,
QPalette::ColorGroup colorGroup = QPalette::Active ) const = 0;
private:
QPalette d_palette;
};
/*!
\brief A simple rose for QwtCompass
*/
class QWT_EXPORT QwtSimpleCompassRose: public QwtCompassRose
{
public:
QwtSimpleCompassRose( int numThorns = 8, int numThornLevels = -1 );
virtual ~QwtSimpleCompassRose();
void setWidth( double w );
double width() const;
void setNumThorns( int count );
int numThorns() const;
void setNumThornLevels( int count );
int numThornLevels() const;
void setShrinkFactor( double factor );
double shrinkFactor() const;
virtual void draw( QPainter *, const QPointF &center, double radius,
double north, QPalette::ColorGroup = QPalette::Active ) const;
static void drawRose( QPainter *, const QPalette &,
const QPointF &center, double radius, double origin, double width,
int numThorns, int numThornLevels, double shrinkFactor );
private:
class PrivateData;
PrivateData *d_data;
};
#endif
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* 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
*****************************************************************************/
#ifndef _QWT_COMPAT_H_
#define _QWT_COMPAT_H_
#include "qwt_global.h"
#include "qwt_interval.h"
#include "qwt_point_3d.h"
#include <qlist.h>
#include <qvector.h>
#include <qpoint.h>
#include <qsize.h>
#include <qrect.h>
#include <qpolygon.h>
// A couple of definition for Qwt5 compatibility
#define qwtMax qMax
#define qwtMin qMin
#define qwtAbs qAbs
#define qwtRound qRound
#define QwtArray QVector
typedef QList<double> QwtValueList;
typedef QPointF QwtDoublePoint;
typedef QSizeF QwtDoubleSize;
typedef QRectF QwtDoubleRect;
typedef QPolygon QwtPolygon;
typedef QPolygonF QwtPolygonF;
typedef QwtInterval QwtDoubleInterval;
typedef QwtPoint3D QwtDoublePoint3D;
#endif
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* 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_arrow_button.h"
#include "qwt_math.h"
#include "qwt_counter.h"
#include <qlayout.h>
#include <qlineedit.h>
#include <qvalidator.h>
#include <qevent.h>
#include <qstyle.h>
class QwtCounter::PrivateData
{
public:
PrivateData():
minimum( 0.0 ),
maximum( 0.0 ),
singleStep( 1.0 ),
isValid( false ),
value( 0.0 ),
wrapping( false )
{
increment[Button1] = 1;
increment[Button2] = 10;
increment[Button3] = 100;
}
QwtArrowButton *buttonDown[ButtonCnt];
QwtArrowButton *buttonUp[ButtonCnt];
QLineEdit *valueEdit;
int increment[ButtonCnt];
int numButtons;
double minimum;
double maximum;
double singleStep;
bool isValid;
double value;
bool wrapping;
};
/*!
The counter is initialized with a range is set to [0.0, 1.0] with
0.01 as single step size. The value is invalid.
The default number of buttons is set to 2. The default increments are:
\li Button 1: 1 step
\li Button 2: 10 steps
\li Button 3: 100 steps
\param parent
*/
QwtCounter::QwtCounter( QWidget *parent ):
QWidget( parent )
{
initCounter();
}
void QwtCounter::initCounter()
{
d_data = new PrivateData;
QHBoxLayout *layout = new QHBoxLayout( this );
layout->setSpacing( 0 );
layout->setMargin( 0 );
for ( int i = ButtonCnt - 1; i >= 0; i-- )
{
QwtArrowButton *btn =
new QwtArrowButton( i + 1, Qt::DownArrow, this );
btn->setFocusPolicy( Qt::NoFocus );
btn->installEventFilter( this );
layout->addWidget( btn );
connect( btn, SIGNAL( released() ), SLOT( btnReleased() ) );
connect( btn, SIGNAL( clicked() ), SLOT( btnClicked() ) );
d_data->buttonDown[i] = btn;
}
d_data->valueEdit = new QLineEdit( this );
d_data->valueEdit->setReadOnly( false );
d_data->valueEdit->setValidator( new QDoubleValidator( d_data->valueEdit ) );
layout->addWidget( d_data->valueEdit );
connect( d_data->valueEdit, SIGNAL( editingFinished() ),
SLOT( textChanged() ) );
layout->setStretchFactor( d_data->valueEdit, 10 );
for ( int i = 0; i < ButtonCnt; i++ )
{
QwtArrowButton *btn =
new QwtArrowButton( i + 1, Qt::UpArrow, this );
btn->setFocusPolicy( Qt::NoFocus );
btn->installEventFilter( this );
layout->addWidget( btn );
connect( btn, SIGNAL( released() ), SLOT( btnReleased() ) );
connect( btn, SIGNAL( clicked() ), SLOT( btnClicked() ) );
d_data->buttonUp[i] = btn;
}
setNumButtons( 2 );
setRange( 0.0, 1.0 );
setSingleStep( 0.001 );
setValue( 0.0 );
setSizePolicy(
QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) );
setFocusProxy( d_data->valueEdit );
setFocusPolicy( Qt::StrongFocus );
}
//! Destructor
QwtCounter::~QwtCounter()
{
delete d_data;
}
/*!
Set the counter to be in valid/invalid state
When the counter is set to invalid, no numbers are displayed and
the buttons are disabled.
\param on If true the counter will be set as valid
\sa setValue(), isValid()
*/
void QwtCounter::setValid( bool on )
{
if ( on != d_data->isValid )
{
d_data->isValid = on;
updateButtons();
if ( d_data->isValid )
{
showNumber( value() );
Q_EMIT valueChanged( value() );
}
else
{
d_data->valueEdit->setText( QString::null );
}
}
}
/*!
\return True, if the value is valid
\sa setValid(), setValue()
*/
bool QwtCounter::isValid() const
{
return d_data->isValid;
}
/*!
\brief Allow/disallow the user to manually edit the value
\param on True disable editing
\sa isReadOnly()
*/
void QwtCounter::setReadOnly( bool on )
{
d_data->valueEdit->setReadOnly( on );
}
/*!
\return True, when the line line edit is read only. (default is no)
\sa setReadOnly()
*/
bool QwtCounter::isReadOnly() const
{
return d_data->valueEdit->isReadOnly();
}
/*!
\brief Set a new value without adjusting to the step raster
The state of the counter is set to be valid.
\param value New value
\sa isValid(), value(), valueChanged()
\warning The value is clipped when it lies outside the range.
*/
void QwtCounter::setValue( double value )
{
const double vmin = qMin( d_data->minimum, d_data->maximum );
const double vmax = qMax( d_data->minimum, d_data->maximum );
value = qBound( vmin, value, vmax );
if ( !d_data->isValid || value != d_data->value )
{
d_data->isValid = true;
d_data->value = value;
showNumber( value );
updateButtons();
Q_EMIT valueChanged( value );
}
}
/*!
\return Current value of the counter
\sa setValue(), valueChanged()
*/
double QwtCounter::value() const
{
return d_data->value;
}
/*!
\brief Set the minimum and maximum values
The maximum is adjusted if necessary to ensure that the range remains valid.
The value might be modified to be inside of the range.
\param min Minimum value
\param max Maximum value
\sa minimum(), maximum()
*/
void QwtCounter::setRange( double min, double max )
{
max = qMax( min, max );
if ( d_data->maximum == max && d_data->minimum == min )
return;
d_data->minimum = min;
d_data->maximum = max;
setSingleStep( singleStep() );
const double value = qBound( min, d_data->value, max );
if ( value != d_data->value )
{
d_data->value = value;
if ( d_data->isValid )
{
showNumber( value );
Q_EMIT valueChanged( value );
}
}
updateButtons();
}
/*!
Set the minimum value of the range
\param value Minimum value
\sa setRange(), setMaximum(), minimum()
\note The maximum is adjusted if necessary to ensure that the range remains valid.
*/
void QwtCounter::setMinimum( double value )
{
setRange( value, maximum() );
}
/*!
\return The minimum of the range
\sa setRange(), setMinimum(), maximum()
*/
double QwtCounter::minimum() const
{
return d_data->minimum;
}
/*!
Set the maximum value of the range
\param value Maximum value
\sa setRange(), setMinimum(), maximum()
*/
void QwtCounter::setMaximum( double value )
{
setRange( minimum(), value );
}
/*!
\return The maximum of the range
\sa setRange(), setMaximum(), minimum()
*/
double QwtCounter::maximum() const
{
return d_data->maximum;
}
/*!
\brief Set the step size of the counter
A value <= 0.0 disables stepping
\param stepSize Single step size
\sa singleStep()
*/
void QwtCounter::setSingleStep( double stepSize )
{
d_data->singleStep = qMax( stepSize, 0.0 );
}
/*!
\return Single step size
\sa setSingleStep()
*/
double QwtCounter::singleStep() const
{
return d_data->singleStep;
}
/*!
\brief En/Disable wrapping
If wrapping is true stepping up from maximum() value will take
you to the minimum() value and vice versa.
\param on En/Disable wrapping
\sa wrapping()
*/
void QwtCounter::setWrapping( bool on )
{
d_data->wrapping = on;
}
/*!
\return True, when wrapping is set
\sa setWrapping()
*/
bool QwtCounter::wrapping() const
{
return d_data->wrapping;
}
/*!
Specify the number of buttons on each side of the label
\param numButtons Number of buttons
\sa numButtons()
*/
void QwtCounter::setNumButtons( int numButtons )
{
if ( numButtons < 0 || numButtons > QwtCounter::ButtonCnt )
return;
for ( int i = 0; i < QwtCounter::ButtonCnt; i++ )
{
if ( i < numButtons )
{
d_data->buttonDown[i]->show();
d_data->buttonUp[i]->show();
}
else
{
d_data->buttonDown[i]->hide();
d_data->buttonUp[i]->hide();
}
}
d_data->numButtons = numButtons;
}
/*!
\return The number of buttons on each side of the widget.
\sa setNumButtons()
*/
int QwtCounter::numButtons() const
{
return d_data->numButtons;
}
/*!
Specify the number of steps by which the value
is incremented or decremented when a specified button
is pushed.
\param button Button index
\param numSteps Number of steps
\sa incSteps()
*/
void QwtCounter::setIncSteps( QwtCounter::Button button, int numSteps )
{
if ( button >= 0 && button < QwtCounter::ButtonCnt )
d_data->increment[ button ] = numSteps;
}
/*!
\return The number of steps by which a specified button increments the value
or 0 if the button is invalid.
\param button Button index
\sa setIncSteps()
*/
int QwtCounter::incSteps( QwtCounter::Button button ) const
{
if ( button >= 0 && button < QwtCounter::ButtonCnt )
return d_data->increment[ button ];
return 0;
}
/*!
Set the number of increment steps for button 1
\param nSteps Number of steps
*/
void QwtCounter::setStepButton1( int nSteps )
{
setIncSteps( QwtCounter::Button1, nSteps );
}
//! returns the number of increment steps for button 1
int QwtCounter::stepButton1() const
{
return incSteps( QwtCounter::Button1 );
}
/*!
Set the number of increment steps for button 2
\param nSteps Number of steps
*/
void QwtCounter::setStepButton2( int nSteps )
{
setIncSteps( QwtCounter::Button2, nSteps );
}
//! returns the number of increment steps for button 2
int QwtCounter::stepButton2() const
{
return incSteps( QwtCounter::Button2 );
}
/*!
Set the number of increment steps for button 3
\param nSteps Number of steps
*/
void QwtCounter::setStepButton3( int nSteps )
{
setIncSteps( QwtCounter::Button3, nSteps );
}
//! returns the number of increment steps for button 3
int QwtCounter::stepButton3() const
{
return incSteps( QwtCounter::Button3 );
}
//! Set from lineedit
void QwtCounter::textChanged()
{
bool converted = false;
const double value = d_data->valueEdit->text().toDouble( &converted );
if ( converted )
setValue( value );
}
/*!
Handle QEvent::PolishRequest events
\param event Event
\return see QWidget::event()
*/
bool QwtCounter::event( QEvent *event )
{
if ( event->type() == QEvent::PolishRequest )
{
const int w = d_data->valueEdit->fontMetrics().width( "W" ) + 8;
for ( int i = 0; i < ButtonCnt; i++ )
{
d_data->buttonDown[i]->setMinimumWidth( w );
d_data->buttonUp[i]->setMinimumWidth( w );
}
}
return QWidget::event( event );
}
/*!
Handle key events
- Ctrl + Qt::Key_Home\n
Step to minimum()
- Ctrl + Qt::Key_End\n
Step to maximum()
- Qt::Key_Up\n
Increment by incSteps(QwtCounter::Button1)
- Qt::Key_Down\n
Decrement by incSteps(QwtCounter::Button1)
- Qt::Key_PageUp\n
Increment by incSteps(QwtCounter::Button2)
- Qt::Key_PageDown\n
Decrement by incSteps(QwtCounter::Button2)
- Shift + Qt::Key_PageUp\n
Increment by incSteps(QwtCounter::Button3)
- Shift + Qt::Key_PageDown\n
Decrement by incSteps(QwtCounter::Button3)
\param event Key event
*/
void QwtCounter::keyPressEvent ( QKeyEvent *event )
{
bool accepted = true;
switch ( event->key() )
{
case Qt::Key_Home:
{
if ( event->modifiers() & Qt::ControlModifier )
setValue( minimum() );
else
accepted = false;
break;
}
case Qt::Key_End:
{
if ( event->modifiers() & Qt::ControlModifier )
setValue( maximum() );
else
accepted = false;
break;
}
case Qt::Key_Up:
{
incrementValue( d_data->increment[0] );
break;
}
case Qt::Key_Down:
{
incrementValue( -d_data->increment[0] );
break;
}
case Qt::Key_PageUp:
case Qt::Key_PageDown:
{
int increment = d_data->increment[0];
if ( d_data->numButtons >= 2 )
increment = d_data->increment[1];
if ( d_data->numButtons >= 3 )
{
if ( event->modifiers() & Qt::ShiftModifier )
increment = d_data->increment[2];
}
if ( event->key() == Qt::Key_PageDown )
increment = -increment;
incrementValue( increment );
break;
}
default:
{
accepted = false;
}
}
if ( accepted )
{
event->accept();
return;
}
QWidget::keyPressEvent ( event );
}
/*!
Handle wheel events
\param event Wheel event
*/
void QwtCounter::wheelEvent( QWheelEvent *event )
{
event->accept();
if ( d_data->numButtons <= 0 )
return;
int increment = d_data->increment[0];
if ( d_data->numButtons >= 2 )
{
if ( event->modifiers() & Qt::ControlModifier )
increment = d_data->increment[1];
}
if ( d_data->numButtons >= 3 )
{
if ( event->modifiers() & Qt::ShiftModifier )
increment = d_data->increment[2];
}
for ( int i = 0; i < d_data->numButtons; i++ )
{
if ( d_data->buttonDown[i]->geometry().contains( event->pos() ) ||
d_data->buttonUp[i]->geometry().contains( event->pos() ) )
{
increment = d_data->increment[i];
}
}
const int wheel_delta = 120;
#if 1
int delta = event->delta();
if ( delta >= 2 * wheel_delta )
delta /= 2; // Never saw an abs(delta) < 240
#endif
incrementValue( delta / wheel_delta * increment );
}
void QwtCounter::incrementValue( int numSteps )
{
const double min = d_data->minimum;
const double max = d_data->maximum;
double stepSize = d_data->singleStep;
if ( !d_data->isValid || min >= max || stepSize <= 0.0 )
return;
#if 1
stepSize = qMax( stepSize, 1.0e-10 * ( max - min ) );
#endif
double value = d_data->value + numSteps * stepSize;
if ( d_data->wrapping )
{
const double range = max - min;
if ( value < min )
{
value += ::ceil( ( min - value ) / range ) * range;
}
else if ( value > max )
{
value -= ::ceil( ( value - max ) / range ) * range;
}
}
else
{
value = qBound( min, value, max );
}
value = min + qRound( ( value - min ) / stepSize ) * stepSize;
if ( stepSize > 1e-12 )
{
if ( qFuzzyCompare( value + 1.0, 1.0 ) )
{
// correct rounding error if value = 0
value = 0.0;
}
else if ( qFuzzyCompare( value, max ) )
{
// correct rounding error at the border
value = max;
}
}
if ( value != d_data->value )
{
d_data->value = value;
showNumber( d_data->value );
updateButtons();
Q_EMIT valueChanged( d_data->value );
}
}
/*!
\brief Update buttons according to the current value
When the QwtCounter under- or over-flows, the focus is set to the smallest
up- or down-button and counting is disabled.
Counting is re-enabled on a button release event (mouse or space bar).
*/
void QwtCounter::updateButtons()
{
if ( d_data->isValid )
{
// 1. save enabled state of the smallest down- and up-button
// 2. change enabled state on under- or over-flow
for ( int i = 0; i < QwtCounter::ButtonCnt; i++ )
{
d_data->buttonDown[i]->setEnabled( value() > minimum() );
d_data->buttonUp[i]->setEnabled( value() < maximum() );
}
}
else
{
for ( int i = 0; i < QwtCounter::ButtonCnt; i++ )
{
d_data->buttonDown[i]->setEnabled( false );
d_data->buttonUp[i]->setEnabled( false );
}
}
}
/*!
Display number string
\param number Number
*/
void QwtCounter::showNumber( double number )
{
QString text;
text.setNum( number );
const int cursorPos = d_data->valueEdit->cursorPosition();
d_data->valueEdit->setText( text );
d_data->valueEdit->setCursorPosition( cursorPos );
}
//! Button clicked
void QwtCounter::btnClicked()
{
for ( int i = 0; i < ButtonCnt; i++ )
{
if ( d_data->buttonUp[i] == sender() )
incrementValue( d_data->increment[i] );
if ( d_data->buttonDown[i] == sender() )
incrementValue( -d_data->increment[i] );
}
}
//! Button released
void QwtCounter::btnReleased()
{
Q_EMIT buttonReleased( value() );
}
//! A size hint
QSize QwtCounter::sizeHint() const
{
QString tmp;
int w = tmp.setNum( minimum() ).length();
int w1 = tmp.setNum( maximum() ).length();
if ( w1 > w )
w = w1;
w1 = tmp.setNum( minimum() + singleStep() ).length();
if ( w1 > w )
w = w1;
w1 = tmp.setNum( maximum() - singleStep() ).length();
if ( w1 > w )
w = w1;
tmp.fill( '9', w );
QFontMetrics fm( d_data->valueEdit->font() );
w = fm.width( tmp ) + 2;
if ( d_data->valueEdit->hasFrame() )
w += 2 * style()->pixelMetric( QStyle::PM_DefaultFrameWidth );
// Now we replace default sizeHint contribution of d_data->valueEdit by
// what we really need.
w += QWidget::sizeHint().width() - d_data->valueEdit->sizeHint().width();
const int h = qMin( QWidget::sizeHint().height(),
d_data->valueEdit->minimumSizeHint().height() );
return QSize( w, h );
}
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* 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
*****************************************************************************/
#ifndef QWT_COUNTER_H
#define QWT_COUNTER_H
#include "qwt_global.h"
#include <qwidget.h>
/*!
\brief The Counter Widget
A Counter consists of a label displaying a number and
one ore more (up to three) push buttons on each side
of the label which can be used to increment or decrement
the counter's value.
A counter has a range from a minimum value to a maximum value
and a step size. When the wrapping property is set
the counter is circular.
The number of steps by which a button increments or decrements the value
can be specified using setIncSteps(). The number of buttons can be
changed with setNumButtons().
Example:
\code
#include <qwt_counter.h>
QwtCounter *counter = new QwtCounter(parent);
counter->setRange(0.0, 100.0); // From 0.0 to 100
counter->setSingleStep( 1.0 ); // Step size 1.0
counter->setNumButtons(2); // Two buttons each side
counter->setIncSteps(QwtCounter::Button1, 1); // Button 1 increments 1 step
counter->setIncSteps(QwtCounter::Button2, 20); // Button 2 increments 20 steps
connect(counter, SIGNAL(valueChanged(double)), myClass, SLOT(newValue(double)));
\endcode
*/
class QWT_EXPORT QwtCounter : public QWidget
{
Q_OBJECT
Q_PROPERTY( double value READ value WRITE setValue )
Q_PROPERTY( double minimum READ minimum WRITE setMinimum )
Q_PROPERTY( double maximum READ maximum WRITE setMaximum )
Q_PROPERTY( double singleStep READ singleStep WRITE setSingleStep )
Q_PROPERTY( int numButtons READ numButtons WRITE setNumButtons )
Q_PROPERTY( int stepButton1 READ stepButton1 WRITE setStepButton1 )
Q_PROPERTY( int stepButton2 READ stepButton2 WRITE setStepButton2 )
Q_PROPERTY( int stepButton3 READ stepButton3 WRITE setStepButton3 )
Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly )
Q_PROPERTY( bool wrapping READ wrapping WRITE setWrapping )
public:
//! Button index
enum Button
{
//! Button intended for minor steps
Button1,
//! Button intended for medium steps
Button2,
//! Button intended for large steps
Button3,
//! Number of buttons
ButtonCnt
};
explicit QwtCounter( QWidget *parent = NULL );
virtual ~QwtCounter();
void setValid( bool );
bool isValid() const;
void setWrapping( bool );
bool wrapping() const;
bool isReadOnly() const;
void setReadOnly( bool );
void setNumButtons( int n );
int numButtons() const;
void setIncSteps( QwtCounter::Button btn, int nSteps );
int incSteps( QwtCounter::Button btn ) const;
virtual QSize sizeHint() const;
double singleStep() const;
void setSingleStep( double s );
void setRange( double min, double max );
double minimum() const;
void setMinimum( double min );
double maximum() const;
void setMaximum( double max );
void setStepButton1( int nSteps );
int stepButton1() const;
void setStepButton2( int nSteps );
int stepButton2() const;
void setStepButton3( int nSteps );
int stepButton3() const;
double value() const;
public Q_SLOTS:
void setValue( double );
Q_SIGNALS:
/*!
This signal is emitted when a button has been released
\param value The new value
*/
void buttonReleased ( double value );
/*!
This signal is emitted when the counter's value has changed
\param value The new value
*/
void valueChanged ( double value );
protected:
virtual bool event( QEvent * );
virtual void wheelEvent( QWheelEvent * );
virtual void keyPressEvent( QKeyEvent * );
private Q_SLOTS:
void btnReleased();
void btnClicked();
void textChanged();
private:
void incrementValue( int numSteps );
void initCounter();
void updateButtons();
void showNumber( double );
class PrivateData;
PrivateData *d_data;
};
#endif
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* 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_curve_fitter.h"
#include "qwt_math.h"
#include "qwt_spline.h"
#include <qstack.h>
#include <qvector.h>
#if QT_VERSION < 0x040601
#define qFabs(x) ::fabs(x)
#endif
//! Constructor
QwtCurveFitter::QwtCurveFitter()
{
}
//! Destructor
QwtCurveFitter::~QwtCurveFitter()
{
}
class QwtSplineCurveFitter::PrivateData
{
public:
PrivateData():
fitMode( QwtSplineCurveFitter::Auto ),
splineSize( 250 )
{
}
QwtSpline spline;
QwtSplineCurveFitter::FitMode fitMode;
int splineSize;
};
//! Constructor
QwtSplineCurveFitter::QwtSplineCurveFitter()
{
d_data = new PrivateData;
}
//! Destructor
QwtSplineCurveFitter::~QwtSplineCurveFitter()
{
delete d_data;
}
/*!
Select the algorithm used for building the spline
\param mode Mode representing a spline algorithm
\sa fitMode()
*/
void QwtSplineCurveFitter::setFitMode( FitMode mode )
{
d_data->fitMode = mode;
}
/*!
\return Mode representing a spline algorithm
\sa setFitMode()
*/
QwtSplineCurveFitter::FitMode QwtSplineCurveFitter::fitMode() const
{
return d_data->fitMode;
}
/*!
Assign a spline
\param spline Spline
\sa spline()
*/
void QwtSplineCurveFitter::setSpline( const QwtSpline &spline )
{
d_data->spline = spline;
d_data->spline.reset();
}
/*!
\return Spline
\sa setSpline()
*/
const QwtSpline &QwtSplineCurveFitter::spline() const
{
return d_data->spline;
}
/*!
\return Spline
\sa setSpline()
*/
QwtSpline &QwtSplineCurveFitter::spline()
{
return d_data->spline;
}
/*!
Assign a spline size ( has to be at least 10 points )
\param splineSize Spline size
\sa splineSize()
*/
void QwtSplineCurveFitter::setSplineSize( int splineSize )
{
d_data->splineSize = qMax( splineSize, 10 );
}
/*!
\return Spline size
\sa setSplineSize()
*/
int QwtSplineCurveFitter::splineSize() const
{
return d_data->splineSize;
}
/*!
Find a curve which has the best fit to a series of data points
\param points Series of data points
\return Curve points
*/
QPolygonF QwtSplineCurveFitter::fitCurve( const QPolygonF &points ) const
{
const int size = points.size();
if ( size <= 2 )
return points;
FitMode fitMode = d_data->fitMode;
if ( fitMode == Auto )
{
fitMode = Spline;
const QPointF *p = points.data();
for ( int i = 1; i < size; i++ )
{
if ( p[i].x() <= p[i-1].x() )
{
fitMode = ParametricSpline;
break;
}
};
}
if ( fitMode == ParametricSpline )
return fitParametric( points );
else
return fitSpline( points );
}
QPolygonF QwtSplineCurveFitter::fitSpline( const QPolygonF &points ) const
{
d_data->spline.setPoints( points );
if ( !d_data->spline.isValid() )
return points;
QPolygonF fittedPoints( d_data->splineSize );
const double x1 = points[0].x();
const double x2 = points[int( points.size() - 1 )].x();
const double dx = x2 - x1;
const double delta = dx / ( d_data->splineSize - 1 );
for ( int i = 0; i < d_data->splineSize; i++ )
{
QPointF &p = fittedPoints[i];
const double v = x1 + i * delta;
const double sv = d_data->spline.value( v );
p.setX( v );
p.setY( sv );
}
d_data->spline.reset();
return fittedPoints;
}
QPolygonF QwtSplineCurveFitter::fitParametric( const QPolygonF &points ) const
{
int i;
const int size = points.size();
QPolygonF fittedPoints( d_data->splineSize );
QPolygonF splinePointsX( size );
QPolygonF splinePointsY( size );
const QPointF *p = points.data();
QPointF *spX = splinePointsX.data();
QPointF *spY = splinePointsY.data();
double param = 0.0;
for ( i = 0; i < size; i++ )
{
const double x = p[i].x();
const double y = p[i].y();
if ( i > 0 )
{
const double delta = qSqrt( qwtSqr( x - spX[i-1].y() )
+ qwtSqr( y - spY[i-1].y() ) );
param += qMax( delta, 1.0 );
}
spX[i].setX( param );
spX[i].setY( x );
spY[i].setX( param );
spY[i].setY( y );
}
d_data->spline.setPoints( splinePointsX );
if ( !d_data->spline.isValid() )
return points;
const double deltaX =
splinePointsX[size - 1].x() / ( d_data->splineSize - 1 );
for ( i = 0; i < d_data->splineSize; i++ )
{
const double dtmp = i * deltaX;
fittedPoints[i].setX( d_data->spline.value( dtmp ) );
}
d_data->spline.setPoints( splinePointsY );
if ( !d_data->spline.isValid() )
return points;
const double deltaY =
splinePointsY[size - 1].x() / ( d_data->splineSize - 1 );
for ( i = 0; i < d_data->splineSize; i++ )
{
const double dtmp = i * deltaY;
fittedPoints[i].setY( d_data->spline.value( dtmp ) );
}
return fittedPoints;
}
class QwtWeedingCurveFitter::PrivateData
{
public:
PrivateData():
tolerance( 1.0 ),
chunkSize( 0 )
{
}
double tolerance;
uint chunkSize;
};
class QwtWeedingCurveFitter::Line
{
public:
Line( int i1 = 0, int i2 = 0 ):
from( i1 ),
to( i2 )
{
}
int from;
int to;
};
/*!
Constructor
\param tolerance Tolerance
\sa setTolerance(), tolerance()
*/
QwtWeedingCurveFitter::QwtWeedingCurveFitter( double tolerance )
{
d_data = new PrivateData;
setTolerance( tolerance );
}
//! Destructor
QwtWeedingCurveFitter::~QwtWeedingCurveFitter()
{
delete d_data;
}
/*!
Assign the tolerance
The tolerance is the maximum distance, that is acceptable
between the original curve and the smoothed curve.
Increasing the tolerance will reduce the number of the
resulting points.
\param tolerance Tolerance
\sa tolerance()
*/
void QwtWeedingCurveFitter::setTolerance( double tolerance )
{
d_data->tolerance = qMax( tolerance, 0.0 );
}
/*!
\return Tolerance
\sa setTolerance()
*/
double QwtWeedingCurveFitter::tolerance() const
{
return d_data->tolerance;
}
/*!
Limit the number of points passed to a run of the algorithm
The runtime of the Douglas Peucker algorithm increases non linear
with the number of points. For a chunk size > 0 the polygon
is split into pieces passed to the algorithm one by one.
\param numPoints Maximum for the number of points passed to the algorithm
\sa chunkSize()
*/
void QwtWeedingCurveFitter::setChunkSize( uint numPoints )
{
if ( numPoints > 0 )
numPoints = qMax( numPoints, 3U );
d_data->chunkSize = numPoints;
}
/*!
\return Maximum for the number of points passed to a run
of the algorithm - or 0, when unlimited
\sa setChunkSize()
*/
uint QwtWeedingCurveFitter::chunkSize() const
{
return d_data->chunkSize;
}
/*!
\param points Series of data points
\return Curve points
*/
QPolygonF QwtWeedingCurveFitter::fitCurve( const QPolygonF &points ) const
{
QPolygonF fittedPoints;
if ( d_data->chunkSize == 0 )
{
fittedPoints = simplify( points );
}
else
{
for ( int i = 0; i < points.size(); i += d_data->chunkSize )
{
const QPolygonF p = points.mid( i, d_data->chunkSize );
fittedPoints += simplify( p );
}
}
return fittedPoints;
}
QPolygonF QwtWeedingCurveFitter::simplify( const QPolygonF &points ) const
{
const double toleranceSqr = d_data->tolerance * d_data->tolerance;
QStack<Line> stack;
stack.reserve( 500 );
const QPointF *p = points.data();
const int nPoints = points.size();
QVector<bool> usePoint( nPoints, false );
stack.push( Line( 0, nPoints - 1 ) );
while ( !stack.isEmpty() )
{
const Line r = stack.pop();
// initialize line segment
const double vecX = p[r.to].x() - p[r.from].x();
const double vecY = p[r.to].y() - p[r.from].y();
const double vecLength = qSqrt( vecX * vecX + vecY * vecY );
const double unitVecX = ( vecLength != 0.0 ) ? vecX / vecLength : 0.0;
const double unitVecY = ( vecLength != 0.0 ) ? vecY / vecLength : 0.0;
double maxDistSqr = 0.0;
int nVertexIndexMaxDistance = r.from + 1;
for ( int i = r.from + 1; i < r.to; i++ )
{
//compare to anchor
const double fromVecX = p[i].x() - p[r.from].x();
const double fromVecY = p[i].y() - p[r.from].y();
double distToSegmentSqr;
if ( fromVecX * unitVecX + fromVecY * unitVecY < 0.0 )
{
distToSegmentSqr = fromVecX * fromVecX + fromVecY * fromVecY;
}
else
{
const double toVecX = p[i].x() - p[r.to].x();
const double toVecY = p[i].y() - p[r.to].y();
const double toVecLength = toVecX * toVecX + toVecY * toVecY;
const double s = toVecX * ( -unitVecX ) + toVecY * ( -unitVecY );
if ( s < 0.0 )
{
distToSegmentSqr = toVecLength;
}
else
{
distToSegmentSqr = qFabs( toVecLength - s * s );
}
}
if ( maxDistSqr < distToSegmentSqr )
{
maxDistSqr = distToSegmentSqr;
nVertexIndexMaxDistance = i;
}
}
if ( maxDistSqr <= toleranceSqr )
{
usePoint[r.from] = true;
usePoint[r.to] = true;
}
else
{
stack.push( Line( r.from, nVertexIndexMaxDistance ) );
stack.push( Line( nVertexIndexMaxDistance, r.to ) );
}
}
QPolygonF stripped;
for ( int i = 0; i < nPoints; i++ )
{
if ( usePoint[i] )
stripped += p[i];
}
return stripped;
}
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* 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
*****************************************************************************/
#ifndef QWT_CURVE_FITTER_H
#define QWT_CURVE_FITTER_H
#include "qwt_global.h"
#include <qpolygon.h>
#include <qrect.h>
class QwtSpline;
/*!
\brief Abstract base class for a curve fitter
*/
class QWT_EXPORT QwtCurveFitter
{
public:
virtual ~QwtCurveFitter();
/*!
Find a curve which has the best fit to a series of data points
\param polygon Series of data points
\return Curve points
*/
virtual QPolygonF fitCurve( const QPolygonF &polygon ) const = 0;
protected:
QwtCurveFitter();
private:
QwtCurveFitter( const QwtCurveFitter & );
QwtCurveFitter &operator=( const QwtCurveFitter & );
};
/*!
\brief A curve fitter using cubic splines
*/
class QWT_EXPORT QwtSplineCurveFitter: public QwtCurveFitter
{
public:
/*!
Spline type
The default setting is Auto
\sa setFitMode(), FitMode()
*/
enum FitMode
{
/*!
Use the default spline algorithm for polygons with
increasing x values ( p[i-1] < p[i] ), otherwise use
a parametric spline algorithm.
*/
Auto,
//! Use a default spline algorithm
Spline,
//! Use a parametric spline algorithm
ParametricSpline
};
QwtSplineCurveFitter();
virtual ~QwtSplineCurveFitter();
void setFitMode( FitMode );
FitMode fitMode() const;
void setSpline( const QwtSpline& );
const QwtSpline &spline() const;
QwtSpline &spline();
void setSplineSize( int size );
int splineSize() const;
virtual QPolygonF fitCurve( const QPolygonF & ) const;
private:
QPolygonF fitSpline( const QPolygonF & ) const;
QPolygonF fitParametric( const QPolygonF & ) const;
class PrivateData;
PrivateData *d_data;
};
/*!
\brief A curve fitter implementing Douglas and Peucker algorithm
The purpose of the Douglas and Peucker algorithm is that given a 'curve'
composed of line segments to find a curve not too dissimilar but that
has fewer points. The algorithm defines 'too dissimilar' based on the
maximum distance (tolerance) between the original curve and the
smoothed curve.
The runtime of the algorithm increases non linear ( worst case O( n*n ) )
and might be very slow for huge polygons. To avoid performance issues
it might be useful to split the polygon ( setChunkSize() ) and to run the algorithm
for these smaller parts. The disadvantage of having no interpolation
at the borders is for most use cases irrelevant.
The smoothed curve consists of a subset of the points that defined the
original curve.
In opposite to QwtSplineCurveFitter the Douglas and Peucker algorithm reduces
the number of points. By adjusting the tolerance parameter according to the
axis scales QwtSplineCurveFitter can be used to implement different
level of details to speed up painting of curves of many points.
*/
class QWT_EXPORT QwtWeedingCurveFitter: public QwtCurveFitter
{
public:
QwtWeedingCurveFitter( double tolerance = 1.0 );
virtual ~QwtWeedingCurveFitter();
void setTolerance( double );
double tolerance() const;
void setChunkSize( uint );
uint chunkSize() const;
virtual QPolygonF fitCurve( const QPolygonF & ) const;
private:
virtual QPolygonF simplify( const QPolygonF & ) const;
class Line;
class PrivateData;
PrivateData *d_data;
};
#endif
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* 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_math.h"
#include "qwt_data.h"
//! Constructor
QwtData::QwtData()
{
}
//! Destructor
QwtData::~QwtData()
{
}
/*!
Returns the bounding rectangle of the data. If there is
no bounding rect, like for empty data the rectangle is invalid:
QwtDoubleRect::isValid() == false
\warning This is an slow implementation iterating over all points.
It is intended to be overloaded by derived classes. In case of
auto scaling boundingRect() is called for every replot, so it
might be worth to implement a cache, or use x(0), x(size() - 1)
for ordered data ...
*/
QwtDoubleRect QwtData::boundingRect() const
{
const size_t sz = size();
if ( sz <= 0 )
return QwtDoubleRect(1.0, 1.0, -2.0, -2.0); // invalid
double minX, maxX, minY, maxY;
minX = maxX = x(0);
minY = maxY = y(0);
for ( size_t i = 1; i < sz; i++ ) {
const double xv = x(i);
if ( xv < minX )
minX = xv;
if ( xv > maxX )
maxX = xv;
const double yv = y(i);
if ( yv < minY )
minY = yv;
if ( yv > maxY )
maxY = yv;
}
return QwtDoubleRect(minX, minY, maxX - minX, maxY - minY);
}
/*!
Constructor
\param polygon Polygon data
\sa QwtPlotCurve::setData()
*/
#if QT_VERSION >= 0x040000
QwtPolygonFData::QwtPolygonFData(const QPolygonF &polygon):
#else
QwtPolygonFData::QwtPolygonFData(const QwtArray<QwtDoublePoint> &polygon):
#endif
d_data(polygon)
{
}
//! Assignment
QwtPolygonFData& QwtPolygonFData::operator=(
const QwtPolygonFData &data)
{
if (this != &data) {
d_data = data.d_data;
}
return *this;
}
//! \return Size of the data set
size_t QwtPolygonFData::size() const
{
return d_data.size();
}
/*!
Return the x value of data point i
\param i Index
\return x X value of data point i
*/
double QwtPolygonFData::x(size_t i) const
{
return d_data[int(i)].x();
}
/*!
Return the y value of data point i
\param i Index
\return y Y value of data point i
*/
double QwtPolygonFData::y(size_t i) const
{
return d_data[int(i)].y();
}
#if QT_VERSION >= 0x040000
const QPolygonF &QwtPolygonFData::data() const
#else
const QwtArray<QwtDoublePoint> &QwtPolygonFData::data() const
#endif
{
return d_data;
}
/*!
\return Pointer to a copy (virtual copy constructor)
*/
QwtData *QwtPolygonFData::copy() const
{
return new QwtPolygonFData(d_data);
}
/*!
Constructor
\param x Array of x values
\param y Array of y values
\sa QwtPlotCurve::setData
*/
QwtArrayData::QwtArrayData(
const QwtArray<double> &x, const QwtArray<double> &y):
d_x(x),
d_y(y)
{
}
/*!
Constructor
\param x Array of x values
\param y Array of y values
\param size Size of the x and y arrays
\sa QwtPlotCurve::setData
*/
QwtArrayData::QwtArrayData(const double *x, const double *y, size_t size)
{
#if QT_VERSION >= 0x040000
d_x.resize(size);
qMemCopy(d_x.data(), x, size * sizeof(double));
d_y.resize(size);
qMemCopy(d_y.data(), y, size * sizeof(double));
#else
d_x.detach();
d_x.duplicate(x, size);
d_y.detach();
d_y.duplicate(y, size);
#endif
}
//! Assignment
QwtArrayData& QwtArrayData::operator=(const QwtArrayData &data)
{
if (this != &data) {
d_x = data.d_x;
d_y = data.d_y;
}
return *this;
}
//! \return Size of the data set
size_t QwtArrayData::size() const
{
return qwtMin(d_x.size(), d_y.size());
}
/*!
Return the x value of data point i
\param i Index
\return x X value of data point i
*/
double QwtArrayData::x(size_t i) const
{
return d_x[int(i)];
}
/*!
Return the y value of data point i
\param i Index
\return y Y value of data point i
*/
double QwtArrayData::y(size_t i) const
{
return d_y[int(i)];
}
//! \return Array of the x-values
const QwtArray<double> &QwtArrayData::xData() const
{
return d_x;
}
//! \return Array of the y-values
const QwtArray<double> &QwtArrayData::yData() const
{
return d_y;
}
/*!
\return Pointer to a copy (virtual copy constructor)
*/
QwtData *QwtArrayData::copy() const
{
return new QwtArrayData(d_x, d_y);
}
/*!
Returns the bounding rectangle of the data. If there is
no bounding rect, like for empty data the rectangle is invalid:
QwtDoubleRect::isValid() == false
*/
QwtDoubleRect QwtArrayData::boundingRect() const
{
const size_t sz = size();
if ( sz <= 0 )
return QwtDoubleRect(1.0, 1.0, -2.0, -2.0); // invalid
double minX, maxX, minY, maxY;
QwtArray<double>::ConstIterator xIt = d_x.begin();
QwtArray<double>::ConstIterator yIt = d_y.begin();
QwtArray<double>::ConstIterator end = d_x.begin() + sz;
minX = maxX = *xIt++;
minY = maxY = *yIt++;
while ( xIt < end ) {
const double xv = *xIt++;
if ( xv < minX )
minX = xv;
if ( xv > maxX )
maxX = xv;
const double yv = *yIt++;
if ( yv < minY )
minY = yv;
if ( yv > maxY )
maxY = yv;
}
return QwtDoubleRect(minX, minY, maxX - minX, maxY - minY);
}
/*!
Constructor
\param x Array of x values
\param y Array of y values
\param size Size of the x and y arrays
\warning The programmer must assure that the memory blocks referenced
by the pointers remain valid during the lifetime of the
QwtPlotCPointer object.
\sa QwtPlotCurve::setData(), QwtPlotCurve::setRawData()
*/
QwtCPointerData::QwtCPointerData(
const double *x, const double *y, size_t size):
d_x(x),
d_y(y),
d_size(size)
{
}
//! Assignment
QwtCPointerData& QwtCPointerData::operator=(const QwtCPointerData &data)
{
if (this != &data) {
d_x = data.d_x;
d_y = data.d_y;
d_size = data.d_size;
}
return *this;
}
//! \return Size of the data set
size_t QwtCPointerData::size() const
{
return d_size;
}
/*!
Return the x value of data point i
\param i Index
\return x X value of data point i
*/
double QwtCPointerData::x(size_t i) const
{
return d_x[int(i)];
}
/*!
Return the y value of data point i
\param i Index
\return y Y value of data point i
*/
double QwtCPointerData::y(size_t i) const
{
return d_y[int(i)];
}
//! \return Array of the x-values
const double *QwtCPointerData::xData() const
{
return d_x;
}
//! \return Array of the y-values
const double *QwtCPointerData::yData() const
{
return d_y;
}
/*!
\return Pointer to a copy (virtual copy constructor)
*/
QwtData *QwtCPointerData::copy() const
{
return new QwtCPointerData(d_x, d_y, d_size);
}
/*!
Returns the bounding rectangle of the data. If there is
no bounding rect, like for empty data the rectangle is invalid:
QwtDoubleRect::isValid() == false
*/
QwtDoubleRect QwtCPointerData::boundingRect() const
{
const size_t sz = size();
if ( sz <= 0 )
return QwtDoubleRect(1.0, 1.0, -2.0, -2.0); // invalid
double minX, maxX, minY, maxY;
const double *xIt = d_x;
const double *yIt = d_y;
const double *end = d_x + sz;
minX = maxX = *xIt++;
minY = maxY = *yIt++;
while ( xIt < end ) {
const double xv = *xIt++;
if ( xv < minX )
minX = xv;
if ( xv > maxX )
maxX = xv;
const double yv = *yIt++;
if ( yv < minY )
minY = yv;
if ( yv > maxY )
maxY = yv;
}
return QwtDoubleRect(minX, minY, maxX - minX, maxY - minY);
}
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* 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
*****************************************************************************/
// vim: expandtab
#ifndef QWT_DATA_H
#define QWT_DATA_H 1
#include "qwt_global.h"
#include "qwt_array.h"
#include "qwt_double_rect.h"
#if QT_VERSION >= 0x040000
#include <QPolygonF>
#endif
// MOC_SKIP_BEGIN
#if defined(QWT_TEMPLATEDLL)
template class QWT_EXPORT QwtArray<double>;
#if QT_VERSION < 0x040000
#ifndef QWTARRAY_TEMPLATE_QWTDOUBLEPOINT // by mjo3
#define QWTARRAY_TEMPLATE_QWTDOUBLEPOINT
template class QWT_EXPORT QwtArray<QwtDoublePoint>;
#endif //end of QWTARRAY_TEMPLATE_QWTDOUBLEPOINT
#endif
#endif
// MOC_SKIP_END
/*!
\brief QwtData defines an interface to any type of curve data.
Classes, derived from QwtData may:
- store the data in almost any type of container
- calculate the data on the fly instead of storing it
*/
class QWT_EXPORT QwtData
{
public:
QwtData();
virtual ~QwtData();
//! \return Pointer to a copy (virtual copy constructor)
virtual QwtData *copy() const = 0;
//! \return Size of the data set
virtual size_t size() const = 0;
/*!
Return the x value of data point i
\param i Index
\return x X value of data point i
*/
virtual double x(size_t i) const = 0;
/*!
Return the y value of data point i
\param i Index
\return y Y value of data point i
*/
virtual double y(size_t i) const = 0;
virtual QwtDoubleRect boundingRect() const;
protected:
/*!
Assignment operator (virtualized)
*/
QwtData &operator=(const QwtData &);
};
/*!
\brief Data class containing a single QwtArray<QwtDoublePoint> object.
*/
class QWT_EXPORT QwtPolygonFData: public QwtData
{
public:
#if QT_VERSION < 0x040000
QwtPolygonFData(const QwtArray<QwtDoublePoint> &);
#else
QwtPolygonFData(const QPolygonF &);
#endif
QwtPolygonFData &operator=(const QwtPolygonFData &);
virtual QwtData *copy() const;
virtual size_t size() const;
virtual double x(size_t i) const;
virtual double y(size_t i) const;
#if QT_VERSION < 0x040000
const QwtArray<QwtDoublePoint> &data() const;
#else
const QPolygonF &data() const;
#endif
private:
#if QT_VERSION < 0x040000
QwtArray<QwtDoublePoint> d_data;
#else
QPolygonF d_data;
#endif
};
/*!
\brief Data class containing two QwtArray<double> objects.
*/
class QWT_EXPORT QwtArrayData: public QwtData
{
public:
QwtArrayData(const QwtArray<double> &x, const QwtArray<double> &y);
QwtArrayData(const double *x, const double *y, size_t size);
QwtArrayData &operator=(const QwtArrayData &);
virtual QwtData *copy() const;
virtual size_t size() const;
virtual double x(size_t i) const;
virtual double y(size_t i) const;
const QwtArray<double> &xData() const;
const QwtArray<double> &yData() const;
virtual QwtDoubleRect boundingRect() const;
private:
QwtArray<double> d_x;
QwtArray<double> d_y;
};
/*!
\brief Data class containing two pointers to memory blocks of doubles.
*/
class QWT_EXPORT QwtCPointerData: public QwtData
{
public:
QwtCPointerData(const double *x, const double *y, size_t size);
QwtCPointerData &operator=(const QwtCPointerData &);
virtual QwtData *copy() const;
virtual size_t size() const;
virtual double x(size_t i) const;
virtual double y(size_t i) const;
const double *xData() const;
const double *yData() const;
virtual QwtDoubleRect boundingRect() const;
private:
const double *d_x;
const double *d_y;
size_t d_size;
};
#endif // !QWT_DATA
#include "qwt_date.h"
#include <qdebug.h>
#include <qlocale.h>
#include <math.h>
#include <limits>
#include <limits.h>
#if QT_VERSION >= 0x050000
typedef qint64 QwtJulianDay;
static const QwtJulianDay minJulianDayD = Q_INT64_C( -784350574879 );
static const QwtJulianDay maxJulianDayD = Q_INT64_C( 784354017364 );
#else
// QDate stores the Julian day as unsigned int, but
// but it is QDate::fromJulianDay( int ). That's why
// we have the range [ 1, INT_MAX ]
typedef int QwtJulianDay;
static const QwtJulianDay minJulianDayD = 1;
static const QwtJulianDay maxJulianDayD = std::numeric_limits<int>::max();
#endif
static inline Qt::DayOfWeek qwtFirstDayOfWeek()
{
#if QT_VERSION >= 0x040800
return QLocale().firstDayOfWeek();
#else
switch( QLocale().country() )
{
case QLocale::Maldives:
return Qt::Friday;
case QLocale::Afghanistan:
case QLocale::Algeria:
case QLocale::Bahrain:
case QLocale::Djibouti:
case QLocale::Egypt:
case QLocale::Eritrea:
case QLocale::Ethiopia:
case QLocale::Iran:
case QLocale::Iraq:
case QLocale::Jordan:
case QLocale::Kenya:
case QLocale::Kuwait:
case QLocale::LibyanArabJamahiriya:
case QLocale::Morocco:
case QLocale::Oman:
case QLocale::Qatar:
case QLocale::SaudiArabia:
case QLocale::Somalia:
case QLocale::Sudan:
case QLocale::Tunisia:
case QLocale::Yemen:
return Qt::Saturday;
case QLocale::AmericanSamoa:
case QLocale::Argentina:
case QLocale::Azerbaijan:
case QLocale::Botswana:
case QLocale::Canada:
case QLocale::China:
case QLocale::FaroeIslands:
case QLocale::Georgia:
case QLocale::Greenland:
case QLocale::Guam:
case QLocale::HongKong:
case QLocale::Iceland:
case QLocale::India:
case QLocale::Ireland:
case QLocale::Israel:
case QLocale::Jamaica:
case QLocale::Japan:
case QLocale::Kyrgyzstan:
case QLocale::Lao:
case QLocale::Malta:
case QLocale::MarshallIslands:
case QLocale::Macau:
case QLocale::Mongolia:
case QLocale::NewZealand:
case QLocale::NorthernMarianaIslands:
case QLocale::Pakistan:
case QLocale::Philippines:
case QLocale::RepublicOfKorea:
case QLocale::Singapore:
case QLocale::SyrianArabRepublic:
case QLocale::Taiwan:
case QLocale::Thailand:
case QLocale::TrinidadAndTobago:
case QLocale::UnitedStates:
case QLocale::UnitedStatesMinorOutlyingIslands:
case QLocale::USVirginIslands:
case QLocale::Uzbekistan:
case QLocale::Zimbabwe:
return Qt::Sunday;
default:
return Qt::Monday;
}
#endif
}
static inline void qwtFloorTime(
QwtDate::IntervalType intervalType, QDateTime &dt )
{
// when dt is inside the special hour where DST is ending
// an hour is no unique. Therefore we have to
// use UTC time.
const Qt::TimeSpec timeSpec = dt.timeSpec();
if ( timeSpec == Qt::LocalTime )
dt = dt.toTimeSpec( Qt::UTC );
const QTime t = dt.time();
switch( intervalType )
{
case QwtDate::Second:
{
dt.setTime( QTime( t.hour(), t.minute(), t.second() ) );
break;
}
case QwtDate::Minute:
{
dt.setTime( QTime( t.hour(), t.minute(), 0 ) );
break;
}
case QwtDate::Hour:
{
dt.setTime( QTime( t.hour(), 0, 0 ) );
break;
}
default:
break;
}
if ( timeSpec == Qt::LocalTime )
dt = dt.toTimeSpec( Qt::LocalTime );
}
static inline QDateTime qwtToTimeSpec(
const QDateTime &dt, Qt::TimeSpec spec )
{
if ( dt.timeSpec() == spec )
return dt;
const qint64 jd = dt.date().toJulianDay();
if ( jd < 0 || jd >= INT_MAX )
{
// the conversion between local time and UTC
// is internally limited. To avoid
// overflows we simply ignore the difference
// for those dates
QDateTime dt2 = dt;
dt2.setTimeSpec( spec );
return dt2;
}
return dt.toTimeSpec( spec );
}
static inline double qwtToJulianDay( int year, int month, int day )
{
// code from QDate but using doubles to avoid overflows
// for large values
const int m1 = ( month - 14 ) / 12;
const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12;
const double y1 = ::floor( ( 4900.0 + year + m1 ) / 100 );
return ::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2
- ::floor( ( 3 * y1 ) / 4 ) + day - 32075;
}
static inline qint64 qwtFloorDiv64( qint64 a, int b )
{
if ( a < 0 )
a -= b - 1;
return a / b;
}
static inline qint64 qwtFloorDiv( int a, int b )
{
if ( a < 0 )
a -= b - 1;
return a / b;
}
static inline QDate qwtToDate( int year, int month = 1, int day = 1 )
{
#if QT_VERSION >= 0x050000
return QDate( year, month, day );
#else
if ( year > 100000 )
{
// code from QDate but using doubles to avoid overflows
// for large values
const int m1 = ( month - 14 ) / 12;
const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12;
const double y1 = ::floor( ( 4900.0 + year + m1 ) / 100 );
const double jd = ::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2
- ::floor( ( 3 * y1 ) / 4 ) + day - 32075;
if ( jd > maxJulianDayD )
{
qWarning() << "qwtToDate: overflow";
return QDate();
}
return QDate::fromJulianDay( static_cast<QwtJulianDay>( jd ) );
}
else
{
return QDate( year, month, day );
}
#endif
}
/*!
Translate from double to QDateTime
\param value Number of milliseconds since the epoch,
1970-01-01T00:00:00 UTC
\param timeSpec Time specification
\return Datetime value
\sa toDouble(), QDateTime::setMSecsSinceEpoch()
\note The return datetime for Qt::OffsetFromUTC will be Qt::UTC
*/
QDateTime QwtDate::toDateTime( double value, Qt::TimeSpec timeSpec )
{
const int msecsPerDay = 86400000;
const double days = static_cast<qint64>( ::floor( value / msecsPerDay ) );
const double jd = QwtDate::JulianDayForEpoch + days;
if ( ( jd > maxJulianDayD ) || ( jd < minJulianDayD ) )
{
qWarning() << "QwtDate::toDateTime: overflow";
return QDateTime();
}
const QDate d = QDate::fromJulianDay( static_cast<QwtJulianDay>( jd ) );
const int msecs = static_cast<int>( value - days * msecsPerDay );
static const QTime timeNull( 0, 0, 0, 0 );
QDateTime dt( d, timeNull.addMSecs( msecs ), Qt::UTC );
if ( timeSpec == Qt::LocalTime )
dt = qwtToTimeSpec( dt, timeSpec );
return dt;
}
/*!
Translate from QDateTime to double
\param dateTime Datetime value
\return Number of milliseconds since 1970-01-01T00:00:00 UTC has passed.
\sa toDateTime(), QDateTime::toMSecsSinceEpoch()
\warning For values very far below or above 1970-01-01 UTC rounding errors
will happen due to the limited significance of a double.
*/
double QwtDate::toDouble( const QDateTime &dateTime )
{
const int msecsPerDay = 86400000;
const QDateTime dt = qwtToTimeSpec( dateTime, Qt::UTC );
const double days = dt.date().toJulianDay() - QwtDate::JulianDayForEpoch;
const QTime time = dt.time();
const double secs = 3600.0 * time.hour() +
60.0 * time.minute() + time.second();
return days * msecsPerDay + time.msec() + 1000.0 * secs;
}
/*!
Ceil a datetime according the interval type
\param dateTime Datetime value
\param intervalType Interval type, how to ceil.
F.e. when intervalType = QwtDate::Months, the result
will be ceiled to the next beginning of a month
\return Ceiled datetime
\sa floor()
*/
QDateTime QwtDate::ceil( const QDateTime &dateTime, IntervalType intervalType )
{
if ( dateTime.date() >= QwtDate::maxDate() )
return dateTime;
QDateTime dt = dateTime;
switch ( intervalType )
{
case QwtDate::Millisecond:
{
break;
}
case QwtDate::Second:
{
qwtFloorTime( QwtDate::Second, dt );
if ( dt < dateTime )
dt.addSecs( 1 );
break;
}
case QwtDate::Minute:
{
qwtFloorTime( QwtDate::Minute, dt );
if ( dt < dateTime )
dt.addSecs( 60 );
break;
}
case QwtDate::Hour:
{
qwtFloorTime( QwtDate::Hour, dt );
if ( dt < dateTime )
dt.addSecs( 3600 );
break;
}
case QwtDate::Day:
{
dt.setTime( QTime( 0, 0 ) );
if ( dt < dateTime )
dt = dt.addDays( 1 );
break;
}
case QwtDate::Week:
{
dt.setTime( QTime( 0, 0 ) );
if ( dt < dateTime )
dt = dt.addDays( 1 );
int days = qwtFirstDayOfWeek() - dt.date().dayOfWeek();
if ( days < 0 )
days += 7;
dt = dt.addDays( days );
break;
}
case QwtDate::Month:
{
dt.setTime( QTime( 0, 0 ) );
dt.setDate( qwtToDate( dateTime.date().year(),
dateTime.date().month() ) );
if ( dt < dateTime )
dt.addMonths( 1 );
break;
}
case QwtDate::Year:
{
dt.setTime( QTime( 0, 0 ) );
const QDate d = dateTime.date();
int year = d.year();
if ( d.month() > 1 || d.day() > 1 || !dateTime.time().isNull() )
year++;
if ( year == 0 )
year++; // there is no year 0
dt.setDate( qwtToDate( year ) );
break;
}
}
return dt;
}
/*!
Floor a datetime according the interval type
\param dateTime Datetime value
\param intervalType Interval type, how to ceil.
F.e. when intervalType = QwtDate::Months,
the result will be ceiled to the next
beginning of a month
\return Floored datetime
\sa floor()
*/
QDateTime QwtDate::floor( const QDateTime &dateTime,
IntervalType intervalType )
{
if ( dateTime.date() <= QwtDate::minDate() )
return dateTime;
QDateTime dt = dateTime;
switch ( intervalType )
{
case QwtDate::Millisecond:
{
break;
}
case QwtDate::Second:
case QwtDate::Minute:
case QwtDate::Hour:
{
qwtFloorTime( intervalType, dt );
break;
}
case QwtDate::Day:
{
dt.setTime( QTime( 0, 0 ) );
break;
}
case QwtDate::Week:
{
dt.setTime( QTime( 0, 0 ) );
int days = dt.date().dayOfWeek() - qwtFirstDayOfWeek();
if ( days < 0 )
days += 7;
dt = dt.addDays( -days );
break;
}
case QwtDate::Month:
{
dt.setTime( QTime( 0, 0 ) );
const QDate date = qwtToDate( dt.date().year(),
dt.date().month() );
dt.setDate( date );
break;
}
case QwtDate::Year:
{
dt.setTime( QTime( 0, 0 ) );
const QDate date = qwtToDate( dt.date().year() );
dt.setDate( date );
break;
}
}
return dt;
}
/*!
Minimum for the supported date range
The range of valid dates depends on how QDate stores the
Julian day internally.
- For Qt4 it is "Tue Jan 2 -4713"
- For Qt5 it is "Thu Jan 1 -2147483648"
\return minimum of the date range
\sa maxDate()
*/
QDate QwtDate::minDate()
{
static QDate date;
if ( !date.isValid() )
date = QDate::fromJulianDay( minJulianDayD );
return date;
}
/*!
Maximum for the supported date range
The range of valid dates depends on how QDate stores the
Julian day internally.
- For Qt4 it is "Tue Jun 3 5874898"
- For Qt5 it is "Tue Dec 31 2147483647"
\return maximum of the date range
\sa minDate()
\note The maximum differs between Qt4 and Qt5
*/
QDate QwtDate::maxDate()
{
static QDate date;
if ( !date.isValid() )
date = QDate::fromJulianDay( maxJulianDayD );
return date;
}
/*!
\brief Date of the first day of the first week for a year
The first day of a week depends on the current locale
( QLocale::firstDayOfWeek() ).
\param year Year
\param type Option how to identify the first week
\return First day of week 0
\sa QLocale::firstDayOfWeek(), weekNumber()
*/
QDate QwtDate::dateOfWeek0( int year, Week0Type type )
{
const Qt::DayOfWeek firstDayOfWeek = qwtFirstDayOfWeek();
QDate dt0( year, 1, 1 );
// floor to the first day of the week
int days = dt0.dayOfWeek() - firstDayOfWeek;
if ( days < 0 )
days += 7;
dt0 = dt0.addDays( -days );
if ( type == QwtDate::FirstThursday )
{
// according to ISO 8601 the first week is defined
// by the first thursday.
int d = Qt::Thursday - firstDayOfWeek;
if ( d < 0 )
d += 7;
if ( dt0.addDays( d ).year() < year )
dt0 = dt0.addDays( 7 );
}
return dt0;
}
/*!
Find the week number of a date
- QwtDate::FirstThursday\n
Corresponding to ISO 8601 ( see QDate::weekNumber() ).
- QwtDate::FirstDay\n
Number of weeks that have begun since dateOfWeek0().
\param date Date
\param type Option how to identify the first week
\return Week number, starting with 1
*/
int QwtDate::weekNumber( const QDate &date, Week0Type type )
{
int weekNo;
if ( type == QwtDate::FirstDay )
{
const QDate day0 = dateOfWeek0( date.year(), type );
weekNo = day0.daysTo( date ) / 7 + 1;
}
else
{
weekNo = date.weekNumber();
}
return weekNo;
}
/*!
Offset in seconds from Coordinated Universal Time
The offset depends on the time specification of dateTime:
- Qt::UTC
0, dateTime has no offset
- Qt::OffsetFromUTC
returns dateTime.utcOffset()
- Qt::LocalTime:
number of seconds from the UTC
For Qt::LocalTime the offset depends on the timezone and
daylight savings.
\param dateTime Datetime value
\return Offset in seconds
*/
int QwtDate::utcOffset( const QDateTime &dateTime )
{
int seconds = 0;
switch( dateTime.timeSpec() )
{
case Qt::UTC:
{
break;
}
case Qt::OffsetFromUTC:
{
seconds = dateTime.utcOffset();
}
default:
{
const QDateTime dt1( dateTime.date(), dateTime.time(), Qt::UTC );
seconds = dateTime.secsTo( dt1 );
}
}
return seconds;
}
/*!
Translate a datetime into a string
Beside the format expressions documented in QDateTime::toString()
the following expressions are supported:
- w\n
week number: ( 1 - 53 )
- ww\n
week number with a leading zero ( 01 - 53 )
\param dateTime Datetime value
\param format Format string
\param week0Type Specification of week 0
\return Datetime string
\sa QDateTime::toString(), weekNumber(), QwtDateScaleDraw
*/
QString QwtDate::toString( const QDateTime &dateTime,
const QString & format, Week0Type week0Type )
{
QString weekNo;
weekNo.setNum( QwtDate::weekNumber( dateTime.date(), week0Type ) );
QString weekNoWW;
if ( weekNo.length() == 1 )
weekNoWW += "0";
weekNoWW += weekNo;
QString fmt = format;
fmt.replace( "ww", weekNoWW );
fmt.replace( "w", weekNo );
return dateTime.toString( fmt );
}
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* 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
*****************************************************************************/
#ifndef _QWT_DATE_H_
#define _QWT_DATE_H_
#include "qwt_global.h"
#include <qdatetime.h>
/*!
\brief A collection of methods around date/time values
Qt offers convenient classes for dealing with date/time values,
but Qwt uses coordinate systems that are based on doubles.
QwtDate offers methods to translate from QDateTime to double and v.v.
A double is interpreted as the number of milliseconds since
1970-01-01T00:00:00 Universal Coordinated Time - also known
as "The Epoch".
While the range of the Julian day in Qt4 is limited to [0, MAX_INT],
Qt5 stores it as qint64 offering a huge range of valid dates.
As the significance of a double is below this ( assuming a
fraction of 52 bits ) the translation is not
bijective with rounding errors for dates very far from Epoch.
For a resolution of 1 ms those start to happen for dates above the
year 144683.
An axis for a date/time interval is expected to be aligned
and divided in time/date units like seconds, minutes, ...
QwtDate offers several algorithms that are needed to
calculate these axes.
\sa QwtDateScaleEngine, QwtDateScaleDraw, QDate, QTime
*/
class QWT_EXPORT QwtDate
{
public:
/*!
How to identify the first week of year differs between
countries.
*/
enum Week0Type
{
/*!
According to ISO 8601 the first week of a year is defined
as "the week with the year's first Thursday in it".
FirstThursday corresponds to the numbering that is
implemented in QDate::weekNumber().
*/
FirstThursday,
/*!
"The week with January 1.1 in it."
In the U.S. this definition is more common than
FirstThursday.
*/
FirstDay
};
/*!
Classification of an time interval
Time intervals needs to be classified to decide how to
align and divide it.
*/
enum IntervalType
{
//! The interval is related to milliseconds
Millisecond,
//! The interval is related to seconds
Second,
//! The interval is related to minutes
Minute,
//! The interval is related to hours
Hour,
//! The interval is related to days
Day,
//! The interval is related to weeks
Week,
//! The interval is related to months
Month,
//! The interval is related to years
Year
};
enum
{
//! The Julian day of "The Epoch"
JulianDayForEpoch = 2440588
};
static QDate minDate();
static QDate maxDate();
static QDateTime toDateTime( double value,
Qt::TimeSpec = Qt::UTC );
static double toDouble( const QDateTime & );
static QDateTime ceil( const QDateTime &, IntervalType );
static QDateTime floor( const QDateTime &, IntervalType );
static QDate dateOfWeek0( int year, Week0Type );
static int weekNumber( const QDate &, Week0Type );
static int utcOffset( const QDateTime & );
static QString toString( const QDateTime &,
const QString & format, Week0Type );
};
#endif
#include "qwt_date_scale_draw.h"
class QwtDateScaleDraw::PrivateData
{
public:
PrivateData( Qt::TimeSpec spec ):
timeSpec( spec ),
utcOffset( 0 ),
week0Type( QwtDate::FirstThursday )
{
dateFormats[ QwtDate::Millisecond ] = "hh:mm:ss:zzz\nddd dd MMM yyyy";
dateFormats[ QwtDate::Second ] = "hh:mm:ss\nddd dd MMM yyyy";
dateFormats[ QwtDate::Minute ] = "hh:mm\nddd dd MMM yyyy";
dateFormats[ QwtDate::Hour ] = "hh:mm\nddd dd MMM yyyy";
dateFormats[ QwtDate::Day ] = "ddd dd MMM yyyy";
dateFormats[ QwtDate::Week ] = "Www yyyy";
dateFormats[ QwtDate::Month ] = "MMM yyyy";
dateFormats[ QwtDate::Year ] = "yyyy";
}
Qt::TimeSpec timeSpec;
int utcOffset;
QwtDate::Week0Type week0Type;
QString dateFormats[ QwtDate::Year + 1 ];
};
/*!
\brief Constructor
The default setting is to display tick labels for the
given time specification. The first week of a year is defined like
for QwtDate::FirstThursday.
\param timeSpec Time specification
\sa setTimeSpec(), setWeek0Type()
*/
QwtDateScaleDraw::QwtDateScaleDraw( Qt::TimeSpec timeSpec )
{
d_data = new PrivateData( timeSpec );
}
//! Destructor
QwtDateScaleDraw::~QwtDateScaleDraw()
{
delete d_data;
}
/*!
Set the time specification used for the tick labels
\param timeSpec Time specification
\sa timeSpec(), setUtcOffset(), toDateTime()
*/
void QwtDateScaleDraw::setTimeSpec( Qt::TimeSpec timeSpec )
{
d_data->timeSpec = timeSpec;
}
/*!
\return Time specification used for the tick labels
\sa setTimeSpec(), utcOffset(), toDateTime()
*/
Qt::TimeSpec QwtDateScaleDraw::timeSpec() const
{
return d_data->timeSpec;
}
/*!
Set the offset in seconds from Coordinated Universal Time
\param seconds Offset in seconds
\note The offset has no effect beside for the time specification
Qt::OffsetFromUTC.
\sa QDate::utcOffset(), setTimeSpec(), toDateTime()
*/
void QwtDateScaleDraw::setUtcOffset( int seconds )
{
d_data->utcOffset = seconds;
}
/*!
\return Offset in seconds from Coordinated Universal Time
\note The offset has no effect beside for the time specification
Qt::OffsetFromUTC.
\sa QDate::setUtcOffset(), setTimeSpec(), toDateTime()
*/
int QwtDateScaleDraw::utcOffset() const
{
return d_data->utcOffset;
}
/*!
Sets how to identify the first week of a year.
\param week0Type Mode how to identify the first week of a year
\sa week0Type().
\note week0Type has no effect beside for intervals classified as
QwtDate::Week.
*/
void QwtDateScaleDraw::setWeek0Type( QwtDate::Week0Type week0Type )
{
d_data->week0Type = week0Type;
}
/*!
\return Setting how to identify the first week of a year.
\sa setWeek0Type()
*/
QwtDate::Week0Type QwtDateScaleDraw::week0Type() const
{
return d_data->week0Type;
}
/*!
Set the default format string for an datetime interval type
\param intervalType Interval type
\param format Default format string
\sa dateFormat(), dateFormatOfDate(), QwtDate::toString()
*/
void QwtDateScaleDraw::setDateFormat(
QwtDate::IntervalType intervalType, const QString &format )
{
if ( intervalType >= QwtDate::Millisecond &&
intervalType <= QwtDate::Year )
{
d_data->dateFormats[ intervalType ] = format;
}
}
/*!
\param intervalType Interval type
\return Default format string for an datetime interval type
\sa setDateFormat(), dateFormatOfDate()
*/
QString QwtDateScaleDraw::dateFormat(
QwtDate::IntervalType intervalType ) const
{
if ( intervalType >= QwtDate::Millisecond &&
intervalType <= QwtDate::Year )
{
return d_data->dateFormats[ intervalType ];
}
return QString::null;
}
/*!
Format string for the representation of a datetime
dateFormatOfDate() is intended to be overloaded for
situations, where formats are individual for specific
datetime values.
The default setting ignores dateTime and return
the default format for the interval type.
\param dateTime Datetime value
\param intervalType Interval type
\return Format string
\sa setDateFormat(), QwtDate::toString()
*/
QString QwtDateScaleDraw::dateFormatOfDate( const QDateTime &dateTime,
QwtDate::IntervalType intervalType ) const
{
Q_UNUSED( dateTime )
if ( intervalType >= QwtDate::Millisecond &&
intervalType <= QwtDate::Year )
{
return d_data->dateFormats[ intervalType ];
}
return d_data->dateFormats[ QwtDate::Second ];
}
/*!
\brief Convert a value into its representing label
The value is converted to a datetime value using toDateTime()
and converted to a plain text using QwtDate::toString().
\param value Value
\return Label string.
\sa dateFormatOfDate()
*/
QwtText QwtDateScaleDraw::label( double value ) const
{
const QDateTime dt = toDateTime( value );
const QString fmt = dateFormatOfDate(
dt, intervalType( scaleDiv() ) );
return QwtDate::toString( dt, fmt, d_data->week0Type );
}
/*!
Find the less detailed datetime unit, where no rounding
errors happen.
\param scaleDiv Scale division
\return Interval type
\sa dateFormatOfDate()
*/
QwtDate::IntervalType QwtDateScaleDraw::intervalType(
const QwtScaleDiv &scaleDiv ) const
{
int intvType = QwtDate::Year;
bool alignedToWeeks = true;
const QList<double> ticks = scaleDiv.ticks( QwtScaleDiv::MajorTick );
for ( int i = 0; i < ticks.size(); i++ )
{
const QDateTime dt = toDateTime( ticks[i] );
for ( int j = QwtDate::Second; j <= intvType; j++ )
{
const QDateTime dt0 = QwtDate::floor( dt,
static_cast<QwtDate::IntervalType>( j ) );
if ( dt0 != dt )
{
if ( j == QwtDate::Week )
{
alignedToWeeks = false;
}
else
{
intvType = j - 1;
break;
}
}
}
if ( intvType == QwtDate::Millisecond )
break;
}
if ( intvType == QwtDate::Week && !alignedToWeeks )
intvType = QwtDate::Day;
return static_cast<QwtDate::IntervalType>( intvType );
}
/*!
Translate a double value into a QDateTime object.
\return QDateTime object initialized with timeSpec() and utcOffset().
\sa timeSpec(), utcOffset(), QwtDate::toDateTime()
*/
QDateTime QwtDateScaleDraw::toDateTime( double value ) const
{
QDateTime dt = QwtDate::toDateTime( value, d_data->timeSpec );
if ( d_data->timeSpec == Qt::OffsetFromUTC )
{
dt = dt.addSecs( d_data->utcOffset );
dt.setUtcOffset( d_data->utcOffset );
}
return dt;
}
#ifndef _QWT_DATE_SCALE_DRAW_H_
#define _QWT_DATE_SCALE_DRAW_H_ 1
#include "qwt_global.h"
#include "qwt_scale_draw.h"
#include "qwt_date.h"
/*!
\brief A class for drawing datetime scales
QwtDateScaleDraw displays values as datetime labels.
The format of the labels depends on the alignment of
the major tick labels.
The default format strings are:
- Millisecond\n
"hh:mm:ss:zzz\nddd dd MMM yyyy"
- Second\n
"hh:mm:ss\nddd dd MMM yyyy"
- Minute\n
"hh:mm\nddd dd MMM yyyy"
- Hour\n
"hh:mm\nddd dd MMM yyyy"
- Day\n
"ddd dd MMM yyyy"
- Week\n
"Www yyyy"
- Month\n
"MMM yyyy"
- Year\n
"yyyy"
The format strings can be modified using setDateFormat()
or individually for each tick label by overloading dateFormatOfDate(),
Usually QwtDateScaleDraw is used in combination with
QwtDateScaleEngine, that calculates scales for datetime
intervals.
\sa QwtDateScaleEngine, QwtPlot::setAxisScaleDraw()
*/
class QWT_EXPORT QwtDateScaleDraw: public QwtScaleDraw
{
public:
QwtDateScaleDraw( Qt::TimeSpec = Qt::LocalTime );
virtual ~QwtDateScaleDraw();
void setDateFormat( QwtDate::IntervalType, const QString & );
QString dateFormat( QwtDate::IntervalType ) const;
void setTimeSpec( Qt::TimeSpec );
Qt::TimeSpec timeSpec() const;
void setUtcOffset( int seconds );
int utcOffset() const;
void setWeek0Type( QwtDate::Week0Type );
QwtDate::Week0Type week0Type() const;
virtual QwtText label( double ) const;
QDateTime toDateTime( double ) const;
protected:
virtual QwtDate::IntervalType
intervalType( const QwtScaleDiv & ) const;
virtual QString dateFormatOfDate( const QDateTime &,
QwtDate::IntervalType ) const;
private:
class PrivateData;
PrivateData *d_data;
};
#endif
#include "qwt_date_scale_engine.h"
#include "qwt_math.h"
#include "qwt_transform.h"
#include <qdatetime.h>
#include <limits.h>
static inline double qwtMsecsForType( QwtDate::IntervalType 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 ? ::ceil( d ) : ::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:
{
const double secsTo = minDate.secsTo( maxDate );
const double msecs = maxDate.time().msec() -
minDate.time().msec();
return secsTo * 1000 + msecs;
}
case QwtDate::Second:
{
return minDate.secsTo( maxDate );
}
case QwtDate::Minute:
{
const double secsTo = minDate.secsTo( maxDate );
return ::floor( secsTo / 60 );
}
case QwtDate::Hour:
{
const double secsTo = minDate.secsTo( maxDate );
return ::floor( secsTo / 3600 );
}
case QwtDate::Day:
{
return minDate.daysTo( maxDate );
}
case QwtDate::Week:
{
return ::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 = ::floor( ::log( stepSize ) / ::log( double( base ) ) );
const double fraction = qPow( 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 = qCeil( 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 = qCeil( v );
else
stepSize = qCeil( 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 = double( 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 = double( 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 = double( 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 = double( 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 = double( 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 )
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;
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 = qFloor( 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;
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 = qFloor( 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 >= 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:
PrivateData( Qt::TimeSpec spec ):
timeSpec( spec ),
utcOffset( 0 ),
week0Type( QwtDate::FirstThursday ),
maxWeeks( 4 )
{
}
Qt::TimeSpec timeSpec;
int utcOffset;
QwtDate::Week0Type week0Type;
int maxWeeks;
};
/*!
\brief Constructor
The engine is initialized to build scales for the
given time specification. It classifies intervals > 4 weeks
as >= Qt::Month. The first week of a year is defined like
for QwtDate::FirstThursday.
\param timeSpec Time specification
\sa setTimeSpec(), setMaxWeeks(), setWeek0Type()
*/
QwtDateScaleEngine::QwtDateScaleEngine( Qt::TimeSpec timeSpec ):
QwtLinearScaleEngine( 10 )
{
d_data = new PrivateData( timeSpec );
}
//! Destructor
QwtDateScaleEngine::~QwtDateScaleEngine()
{
delete d_data;
}
/*!
Set the time specification used by the engine
\param timeSpec Time specification
\sa timeSpec(), setUtcOffset(), toDateTime()
*/
void QwtDateScaleEngine::setTimeSpec( Qt::TimeSpec timeSpec )
{
d_data->timeSpec = timeSpec;
}
/*!
\return Time specification used by the engine
\sa setTimeSpec(), utcOffset(), toDateTime()
*/
Qt::TimeSpec QwtDateScaleEngine::timeSpec() const
{
return d_data->timeSpec;
}
/*!
Set the offset in seconds from Coordinated Universal Time
\param seconds Offset in seconds
\note The offset has no effect beside for the time specification
Qt::OffsetFromUTC.
\sa QDate::utcOffset(), setTimeSpec(), toDateTime()
*/
void QwtDateScaleEngine::setUtcOffset( int seconds )
{
d_data->utcOffset = seconds;
}
/*!
\return Offset in seconds from Coordinated Universal Time
\note The offset has no effect beside for the time specification
Qt::OffsetFromUTC.
\sa QDate::setUtcOffset(), setTimeSpec(), toDateTime()
*/
int QwtDateScaleEngine::utcOffset() const
{
return d_data->utcOffset;
}
/*!
Sets how to identify the first week of a year.
\param week0Type Mode how to identify the first week of a year
\sa week0Type(), setMaxWeeks()
\note week0Type has no effect beside for intervals classified as
QwtDate::Week.
*/
void QwtDateScaleEngine::setWeek0Type( QwtDate::Week0Type week0Type )
{
d_data->week0Type = week0Type;
}
/*!
\return Setting how to identify the first week of a year.
\sa setWeek0Type(), maxWeeks()
*/
QwtDate::Week0Type QwtDateScaleEngine::week0Type() const
{
return d_data->week0Type;
}
/*!
Set a upper limit for the number of weeks, when an interval
can be classified as Qt::Week.
The default setting is 4 weeks.
\param weeks Upper limit for the number of weeks
\note In business charts a year is often devided
into weeks [1-52]
\sa maxWeeks(), setWeek0Type()
*/
void QwtDateScaleEngine::setMaxWeeks( int weeks )
{
d_data->maxWeeks = qMax( weeks, 0 );
}
/*!
\return Upper limit for the number of weeks, when an interval
can be classified as Qt::Week.
\sa setMaxWeeks(), week0Type()
*/
int QwtDateScaleEngine::maxWeeks() const
{
return d_data->maxWeeks;
}
/*!
Classification of a date/time interval division
\param minDate Minimum ( = earlier ) of the interval
\param maxDate Maximum ( = later ) of the interval
\param maxSteps Maximum for the number of steps
\return Interval classification
*/
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 > d_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;
}
/*!
Align and divide an interval
The algorithm aligns and divides the interval into steps.
Datetime interval divisions are usually not equidistant and the
calculated stepSize can only be used as an approximation
for the steps calculated by divideScale().
\param maxNumSteps Max. number of steps
\param x1 First limit of the interval (In/Out)
\param x2 Second limit of the interval (In/Out)
\param stepSize Step size (Out)
\sa QwtScaleEngine::setAttribute()
*/
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;
}
}
/*!
\brief Calculate a scale division for a date/time interval
\param x1 First interval limit
\param x2 Second interval limit
\param maxMajorSteps Maximum for the number of major steps
\param maxMinorSteps Maximum number of minor steps
\param stepSize Step size. If stepSize == 0, the scaleEngine
calculates one.
\return Calculated scale division
*/
QwtScaleDiv QwtDateScaleEngine::divideScale( double x1, double x2,
int maxMajorSteps, int maxMinorSteps, double stepSize ) const
{
if ( maxMajorSteps < 1 )
maxMajorSteps = 1;
const double min = qMin( x1, x2 );
const double max = qMax( 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 = qCeil( ( 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;
}
/*!
Align a date/time value for a step size
For Qt::Day alignments there is no "natural day 0" -
instead the first day of the year is used to avoid jumping
major ticks positions when panning a scale. For other alignments
( f.e according to the first day of the month ) alignDate()
has to be overloaded.
\param dateTime Date/time value
\param stepSize Step size
\param intervalType Interval type
\param up When true dateTime is ceiled - otherwise it is floored
\return Aligned date/time value
*/
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 )
{
dt.setUtcOffset( 0 );
}
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(), d_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 )
{
dt.setUtcOffset( dateTime.utcOffset() );
}
return dt;
}
/*!
Translate a double value into a QDateTime object.
For QDateTime result is bounded by QwtDate::minDate() and QwtDate::maxDate()
\return QDateTime object initialized with timeSpec() and utcOffset().
\sa timeSpec(), utcOffset(), QwtDate::toDateTime()
*/
QDateTime QwtDateScaleEngine::toDateTime( double value ) const
{
QDateTime dt = QwtDate::toDateTime( value, d_data->timeSpec );
if ( !dt.isValid() )
{
const QDate date = ( value <= 0.0 )
? QwtDate::minDate() : QwtDate::maxDate();
dt = QDateTime( date, QTime( 0, 0 ), d_data->timeSpec );
}
if ( d_data->timeSpec == Qt::OffsetFromUTC )
{
dt = dt.addSecs( d_data->utcOffset );
dt.setUtcOffset( d_data->utcOffset );
}
return dt;
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment