#include "Marker.h" #include #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 items{memory_resource(Memory_Domain::Other)}; void push(std::span value) { items.assign(value.begin(), value.end()); mark_latest_pushed(); } void push(std::pmr::vector&& value) { items = std::move(value); mark_latest_pushed(); } void clear() override { items.clear(); clear_latest(); } }; struct Marker_Private : Typed_Render_Data { std::pmr::vector 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&) override { sync_state_pipeline(); Marker_Input_Data* input = render_input_data(); if (!input->pending) return; prepared_items.swap(input->items); use_prepared_items = true; clear_render_input(); } void draw(Canvas& canvas, const Render_Frame_Snapshot&) override { Marker_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()); 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 items) { if (!ok()) return; Render_Input_Lease input(d()); input->push(items); } void Marker::update_items(std::pmr::vector&& items) { if (!ok()) return; Render_Input_Lease 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 parent, std::shared_ptr domain_axis, std::shared_ptr 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::Builder::build() { auto ret = renderive::make_shared(); ret->init(parent); Marker_Private* pd = ret->d(); Render_Edit_Lease edit(pd); Marker_Render_State* sc = edit.operator->(); PROP_R(std::weak_ptr, domain_axis) PROP_R(std::weak_ptr, 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