156 lines
6.8 KiB
C++
156 lines
6.8 KiB
C++
#pragma once
|
|
|
|
#include "AbsAxis.h"
|
|
|
|
namespace YSG {
|
|
struct AbsAxisRenderState : RenderState, AbsAxis_Prop {
|
|
|
|
size_t pixelSize{};
|
|
int numberPrecision = 2;
|
|
double coordStart = 0;
|
|
double coordLength = 20;
|
|
void scaleRange(double factor, double center) {
|
|
coordStart = (coordStart - center) * factor + center;
|
|
coordLength *= factor;
|
|
}
|
|
};
|
|
struct AbsAxisTempData : TempData {};
|
|
|
|
struct AbsAxisPrivate : RenderData {
|
|
D_Ptr(AbsAxis)
|
|
int mTickCount = 2;
|
|
std::atomic<int> pixelStart{};
|
|
double getMantissa(double input, double *magnitude=nullptr);
|
|
[[nodiscard]] double cleanMantissa(double input);
|
|
[[nodiscard]] double pickClosest(double target, const QVector<double> &candidates);
|
|
|
|
|
|
void prepareData() override {
|
|
SpinLockGuard guard(&mBufferLock);
|
|
loadCache();
|
|
}
|
|
|
|
void draw(QPainter* painter) override {
|
|
AbsAxisRenderState* s = renderState();
|
|
pixelStart = s->orientation == Qt::Horizontal ? s->x : s->y;
|
|
Range range = {s->coordStart, s->coordStart + s->coordLength};
|
|
AbsAxis* q = this->q();
|
|
double tickStep = q->getTickStep(range);
|
|
QVector<double> ticks = q->createTickVector(tickStep, range);
|
|
int subTickCount = q->getSubTickCount(tickStep);
|
|
QVector<double> subTicks = q->createSubTickVector(subTickCount, ticks, range);
|
|
QVector<QString> labels = q->createLabelVector(ticks, SRC::Render);
|
|
while (!ticks.empty() && !range.contain(ticks.last())) {
|
|
ticks.removeLast();
|
|
}
|
|
while (!subTicks.empty() && !range.contain(subTicks.last())) {
|
|
subTicks.removeLast();
|
|
}
|
|
QFontMetrics fm(painter->font());
|
|
int h = fm.height();
|
|
painter->setPen(s->color);
|
|
if(s->orientation == Qt::Horizontal) {
|
|
painter->drawLine(s->x, s->y, s->x + s->pixelSize, s->y);
|
|
int n = ticks.size();
|
|
for(int i = 0; i < n; ++i) {
|
|
double tick = ticks[i];
|
|
double x = q->coordToPixel(tick, SRC::Render);
|
|
painter->drawLine((int)x, s->y, (int)x, s->y + s->tickLength);
|
|
const QString& label = labels[i];
|
|
QPoint start((int)x, s->y + s->tickLength);
|
|
painter->translate(start);
|
|
painter->rotate(s->rotate);
|
|
painter->drawText(0, 0, label);
|
|
painter->resetTransform();
|
|
}
|
|
for(double tick : subTicks) {
|
|
double x = q->coordToPixel(tick, SRC::Render);
|
|
painter->drawLine((int)x, s->y, (int)x, s->y + s->subTickLength);
|
|
}
|
|
} else if(s->orientation == Qt::Vertical) {
|
|
painter->drawLine(s->x, s->y, s->x, s->y + s->pixelSize);
|
|
int n = ticks.size();
|
|
for(int i = 0; i < n; ++i) {
|
|
double tick = ticks[i];
|
|
double y = q->coordToPixel(tick, SRC::Render);
|
|
const QString& label = labels[i];
|
|
painter->drawLine(s->x, (int)y, s->x + s->tickLength, (int)y);
|
|
QPoint start = QPoint(s->x + s->tickLength * 2, (int)y + h/2);
|
|
painter->translate(start);
|
|
painter->rotate(s->rotate);
|
|
painter->drawText(0, 0, label);
|
|
painter->resetTransform();
|
|
}
|
|
for(double tick : subTicks) {
|
|
double y = q->coordToPixel(tick, SRC::Render);
|
|
painter->drawLine(s->x, (int)y, s->x + s->subTickLength, (int)y);
|
|
}
|
|
} else {
|
|
qDebug() << "error s->mOrientation == " << s->orientation;
|
|
}
|
|
if(s->unitText.isEmpty()) return;
|
|
double w = fm.horizontalAdvance(s->unitText);
|
|
|
|
if(s->orientation == Qt::Horizontal) {
|
|
double x = pixelStart + s->pixelSize - w;
|
|
double y = s->y - h;
|
|
QRectF area(x, y, w, h);
|
|
painter->fillRect(area, s->unitTextBackgroundBrush);
|
|
painter->setPen(s->unitTextPen);
|
|
painter->setFont(s->unitTextFont);
|
|
painter->drawText(area, Qt::AlignCenter, s->unitText);
|
|
} else if (s->orientation == Qt::Vertical) {
|
|
double x = s->x;
|
|
double y = pixelStart;
|
|
QRectF area(x, y, w, h);
|
|
painter->fillRect(area, s->unitTextBackgroundBrush);
|
|
painter->setPen(s->unitTextPen);
|
|
painter->setFont(s->unitTextFont);
|
|
painter->drawText(area, Qt::AlignCenter, s->unitText);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
[[nodiscard]] virtual double pixelToCoord(double pixel, SRC src) {
|
|
auto s = static_cast<AbsAxisRenderState*>(getState(src));
|
|
return s->coordStart + s->coordLength * (pixel - (double)pixelStart)/(double)s->pixelSize;
|
|
}
|
|
[[nodiscard]] virtual double coordToPixel(double coord, SRC src) {
|
|
auto s = static_cast<AbsAxisRenderState*>(getState(src));
|
|
return pixelStart + (double)s->pixelSize * (coord - s->coordStart)/s->coordLength;
|
|
}
|
|
virtual double getTickStep(const Range &range){
|
|
double exactStep = range.size()/double(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
|
|
return cleanMantissa(exactStep);
|
|
}
|
|
virtual int getSubTickCount(double tickStep);
|
|
virtual QVector<double> createTickVector(double tickStep, const Range &range);
|
|
virtual QVector<double> createSubTickVector(int subTickCount, const QVector<double> &ticks, const Range& range);
|
|
virtual QString getTickLabel(double tick, SRC);
|
|
virtual QVector<QString> createLabelVector(const QVector<double> &ticks, SRC);
|
|
};
|
|
|
|
template <typename That>
|
|
void AbsAxis::BuilderT<That>::set(AbsAxis* ret) {
|
|
ret->init(plot, layerName);
|
|
AbsAxisPrivate* pd = ret->d();
|
|
AbsAxisRenderState* sc = pd->renderStateCache();
|
|
sc->x = this->x;
|
|
sc->y = this->y;
|
|
sc->orientation = this->orientation;
|
|
sc->pixelSize = this->pixelSize;
|
|
sc->tickLength = this->tickLength;
|
|
sc->subTickLength = this->subTickLength;
|
|
sc->color = this->color;
|
|
sc->formatChar = this->formatChar;
|
|
sc->locale = this->locale;
|
|
sc->unitText = this->unitText;
|
|
sc->unitTextFont = this->unitTextFont;
|
|
sc->unitTextPen = this->unitTextPen;
|
|
sc->unitTextBackgroundBrush = this->unitTextBackgroundBrush;
|
|
sc->rotate = this->rotate;
|
|
}
|
|
|
|
}
|
|
|