Ver Fonte

Improve on automatic circle segment count calculation. (#3808) Amends

ocornut há 4 anos atrás
pai
commit
fb15d8c858
5 ficheiros alterados com 41 adições e 48 exclusões
  1. 8 0
      docs/CHANGELOG.txt
  2. 2 1
      imgui.cpp
  3. 18 20
      imgui_demo.cpp
  4. 3 8
      imgui_draw.cpp
  5. 10 19
      imgui_internal.h

+ 8 - 0
docs/CHANGELOG.txt

@@ -35,8 +35,16 @@ HOW TO UPDATE?
  VERSION 1.82 WIP (In Progresss)
  VERSION 1.82 WIP (In Progresss)
 -----------------------------------------------------------------------
 -----------------------------------------------------------------------
 
 
+Breaking Changes:
+
+ - Style: renamed rarely used style.CircleSegmentMaxError (old default = 1.60f)
+   to style.CircleTessellationMaxError (new default = 0.30f) as its meaning changed. (#3808) [@thedmd]
+
 Other Changes:
 Other Changes:
 
 
+- ImDrawList: AddCircle, AddCircleFilled(): Tweaked default segment count calculation to honor MaxError
+  with more accuracy. Made default segment count always even for better looking result. (#3808) [@thedmd]
+- ImDrawList: AddCircle, AddCircleFilled(): New default for style.
 - CI: Use a dedicated "scheduled" workflow to trigger scheduled builds. Forks may disable this workflow if 
 - CI: Use a dedicated "scheduled" workflow to trigger scheduled builds. Forks may disable this workflow if 
   scheduled builds builds are not required. [@rokups]
   scheduled builds builds are not required. [@rokups]
 
 

+ 2 - 1
imgui.cpp

@@ -375,6 +375,7 @@ CODE
  When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
  When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
  You can read releases logs https://github.com/ocornut/imgui/releases for more details.
 
 
+ - 2021/02/17 (1.82) - renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as the meaning of the value changed.
  - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete).
  - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete).
                      - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete).
                      - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete).
                      - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete).
                      - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete).
@@ -986,7 +987,7 @@ ImGuiStyle::ImGuiStyle()
     AntiAliasedLinesUseTex  = true;             // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering.
     AntiAliasedLinesUseTex  = true;             // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering.
     AntiAliasedFill         = true;             // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
     AntiAliasedFill         = true;             // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
     CurveTessellationTol    = 1.25f;            // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
     CurveTessellationTol    = 1.25f;            // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
-    CircleTessellationMaxError = 0.25f;         // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
+    CircleTessellationMaxError = 0.30f;         // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
 
 
     // Default theme
     // Default theme
     ImGui::StyleColorsDark(this);
     ImGui::StyleColorsDark(this);

+ 18 - 20
imgui_demo.cpp

@@ -6034,41 +6034,39 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
             if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f;
             if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f;
 
 
             // When editing the "Circle Segment Max Error" value, draw a preview of its effect on auto-tessellated circles.
             // When editing the "Circle Segment Max Error" value, draw a preview of its effect on auto-tessellated circles.
-            ImGui::DragFloat("Circle Tessellation Max Error", &style.CircleTessellationMaxError , 0.005f, 0.10f, 10.0f, "%.2f", ImGuiSliderFlags_AlwaysClamp);
+            ImGui::DragFloat("Circle Tessellation Max Error", &style.CircleTessellationMaxError , 0.005f, 0.10f, 5.0f, "%.2f", ImGuiSliderFlags_AlwaysClamp);
             if (ImGui::IsItemActive())
             if (ImGui::IsItemActive())
             {
             {
                 ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos());
                 ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos());
                 ImGui::BeginTooltip();
                 ImGui::BeginTooltip();
-                ImGui::TextUnformatted("N - number of segments");
-                ImGui::TextUnformatted("R - radius");
+                ImGui::TextUnformatted("(R = radius, N = number of segments)");
                 ImGui::Spacing();
                 ImGui::Spacing();
                 ImDrawList* draw_list = ImGui::GetWindowDrawList();
                 ImDrawList* draw_list = ImGui::GetWindowDrawList();
-                const float min_widget_width = ImGui::CalcTextSize("N: MM\nR: MM.MM").x;
-                float RAD_MIN = 5.0f, RAD_MAX = 80.0f;
-                for (int n = 0; n < 9; n++)
+                const float min_widget_width = ImGui::CalcTextSize("N: MMM\nR: MMM").x;
+                for (int n = 0; n < 8; n++)
                 {
                 {
-                    const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (9.0f - 1.0f);
-
-                    const int segment_count = draw_list->_CalcCircleAutoSegmentCount(rad);
+                    const float RAD_MIN = 5.0f;
+                    const float RAD_MAX = 70.0f;
+                    const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (8.0f - 1.0f);
 
 
                     ImGui::BeginGroup();
                     ImGui::BeginGroup();
-                    ImGui::Text("R: %.f", rad);
-                    ImGui::Text("N: %d", segment_count);
 
 
-                    const float circle_diameter = rad * 2.0f;
-                    const float canvas_width    = IM_MAX(min_widget_width, circle_diameter);
-                    const float offset_x        = floorf(canvas_width * 0.5f);
-                    const float offset_y        = floorf(RAD_MAX);
-                    const ImVec2 p              = ImGui::GetCursorScreenPos();
-                    draw_list->AddCircle(ImVec2(p.x + offset_x, p.y + offset_y), rad, ImGui::GetColorU32(ImGuiCol_Text));
+                    ImGui::Text("R: %.f\nN: %d", rad, draw_list->_CalcCircleAutoSegmentCount(rad));
+
+                    const float canvas_width = IM_MAX(min_widget_width, rad * 2.0f);
+                    const float offset_x     = floorf(canvas_width * 0.5f);
+                    const float offset_y     = floorf(RAD_MAX);
 
 
+                    const ImVec2 p1 = ImGui::GetCursorScreenPos();
+                    draw_list->AddCircle(ImVec2(p1.x + offset_x, p1.y + offset_y), rad, ImGui::GetColorU32(ImGuiCol_Text));
                     ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2));
                     ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2));
-                    ImGui::Text("N: %d", segment_count);
 
 
-                    const ImVec2 p2             = ImGui::GetCursorScreenPos();
+                    /*
+                    const ImVec2 p2 = ImGui::GetCursorScreenPos();
                     draw_list->AddCircleFilled(ImVec2(p2.x + offset_x, p2.y + offset_y), rad, ImGui::GetColorU32(ImGuiCol_Text));
                     draw_list->AddCircleFilled(ImVec2(p2.x + offset_x, p2.y + offset_y), rad, ImGui::GetColorU32(ImGuiCol_Text));
-
                     ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2));
                     ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2));
+                    */
+
                     ImGui::EndGroup();
                     ImGui::EndGroup();
                     ImGui::SameLine();
                     ImGui::SameLine();
                 }
                 }

+ 3 - 8
imgui_draw.cpp

@@ -544,17 +544,12 @@ void ImDrawList::_OnChangedVtxOffset()
 
 
 int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const
 int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const
 {
 {
-    int num_segments = 0;
-
-    const int  radius_idx = (int)ImCeil(radius); // Use ceil to never reduce accuracy
-
     // Automatic segment count
     // Automatic segment count
+    const int radius_idx = (int)(radius + 0.999f); // ceil to never reduce accuracy
     if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts))
     if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts))
-        num_segments = _Data->CircleSegmentCounts[radius_idx]; // Use cached value
+        return _Data->CircleSegmentCounts[radius_idx]; // Use cached value
     else
     else
-        num_segments = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError);
-
-    return num_segments;
+        return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError);
 }
 }
 
 
 // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)
 // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)

+ 10 - 19
imgui_internal.h

@@ -617,29 +617,20 @@ struct IMGUI_API ImChunkStream
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 
 
 // ImDrawList: Helper function to calculate a circle's segment count given its radius and a "maximum error" value.
 // ImDrawList: Helper function to calculate a circle's segment count given its radius and a "maximum error" value.
-//
-// Estimation of number of circle segment based on error is derived using method described in
-// this post (https://stackoverflow.com/a/2244088/15194693).
+// Estimation of number of circle segment based on error is derived using method described in https://stackoverflow.com/a/2244088/15194693
 // Number of segments (N) is calculated using equation:
 // Number of segments (N) is calculated using equation:
-//
-//            +-                     -+
-//            |           pi          |
-//   N = ceil | --------------------- |     where r > 0, error <= r
-//            |  acos(1 - error / r)  |
-//            +-                     -+
-//
-// Note:
-//     Equation is significantly simpler that one in the post thanks for choosing segment
-//     that is perpendicular to X axis. Follow steps in the article from this starting condition
-//     and you will get this result.
+//   N = ceil ( pi / acos(1 - error / r) )     where r > 0, error <= r
+// Our equation is significantly simpler that one in the post thanks for choosing segment that is
+// perpendicular to X axis. Follow steps in the article from this starting condition and you will
+// will get this result.
 //
 //
 // Rendering circles with an odd number of segments, while mathematically correct will produce
 // Rendering circles with an odd number of segments, while mathematically correct will produce
-// asymmetrical results on the raster grid. Therefore we're rounding N to next even number.
-// (7 became 8, 11 became 12, but 8 will still be 8).
+// asymmetrical results on the raster grid. Therefore we're rounding N to next even number (7->8, 8->8, 9->10 etc.)
 //
 //
+#define IM_ROUNDUP_TO_EVEN(_V)                                  ((((_V) + 1) / 2) * 2)
 #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN                     4
 #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN                     4
 #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX                     512
 #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX                     512
-#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(_RAD,_MAXERROR)    ImClamp((((int)ImCeil(IM_PI / ImAcos(1 - ImMin((_MAXERROR), (_RAD)) / (_RAD))) + 1) / 2) * 2, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX)
+#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(_RAD,_MAXERROR)    ImClamp(IM_ROUNDUP_TO_EVEN((int)ImCeil(IM_PI / ImAcos(1 - ImMin((_MAXERROR), (_RAD)) / (_RAD)))), IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX)
 
 
 // ImDrawList: You may set this to higher values (e.g. 2 or 3) to increase tessellation of fast rounded corners path.
 // ImDrawList: You may set this to higher values (e.g. 2 or 3) to increase tessellation of fast rounded corners path.
 #ifndef IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER
 #ifndef IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER
@@ -660,8 +651,8 @@ struct IMGUI_API ImDrawListSharedData
 
 
     // [Internal] Lookup tables
     // [Internal] Lookup tables
     ImVec2          ArcFastVtx[12 * IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER];  // FIXME: Bake rounded corners fill/borders in atlas
     ImVec2          ArcFastVtx[12 * IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER];  // FIXME: Bake rounded corners fill/borders in atlas
-    ImU8            CircleSegmentCounts[64];                // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead)
-    const ImVec4*   TexUvLines;                             // UV of anti-aliased lines in the atlas
+    ImU8            CircleSegmentCounts[64];    // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead)
+    const ImVec4*   TexUvLines;                 // UV of anti-aliased lines in the atlas
 
 
     ImDrawListSharedData();
     ImDrawListSharedData();
     void SetCircleTessellationMaxError(float max_error);
     void SetCircleTessellationMaxError(float max_error);