-
-
Notifications
You must be signed in to change notification settings - Fork 10.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AddCircle{Filled} - Use _PathArcToFastEx directly to emit circle vert… #4421
Conversation
…ices for 12 segment case (ocornut#4419) Before introduction of adaptive arcs PathArcToFast() were operating on 12 vertices table. Now it is replaced by internal _PathArcToFastEx() doing same job but with greater granularity, PathArcToFast was left as a fallback.
Hello @thedmd, |
Actually, the bug reported in #4419: Comes from the fact that the
Removing the if/else and just calling |
Hi. This function will calculate appropriate step basing on radius, so it will skip as many vertices as it can. |
Unless I'm misunderstanding something, It's even worse than I thought:
|
12 case was there for compatibility, maybe a misjudged one. In the end, even without if code will most likely end up using lookup table anyway. Only just a bit latter. |
It actually doesn't: // Obtain segment count
if (num_segments <= 0)
{
// Automatic segment count
num_segments = _CalcCircleAutoSegmentCount(radius);
}
else
{
// Explicit segment count (still clamp to avoid drawing insanely tessellated shapes)
num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX);
}
// Because we are filling a closed shape we remove 1 from the count of segments/points
const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;
if (num_segments == 12)
PathArcToFast(center, radius - 0.5f, 0, 12 - 1);
else
PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1);
PathStroke(col, ImDrawFlags_Closed, thickness);
} num_segments is calculated so PathArcTo() is called with that value, and PathArcTo() does if (num_segments > 0)
{
_PathArcToN(center, radius, a_min, a_max, num_segments);
return;
} Which calls cos/sin. e45847d already had this in TL;DR; AddCircleXXX() never uses the optimizations apart accidentally (and incorrectly) in the num_segments==12 case (which is the reported bug that got us to notice this). |
Digging trough the code commits (up to 2015)
AddNgon{Filled} were introduced which are exact copy of AddCircle from point 1.
Adaptive segment calculation were followed by a patch that used PathArcToFast()
Default argument N == 12 changed to 0, which enabled adaptive segment calculation
At this point AddCircle{Filled} became smooth for N == 12, except the closing segmennt,
AddCircle{Filled}AddCircle() N == 12 is broken, because it acts like adaptive version N == 0. Proposed solutionForce step size in _PathArcToFastEx() call to emit 12 vertices and still use PathArcToFast()PathArcToFast() is used across ImGui code base, but it also user-facing API. Proposed solutionRename PathArcToFast() to _PathArcToFast12() and convert ImGui internal calls Add PathArcToFast() that always up emit 12 vertices. |
…ices for 12 segment case (ocornut#4419) (fixup)
I did an update to PR, where to make AddCircle use _PathArcToFastEx() for adaptive path, and added two fast paths. I suspect there will be some questions or changes that can be made to further improve this routine. Note: I made _PathArcToFastEx() to make full circle with begin/end overlapping and then I pop the last vertex. This way circle is vertices will be symmetric. void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness)
{
if ((col & IM_COL32_A_MASK) == 0 || radius <= 0.0f)
return;
if (num_segments <= 0)
{
// Use arc with automatic segment count
_PathArcToFastEx(center, radius - 0.5f, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0);
_Path.pop_back();
}
#if IM_IS_DIVISIBLE_BY(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 12)
else if (num_segments == 12)
{
// If IM_DRAWLIST_ARCFAST_SAMPLE_MAX is dividable by 12, we can use _PathArcToFastEx
// with constant step size.
_PathArcToFastEx(center, radius - 0.5f, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12);
_Path.pop_back();
}
#endif
#if IM_IS_DIVISIBLE_BY(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 24)
else if (num_segments == 24)
{
// If IM_DRAWLIST_ARCFAST_SAMPLE_MAX is dividable by 12, we can use _PathArcToFastEx
// with constant step size.
_PathArcToFastEx(center, radius - 0.5f, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 24);
_Path.pop_back();
}
#endif
else
{
// Explicit segment count (still clamp to avoid drawing insanely tessellated shapes)
num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX);
// Because we are filling a closed shape we remove 1 from the count of segments/points
const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;
PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1);
}
PathStroke(col, ImDrawFlags_Closed, thickness);
} |
Merged the main part of the fix for now, thanks! |
So, for now we can't use 12 segments count "magic" for cached PathArc? |
Does adaptive version work for you? Is will use fast path and also emit enough vertices to produce smooth result. |
yes it's working, and i already understood that 12 segments count now is just 12 segments count, and 0 is used for both automatic / cached segments circle |
0c1e5bd
to
bb6a60b
Compare
…ices for 12 segment case (#4419)
Before introduction of adaptive arcs PathArcToFast() were operating on 12 vertices table.
Now it is replaced by internal _PathArcToFastEx() doing same job but with greater granularity,
PathArcToFast was left as a fallback.