From 8e6ed19abf37067b922b11bbbf7cc52714325aba Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 15 Mar 2026 18:39:40 +0300 Subject: [PATCH] added dashed bar --- examples/example_hud/gui/main_window.cpp | 23 ++++- examples/example_hud/gui/main_window.hpp | 7 +- include/omath/hud/entity_overlay.hpp | 16 +++ source/hud/entity_overlay.cpp | 121 +++++++++++++++++++++++ 4 files changed, 161 insertions(+), 6 deletions(-) diff --git a/examples/example_hud/gui/main_window.cpp b/examples/example_hud/gui/main_window.cpp index 0cafd8e..5534f09 100644 --- a/examples/example_hud/gui/main_window.cpp +++ b/examples/example_hud/gui/main_window.cpp @@ -97,10 +97,16 @@ namespace imgui_desktop::gui ImGui::SliderFloat("Width##bar", &m_bar_width, 1.f, 20.f); ImGui::SliderFloat("Value##bar", &m_bar_value, 0.f, 1.f); ImGui::SliderFloat("Offset##bar", &m_bar_offset, 1.f, 20.f); - ImGui::Checkbox("Right##bar", &m_show_right_bar); ImGui::SameLine(); - ImGui::Checkbox("Left##bar", &m_show_left_bar); - ImGui::Checkbox("Top##bar", &m_show_top_bar); ImGui::SameLine(); - ImGui::Checkbox("Bottom##bar", &m_show_bottom_bar); + ImGui::Checkbox("Right##bar", &m_show_right_bar); ImGui::SameLine(); + ImGui::Checkbox("Left##bar", &m_show_left_bar); + ImGui::Checkbox("Top##bar", &m_show_top_bar); ImGui::SameLine(); + ImGui::Checkbox("Bottom##bar", &m_show_bottom_bar); + ImGui::Checkbox("Right dashed##bar", &m_show_right_dashed_bar); ImGui::SameLine(); + ImGui::Checkbox("Left dashed##bar", &m_show_left_dashed_bar); + ImGui::Checkbox("Top dashed##bar", &m_show_top_dashed_bar); ImGui::SameLine(); + ImGui::Checkbox("Bot dashed##bar", &m_show_bottom_dashed_bar); + ImGui::SliderFloat("Dash len##bar", &m_bar_dash_len, 2.f, 20.f); + ImGui::SliderFloat("Dash gap##bar", &m_bar_dash_gap, 1.f, 15.f); } if (ImGui::CollapsingHeader("Labels", ImGuiTreeNodeFlags_DefaultOpen)) @@ -171,6 +177,15 @@ namespace imgui_desktop::gui ent.add_top_bar(m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, m_bar_value, m_bar_offset); if (m_show_bottom_bar) ent.add_bottom_bar(m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, m_bar_value, m_bar_offset); + + if (m_show_right_dashed_bar) + ent.add_right_dashed_bar(m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, m_bar_value, m_bar_dash_len, m_bar_dash_gap, m_bar_offset); + if (m_show_left_dashed_bar) + ent.add_left_dashed_bar(m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, m_bar_value, m_bar_dash_len, m_bar_dash_gap, m_bar_offset); + if (m_show_top_dashed_bar) + ent.add_top_dashed_bar(m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, m_bar_value, m_bar_dash_len, m_bar_dash_gap, m_bar_offset); + if (m_show_bottom_dashed_bar) + ent.add_bottom_dashed_bar(m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width, m_bar_value, m_bar_dash_len, m_bar_dash_gap, m_bar_offset); } void MainWindow::draw_labels(omath::hud::EntityOverlay& ent) const diff --git a/examples/example_hud/gui/main_window.hpp b/examples/example_hud/gui/main_window.hpp index dc4c819..9a6f567 100644 --- a/examples/example_hud/gui/main_window.hpp +++ b/examples/example_hud/gui/main_window.hpp @@ -46,8 +46,11 @@ namespace imgui_desktop::gui omath::Color m_bar_bg_color{0.f, 0.f, 0.f, 0.5f}; omath::Color m_bar_outline_color{0.f, 0.f, 0.f, 1.f}; float m_bar_width = 4.f, m_bar_value = 0.75f, m_bar_offset = 5.f; - bool m_show_right_bar = true, m_show_left_bar = true; - bool m_show_top_bar = true, m_show_bottom_bar = true; + bool m_show_right_bar = true, m_show_left_bar = true; + bool m_show_top_bar = true, m_show_bottom_bar = true; + bool m_show_right_dashed_bar = false, m_show_left_dashed_bar = false; + bool m_show_top_dashed_bar = false, m_show_bottom_dashed_bar = false; + float m_bar_dash_len = 6.f, m_bar_dash_gap = 4.f; // Labels float m_label_offset = 3.f; diff --git a/include/omath/hud/entity_overlay.hpp b/include/omath/hud/entity_overlay.hpp index 9b13818..6e18a80 100644 --- a/include/omath/hud/entity_overlay.hpp +++ b/include/omath/hud/entity_overlay.hpp @@ -60,6 +60,18 @@ namespace omath::hud void add_skeleton(const Color& color, float thickness = 1.f) const; + void add_right_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color, + float width, float ratio, float dash_len, float gap_len, float offset = 5.f); + + void add_left_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color, + float width, float ratio, float dash_len, float gap_len, float offset = 5.f); + + void add_top_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color, + float height, float ratio, float dash_len, float gap_len, float offset = 5.f); + + void add_bottom_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color, + float height, float ratio, float dash_len, float gap_len, float offset = 5.f); + void add_bottom_bar(const Color& color, const Color& outline_color, const Color& bg_color, float height, float ratio, float offset = 5.f); @@ -108,6 +120,10 @@ namespace omath::hud 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; + void draw_dashed_fill(const Vector2& origin, const Vector2& step_dir, + const Vector2& perp_dir, float full_len, float filled_len, + const Color& fill_color, const Color& split_color, + float dash_len, float gap_len) const; CanvasBox m_canvas; Vector2 m_text_cursor_right; Vector2 m_text_cursor_top; diff --git a/source/hud/entity_overlay.cpp b/source/hud/entity_overlay.cpp index de50f20..7807bd2 100644 --- a/source/hud/entity_overlay.cpp +++ b/source/hud/entity_overlay.cpp @@ -122,6 +122,127 @@ namespace omath::hud + Vector2{m_canvas.bottom_right_corner.x - m_canvas.bottom_left_corner.x, 0.f} / 2; m_renderer->add_line(start_pos, line_end, color, width); } + void EntityOverlay::draw_dashed_fill(const Vector2& origin, const Vector2& step_dir, + const Vector2& perp_dir, const float full_len, + const float filled_len, const Color& fill_color, + const Color& split_color, const float dash_len, + const float gap_len) const + { + if (full_len <= 0.f) + return; + + const float step = dash_len + gap_len; + const float n = std::floor((full_len + gap_len) / step); + if (n < 1.f) + return; + + const float used = n * dash_len + (n - 1.f) * gap_len; + const float offset = (full_len - used) / 2.f; + + const auto fill_rect = [&](const Vector2& a, const Vector2& b, const Color& c) + { + m_renderer->add_filled_rectangle( + {std::min(a.x, b.x), std::min(a.y, b.y)}, + {std::max(a.x, b.x), std::max(a.y, b.y)}, c); + }; + + // Draw split lines (gaps) across the full bar first + // Leading gap + if (offset > 0.f) + fill_rect(origin, origin + step_dir * offset + perp_dir, split_color); + + for (float i = 0.f; i < n; ++i) + { + const float dash_start = offset + i * step; + const float dash_end = dash_start + dash_len; + const float gap_start = dash_end; + const float gap_end = dash_start + step; + + // Fill dash only up to filled_len + if (dash_start < filled_len) + { + const auto a = origin + step_dir * dash_start; + const auto b = a + step_dir * std::min(dash_len, filled_len - dash_start) + perp_dir; + fill_rect(a, b, fill_color); + } + + // Split line (gap) — always drawn across full bar + if (i < n - 1.f && gap_start < full_len) + { + const auto a = origin + step_dir * gap_start; + const auto b = origin + step_dir * std::min(gap_end, full_len) + perp_dir; + fill_rect(a, b, split_color); + } + } + + // Trailing gap + const float trail_start = offset + n * dash_len + (n - 1.f) * gap_len; + if (trail_start < full_len) + fill_rect(origin + step_dir * trail_start, origin + step_dir * full_len + perp_dir, split_color); + } + + void EntityOverlay::add_right_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color, + const float width, float ratio, const float dash_len, + const float gap_len, const float offset) + { + ratio = std::clamp(ratio, 0.f, 1.f); + const float height = std::abs(m_canvas.top_right_corner.y - m_canvas.bottom_right_corner.y); + const auto bar_start = m_canvas.bottom_right_corner + Vector2{offset, 0.f}; + + m_renderer->add_filled_rectangle(bar_start, bar_start + Vector2{width, -height}, bg_color); + draw_dashed_fill(bar_start, {0.f, -1.f}, {width, 0.f}, height, height * ratio, + color, outline_color, dash_len, gap_len); + m_renderer->add_rectangle(bar_start - Vector2{1.f, 0.f}, + bar_start + Vector2{width, -height}, outline_color); + m_text_cursor_right.x += offset + width; + } + + void EntityOverlay::add_left_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color, + const float width, float ratio, const float dash_len, + const float gap_len, const float offset) + { + ratio = std::clamp(ratio, 0.f, 1.f); + const float height = std::abs(m_canvas.top_left_corner.y - m_canvas.bottom_left_corner.y); + const auto bar_start = m_canvas.bottom_left_corner + Vector2{-(offset + width), 0.f}; + + m_renderer->add_filled_rectangle(bar_start, bar_start + Vector2{width, -height}, bg_color); + draw_dashed_fill(bar_start, {0.f, -1.f}, {width, 0.f}, height, height * ratio, + color, outline_color, dash_len, gap_len); + m_renderer->add_rectangle(bar_start - Vector2{1.f, 0.f}, + bar_start + Vector2{width, -height}, outline_color); + m_text_cursor_left.x -= offset + width; + } + + void EntityOverlay::add_top_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color, + const float height, float ratio, const float dash_len, + const float gap_len, const float offset) + { + ratio = std::clamp(ratio, 0.f, 1.f); + const float bar_w = std::abs(m_canvas.top_left_corner.x - m_canvas.top_right_corner.x); + const auto bar_start = m_canvas.top_left_corner - Vector2{0.f, offset}; + + m_renderer->add_filled_rectangle(bar_start, bar_start + Vector2{bar_w, -height}, bg_color); + draw_dashed_fill(bar_start, {1.f, 0.f}, {0.f, -height}, bar_w, bar_w * ratio, + color, outline_color, dash_len, gap_len); + m_renderer->add_rectangle(bar_start, bar_start + Vector2{bar_w, -height}, outline_color); + m_text_cursor_top.y -= offset + height; + } + + void EntityOverlay::add_bottom_dashed_bar(const Color& color, const Color& outline_color, const Color& bg_color, + const float height, float ratio, const float dash_len, + const float gap_len, const float offset) + { + ratio = std::clamp(ratio, 0.f, 1.f); + const float bar_w = std::abs(m_canvas.bottom_left_corner.x - m_canvas.bottom_right_corner.x); + const auto bar_start = m_canvas.bottom_left_corner + Vector2{0.f, offset}; + + m_renderer->add_filled_rectangle(bar_start, bar_start + Vector2{bar_w, height}, bg_color); + draw_dashed_fill(bar_start, {1.f, 0.f}, {0.f, height}, bar_w, bar_w * ratio, + color, outline_color, dash_len, gap_len); + m_renderer->add_rectangle(bar_start, bar_start + Vector2{bar_w, height}, outline_color); + m_text_cursor_bottom.y += offset + height; + } + void EntityOverlay::add_skeleton(const Color& color, const float thickness) const { // Maps normalized (rx in [0,1], ry in [0,1]) to canvas screen position