#pragma once #include "../renderable/Renderable.h" #include #include #include #include #include #include namespace renderive { struct Axis_Transform { Range coordinate_range; double pixel_origin{}; double pixel_length{}; [[nodiscard]] double coord_to_pixel(double coordinate) const noexcept { const double span = coordinate_range.length(); if (span == 0.0) return pixel_origin; return pixel_origin + (coordinate - coordinate_range.origin) / span * pixel_length; } [[nodiscard]] double pixel_to_coord(double pixel) const noexcept { if (pixel_length == 0.0) return coordinate_range.origin; return coordinate_range.origin + (pixel - pixel_origin) / pixel_length * coordinate_range.length(); } }; class LIB_DECL Abs_Axis : public Renderable { public: explicit Abs_Axis(Plot_Core& plot, Orientation orientation = Orientation::Horizontal); ~Abs_Axis() override; [[nodiscard]] int x() const; void set_x(int value); [[nodiscard]] int y() const; void set_y(int value); [[nodiscard]] Orientation orientation() const; void set_orientation(Orientation value); [[nodiscard]] std::size_t pixel_length() const; void set_pixel_length(std::size_t value); [[nodiscard]] int tick_length() const; void set_tick_length(int value); [[nodiscard]] int sub_tick_length() const; void set_sub_tick_length(int value); [[nodiscard]] Color color() const; void set_color(Color value); [[nodiscard]] Number_Locale locale() const; void set_locale(Number_Locale value); [[nodiscard]] std::string unit_text() const; void set_unit_text(std::string value); [[nodiscard]] Font unit_text_font() const; void set_unit_text_font(Font value); [[nodiscard]] Pen unit_text_pen() const; void set_unit_text_pen(Pen value); [[nodiscard]] Brush unit_text_background_brush() const; void set_unit_text_background_brush(Brush value); [[nodiscard]] int label_rotation_degrees() const; void set_label_rotation_degrees(int value); [[nodiscard]] virtual Range coord_range() const = 0; [[nodiscard]] virtual double pixel_to_coord(double pixel) const; [[nodiscard]] virtual double coord_to_pixel(double coordinate) const; [[nodiscard]] Axis_Transform transform() const; [[nodiscard]] double start_coord() const; [[nodiscard]] double end_coord() const; [[nodiscard]] int pixel_sample_count(Range range) const; [[nodiscard]] int pixel_sample_count() const; [[nodiscard]] virtual double tick_step(Range range) const; [[nodiscard]] virtual int sub_tick_count(double major_step) const; [[nodiscard]] virtual std::string tick_label(double tick) const; protected: struct State { int x{}; int y{}; Orientation orientation = Orientation::Horizontal; std::size_t pixel_length{}; int tick_length = 10; int sub_tick_length = 5; Color color = Color::white(); Number_Locale locale; std::string unit_text; Font unit_text_font; Pen unit_text_pen{Color::white()}; Brush unit_text_background_brush{Color::black(), Brush_Style::Solid}; int label_rotation_degrees{}; }; [[nodiscard]] State axis_state() const; void paint(detail::Painter& painter) override; private: mutable std::mutex axis_mutex_; State axis_state_; }; template class Axis_Builder_Base { public: Derived& set_x(int value) { x_ = value; return derived(); } Derived& set_y(int value) { y_ = value; return derived(); } Derived& set_orientation(Orientation value) { orientation_ = value; return derived(); } Derived& set_pixel_length(std::size_t value) { pixel_length_ = value; return derived(); } Derived& set_tick_length(int value) { tick_length_ = value; return derived(); } Derived& set_sub_tick_length(int value) { sub_tick_length_ = value; return derived(); } Derived& set_color(Color value) { color_ = value; return derived(); } Derived& set_unit_text(std::string value) { unit_text_ = std::move(value); return derived(); } Derived& set_unit_text_font(Font value) { unit_text_font_ = value; return derived(); } Derived& set_unit_text_pen(Pen value) { unit_text_pen_ = value; return derived(); } Derived& set_unit_text_background_brush(Brush value) { unit_background_ = value; return derived(); } Derived& set_label_rotation_degrees(int value) { label_rotation_ = value; return derived(); } protected: explicit Axis_Builder_Base(std::shared_ptr parent, Orientation orientation) : parent_(std::move(parent)), orientation_(orientation) {} void apply(Abs_Axis& axis) const { axis.set_x(x_); axis.set_y(y_); axis.set_orientation(orientation_); axis.set_pixel_length(pixel_length_); axis.set_tick_length(tick_length_); axis.set_sub_tick_length(sub_tick_length_); axis.set_color(color_); axis.set_unit_text(unit_text_); axis.set_unit_text_font(unit_text_font_); axis.set_unit_text_pen(unit_text_pen_); axis.set_unit_text_background_brush(unit_background_); axis.set_label_rotation_degrees(label_rotation_); } [[nodiscard]] Derived& derived() { return static_cast(*this); } std::shared_ptr parent_; int x_{}; int y_{}; Orientation orientation_; std::size_t pixel_length_{}; int tick_length_ = 10; int sub_tick_length_ = 5; Color color_ = Color::white(); std::string unit_text_; Font unit_text_font_; Pen unit_text_pen_{Color::white()}; Brush unit_background_{Color::black(), Brush_Style::Solid}; int label_rotation_{}; }; class LIB_DECL Axis : public Abs_Axis, public Event_Handler { public: explicit Axis(Plot_Core& plot, Orientation orientation = Orientation::Horizontal); [[nodiscard]] Range coord_range() const override; [[nodiscard]] int label_precision() const; void set_label_precision(int value); [[nodiscard]] double coord_start() const; void set_coord_start(double value); [[nodiscard]] double coord_length() const; void set_coord_length(double value); void set_coord_range(Range range); void set_use_wheel(bool value); void set_use_drag(bool value); [[nodiscard]] bool use_wheel() const; [[nodiscard]] bool use_drag() const; void handle_event(const Event& event) override; [[nodiscard]] std::string tick_label(double tick) const override; class Builder : public Axis_Builder_Base { public: Builder(std::shared_ptr parent, Orientation orientation); Builder& set_coord_range(Range value) { coordinates_ = value; return *this; } Builder& set_label_precision(int value) { precision_ = value; return *this; } Builder& set_use_wheel(bool value) { wheel_ = value; return *this; } Builder& set_use_drag(bool value) { drag_ = value; return *this; } std::shared_ptr build(); private: Range coordinates_{0.0, 20.0}; int precision_ = 2; bool wheel_{}; bool drag_{}; }; private: mutable std::mutex interaction_mutex_; Range coordinates_{0.0, 20.0}; int precision_ = 2; bool wheel_enabled_{}; bool drag_enabled_{}; bool dragging_{}; PointF last_pointer_{}; }; class LIB_DECL Frequency_Axis : public Axis { public: explicit Frequency_Axis(Plot_Core& plot, Orientation orientation = Orientation::Horizontal); [[nodiscard]] std::string tick_label(double tick) const override; class Builder : public Axis_Builder_Base { public: Builder(std::shared_ptr parent, Orientation orientation); Builder& set_coord_range(Range value) { coordinates_ = value; return *this; } Builder& set_label_precision(int value) { precision_ = value; return *this; } Builder& set_use_wheel(bool value) { wheel_ = value; return *this; } Builder& set_use_drag(bool value) { drag_ = value; return *this; } std::shared_ptr build(); private: Range coordinates_{0.0, 20.0}; int precision_ = 2; bool wheel_{}; bool drag_{}; }; }; class LIB_DECL Time_Axis : public Abs_Axis { public: explicit Time_Axis(Plot_Core& plot, Orientation orientation = Orientation::Horizontal); [[nodiscard]] int visible_time_point_count() const; void set_visible_time_point_count(int value); [[nodiscard]] int tick_label_spacing_px() const; void set_tick_label_spacing_px(int value); [[nodiscard]] std::string time_format() const; void set_time_format(std::string value); [[nodiscard]] Font font() const; void set_font(Font value); [[nodiscard]] bool newest_at_axis_start() const; void set_newest_at_axis_start(bool value); [[nodiscard]] std::size_t time_point_count() const; int append_time(Time_Of_Day time); [[nodiscard]] Time_Of_Day tick_to_time(int tick) const; [[nodiscard]] Range coord_range() const override; [[nodiscard]] double tick_step(Range range) const override; [[nodiscard]] std::string tick_label(double tick) const override; class Builder : public Axis_Builder_Base { public: Builder(std::shared_ptr parent, Orientation orientation); Builder& set_visible_time_point_count(int value) { visible_count_ = value; return *this; } Builder& set_tick_label_spacing_px(int value) { spacing_ = value; return *this; } Builder& set_time_format(std::string value) { format_ = std::move(value); return *this; } Builder& set_font(Font value) { font_ = value; return *this; } Builder& set_newest_at_axis_start(bool value) { newest_at_start_ = value; return *this; } std::shared_ptr build(); private: int visible_count_ = 100; int spacing_ = 8; std::string format_ = "mm:ss.zzz"; Font font_; bool newest_at_start_{}; }; private: struct Time_State { int visible_count = 100; int tick_label_spacing_px = 8; std::string format = "mm:ss.zzz"; bool newest_at_start{}; int next_tick{}; std::deque> samples; }; mutable std::mutex time_mutex_; Time_State time_state_; }; } // namespace renderive