mirror of
https://github.com/Drezil/imgui.git
synced 2024-11-14 17:07:01 +00:00
Improve on automatic circle segment count calculation. (#3808)
This commit is contained in:
parent
b47aa46d81
commit
f107693d9b
@ -986,7 +986,7 @@ ImGuiStyle::ImGuiStyle()
|
||||
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.).
|
||||
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
|
||||
ImGui::StyleColorsDark(this);
|
||||
@ -3878,7 +3878,7 @@ void ImGui::NewFrame()
|
||||
virtual_space.Add(g.Viewports[n]->GetMainRect());
|
||||
g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();
|
||||
g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
|
||||
g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError);
|
||||
g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError);
|
||||
g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
|
||||
if (g.Style.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[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.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.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);
|
||||
|
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 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 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];
|
||||
|
||||
IMGUI_API ImGuiStyle();
|
||||
@ -2463,6 +2463,7 @@ struct ImDrawList
|
||||
IMGUI_API void _OnChangedClipRect();
|
||||
IMGUI_API void _OnChangedTextureID();
|
||||
IMGUI_API void _OnChangedVtxOffset();
|
||||
IMGUI_API int _CalcCircleAutoSegmentCount(float radius) const;
|
||||
};
|
||||
|
||||
// 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;
|
||||
|
||||
// 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())
|
||||
{
|
||||
ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos());
|
||||
ImGui::BeginTooltip();
|
||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
ImGui::TextUnformatted("N - number of segments");
|
||||
ImGui::TextUnformatted("R - radius");
|
||||
ImGui::Spacing();
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
float RAD_MIN = 10.0f, RAD_MAX = 80.0f;
|
||||
float off_x = 10.0f;
|
||||
for (int n = 0; n < 7; n++)
|
||||
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 rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (7.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 float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (9.0f - 1.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::SameLine();
|
||||
|
@ -376,7 +376,7 @@ ImDrawListSharedData::ImDrawListSharedData()
|
||||
}
|
||||
}
|
||||
|
||||
void ImDrawListSharedData::SetCircleSegmentMaxError(float max_error)
|
||||
void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
|
||||
{
|
||||
if (CircleSegmentMaxError == max_error)
|
||||
return;
|
||||
@ -384,8 +384,7 @@ void ImDrawListSharedData::SetCircleSegmentMaxError(float max_error)
|
||||
for (int i = 0; i < IM_ARRAYSIZE(CircleSegmentCounts); i++)
|
||||
{
|
||||
const float radius = (float)i;
|
||||
const int segment_count = (i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : 0;
|
||||
CircleSegmentCounts[i] = (ImU8)ImMin(segment_count, 255);
|
||||
CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -543,6 +542,21 @@ void ImDrawList::_OnChangedVtxOffset()
|
||||
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)
|
||||
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)
|
||||
{
|
||||
// Automatic segment count
|
||||
const int radius_idx = (int)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);
|
||||
num_segments = _CalcCircleAutoSegmentCount(radius);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1316,11 +1326,7 @@ void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col,
|
||||
if (num_segments <= 0)
|
||||
{
|
||||
// Automatic segment count
|
||||
const int radius_idx = (int)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);
|
||||
num_segments = _CalcCircleAutoSegmentCount(radius);
|
||||
}
|
||||
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.
|
||||
// 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_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.
|
||||
#ifndef IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER
|
||||
@ -641,11 +660,11 @@ struct IMGUI_API ImDrawListSharedData
|
||||
|
||||
// [Internal] Lookup tables
|
||||
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();
|
||||
void SetCircleSegmentMaxError(float max_error);
|
||||
void SetCircleTessellationMaxError(float max_error);
|
||||
};
|
||||
|
||||
struct ImDrawDataBuilder
|
||||
|
Loading…
Reference in New Issue
Block a user