211 lines
7.7 KiB
C++
211 lines
7.7 KiB
C++
#include <cmath>
|
|
#include <algorithm>
|
|
#include "Abs_Axis_p.h"
|
|
namespace renderive {
|
|
RENDERIVE_DEFINE_RENDERABLE_BINDING(Abs_Axis, Abs_Axis_Private, Abs_Axis_Render_State, Abs_Axis_Input_Data, "AbsAxis")
|
|
RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, int, x)
|
|
RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, int, y)
|
|
RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, Orientation, orientation)
|
|
size_t Abs_Axis::pixel_length() {
|
|
return d()->edit_state_value(&Abs_Axis_Render_State::pixel_length);
|
|
}
|
|
void Abs_Axis::set_pixel_length(size_t value) {
|
|
d()->set_state_value(&Abs_Axis_Render_State::pixel_length, std::max<std::size_t>(1, value));
|
|
}
|
|
RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, int, tick_length)
|
|
RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, int, sub_tick_length)
|
|
RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, Color, color)
|
|
RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, Number_Locale, locale)
|
|
RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, std::string, unit_text)
|
|
RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, Font, unit_text_font)
|
|
RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, Pen, unit_text_pen)
|
|
RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, Brush, unit_text_background_brush)
|
|
RENDERIVE_DEFINE_STATE_PROP(Abs_Axis, Abs_Axis_Render_State, int, label_rotation_degrees)
|
|
double Abs_Axis::pixel_to_coord(double pixel) {
|
|
return d()->edit_pixel_to_coord(pixel);
|
|
}
|
|
double Abs_Axis::coord_to_pixel(double coord) {
|
|
return d()->edit_coord_to_pixel(coord);
|
|
}
|
|
std::vector<double> Abs_Axis_Private::create_tick_vector(double tick_step, const Range& range) {
|
|
double tick_origin = range.origin;
|
|
std::vector<double> result;
|
|
auto first_step = static_cast<std::int64_t>(std::floor(std::abs((range.origin - tick_origin) / tick_step)));
|
|
auto last_step = static_cast<std::int64_t>(std::ceil(std::abs(range.target - tick_origin) / tick_step));
|
|
int tick_count = static_cast<int>(std::abs(last_step - first_step) + 1);
|
|
if (tick_count < 0)
|
|
tick_count = 0;
|
|
if (range.origin > range.target)
|
|
tick_step = -tick_step;
|
|
result.resize(tick_count);
|
|
for (int i = 0; i < tick_count; ++i) {
|
|
result[i] = tick_origin + (double)(first_step + i) * tick_step;
|
|
}
|
|
return result;
|
|
}
|
|
std::vector<std::string> Abs_Axis_Private::create_label_vector(const std::vector<double>& ticks, Abs_Axis_Render_State* state) {
|
|
std::vector<std::string> result;
|
|
result.reserve(ticks.size());
|
|
for (double tick_coord : ticks)
|
|
result.push_back(get_tick_label(tick_coord, state));
|
|
return result;
|
|
}
|
|
std::vector<double> Abs_Axis_Private::create_sub_tick_vector(int sub_tick_count, const std::vector<double>& ticks, const Range& range) {
|
|
std::vector<double> result;
|
|
if (sub_tick_count <= 0 || ticks.size() < 2)
|
|
return result;
|
|
result.reserve((ticks.size() - 1) * sub_tick_count);
|
|
for (int i = 1; i < ticks.size(); ++i) {
|
|
double sub_tick_step = (ticks.at(i) - ticks.at(i - 1)) / double(sub_tick_count + 1);
|
|
for (int k = 1; k <= sub_tick_count; ++k)
|
|
result.push_back(ticks.at(i - 1) + k * sub_tick_step);
|
|
}
|
|
while (!result.empty() && !range.contains(result.back())) {
|
|
result.pop_back();
|
|
}
|
|
return result;
|
|
}
|
|
int Abs_Axis_Private::get_sub_tick_count(double tick_step) {
|
|
int result = 1;
|
|
double epsilon = 0.01;
|
|
double int_partf;
|
|
int int_part;
|
|
double frac_part = modf(get_mantissa(tick_step), &int_partf);
|
|
int_part = int(int_partf);
|
|
if (frac_part < epsilon || 1.0 - frac_part < epsilon) {
|
|
if (1.0 - frac_part < epsilon)
|
|
++int_part;
|
|
switch (int_part) {
|
|
case 1:
|
|
result = 4;
|
|
break;
|
|
case 2:
|
|
result = 3;
|
|
break;
|
|
case 3:
|
|
result = 2;
|
|
break;
|
|
case 4:
|
|
result = 3;
|
|
break;
|
|
case 5:
|
|
result = 4;
|
|
break;
|
|
case 6:
|
|
result = 2;
|
|
break;
|
|
case 7:
|
|
result = 6;
|
|
break;
|
|
case 8:
|
|
result = 3;
|
|
break;
|
|
case 9:
|
|
result = 2;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (std::abs(frac_part - 0.5) < epsilon) {
|
|
switch (int_part) {
|
|
case 1:
|
|
result = 2;
|
|
break;
|
|
case 2:
|
|
result = 4;
|
|
break;
|
|
case 3:
|
|
result = 4;
|
|
break;
|
|
case 4:
|
|
result = 2;
|
|
break;
|
|
case 5:
|
|
result = 4;
|
|
break;
|
|
case 6:
|
|
result = 4;
|
|
break;
|
|
case 7:
|
|
result = 2;
|
|
break;
|
|
case 8:
|
|
result = 4;
|
|
break;
|
|
case 9:
|
|
result = 4;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
double Abs_Axis_Private::get_mantissa(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 = get_mantissa(input, &magnitude);
|
|
return pick_closest(mantissa, {1.0, 2.0, 2.5, 5.0, 10.0}) * magnitude;
|
|
}
|
|
double Abs_Axis_Private::pick_closest(double target, const std::vector<double>& candidates) {
|
|
if (candidates.empty())
|
|
return target;
|
|
if (candidates.size() == 1)
|
|
return candidates.front();
|
|
std::vector<double>::const_iterator it = std::lower_bound(candidates.cbegin(), candidates.cend(), target);
|
|
if (it == candidates.cend())
|
|
return *(it - 1);
|
|
else if (it == candidates.cbegin())
|
|
return *it;
|
|
else
|
|
return target - *(it - 1) < *it - target ? *(it - 1) : *it;
|
|
}
|
|
std::string Abs_Axis_Private::get_tick_label(double tick, Abs_Axis_Render_State* state) {
|
|
return format_number(tick, Number_Format{.precision = state->label_precision});
|
|
}
|
|
double Abs_Axis::start_coord() {
|
|
return d()->edit_axis_state()->coord_start;
|
|
}
|
|
double Abs_Axis::end_coord() {
|
|
auto render_state = d()->edit_axis_state();
|
|
return render_state->coord_start + render_state->coord_length;
|
|
}
|
|
Range Abs_Axis::coord_range() {
|
|
auto render_state = d()->edit_axis_state();
|
|
return {render_state->coord_start, render_state->coord_start + render_state->coord_length};
|
|
}
|
|
int Abs_Axis::pixel_sample_count(Range range) {
|
|
auto render_state = d()->edit_axis_state();
|
|
double length = coord_range().length();
|
|
if (length == 0.0)
|
|
return 0;
|
|
return static_cast<int>(std::abs(range.length() / length) * render_state->pixel_length);
|
|
}
|
|
int Abs_Axis::pixel_sample_count() {
|
|
auto render_state = d()->edit_axis_state();
|
|
return static_cast<int>(render_state->pixel_length);
|
|
}
|
|
double Abs_Axis::tick_step(const Range& range) {
|
|
return d()->get_tick_step(range);
|
|
}
|
|
int Abs_Axis::sub_tick_count(double tick_step) {
|
|
return d()->get_sub_tick_count(tick_step);
|
|
}
|
|
std::string Abs_Axis::tick_label(double tick) {
|
|
return d()->edit_tick_label(tick);
|
|
}
|
|
std::vector<std::string> Abs_Axis::create_label_vector(const std::vector<double>& ticks) {
|
|
return d()->edit_label_vector(ticks);
|
|
}
|
|
std::vector<double> Abs_Axis::create_tick_vector(double tick_step, const Range& range) {
|
|
return d()->create_tick_vector(tick_step, range);
|
|
}
|
|
std::vector<double> Abs_Axis::create_sub_tick_vector(int sub_tick_count, const std::vector<double>& ticks, const Range& range) {
|
|
return d()->create_sub_tick_vector(sub_tick_count, ticks, range);
|
|
}
|
|
} // namespace renderive
|