// SourceSamples
class SourceSamples : public QwtSeriesData<QPointF>
{
public:
explicit SourceSamples();
private: // virtual overrides
size_t size() const final;
QPointF sample
(size_t i
) const final;
void setRectOfInterest
(const QRectF &rect
) final;
QRectF boundingRect
() const final;
private:
static size_t getFactor(size_t pointsCount);
void updateCache
(const QRectF &rect
);
private:
// Emulates the data from the external storage (e.g. file or DB).
QVector<QPointF> m_storage;
// Set of samples for current details level.
QVector<QPointF> m_cache;
};
// Calculates factor which allow to have always ~500 points to draw.
size_t SourceSamples::getFactor(size_t pointsCount)
{
const auto factor = pointsCount / 500;
return (factor > 0) ? factor : 1;
}
// Refills the internal samples cache for the required rect.
void SourceSamples
::updateCache(const QRectF &rect
) {
// Binary search indexes of minimum and maximum points for
// the X-axis of passed rect in "external" data storage
// (which is simulates by the m_storage in our cases).
const auto begin = m_storage.cbegin();
const auto end = m_storage.cend();
return p1.x() < p2.x(); };
// Find iterator of minimal point.
auto lower = std::lower_bound(begin, end, p1, compare);
// Find iterator of maximum point.
const QPointF p2
(rect.
x() + rect.
width(),
0);
auto upper = std::upper_bound(begin, end, p2, compare);
// TODO: Should we to do this?
if (lower == end)
lower = begin;
if (upper == end)
upper = end - 1;
// Calculate indexed of points in "external" storage.
// (in principle, it can be omitted, as we have iterators).
const auto lowerIndex = std::distance(begin, lower);
const auto upperIndex = std::distance(begin, upper);
auto pointsCount = upperIndex - lowerIndex;
// Get required "weeding" factor.
const size_t factor = getFactor(pointsCount);
qDebug() << "New factor" << factor << "for" << pointsCount << "points"
<< "from" << lowerIndex << "," << p1.x()
<< "to" << upperIndex << "," << p2.x();
// Desired number of points, which will be displayed.
pointsCount = qRound(pointsCount / double(factor)) + 1;
qDebug() << "Reduced up to" << pointsCount << "points";
// Refill the cache again with every "factor" step.
m_cache.clear();
// Always add the minimum point of range.
m_cache += m_storage.at(lowerIndex);
for (size_t i = 1; i < pointsCount - 1; ++i) {
// Add the middle points.
const size_t n =(i * factor) + lowerIndex;
m_cache += m_storage.at(n);
}
// Always add the maximum point of range.
m_cache += m_storage.at(upperIndex);
}
SourceSamples::SourceSamples()
{
// Fill the "external" storage with all available points
// (we just emulate the external storage as file, or DB,
// via the array of data, for simplification of a code.
size_t pointsCount = 10000000;
m_storage.resize(pointsCount);
double x = 0.0;
for (size_t i = 0; i < pointsCount; ++i) {
double y = std::sin(x);
m_storage[i] = { x, y };
x += 0.01;
}
// Fill the cache for the initial details level. We starts
// it for "low" resolution (we want to view whole range).
const size_t factor = getFactor(m_storage.size());
pointsCount = qRound(m_storage.size() / double(factor)) + 1;
qDebug() << Q_FUNC_INFO << "Initialize with" << pointsCount << "points";
// Fill the cache with every "factor" step.
// Always add the minimum point of range.
m_cache += m_storage.first();
for (size_t i = 1; i < pointsCount - 1; ++i) {
const size_t n = i * factor;
// Add the middle points.
m_cache += m_storage.at(n);
}
// Always add the maximum point of range.
m_cache += m_storage.last();
}
size_t SourceSamples::size() const
{
return m_cache.size();
}
QPointF SourceSamples
::sample(size_t i
) const {
return m_cache.at(i);
}
void SourceSamples
::setRectOfInterest(const QRectF &rect
) {
updateCache(rect);
// TODO: Should we to do it?
d_boundingRect = rect;
}
QRectF SourceSamples
::boundingRect() const {
if (d_boundingRect.width() < 0.0)
d_boundingRect = qwtBoundingRect(*this);
return d_boundingRect;
}
// RandomPlot
RandomPlot
::RandomPlot(QWidget *parent
){
setAutoReplot(false);
grid->enableXMin(true);
grid
->setMajorPen
(QPen(Qt
::black,
0, Qt
::DotLine));
grid
->setMinorPen
(QPen(Qt
::gray,
0, Qt
::DotLine));
grid->attach(this);
// We want to have a zoom with the X-axis only.
auto series = new SourceSamples;
curve->setData(series);
curve->attach(this);
}
Bookmarks