mirror of
				https://github.com/Drezil/imgui.git
				synced 2025-10-31 05:01:05 +01:00 
			
		
		
		
	Improve on automatic circle segment count calculation. (#3808)
This commit is contained in:
		| @@ -986,7 +986,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. | ||||||
|     CircleSegmentMaxError   = 1.60f;            // 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.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. | ||||||
|  |  | ||||||
|     // Default theme |     // Default theme | ||||||
|     ImGui::StyleColorsDark(this); |     ImGui::StyleColorsDark(this); | ||||||
| @@ -3878,7 +3878,7 @@ void ImGui::NewFrame() | |||||||
|         virtual_space.Add(g.Viewports[n]->GetMainRect()); |         virtual_space.Add(g.Viewports[n]->GetMainRect()); | ||||||
|     g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4(); |     g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4(); | ||||||
|     g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; |     g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; | ||||||
|     g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError); |     g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError); | ||||||
|     g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; |     g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; | ||||||
|     if (g.Style.AntiAliasedLines) |     if (g.Style.AntiAliasedLines) | ||||||
|         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; |         g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines; | ||||||
| @@ -7056,7 +7056,7 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() | |||||||
|     IM_ASSERT(g.IO.Fonts->Fonts.Size > 0                                && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?"); |     IM_ASSERT(g.IO.Fonts->Fonts.Size > 0                                && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?"); | ||||||
|     IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()                          && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?"); |     IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()                          && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?"); | ||||||
|     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting!"); |     IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting!"); | ||||||
|     IM_ASSERT(g.Style.CircleSegmentMaxError > 0.0f                      && "Invalid style setting!"); |     IM_ASSERT(g.Style.CircleTessellationMaxError  > 0.0f                && "Invalid style setting!"); | ||||||
|     IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f            && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations |     IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f            && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations | ||||||
|     IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting."); |     IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting."); | ||||||
|     IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right); |     IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right); | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								imgui.h
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								imgui.h
									
									
									
									
									
								
							| @@ -1723,7 +1723,7 @@ struct ImGuiStyle | |||||||
|     bool        AntiAliasedLinesUseTex;     // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering. Latched at the beginning of the frame (copied to ImDrawList). |     bool        AntiAliasedLinesUseTex;     // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering. Latched at the beginning of the frame (copied to ImDrawList). | ||||||
|     bool        AntiAliasedFill;            // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList). |     bool        AntiAliasedFill;            // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList). | ||||||
|     float       CurveTessellationTol;       // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. |     float       CurveTessellationTol;       // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. | ||||||
|     float       CircleSegmentMaxError;      // 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. |     float       CircleTessellationMaxError; // 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. | ||||||
|     ImVec4      Colors[ImGuiCol_COUNT]; |     ImVec4      Colors[ImGuiCol_COUNT]; | ||||||
|  |  | ||||||
|     IMGUI_API ImGuiStyle(); |     IMGUI_API ImGuiStyle(); | ||||||
| @@ -2463,6 +2463,7 @@ struct ImDrawList | |||||||
|     IMGUI_API void  _OnChangedClipRect(); |     IMGUI_API void  _OnChangedClipRect(); | ||||||
|     IMGUI_API void  _OnChangedTextureID(); |     IMGUI_API void  _OnChangedTextureID(); | ||||||
|     IMGUI_API void  _OnChangedVtxOffset(); |     IMGUI_API void  _OnChangedVtxOffset(); | ||||||
|  |     IMGUI_API int   _CalcCircleAutoSegmentCount(float radius) const; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // All draw data to render a Dear ImGui frame | // All draw data to render a Dear ImGui frame | ||||||
|   | |||||||
| @@ -6034,22 +6034,44 @@ 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 Segment Max Error", &style.CircleSegmentMaxError, 0.01f, 0.10f, 10.0f, "%.2f"); |             ImGui::DragFloat("Circle Tessellation Max Error", &style.CircleTessellationMaxError , 0.005f, 0.10f, 10.0f, "%.2f", ImGuiSliderFlags_AlwaysClamp); | ||||||
|             if (ImGui::IsItemActive()) |             if (ImGui::IsItemActive()) | ||||||
|             { |             { | ||||||
|                 ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos()); |                 ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos()); | ||||||
|                 ImGui::BeginTooltip(); |                 ImGui::BeginTooltip(); | ||||||
|                 ImVec2 p = ImGui::GetCursorScreenPos(); |                 ImGui::TextUnformatted("N - number of segments"); | ||||||
|  |                 ImGui::TextUnformatted("R - radius"); | ||||||
|  |                 ImGui::Spacing(); | ||||||
|                 ImDrawList* draw_list = ImGui::GetWindowDrawList(); |                 ImDrawList* draw_list = ImGui::GetWindowDrawList(); | ||||||
|                 float RAD_MIN = 10.0f, RAD_MAX = 80.0f; |                 const float min_widget_width = ImGui::CalcTextSize("N: MM\nR: MM.MM").x; | ||||||
|                 float off_x = 10.0f; |                 float RAD_MIN = 5.0f, RAD_MAX = 80.0f; | ||||||
|                 for (int n = 0; n < 7; n++) |                 for (int n = 0; n < 9; n++) | ||||||
|                 { |                 { | ||||||
|                     const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (7.0f - 1.0f); |                     const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (9.0f - 1.0f); | ||||||
|                     draw_list->AddCircle(ImVec2(p.x + off_x + rad, p.y + RAD_MAX), rad, ImGui::GetColorU32(ImGuiCol_Text), 0); |  | ||||||
|                     off_x += 10.0f + rad * 2.0f; |                     const int segment_count = draw_list->_CalcCircleAutoSegmentCount(rad); | ||||||
|  |  | ||||||
|  |                     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::Dummy(ImVec2(canvas_width, RAD_MAX * 2)); | ||||||
|  |                     ImGui::Text("N: %d", segment_count); | ||||||
|  |  | ||||||
|  |                     const ImVec2 p2             = ImGui::GetCursorScreenPos(); | ||||||
|  |                     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::EndGroup(); | ||||||
|  |                     ImGui::SameLine(); | ||||||
|                 } |                 } | ||||||
|                 ImGui::Dummy(ImVec2(off_x, RAD_MAX * 2.0f)); |  | ||||||
|                 ImGui::EndTooltip(); |                 ImGui::EndTooltip(); | ||||||
|             } |             } | ||||||
|             ImGui::SameLine(); |             ImGui::SameLine(); | ||||||
|   | |||||||
| @@ -376,7 +376,7 @@ ImDrawListSharedData::ImDrawListSharedData() | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| void ImDrawListSharedData::SetCircleSegmentMaxError(float max_error) | void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error) | ||||||
| { | { | ||||||
|     if (CircleSegmentMaxError == max_error) |     if (CircleSegmentMaxError == max_error) | ||||||
|         return; |         return; | ||||||
| @@ -384,8 +384,7 @@ void ImDrawListSharedData::SetCircleSegmentMaxError(float max_error) | |||||||
|     for (int i = 0; i < IM_ARRAYSIZE(CircleSegmentCounts); i++) |     for (int i = 0; i < IM_ARRAYSIZE(CircleSegmentCounts); i++) | ||||||
|     { |     { | ||||||
|         const float radius = (float)i; |         const float radius = (float)i; | ||||||
|         const int segment_count = (i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : 0; |         CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : 0); | ||||||
|         CircleSegmentCounts[i] = (ImU8)ImMin(segment_count, 255); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -543,6 +542,21 @@ void ImDrawList::_OnChangedVtxOffset() | |||||||
|     curr_cmd->VtxOffset = _CmdHeader.VtxOffset; |     curr_cmd->VtxOffset = _CmdHeader.VtxOffset; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  |     if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts)) | ||||||
|  |         num_segments = _Data->CircleSegmentCounts[radius_idx]; // Use cached value | ||||||
|  |     else | ||||||
|  |         num_segments = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError); | ||||||
|  |  | ||||||
|  |     return num_segments; | ||||||
|  | } | ||||||
|  |  | ||||||
| // 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) | ||||||
| void ImDrawList::PushClipRect(ImVec2 cr_min, ImVec2 cr_max, bool intersect_with_current_clip_rect) | void ImDrawList::PushClipRect(ImVec2 cr_min, ImVec2 cr_max, bool intersect_with_current_clip_rect) | ||||||
| { | { | ||||||
| @@ -1286,11 +1300,7 @@ void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int nu | |||||||
|     if (num_segments <= 0) |     if (num_segments <= 0) | ||||||
|     { |     { | ||||||
|         // Automatic segment count |         // Automatic segment count | ||||||
|         const int radius_idx = (int)radius; |         num_segments = _CalcCircleAutoSegmentCount(radius); | ||||||
|         if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts)) |  | ||||||
|             num_segments = _Data->CircleSegmentCounts[radius_idx]; // Use cached value |  | ||||||
|         else |  | ||||||
|             num_segments = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError); |  | ||||||
|     } |     } | ||||||
|     else |     else | ||||||
|     { |     { | ||||||
| @@ -1316,11 +1326,7 @@ void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, | |||||||
|     if (num_segments <= 0) |     if (num_segments <= 0) | ||||||
|     { |     { | ||||||
|         // Automatic segment count |         // Automatic segment count | ||||||
|         const int radius_idx = (int)radius; |         num_segments = _CalcCircleAutoSegmentCount(radius); | ||||||
|         if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts)) |  | ||||||
|             num_segments = _Data->CircleSegmentCounts[radius_idx]; // Use cached value |  | ||||||
|         else |  | ||||||
|             num_segments = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError); |  | ||||||
|     } |     } | ||||||
|     else |     else | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -617,10 +617,29 @@ 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. | ||||||
| // FIXME: the minimum number of auto-segment may be undesirably high for very small radiuses (e.g. 1.0f) | // | ||||||
| #define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN                     12 | // Estimation of number of circle segment based on error is derived using method described in | ||||||
|  | // this post (https://stackoverflow.com/a/2244088/15194693). | ||||||
|  | // 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. | ||||||
|  | // | ||||||
|  | // 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). | ||||||
|  | // | ||||||
|  | #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)((IM_PI * 2.0f) / ImAcos(((_RAD) - (_MAXERROR)) / (_RAD))), IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX) | #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) | ||||||
|  |  | ||||||
| // 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 | ||||||
| @@ -641,11 +660,11 @@ 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) |     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 |     const ImVec4*   TexUvLines;                             // UV of anti-aliased lines in the atlas | ||||||
|  |  | ||||||
|     ImDrawListSharedData(); |     ImDrawListSharedData(); | ||||||
|     void SetCircleSegmentMaxError(float max_error); |     void SetCircleTessellationMaxError(float max_error); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct ImDrawDataBuilder | struct ImDrawDataBuilder | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user