Skip to content

Commit

Permalink
Replace PathArcTo() and PathArcToFast() with adaptive versions.
Browse files Browse the repository at this point in the history
  • Loading branch information
thedmd committed Mar 2, 2021
1 parent cfa7862 commit d836189
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 70 deletions.
7 changes: 3 additions & 4 deletions imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -2418,10 +2418,8 @@ struct ImDrawList
inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size - 1], &pos, 8) != 0) _Path.push_back(pos); }
inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; } // Note: Anti-aliased filling requires points to be in clockwise order.
inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness); _Path.Size = 0; }
IMGUI_API void PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 10);
IMGUI_API void PathArcTo2(const ImVec2& center, float radius, float a_min, float a_max, int num_segments);
IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle
IMGUI_API void PathArcToFast2(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle
IMGUI_API void PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 0);
IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle
IMGUI_API void PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); // Cubic Bezier (4 control points)
IMGUI_API void PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0); // Quadratic Bezier (3 control points)
IMGUI_API void PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, ImDrawCornerFlags rounding_corners = ImDrawCornerFlags_All);
Expand Down Expand Up @@ -2468,6 +2466,7 @@ struct ImDrawList
IMGUI_API int _CalcCircleAutoSegmentCount(float radius) const;
IMGUI_API int _CalcArcAutoSegmentStepSize(float radius) const;
IMGUI_API void _PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step);
IMGUI_API void _PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments);
};

// All draw data to render a Dear ImGui frame
Expand Down
82 changes: 25 additions & 57 deletions imgui_draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,20 +369,15 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
ImDrawListSharedData::ImDrawListSharedData()
{
memset(this, 0, sizeof(*this));
for (int i = 0; i < IM_ARRAYSIZE(ArcFastVtx); i++)
{
const float a = ((float)i * 2 * IM_PI) / (float)IM_ARRAYSIZE(ArcFastVtx);
ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a));
}

// Sample quarter of the circle.
for (int i = 0; i < IM_ARRAYSIZE(ArcFastExVtx); i++)
for (int i = 0; i < IM_ARRAYSIZE(ArcFastVtx); i++)
{
const float a = ((float)i * 0.5f * IM_PI) / (float)IM_ARRAYSIZE(ArcFastExVtx);
ArcFastExVtx[i] = ImVec2(ImCos(a), ImSin(a));
const float a = ((float)i * 0.5f * IM_PI) / (float)IM_ARRAYSIZE(ArcFastVtx);
ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a));
}

ArcFastExRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);
ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);
}

void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
Expand All @@ -399,7 +394,7 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : 0);
}

ArcFastExRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);
ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);
}

// Initialize before use in a new frame. We always have a command ready in the buffer.
Expand Down Expand Up @@ -1042,7 +1037,7 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun

int ImDrawList::_CalcArcAutoSegmentStepSize(float radius) const
{
if (radius <= 0.0f || radius > _Data->ArcFastExRadiusCutoff)
if (radius <= 0.0f || radius > _Data->ArcFastRadiusCutoff)
return 0;

const int n = _CalcCircleAutoSegmentCount(radius);
Expand Down Expand Up @@ -1117,23 +1112,23 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_

if (sample_index < IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE)
{
s = _Data->ArcFastExVtx[sample_index];
s = _Data->ArcFastVtx[sample_index];
}
else if (sample_index < IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE * 2)
{
const ImVec2& c = _Data->ArcFastExVtx[sample_index - IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE];
const ImVec2& c = _Data->ArcFastVtx[sample_index - IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE];
s.x = -c.y;
s.y = c.x;
}
else if (sample_index < IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE * 3)
{
const ImVec2& c = _Data->ArcFastExVtx[sample_index - IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE * 2];
const ImVec2& c = _Data->ArcFastVtx[sample_index - IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE * 2];
s.x = -c.x;
s.y = -c.y;
}
else
{
const ImVec2& c = _Data->ArcFastExVtx[sample_index - IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE * 3];
const ImVec2& c = _Data->ArcFastVtx[sample_index - IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE * 3];
s.x = c.y;
s.y = -c.x;
}
Expand All @@ -1154,23 +1149,23 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_

if (sample_index < IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE)
{
s = _Data->ArcFastExVtx[sample_index];
s = _Data->ArcFastVtx[sample_index];
}
else if (sample_index < IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE * 2)
{
const ImVec2& c = _Data->ArcFastExVtx[sample_index - IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE];
const ImVec2& c = _Data->ArcFastVtx[sample_index - IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE];
s.x = -c.y;
s.y = c.x;
}
else if (sample_index < IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE * 3)
{
const ImVec2& c = _Data->ArcFastExVtx[sample_index - IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE * 2];
const ImVec2& c = _Data->ArcFastVtx[sample_index - IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE * 2];
s.x = -c.x;
s.y = -c.y;
}
else
{
const ImVec2& c = _Data->ArcFastExVtx[sample_index - IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE * 3];
const ImVec2& c = _Data->ArcFastVtx[sample_index - IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE * 3];
s.x = c.y;
s.y = -c.x;
}
Expand All @@ -1184,7 +1179,7 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_
IM_ASSERT(_Path.Data + _Path.Size == out_ptr);
}

void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12)
void ImDrawList::_PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments)
{
if (radius < 0.0f)
return;
Expand All @@ -1194,24 +1189,19 @@ void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_
_Path.push_back(center);
return;
}
IM_ASSERT(a_min_of_12 <= a_max_of_12);

// For legacy reason the PathArcToFast() always takes angles where 2*PI is represented by 12,
// but it is possible to set IM_DRAWLIST_ARCFAST_TESSELATION_MULTIPLIER to a higher value. This should compile to a no-op otherwise.
#if IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER != 1
a_min_of_12 *= IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER;
a_max_of_12 *= IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER;
#endif
IM_ASSERT(a_min <= a_max);

_Path.reserve(_Path.Size + (a_max_of_12 - a_min_of_12 + 1));
for (int a = a_min_of_12; a <= a_max_of_12; a++)
// Note that we are adding a point at both a_min and a_max.
// If you are trying to draw a full closed circle you don't want the overlapping points!
_Path.reserve(_Path.Size + (num_segments + 1));
for (int i = 0; i <= num_segments; i++)
{
const ImVec2& c = _Data->ArcFastVtx[a % IM_ARRAYSIZE(_Data->ArcFastVtx)];
_Path.push_back(ImVec2(center.x + c.x * radius, center.y + c.y * radius));
const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);
_Path.push_back(ImVec2(center.x + ImCos(a) * radius, center.y + ImSin(a) * radius));
}
}

void ImDrawList::PathArcToFast2(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12)
void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12)
{
if (radius < 0.0f)
return;
Expand Down Expand Up @@ -1241,29 +1231,7 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
}
IM_ASSERT(a_min <= a_max);

// Note that we are adding a point at both a_min and a_max.
// If you are trying to draw a full closed circle you don't want the overlapping points!
_Path.reserve(_Path.Size + (num_segments + 1));
for (int i = 0; i <= num_segments; i++)
{
const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);
_Path.push_back(ImVec2(center.x + ImCos(a) * radius, center.y + ImSin(a) * radius));
}
}

void ImDrawList::PathArcTo2(const ImVec2& center, float radius, float a_min, float a_max, int num_segments)
{
if (radius < 0.0f)
return;

if (radius == 0.0f)
{
_Path.push_back(center);
return;
}
IM_ASSERT(a_min <= a_max);

if (num_segments <= 0 && (radius <= _Data->ArcFastExRadiusCutoff))
if (num_segments <= 0 && (radius <= _Data->ArcFastRadiusCutoff))
{
// Determine first and last sample in lookup table that belong to the arc.
const int a_min_sample = (int)ImCeil(IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f));
Expand Down Expand Up @@ -1298,7 +1266,7 @@ void ImDrawList::PathArcTo2(const ImVec2& center, float radius, float a_min, flo
num_segments = arc_segment_count;
}

PathArcTo(center, radius, a_min, a_max, num_segments);
_PathArcToN(center, radius, a_min, a_max, num_segments);
}
}

Expand Down
12 changes: 3 additions & 9 deletions imgui_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -636,14 +636,9 @@ struct IMGUI_API ImChunkStream
#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(_N,_MAXERROR) ((_MAXERROR) / (1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI))))
#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_ERROR(_N,_RAD) ((1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI))) / (_RAD))

// 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
#define IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER 1
#endif

// ImDrawList: Lookup table size for adaptive arc drawing, cover quarter of the circle.
#ifndef IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE
#define IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE 12 // Number of samples in lookup table.
#define IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE 12 // Number of samples in lookup table.
#endif
#define IM_DRAWLIST_ARCFAST_SAMPLE_MAX (4 * IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE) // Sample index _PathArcToFastEx() for 360 angle.

Expand All @@ -660,9 +655,8 @@ struct IMGUI_API ImDrawListSharedData
ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards)

// [Internal] Lookup tables
ImVec2 ArcFastVtx[12 * IM_DRAWLIST_ARCFAST_TESSELLATION_MULTIPLIER]; // FIXME: Bake rounded corners fill/borders in atlas
ImVec2 ArcFastExVtx[IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE]; // Sample points on the quarter of the circle.
float ArcFastExRadiusCutoff; // Cutoff radius after which arc drawing will fallback to slower PathArcTo()
ImVec2 ArcFastVtx[IM_DRAWLIST_ARCFAST_LOOKUP_TABLE_SIZE]; // Sample points on the quarter of the circle.
float ArcFastRadiusCutoff; // Cutoff radius after which arc drawing will fallback to slower PathArcTo()
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

Expand Down

0 comments on commit d836189

Please sign in to comment.