From d8632dc74c8526f9f280455cc6924575c95ec8e9 Mon Sep 17 00:00:00 2001 From: Orange Date: Mon, 16 Mar 2026 03:03:23 +0300 Subject: [PATCH] added progress ring --- examples/example_hud/gui/main_window.cpp | 18 ++- examples/example_hud/gui/main_window.hpp | 6 + include/omath/hud/entity_overlay.hpp | 17 +++ include/omath/hud/entity_overlay_widgets.hpp | 13 +- include/omath/hud/hud_renderer_interface.hpp | 10 ++ .../renderer_realizations/imgui_renderer.hpp | 6 + source/hud/entity_overlay.cpp | 113 +++++++++++++++++- .../renderer_realizations/imgui_renderer.cpp | 19 +++ 8 files changed, 196 insertions(+), 6 deletions(-) diff --git a/examples/example_hud/gui/main_window.cpp b/examples/example_hud/gui/main_window.cpp index 05c022f..c0da3c3 100644 --- a/examples/example_hud/gui/main_window.cpp +++ b/examples/example_hud/gui/main_window.cpp @@ -138,6 +138,17 @@ namespace imgui_desktop::gui ImGui::SliderFloat("Thick##skel", &m_skel_thickness, 0.5f, 5.f); } + if (ImGui::CollapsingHeader("Progress Ring")) + { + ImGui::Checkbox("Show##ring", &m_show_ring); + ImGui::ColorEdit4("Color##ring", reinterpret_cast(&m_ring_color), ImGuiColorEditFlags_NoInputs); + ImGui::ColorEdit4("BG##ring", reinterpret_cast(&m_ring_bg), ImGuiColorEditFlags_NoInputs); + ImGui::SliderFloat("Radius##ring", &m_ring_radius, 4.f, 30.f); + ImGui::SliderFloat("Value##ring", &m_ring_ratio, 0.f, 1.f); + ImGui::SliderFloat("Thick##ring", &m_ring_thickness, 0.5f, 6.f); + ImGui::SliderFloat("Offset##ring", &m_ring_offset, 0.f, 15.f); + } + if (ImGui::CollapsingHeader("Snap Line")) { ImGui::Checkbox("Show##snap", &m_show_snap); @@ -166,7 +177,6 @@ namespace imgui_desktop::gui when(m_show_cornered_box, CorneredBox{omath::Color::from_rgba(255, 0, 255, 255), m_box_fill, m_corner_ratio, m_box_thickness}), when(m_show_dashed_box, DashedBox{m_dash_color, m_dash_len, m_dash_gap, m_dash_thickness}), - RightSide{ when(m_show_right_bar, bar), when(m_show_right_dashed_bar, dbar), @@ -176,6 +186,12 @@ namespace imgui_desktop::gui Label{{1.f, 0.f, 0.f, 1.f}, m_label_offset, m_outlined, "Shield: 125/125"}), when(m_show_right_labels, Label{{1.f, 0.f, 1.f, 1.f}, m_label_offset, m_outlined, "*LOCKED*"}), + + Spacer{10}, + when(m_show_ring, ProgressRing{m_ring_color, m_ring_bg, m_ring_radius, m_ring_ratio, + m_ring_thickness, m_ring_offset}), + when(m_show_ring, ProgressRing{m_ring_color, m_ring_bg, m_ring_radius, m_ring_ratio, + m_ring_thickness, m_ring_offset}), }, LeftSide{ when(m_show_left_bar, bar), diff --git a/examples/example_hud/gui/main_window.hpp b/examples/example_hud/gui/main_window.hpp index 6572bdf..d7eb540 100644 --- a/examples/example_hud/gui/main_window.hpp +++ b/examples/example_hud/gui/main_window.hpp @@ -61,6 +61,12 @@ namespace imgui_desktop::gui float m_skel_thickness = 1.f; bool m_show_skeleton = false; + // Progress ring + omath::Color m_ring_color = omath::Color::from_rgba(0, 200, 255, 255); + omath::Color m_ring_bg{0.3f, 0.3f, 0.3f, 0.5f}; + float m_ring_radius = 10.f, m_ring_ratio = 0.65f, m_ring_thickness = 2.5f, m_ring_offset = 5.f; + bool m_show_ring = false; + // Snap line omath::Color m_snap_color = omath::Color::from_rgba(255, 50, 50, 255); float m_snap_width = 1.5f; diff --git a/include/omath/hud/entity_overlay.hpp b/include/omath/hud/entity_overlay.hpp index 2bfb873..ac8acef 100644 --- a/include/omath/hud/entity_overlay.hpp +++ b/include/omath/hud/entity_overlay.hpp @@ -118,6 +118,22 @@ namespace omath::hud std::string_view{std::vformat(fmt.get(), std::make_format_args(args...))}); } + // ── Spacers ───────────────────────────────────────────────────── + EntityOverlay& add_right_spacer(float size); + EntityOverlay& add_left_spacer(float size); + EntityOverlay& add_top_spacer(float size); + EntityOverlay& add_bottom_spacer(float size); + + // ── Progress rings ────────────────────────────────────────────── + EntityOverlay& add_right_progress_ring(const Color& color, const Color& bg, float radius, float ratio, + float thickness = 2.f, float offset = 5.f, int segments = 0); + EntityOverlay& add_left_progress_ring(const Color& color, const Color& bg, float radius, float ratio, + float thickness = 2.f, float offset = 5.f, int segments = 0); + EntityOverlay& add_top_progress_ring(const Color& color, const Color& bg, float radius, float ratio, + float thickness = 2.f, float offset = 5.f, int segments = 0); + EntityOverlay& add_bottom_progress_ring(const Color& color, const Color& bg, float radius, float ratio, + float thickness = 2.f, float offset = 5.f, int segments = 0); + // ── Misc ───────────────────────────────────────────────────────── EntityOverlay& add_snap_line(const Vector2& start_pos, const Color& color, float width); @@ -151,6 +167,7 @@ namespace omath::hud void dispatch(const widget::BottomSide& bottom_side); void dispatch(const widget::Skeleton& skeleton); void dispatch(const widget::SnapLine& snap_line); + void draw_progress_ring(const Vector2& center, const widget::ProgressRing& ring); void draw_outlined_text(const Vector2& position, const Color& color, const std::string_view& text); void draw_dashed_line(const Vector2& from, const Vector2& to, const Color& color, float dash_len, float gap_len, float thickness) const; diff --git a/include/omath/hud/entity_overlay_widgets.hpp b/include/omath/hud/entity_overlay_widgets.hpp index 248100e..eaf2fc4 100644 --- a/include/omath/hud/entity_overlay_widgets.hpp +++ b/include/omath/hud/entity_overlay_widgets.hpp @@ -105,11 +105,22 @@ namespace omath::hud::widget float size; }; + struct ProgressRing + { + Color color; + Color bg{0.3f, 0.3f, 0.3f, 0.5f}; + float radius = 12.f; + float ratio; + float thickness = 2.f; + float offset = 5.f; + int segments = 32; + }; + // ── Side widget variant ─────────────────────────────────────────────────── struct None { }; ///< No-op placeholder — used by widget::when for disabled elements. - using SideWidget = std::variant, Spacer>; + using SideWidget = std::variant, Spacer, ProgressRing>; // ── Side containers ─────────────────────────────────────────────────────── // Storing std::initializer_list is safe here: the backing array diff --git a/include/omath/hud/hud_renderer_interface.hpp b/include/omath/hud/hud_renderer_interface.hpp index 8d62532..e22b9b9 100644 --- a/include/omath/hud/hud_renderer_interface.hpp +++ b/include/omath/hud/hud_renderer_interface.hpp @@ -24,6 +24,16 @@ namespace omath::hud virtual void add_filled_rectangle(const Vector2& min, const Vector2& max, const Color& color) = 0; + virtual void add_circle(const Vector2& center, float radius, const Color& color, float thickness, + int segments = 0) = 0; + + virtual void add_filled_circle(const Vector2& center, float radius, const Color& color, + int segments = 0) = 0; + + /// Draw an arc (partial circle outline). Angles in radians, 0 = right (+X), counter-clockwise. + virtual void add_arc(const Vector2& center, float radius, float a_min, float a_max, const Color& color, + float thickness, int segments = 0) = 0; + virtual void add_text(const Vector2& position, const Color& color, const std::string_view& text) = 0; [[nodiscard]] diff --git a/include/omath/hud/renderer_realizations/imgui_renderer.hpp b/include/omath/hud/renderer_realizations/imgui_renderer.hpp index 5645de2..bc1442c 100644 --- a/include/omath/hud/renderer_realizations/imgui_renderer.hpp +++ b/include/omath/hud/renderer_realizations/imgui_renderer.hpp @@ -17,6 +17,12 @@ namespace omath::hud void add_filled_polyline(const std::span>& vertexes, const Color& color) override; void add_rectangle(const Vector2& min, const Vector2& max, const Color& color) override; void add_filled_rectangle(const Vector2& min, const Vector2& max, const Color& color) override; + void add_circle(const Vector2& center, float radius, const Color& color, float thickness, + int segments = 0) override; + void add_filled_circle(const Vector2& center, float radius, const Color& color, + int segments = 0) override; + void add_arc(const Vector2& center, float radius, float a_min, float a_max, const Color& color, + float thickness, int segments = 0) override; void add_text(const Vector2& position, const Color& color, const std::string_view& text) override; [[nodiscard]] virtual Vector2 calc_text_size(const std::string_view& text) override; diff --git a/source/hud/entity_overlay.cpp b/source/hud/entity_overlay.cpp index ff60295..a397707 100644 --- a/source/hud/entity_overlay.cpp +++ b/source/hud/entity_overlay.cpp @@ -457,6 +457,76 @@ namespace omath::hud m_text_cursor_left(m_canvas.top_left_corner), m_renderer(renderer) { } + // ── Spacers ───────────────────────────────────────────────────────────────── + EntityOverlay& EntityOverlay::add_right_spacer(const float size) + { + m_text_cursor_right.x += size; + return *this; + } + + EntityOverlay& EntityOverlay::add_left_spacer(const float size) + { + m_text_cursor_left.x -= size; + return *this; + } + + EntityOverlay& EntityOverlay::add_top_spacer(const float size) + { + m_text_cursor_top.y -= size; + return *this; + } + + EntityOverlay& EntityOverlay::add_bottom_spacer(const float size) + { + m_text_cursor_bottom.y += size; + return *this; + } + + // ── Progress rings ────────────────────────────────────────────────────────── + EntityOverlay& EntityOverlay::add_right_progress_ring(const Color& color, const Color& bg, const float radius, + const float ratio, const float thickness, const float offset, + const int segments) + { + const auto cx = m_text_cursor_right.x + offset + radius; + const auto cy = m_text_cursor_right.y + radius; + draw_progress_ring({cx, cy}, widget::ProgressRing{color, bg, radius, ratio, thickness, offset, segments}); + m_text_cursor_right.y += radius * 2.f; + return *this; + } + + EntityOverlay& EntityOverlay::add_left_progress_ring(const Color& color, const Color& bg, const float radius, + const float ratio, const float thickness, const float offset, + const int segments) + { + const auto cx = m_text_cursor_left.x - offset - radius; + const auto cy = m_text_cursor_left.y + radius; + draw_progress_ring({cx, cy}, widget::ProgressRing{color, bg, radius, ratio, thickness, offset, segments}); + m_text_cursor_left.y += radius * 2.f; + return *this; + } + + EntityOverlay& EntityOverlay::add_top_progress_ring(const Color& color, const Color& bg, const float radius, + const float ratio, const float thickness, const float offset, + const int segments) + { + m_text_cursor_top.y -= radius * 2.f; + const auto cx = m_text_cursor_top.x + radius; + const auto cy = m_text_cursor_top.y - offset + radius; + draw_progress_ring({cx, cy}, widget::ProgressRing{color, bg, radius, ratio, thickness, offset, segments}); + return *this; + } + + EntityOverlay& EntityOverlay::add_bottom_progress_ring(const Color& color, const Color& bg, const float radius, + const float ratio, const float thickness, const float offset, + const int segments) + { + const auto cx = m_text_cursor_bottom.x + radius; + const auto cy = m_text_cursor_bottom.y + offset + radius; + draw_progress_ring({cx, cy}, widget::ProgressRing{color, bg, radius, ratio, thickness, offset, segments}); + m_text_cursor_bottom.y += radius * 2.f; + return *this; + } + // ── widget dispatch ─────────────────────────────────────────────────────── void EntityOverlay::dispatch(const widget::Box& box) { @@ -483,6 +553,21 @@ namespace omath::hud add_snap_line(snap_line.start, snap_line.color, snap_line.width); } + void EntityOverlay::draw_progress_ring(const Vector2& center, const widget::ProgressRing& ring) + { + constexpr auto pi = std::numbers::pi_v; + const float ratio = std::clamp(ring.ratio, 0.f, 1.f); + + m_renderer->add_circle(center, ring.radius, ring.bg, ring.thickness, ring.segments); + + if (ratio > 0.f) + { + const float a_min = -pi / 2.f; + const float a_max = a_min + ratio * 2.f * pi; + m_renderer->add_arc(center, ring.radius, a_min, a_max, ring.color, ring.thickness, ring.segments); + } + } + // ── Side container dispatch ─────────────────────────────────────────────── void EntityOverlay::dispatch(const widget::RightSide& s) { @@ -511,7 +596,12 @@ namespace omath::hud }, [this](const widget::Spacer& w) { - m_text_cursor_right.x += w.size; + add_right_spacer(w.size); + }, + [this](const widget::ProgressRing& w) + { + add_right_progress_ring(w.color, w.bg, w.radius, w.ratio, w.thickness, w.offset, + w.segments); }, }, child); @@ -544,7 +634,12 @@ namespace omath::hud }, [this](const widget::Spacer& w) { - m_text_cursor_left.x -= w.size; + add_left_spacer(w.size); + }, + [this](const widget::ProgressRing& w) + { + add_left_progress_ring(w.color, w.bg, w.radius, w.ratio, w.thickness, w.offset, + w.segments); }, }, child); @@ -577,7 +672,12 @@ namespace omath::hud }, [this](const widget::Spacer& w) { - m_text_cursor_top.y -= w.size; + add_top_spacer(w.size); + }, + [this](const widget::ProgressRing& w) + { + add_top_progress_ring(w.color, w.bg, w.radius, w.ratio, w.thickness, w.offset, + w.segments); }, }, child); @@ -610,7 +710,12 @@ namespace omath::hud }, [this](const widget::Spacer& w) { - m_text_cursor_bottom.y += w.size; + add_bottom_spacer(w.size); + }, + [this](const widget::ProgressRing& w) + { + add_bottom_progress_ring(w.color, w.bg, w.radius, w.ratio, w.thickness, w.offset, + w.segments); }, }, child); diff --git a/source/hud/renderer_realizations/imgui_renderer.cpp b/source/hud/renderer_realizations/imgui_renderer.cpp index 5d397d4..d7d0c91 100644 --- a/source/hud/renderer_realizations/imgui_renderer.cpp +++ b/source/hud/renderer_realizations/imgui_renderer.cpp @@ -42,6 +42,25 @@ namespace omath::hud ImGui::GetBackgroundDrawList()->AddRectFilled(min.to_im_vec2(), max.to_im_vec2(), color.to_im_color()); } + void ImguiHudRenderer::add_circle(const Vector2& center, const float radius, const Color& color, + const float thickness, const int segments) + { + ImGui::GetBackgroundDrawList()->AddCircle(center.to_im_vec2(), radius, color.to_im_color(), segments, thickness); + } + + void ImguiHudRenderer::add_filled_circle(const Vector2& center, const float radius, const Color& color, + const int segments) + { + ImGui::GetBackgroundDrawList()->AddCircleFilled(center.to_im_vec2(), radius, color.to_im_color(), segments); + } + + void ImguiHudRenderer::add_arc(const Vector2& center, const float radius, const float a_min, const float a_max, + const Color& color, const float thickness, const int segments) + { + ImGui::GetBackgroundDrawList()->PathArcTo(center.to_im_vec2(), radius, a_min, a_max, segments); + ImGui::GetBackgroundDrawList()->PathStroke(color.to_im_color(), ImDrawFlags_None, thickness); + } + void ImguiHudRenderer::add_text(const Vector2& position, const Color& color, const std::string_view& text) { ImGui::GetBackgroundDrawList()->AddText(position.to_im_vec2(), color.to_im_color(), text.data(),