Files
Renderive/Core/primitive/Grid.cpp
T
2026-08-02 18:58:35 +08:00

205 lines
9.1 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& snapshot, const Renderable_Frame_View& frame_view) override {
Grid_Render_State* s = render_state(frame_view);
if (!s)
return;
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(), snapshot);
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