203 lines
9.0 KiB
C++
203 lines
9.0 KiB
C++
#include "Grid.h"
|
|
#include <algorithm>
|
|
#include "../Axis/Abs_Axis_p.h"
|
|
#include "../base/Memory.h"
|
|
#include "../render/Canvas.h"
|
|
namespace renderive {
|
|
struct Grid_Render_State : Render_State, Grid_Prop {};
|
|
struct Grid_Private : Typed_Render_Data<Grid, Grid_Render_State, Input_Data> {
|
|
std::vector<double> create_division_ticks(Range range, int divisions) {
|
|
std::vector<double> ticks;
|
|
if (range.length() == 0.0)
|
|
return ticks;
|
|
divisions = std::max(1, divisions);
|
|
ticks.reserve(divisions + 1);
|
|
double step = range.length() / static_cast<double>(divisions);
|
|
for (int i = 0; i <= divisions; ++i)
|
|
ticks.push_back(range.origin + step * i);
|
|
return ticks;
|
|
}
|
|
void draw_axis_grid(Canvas& canvas, Abs_Axis* axis, const Axis_Frame_Snapshot& axis_frame, const Axis_Frame_Snapshot& cross_frame, const Range& axis_range, const Range& cross_range, const Pen& pen, const Pen& sub_pen, bool sub_grid) {
|
|
if (pen.style == Line_Style::None && (!sub_grid || sub_pen.style == Line_Style::None))
|
|
return;
|
|
double cross0 = cross_frame.coord_to_pixel(cross_range.origin);
|
|
double cross1 = cross_frame.coord_to_pixel(cross_range.target);
|
|
double step = axis->tick_step(axis_range);
|
|
std::vector<double> ticks = axis->create_tick_vector(step, axis_range);
|
|
if (sub_grid && sub_pen.style != Line_Style::None) {
|
|
canvas.set_pen(sub_pen);
|
|
std::vector<double> sub_ticks = axis->create_sub_tick_vector(axis->sub_tick_count(step), ticks, axis_range);
|
|
draw_ticks(canvas, axis_frame, cross0, cross1, sub_ticks);
|
|
}
|
|
if (pen.style != Line_Style::None) {
|
|
canvas.set_pen(pen);
|
|
draw_ticks(canvas, axis_frame, cross0, cross1, ticks);
|
|
}
|
|
}
|
|
void draw_division_grid(Canvas& canvas, const Axis_Frame_Snapshot& axis_frame, const Axis_Frame_Snapshot& cross_frame, const Range& axis_range, const Range& cross_range, const Pen& pen, int divisions) {
|
|
if (pen.style == Line_Style::None)
|
|
return;
|
|
double cross0 = cross_frame.coord_to_pixel(cross_range.origin);
|
|
double cross1 = cross_frame.coord_to_pixel(cross_range.target);
|
|
std::vector<double> ticks = create_division_ticks(axis_range, divisions);
|
|
canvas.set_pen(pen);
|
|
draw_ticks(canvas, axis_frame, cross0, cross1, ticks);
|
|
}
|
|
void draw_ticks(Canvas& canvas, const Axis_Frame_Snapshot& axis, double cross0, double cross1, const std::vector<double>& ticks) {
|
|
bool vertical = axis.orientation == Orientation::Horizontal;
|
|
for (double tick : ticks) {
|
|
double pixel = axis.coord_to_pixel(tick);
|
|
if (vertical)
|
|
canvas.draw_line(PointF{pixel, cross0}, PointF{pixel, cross1});
|
|
else
|
|
canvas.draw_line(PointF{cross0, pixel}, PointF{cross1, pixel});
|
|
}
|
|
}
|
|
void draw(Canvas& canvas, const Render_Frame_Snapshot&) override {
|
|
Grid_Render_State* s = render_state();
|
|
auto domain_axis = s->domain_axis.lock();
|
|
auto value_axis = s->value_axis.lock();
|
|
if (!domain_axis || !value_axis)
|
|
return;
|
|
Axis_Mapping_2D mapping = Axis_Render_Access::mapping(domain_axis.get(), value_axis.get());
|
|
Range domain_range = mapping.domain.coord_range;
|
|
Range value_range = mapping.value.coord_range;
|
|
if (s->domain_range.length() != 0.0)
|
|
domain_range = s->domain_range;
|
|
if (s->value_range.length() != 0.0)
|
|
value_range = s->value_range;
|
|
canvas.save();
|
|
if (s->line_mode == Grid_Line_Mode::Range_Division) {
|
|
if (s->domain_grid)
|
|
draw_division_grid(canvas, mapping.domain, mapping.value, domain_range, value_range, s->domain_pen, s->domain_divisions);
|
|
if (s->value_grid)
|
|
draw_division_grid(canvas, mapping.value, mapping.domain, value_range, domain_range, s->value_pen, s->value_divisions);
|
|
}
|
|
else {
|
|
if (s->domain_grid)
|
|
draw_axis_grid(canvas, domain_axis.get(), mapping.domain, mapping.value, domain_range, value_range, s->domain_pen, s->sub_pen, s->sub_grid);
|
|
if (s->value_grid)
|
|
draw_axis_grid(canvas, value_axis.get(), mapping.value, mapping.domain, value_range, domain_range, s->value_pen, s->sub_pen, s->sub_grid);
|
|
}
|
|
canvas.restore();
|
|
}
|
|
};
|
|
RENDERIVE_DEFINE_RENDERABLE_BINDING(Grid, Grid_Private, Grid_Render_State, Input_Data, "Grid")
|
|
std::shared_ptr<Abs_Axis> Grid::domain_axis() {
|
|
return d()->edit_state_value(&Grid_Render_State::domain_axis).lock();
|
|
}
|
|
void Grid::set_domain_axis(std::shared_ptr<Abs_Axis> value) {
|
|
d()->set_state_value(&Grid_Render_State::domain_axis, std::weak_ptr<Abs_Axis>(value));
|
|
}
|
|
std::shared_ptr<Abs_Axis> Grid::value_axis() {
|
|
return d()->edit_state_value(&Grid_Render_State::value_axis).lock();
|
|
}
|
|
void Grid::set_value_axis(std::shared_ptr<Abs_Axis> value) {
|
|
d()->set_state_value(&Grid_Render_State::value_axis, std::weak_ptr<Abs_Axis>(value));
|
|
}
|
|
bool Grid::domain_grid() {
|
|
return d()->edit_state_value(&Grid_Render_State::domain_grid);
|
|
}
|
|
void Grid::set_domain_grid(bool value) {
|
|
d()->set_state_value(&Grid_Render_State::domain_grid, value);
|
|
}
|
|
bool Grid::value_grid() {
|
|
return d()->edit_state_value(&Grid_Render_State::value_grid);
|
|
}
|
|
void Grid::set_value_grid(bool value) {
|
|
d()->set_state_value(&Grid_Render_State::value_grid, value);
|
|
}
|
|
bool Grid::sub_grid() {
|
|
return d()->edit_state_value(&Grid_Render_State::sub_grid);
|
|
}
|
|
void Grid::set_sub_grid(bool value) {
|
|
d()->set_state_value(&Grid_Render_State::sub_grid, value);
|
|
}
|
|
Grid_Line_Mode Grid::line_mode() {
|
|
return d()->edit_state_value(&Grid_Render_State::line_mode);
|
|
}
|
|
void Grid::set_line_mode(Grid_Line_Mode value) {
|
|
d()->set_state_value(&Grid_Render_State::line_mode, value);
|
|
}
|
|
Range Grid::domain_range() {
|
|
return d()->edit_state_value(&Grid_Render_State::domain_range);
|
|
}
|
|
void Grid::set_domain_range(Range value) {
|
|
if (value.length() == 0.0)
|
|
return;
|
|
d()->set_state_value(&Grid_Render_State::domain_range, value);
|
|
}
|
|
Range Grid::value_range() {
|
|
return d()->edit_state_value(&Grid_Render_State::value_range);
|
|
}
|
|
void Grid::set_value_range(Range value) {
|
|
if (value.length() == 0.0)
|
|
return;
|
|
d()->set_state_value(&Grid_Render_State::value_range, value);
|
|
}
|
|
int Grid::domain_divisions() {
|
|
return d()->edit_state_value(&Grid_Render_State::domain_divisions);
|
|
}
|
|
void Grid::set_domain_divisions(int value) {
|
|
d()->set_state_value(&Grid_Render_State::domain_divisions, std::max(1, value));
|
|
}
|
|
int Grid::value_divisions() {
|
|
return d()->edit_state_value(&Grid_Render_State::value_divisions);
|
|
}
|
|
void Grid::set_value_divisions(int value) {
|
|
d()->set_state_value(&Grid_Render_State::value_divisions, std::max(1, value));
|
|
}
|
|
Pen Grid::domain_pen() {
|
|
return d()->edit_state_value(&Grid_Render_State::domain_pen);
|
|
}
|
|
void Grid::set_domain_pen(Pen value) {
|
|
d()->set_state_value(&Grid_Render_State::domain_pen, std::move(value));
|
|
}
|
|
Pen Grid::value_pen() {
|
|
return d()->edit_state_value(&Grid_Render_State::value_pen);
|
|
}
|
|
void Grid::set_value_pen(Pen value) {
|
|
d()->set_state_value(&Grid_Render_State::value_pen, std::move(value));
|
|
}
|
|
Pen Grid::sub_pen() {
|
|
return d()->edit_state_value(&Grid_Render_State::sub_pen);
|
|
}
|
|
void Grid::set_sub_pen(Pen value) {
|
|
d()->set_state_value(&Grid_Render_State::sub_pen, std::move(value));
|
|
}
|
|
Grid::Builder::Builder(std::shared_ptr<Renderable> parent, std::shared_ptr<Abs_Axis> domain_axis, std::shared_ptr<Abs_Axis> value_axis) {
|
|
ASSERT(parent, "Grid build error! parent == nullptr");
|
|
ASSERT(domain_axis && value_axis, "Grid build error! axis == nullptr");
|
|
ASSERT(domain_axis->attached_to_plot(), "Grid build error! domain_axis->plot == nullptr");
|
|
ASSERT(domain_axis->shares_plot_with(*value_axis), "Grid build error! domain_axis->plot != value_axis->plot");
|
|
ASSERT(parent->shares_plot_with(*domain_axis), "Grid build error! parent->plot != axis->plot");
|
|
ASSERT(domain_axis->orientation() != value_axis->orientation(), "Grid build error! axes must be orthogonal");
|
|
this->parent = parent;
|
|
this->domain_axis = domain_axis;
|
|
this->value_axis = value_axis;
|
|
}
|
|
std::shared_ptr<Grid> Grid::Builder::build() {
|
|
auto ret = renderive::make_shared<Grid>();
|
|
ret->init(parent);
|
|
Grid_Private* pd = ret->d();
|
|
Render_Edit_Lease<Grid_Private, Grid_Render_State> edit(pd);
|
|
Grid_Render_State* sc = edit.operator->();
|
|
PROP_R(std::weak_ptr<Abs_Axis>, domain_axis)
|
|
PROP_R(std::weak_ptr<Abs_Axis>, value_axis)
|
|
PROP_R(bool, domain_grid)
|
|
PROP_R(bool, value_grid)
|
|
PROP_R(bool, sub_grid)
|
|
PROP_R(Grid_Line_Mode, line_mode)
|
|
PROP_R(Range, domain_range)
|
|
PROP_R(Range, value_range)
|
|
PROP_R(int, domain_divisions)
|
|
PROP_R(int, value_divisions)
|
|
PROP_R(Pen, domain_pen)
|
|
PROP_R(Pen, value_pen)
|
|
PROP_R(Pen, sub_pen)
|
|
sc->domain_divisions = std::max(1, sc->domain_divisions);
|
|
sc->value_divisions = std::max(1, sc->value_divisions);
|
|
return ret;
|
|
}
|
|
} // namespace renderive
|