#include "Label.h" #include "../Axis/Abs_Axis_p.h" #include "../base/Memory.h" #include "../render/Canvas.h" namespace renderive { struct Label_Render_State : Render_State, Label_Prop {}; struct Label_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 Label_Private : Typed_Render_Data { std::pmr::vector prepared_items{memory_resource(Memory_Domain::Other)}; bool use_prepared_items = false; void prepare_data(const Plot_Render_Snapshot&) override { sync_state_pipeline(); Label_Input_Data* input = render_input_data(); if (!input->pending) return; prepared_items.swap(input->items); use_prepared_items = true; clear_render_input(); } void draw_item(Canvas& canvas, const Label_Item& item, const Axis_Mapping_2D& mapping) { if (item.text.empty()) return; canvas.save(); canvas.set_font(item.font); Text_Metrics tm = canvas.measure_text(item.text); double text_w = tm.width; double text_h = tm.height; PointF anchor = mapping.map(item.position); PointF top_left{anchor.x + item.offset.x, anchor.y + item.offset.y}; if ((item.alignment & Text_Align::Horizontal_Center) == Text_Align::Horizontal_Center) top_left.x -= text_w / 2.0; else if ((item.alignment & Text_Align::Right) == Text_Align::Right) top_left.x -= text_w; if ((item.alignment & Text_Align::Vertical_Center) == Text_Align::Vertical_Center) top_left.y -= text_h / 2.0; else if ((item.alignment & Text_Align::Bottom) == Text_Align::Bottom) top_left.y -= text_h; RectF rect{top_left.x, top_left.y, text_w, text_h}; if (item.background_brush.style != Brush_Style::None || item.border_pen.style != Line_Style::None) { canvas.set_pen(item.border_pen); canvas.set_brush(item.background_brush); canvas.draw_rect(rect); } canvas.set_pen(item.text_pen); canvas.draw_text(rect, Text_Align::Left | Text_Align::Vertical_Center, item.text); canvas.restore(); } void draw(Canvas& canvas, const Plot_Render_Snapshot&) override { Label_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()); if (use_prepared_items) { for (const Label_Item& item : prepared_items) draw_item(canvas, item, mapping); } else { Label_Item item; item.position = s->position; item.offset = s->offset; item.text = s->text; item.alignment = s->alignment; item.font = s->font; item.text_pen = s->text_pen; item.background_brush = s->background_brush; item.border_pen = s->border_pen; draw_item(canvas, item, mapping); } } }; RENDERIVE_DEFINE_RENDERABLE_BINDING(Label, Label_Private, Label_Render_State, Label_Input_Data, "Label") void Label::update_items(std::span items) { if (!ok()) return; Render_Input_Lease input(d()); input->push(items); } void Label::update_items(std::pmr::vector&& items) { if (!ok()) return; Render_Input_Lease input(d()); input->push(std::move(items)); } RENDERIVE_DEFINE_WEAK_STATE_PROP(Label, Label_Render_State, Abs_Axis, domain_axis) RENDERIVE_DEFINE_WEAK_STATE_PROP(Label, Label_Render_State, Abs_Axis, value_axis) RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, PointF, position) RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, PointF, offset) RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, std::string, text) RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, Text_Align, alignment) RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, Font, font) RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, Pen, text_pen) RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, Brush, background_brush) RENDERIVE_DEFINE_STATE_PROP(Label, Label_Render_State, Pen, border_pen) Label::Builder::Builder(std::shared_ptr parent, std::shared_ptr domain_axis, std::shared_ptr value_axis) { ASSERT(parent, "Label build error! parent == nullptr"); ASSERT(domain_axis && value_axis, "Label build error! axis == nullptr"); ASSERT(domain_axis->attached_to_plot(), "Label build error! domain_axis->plot == nullptr"); ASSERT(domain_axis->shares_plot_with(*value_axis), "Label build error! domain_axis->plot != value_axis->plot"); ASSERT(parent->shares_plot_with(*domain_axis), "Label build error! parent->plot != axis->plot"); ASSERT(domain_axis->orientation() != value_axis->orientation(), "Label build error! axes must be orthogonal"); this->parent = parent; this->domain_axis = domain_axis; this->value_axis = value_axis; } std::shared_ptr