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

279 lines
11 KiB
C++

#include <cmath>
#include "Abs_Axis_p.h"
namespace YSG {
using AbsAxis_Binding = Renderable_Binding<Abs_Axis, Abs_Axis_Private, Abs_Axis_Render_State, Abs_Axis_Input_Data>;
Abs_Axis::Abs_Axis() {
AbsAxis_Binding::init(this, "AbsAxis");
}
Abs_Axis_Private* Abs_Axis::d() {
return AbsAxis_Binding::d(this);
}
Render_Data* Abs_Axis::create_render_data() {
return AbsAxis_Binding::create_render_data();
}
Render_State* Abs_Axis::create_render_state() {
return AbsAxis_Binding::create_render_state();
}
Input_Data* Abs_Axis::create_input_data() {
return AbsAxis_Binding::create_input_data();
}
int Abs_Axis::x(SRC src) {
return d()->state_value(&Abs_Axis_Render_State::x, src);
}
void Abs_Axis::set_x(int value) {
d()->set_state_value(&Abs_Axis_Render_State::x, value);
}
int Abs_Axis::y(SRC src) {
return d()->state_value(&Abs_Axis_Render_State::y, src);
}
void Abs_Axis::set_y(int value) {
d()->set_state_value(&Abs_Axis_Render_State::y, value);
}
Qt::Orientation Abs_Axis::orientation(SRC src) {
return d()->state_value(&Abs_Axis_Render_State::orientation, src);
}
void Abs_Axis::set_orientation(Qt::Orientation value) {
d()->set_state_value(&Abs_Axis_Render_State::orientation, value);
}
size_t Abs_Axis::pixel_size(SRC src) {
return d()->state_value(&Abs_Axis_Render_State::pixel_size, src);
}
void Abs_Axis::set_pixel_size(size_t value) {
d()->set_state_value(&Abs_Axis_Render_State::pixel_size, value);
}
int Abs_Axis::tick_length(SRC src) {
return d()->state_value(&Abs_Axis_Render_State::tick_length, src);
}
void Abs_Axis::set_tick_length(int value) {
d()->set_state_value(&Abs_Axis_Render_State::tick_length, value);
}
int Abs_Axis::sub_tick_length(SRC src) {
return d()->state_value(&Abs_Axis_Render_State::sub_tick_length, src);
}
void Abs_Axis::set_sub_tick_length(int value) {
d()->set_state_value(&Abs_Axis_Render_State::sub_tick_length, value);
}
QColor Abs_Axis::color(SRC src) {
return d()->state_value(&Abs_Axis_Render_State::color, src);
}
void Abs_Axis::set_color(QColor value) {
d()->set_state_value(&Abs_Axis_Render_State::color, std::move(value));
}
QChar Abs_Axis::format_char(SRC src) {
return d()->state_value(&Abs_Axis_Render_State::format_char, src);
}
void Abs_Axis::set_format_char(QChar value) {
d()->set_state_value(&Abs_Axis_Render_State::format_char, std::move(value));
}
QLocale Abs_Axis::locale(SRC src) {
return d()->state_value(&Abs_Axis_Render_State::locale, src);
}
void Abs_Axis::set_locale(QLocale value) {
d()->set_state_value(&Abs_Axis_Render_State::locale, std::move(value));
}
QString Abs_Axis::unit_text(SRC src) {
return d()->state_value(&Abs_Axis_Render_State::unit_text, src);
}
void Abs_Axis::set_unit_text(QString value) {
d()->set_state_value(&Abs_Axis_Render_State::unit_text, std::move(value));
}
QFont Abs_Axis::unit_text_font(SRC src) {
return d()->state_value(&Abs_Axis_Render_State::unit_text_font, src);
}
void Abs_Axis::set_unit_text_font(QFont value) {
d()->set_state_value(&Abs_Axis_Render_State::unit_text_font, std::move(value));
}
QPen Abs_Axis::unit_text_pen(SRC src) {
return d()->state_value(&Abs_Axis_Render_State::unit_text_pen, src);
}
void Abs_Axis::set_unit_text_pen(QPen value) {
d()->set_state_value(&Abs_Axis_Render_State::unit_text_pen, std::move(value));
}
QBrush Abs_Axis::unit_text_background_brush(SRC src) {
return d()->state_value(&Abs_Axis_Render_State::unit_text_background_brush, src);
}
void Abs_Axis::set_unit_text_background_brush(QBrush value) {
d()->set_state_value(&Abs_Axis_Render_State::unit_text_background_brush, std::move(value));
}
int Abs_Axis::rotate(SRC src) {
return d()->state_value(&Abs_Axis_Render_State::rotate, src);
}
void Abs_Axis::set_rotate(int value) {
d()->set_state_value(&Abs_Axis_Render_State::rotate, value);
}
double Abs_Axis::pixel_to_coord(double pixel, SRC src) {
return d()->pixel_to_coord(pixel, src);
}
double Abs_Axis::coord_to_pixel(double coord, SRC src) {
return d()->coord_to_pixel(coord, src);
}
QVector<double> Abs_Axis_Private::create_tick_vector(double tickStep, const Range& range) {
double mTickOrigin = render_state()->coord_start;
QVector<double> result;
// Generate tick positions according to tickStep:
// do not use qFloor here, or we'll lose 64 bit precision
// qDebug() << "tickStep:" << tickStep;
//
auto firstStep = qint64(floor(qAbs((range.lower - mTickOrigin) / tickStep)));
auto lastStep = qint64(ceil((qAbs(range.upper - mTickOrigin) / tickStep)));
int tickcount = int(qAbs(lastStep - firstStep) + 1);
if (tickcount < 0) tickcount = 0;
if (range.lower > range.upper) tickStep = -tickStep;
result.resize(tickcount);
for (int i = 0; i < tickcount; ++i) {
result[i] = mTickOrigin + (double)(firstStep + i) * tickStep;
}
// while (!result.empty() && !range.contain(result.last())) {
// result.removeLast();
// }
return result;
}
QVector<QString> Abs_Axis_Private::create_label_vector(const QVector<double>& ticks, SRC src) {
QVector<QString> result;
result.reserve(ticks.size());
foreach(double tickCoord, ticks)
result.append(get_tick_label(tickCoord, src));
return result;
}
QVector<double> Abs_Axis_Private::create_sub_tick_vector(int subTickCount, const QVector<double>& ticks, const Range& range) {
QVector<double> result;
if (subTickCount <= 0 || ticks.size() < 2)
return result;
result.reserve((ticks.size() - 1) * subTickCount);
for (int i = 1; i < ticks.size(); ++i) {
double subTickStep = (ticks.at(i) - ticks.at(i - 1)) / double(subTickCount + 1);
for (int k = 1; k <= subTickCount; ++k)
result.append(ticks.at(i - 1) + k * subTickStep);
}
while (!result.empty() && !range.contain(result.last())) {
result.removeLast();
}
return result;
}
int Abs_Axis_Private::get_sub_tick_count(double tickStep) {
int result = 1; // default to 1, if no proper value can be found
// separate integer and fractional part of mantissa:
double epsilon = 0.01;
double intPartf;
int intPart;
double fracPart = modf(getMantissa(tickStep), &intPartf);
intPart = int(intPartf);
// handle cases with (almost) integer mantissa:
if (fracPart < epsilon || 1.0 - fracPart < epsilon) {
if (1.0 - fracPart < epsilon)
++intPart;
switch (intPart) {
case 1: result = 4;
break; // 1.0 -> 0.2 substep
case 2: result = 3;
break; // 2.0 -> 0.5 substep
case 3: result = 2;
break; // 3.0 -> 1.0 substep
case 4: result = 3;
break; // 4.0 -> 1.0 substep
case 5: result = 4;
break; // 5.0 -> 1.0 substep
case 6: result = 2;
break; // 6.0 -> 2.0 substep
case 7: result = 6;
break; // 7.0 -> 1.0 substep
case 8: result = 3;
break; // 8.0 -> 2.0 substep
case 9: result = 2;
break; // 9.0 -> 3.0 substep
}
}
else {
// handle cases with significantly fractional mantissa:
if (qAbs(fracPart - 0.5) < epsilon) // *.5 mantissa
{
switch (intPart) {
case 1: result = 2;
break; // 1.5 -> 0.5 substep
case 2: result = 4;
break; // 2.5 -> 0.5 substep
case 3: result = 4;
break; // 3.5 -> 0.7 substep
case 4: result = 2;
break; // 4.5 -> 1.5 substep
case 5: result = 4;
break; // 5.5 -> 1.1 substep (won't occur with default get_tick_step from here on)
case 6: result = 4;
break; // 6.5 -> 1.3 substep
case 7: result = 2;
break; // 7.5 -> 2.5 substep
case 8: result = 4;
break; // 8.5 -> 1.7 substep
case 9: result = 4;
break; // 9.5 -> 1.9 substep
}
}
// if mantissa fraction isn't 0.0 or 0.5, don't bother finding good sub tick marks, leave default
}
return result;
}
double Abs_Axis_Private::getMantissa(double input, double* magnitude) {
const double mag = std::pow(10.0, std::floor(std::log10(input)));
if (magnitude) *magnitude = mag;
return input / mag;
}
double Abs_Axis_Private::clean_mantissa(double input) {
double magnitude;
const double mantissa = getMantissa(input, &magnitude);
return pickClosest(mantissa, QVector<double>() << 1.0 << 2.0 << 2.5 << 5.0 << 10.0) * magnitude;
}
double Abs_Axis_Private::pickClosest(double target, const QVector<double>& candidates) {
if (candidates.size() == 1)
return candidates.first();
QVector<double>::const_iterator it = std::lower_bound(candidates.constBegin(), candidates.constEnd(), target);
if (it == candidates.constEnd())
return *(it - 1);
else if (it == candidates.constBegin())
return *it;
else
return target - *(it - 1) < *it - target ? *(it - 1) : *it;
}
QString Abs_Axis_Private::get_tick_label(double tick, SRC src) {
auto render_state = reinterpret_cast<Abs_Axis_Render_State*>(state(src));
return render_state->locale.toString(tick, render_state->format_char.toLatin1(), render_state->number_precision);
}
double Abs_Axis::start_coord(SRC src) {
auto render_state = d()->state_data(src);
return render_state->coord_start;
}
double Abs_Axis::end_coord(SRC src) {
auto render_state = d()->state_data(src);
return render_state->coord_start + render_state->coord_length;
}
Range Abs_Axis::coord_range(SRC src) {
auto render_state = d()->state_data(src);
return {render_state->coord_start, render_state->coord_start + render_state->coord_length};
}
int Abs_Axis::get_pixel_point_size(Range range, SRC src) {
auto render_state = d()->state_data(src);
return static_cast<int>(qAbs(range.length() / coord_range(src).length()) * render_state->pixel_size);
}
int Abs_Axis::get_pixel_point_size(SRC src) {
auto render_state = d()->state_data(src);
return qAbs(render_state->pixel_size);
}
double Abs_Axis::get_tick_step(const Range& range) {
return d()->get_tick_step(range);
}
int Abs_Axis::get_sub_tick_count(double tickStep) {
return d()->get_sub_tick_count(tickStep);
}
QString Abs_Axis::get_tick_label(double tick, SRC src) {
return d()->get_tick_label(tick, src);
}
QVector<QString> Abs_Axis::create_label_vector(const QVector<double>& ticks, SRC src) {
return d()->create_label_vector(ticks, src);
}
QVector<double> Abs_Axis::create_tick_vector(double tickStep, const Range& range) {
return d()->create_tick_vector(tickStep, range);
}
QVector<double> Abs_Axis::create_sub_tick_vector(int subTickCount, const QVector<double>& ticks, const Range& range) {
return d()->create_sub_tick_vector(subTickCount, ticks, range);
}
}