Files
Renderive/YSGraphic_Core/Axis/Abs_Axis_p.h
T
2026-07-23 18:11:07 +08:00

146 lines
6.5 KiB
C++

#pragma once
#include "Abs_Axis.h"
namespace YSG {
struct Abs_Axis_Render_State : Render_State, AbsAxis_Prop {
size_t pixel_size{};
int number_precision = 2;
double coord_start = 0;
double coord_length = 20;
void scaleRange(double factor, double center) {
coord_start = (coord_start - center) * factor + center;
coord_length *= factor;
}
};
struct Abs_Axis_Input_Data : Input_Data {};
struct Abs_Axis_Private : Typed_Render_Data<Abs_Axis, Abs_Axis_Render_State, Abs_Axis_Input_Data> {
friend class Abs_Axis;
int mTickCount = 2;
std::atomic<int> pixelStart{};
double getMantissa(double input, double* magnitude = nullptr);
[[nodiscard]] double clean_mantissa(double input);
[[nodiscard]] double pickClosest(double target, const QVector<double>& candidates);
void prepare_data() override {
sync_state_pipeline();
}
void draw(QPainter* painter) override {
Abs_Axis_Render_State* s = render_state();
pixelStart = s->orientation == Qt::Horizontal ? s->x : s->y;
Range range = {s->coord_start, s->coord_start + s->coord_length};
Abs_Axis* q = this->q();
double tickStep = q->get_tick_step(range);
QVector<double> ticks = q->create_tick_vector(tickStep, range);
int subTickCount = q->get_sub_tick_count(tickStep);
QVector<double> subTicks = q->create_sub_tick_vector(subTickCount, ticks, range);
QVector<QString> labels = q->create_label_vector(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->pixel_size, s->y);
int n = ticks.size();
for (int i = 0; i < n; ++i) {
double tick = ticks[i];
double x = q->coord_to_pixel(tick, SRC::Render);
painter->drawLine((int)x, s->y, (int)x, s->y + s->tick_length);
const QString& label = labels[i];
QPoint start((int)x, s->y + s->tick_length);
painter->translate(start);
painter->rotate(s->rotate);
painter->drawText(0, 0, label);
painter->resetTransform();
}
for (double tick : subTicks) {
double x = q->coord_to_pixel(tick, SRC::Render);
painter->drawLine((int)x, s->y, (int)x, s->y + s->sub_tick_length);
}
}
else if (s->orientation == Qt::Vertical) {
painter->drawLine(s->x, s->y, s->x, s->y + s->pixel_size);
int n = ticks.size();
for (int i = 0; i < n; ++i) {
double tick = ticks[i];
double y = q->coord_to_pixel(tick, SRC::Render);
const QString& label = labels[i];
painter->drawLine(s->x, (int)y, s->x + s->tick_length, (int)y);
QPoint start = QPoint(s->x + s->tick_length * 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->coord_to_pixel(tick, SRC::Render);
painter->drawLine(s->x, (int)y, s->x + s->sub_tick_length, (int)y);
}
}
else {
qDebug() << "error s->mOrientation == " << s->orientation;
}
if (s->unit_text.isEmpty()) return;
double w = fm.horizontalAdvance(s->unit_text);
if (s->orientation == Qt::Horizontal) {
double x = pixelStart + s->pixel_size - w;
double y = s->y - h;
QRectF area(x, y, w, h);
painter->fillRect(area, s->unit_text_background_brush);
painter->setPen(s->unit_text_pen);
painter->setFont(s->unit_text_font);
painter->drawText(area, Qt::AlignCenter, s->unit_text);
}
else if (s->orientation == Qt::Vertical) {
double x = s->x;
double y = pixelStart;
QRectF area(x, y, w, h);
painter->fillRect(area, s->unit_text_background_brush);
painter->setPen(s->unit_text_pen);
painter->setFont(s->unit_text_font);
painter->drawText(area, Qt::AlignCenter, s->unit_text);
}
}
protected:
[[nodiscard]] virtual double pixel_to_coord(double pixel, SRC src) {
auto s = static_cast<Abs_Axis_Render_State*>(state(src));
return s->coord_start + s->coord_length * (pixel - (double)pixelStart) / (double)s->pixel_size;
}
[[nodiscard]] virtual double coord_to_pixel(double coord, SRC src) {
auto s = static_cast<Abs_Axis_Render_State*>(state(src));
return pixelStart + (double)s->pixel_size * (coord - s->coord_start) / s->coord_length;
}
virtual double get_tick_step(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 clean_mantissa(exactStep);
}
virtual int get_sub_tick_count(double tickStep);
virtual QVector<double> create_tick_vector(double tickStep, const Range& range);
virtual QVector<double> create_sub_tick_vector(int subTickCount, const QVector<double>& ticks, const Range& range);
virtual QString get_tick_label(double tick, SRC);
virtual QVector<QString> create_label_vector(const QVector<double>& ticks, SRC);
};
template <typename That>
void Abs_Axis::BuilderT<That>::set(Abs_Axis* ret) {
ret->init(plot, layerName);
Abs_Axis_Private* pd = ret->d();
Abs_Axis_Render_State* sc = reinterpret_cast<Abs_Axis_Render_State*>(pd->state(State_Edit));
sc->x = this->x;
sc->y = this->y;
sc->orientation = this->orientation;
sc->pixel_size = this->pixel_size;
sc->tick_length = this->tick_length;
sc->sub_tick_length = this->sub_tick_length;
sc->color = this->color;
sc->format_char = this->format_char;
sc->locale = this->locale;
sc->unit_text = this->unit_text;
sc->unit_text_font = this->unit_text_font;
sc->unit_text_pen = this->unit_text_pen;
sc->unit_text_background_brush = this->unit_text_background_brush;
sc->rotate = this->rotate;
}
}