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

137 lines
5.8 KiB
C++

#include "Marker.h"
#include <algorithm>
#include "../Axis/Abs_Axis_p.h"
#include "../base/Memory.h"
#include "../render/Canvas.h"
namespace renderive {
struct Marker_Render_State : Render_State, Marker_Prop {};
struct Marker_Input_Data : Latest_Input_Data {
std::pmr::vector<Marker_Item> items{memory_resource(Memory_Domain::Other)};
void push(std::span<const Marker_Item> value) {
items.assign(value.begin(), value.end());
mark_latest_pushed();
}
void push(std::pmr::vector<Marker_Item>&& value) {
items = std::move(value);
mark_latest_pushed();
}
void clear() override {
items.clear();
clear_latest();
}
};
struct Marker_Private : Typed_Render_Data<Marker, Marker_Render_State, Marker_Input_Data> {
std::pmr::vector<Marker_Item> prepared_items{memory_resource(Memory_Domain::Other)};
bool use_prepared_items = false;
void draw_item(Canvas& canvas, const Marker_Item& item, const Axis_Mapping_2D& mapping) {
PointF point = mapping.map(item.position);
PointF domain_start = mapping.map(item.position.x, mapping.value.coord_range.origin);
PointF domain_end = mapping.map(item.position.x, mapping.value.coord_range.target);
PointF value_start = mapping.map(mapping.domain.coord_range.origin, item.position.y);
PointF value_end = mapping.map(mapping.domain.coord_range.target, item.position.y);
canvas.set_pen(item.pen);
canvas.set_brush(item.brush);
if (item.mode == Marker_Mode::Point) {
canvas.draw_ellipse(point, item.radius, item.radius);
}
else if (item.mode == Marker_Mode::Domain_Line) {
canvas.draw_line(domain_start, domain_end);
}
else if (item.mode == Marker_Mode::Value_Line) {
canvas.draw_line(value_start, value_end);
}
else {
canvas.draw_line(domain_start, domain_end);
canvas.draw_line(value_start, value_end);
}
}
void prepare_data(const Render_Frame_Snapshot&, const Renderable_Frame_View& frame_view) override {
Marker_Input_Data* input = render_input_data(frame_view);
if (!input)
return;
if (!input->pending)
return;
prepared_items.swap(input->items);
use_prepared_items = true;
frame_view.consume_input();
}
void draw(Canvas& canvas, const Render_Frame_Snapshot& snapshot, const Renderable_Frame_View& frame_view) override {
Marker_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);
canvas.save();
if (use_prepared_items) {
for (const Marker_Item& item : prepared_items)
draw_item(canvas, item, mapping);
}
else {
Marker_Item item;
item.position = s->position;
item.radius = s->radius;
item.pen = s->pen;
item.brush = s->brush;
item.mode = s->mode;
draw_item(canvas, item, mapping);
}
canvas.restore();
}
};
RENDERIVE_DEFINE_RENDERABLE_BINDING(Marker, Marker_Private, Marker_Render_State, Marker_Input_Data, "Marker")
void Marker::update_items(std::span<const Marker_Item> items) {
if (!ok())
return;
Render_Input_Lease<Marker_Private, Marker_Input_Data> input(d());
input->push(items);
}
void Marker::update_items(std::pmr::vector<Marker_Item>&& items) {
if (!ok())
return;
Render_Input_Lease<Marker_Private, Marker_Input_Data> input(d());
input->push(std::move(items));
}
RENDERIVE_DEFINE_WEAK_STATE_PROP(Marker, Marker_Render_State, Abs_Axis, domain_axis)
RENDERIVE_DEFINE_WEAK_STATE_PROP(Marker, Marker_Render_State, Abs_Axis, value_axis)
RENDERIVE_DEFINE_STATE_PROP(Marker, Marker_Render_State, PointF, position)
double Marker::radius() {
return d()->edit_state_value(&Marker_Render_State::radius);
}
void Marker::set_radius(double value) {
d()->set_state_value(&Marker_Render_State::radius, std::max(0.0, value));
}
RENDERIVE_DEFINE_STATE_PROP(Marker, Marker_Render_State, Pen, pen)
RENDERIVE_DEFINE_STATE_PROP(Marker, Marker_Render_State, Brush, brush)
RENDERIVE_DEFINE_STATE_PROP(Marker, Marker_Render_State, Marker_Mode, mode)
Marker::Builder::Builder(std::shared_ptr<Renderable> parent, std::shared_ptr<Abs_Axis> domain_axis, std::shared_ptr<Abs_Axis> value_axis) {
ASSERT(parent, "Marker build error! parent == nullptr");
ASSERT(domain_axis && value_axis, "Marker build error! axis == nullptr");
ASSERT(domain_axis->attached_to_plot(), "Marker build error! domain_axis->plot == nullptr");
ASSERT(domain_axis->shares_plot_with(*value_axis), "Marker build error! domain_axis->plot != value_axis->plot");
ASSERT(parent->shares_plot_with(*domain_axis), "Marker build error! parent->plot != axis->plot");
ASSERT(domain_axis->orientation() != value_axis->orientation(), "Marker build error! axes must be orthogonal");
this->parent = parent;
this->domain_axis = domain_axis;
this->value_axis = value_axis;
}
std::shared_ptr<Marker> Marker::Builder::build() {
auto ret = renderive::make_shared<Marker>();
ret->init(parent);
Marker_Private* pd = ret->d();
Render_Edit_Lease<Marker_Private, Marker_Render_State> edit(pd);
Marker_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(PointF, position)
PROP_R(double, radius)
PROP_R(Pen, pen)
PROP_R(Brush, brush)
PROP_R(Marker_Mode, mode)
sc->radius = std::max(0.0, sc->radius);
return ret;
}
} // namespace renderive