#include "Grid.h" #include #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 { std::vector create_division_ticks(Range range, int divisions) { std::vector ticks; if (range.length() == 0.0) return ticks; divisions = std::max(1, divisions); ticks.reserve(divisions + 1); double step = range.length() / static_cast(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 ticks = axis->create_tick_vector(step, axis_range); if (sub_grid && sub_pen.style != Line_Style::None) { canvas.set_pen(sub_pen); std::vector 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 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& 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 Grid::domain_axis() { return d()->edit_state_value(&Grid_Render_State::domain_axis).lock(); } void Grid::set_domain_axis(std::shared_ptr value) { d()->set_state_value(&Grid_Render_State::domain_axis, std::weak_ptr(value)); } std::shared_ptr Grid::value_axis() { return d()->edit_state_value(&Grid_Render_State::value_axis).lock(); } void Grid::set_value_axis(std::shared_ptr value) { d()->set_state_value(&Grid_Render_State::value_axis, std::weak_ptr(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 parent, std::shared_ptr domain_axis, std::shared_ptr 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::Builder::build() { auto ret = renderive::make_shared(); ret->init(parent); Grid_Private* pd = ret->d(); Render_Edit_Lease edit(pd); Grid_Render_State* sc = edit.operator->(); PROP_R(std::weak_ptr, domain_axis) PROP_R(std::weak_ptr, 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