Skip to content

Commit

Permalink
Make PathArcTo accept counter-clockwise angles ocornut#4030
Browse files Browse the repository at this point in the history
  • Loading branch information
thedmd committed Apr 12, 2021
1 parent d6a5cc7 commit dec6346
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 18 deletions.
62 changes: 44 additions & 18 deletions imgui_draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,6 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_
_Path.push_back(center);
return;
}
IM_ASSERT(a_min_sample <= a_max_sample);

// Calculate arc auto segment step size
if (a_step <= 0)
Expand All @@ -1056,7 +1055,7 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_
a_min_sample = normalized_sample;
}

const int sample_range = a_max_sample - a_min_sample;
const int sample_range = ImAbs(a_max_sample - a_min_sample);
const int a_next_step = a_step;

int samples = sample_range + 1;
Expand All @@ -1082,16 +1081,33 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_
ImVec2* out_ptr = _Path.Data + (_Path.Size - samples);

int sample_index = a_min_sample;
for (int a = a_min_sample; a <= a_max_sample; a += a_step, sample_index += a_step, a_step = a_next_step)
if (a_min_sample <= a_max_sample)
{
// a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more
if (sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX)
sample_index -= IM_DRAWLIST_ARCFAST_SAMPLE_MAX;

const ImVec2 s = _Data->ArcFastVtx[sample_index];
out_ptr->x = center.x + s.x * radius;
out_ptr->y = center.y + s.y * radius;
out_ptr++;
for (int a = a_min_sample; a <= a_max_sample; a += a_step, sample_index += a_step, a_step = a_next_step)
{
// a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more
if (sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX)
sample_index -= IM_DRAWLIST_ARCFAST_SAMPLE_MAX;

const ImVec2 s = _Data->ArcFastVtx[sample_index];
out_ptr->x = center.x + s.x * radius;
out_ptr->y = center.y + s.y * radius;
out_ptr++;
}
}
else
{
for (int a = a_min_sample; a >= a_max_sample; a -= a_step, sample_index -= a_step, a_step = a_next_step)
{
// a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more
if (sample_index < 0)
sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;

const ImVec2 s = _Data->ArcFastVtx[sample_index];
out_ptr->x = center.x + s.x * radius;
out_ptr->y = center.y + s.y * radius;
out_ptr++;
}
}

if (extra_max_sample)
Expand All @@ -1116,7 +1132,6 @@ void ImDrawList::_PathArcToN(const ImVec2& center, float radius, float a_min, fl
_Path.push_back(center);
return;
}
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!
Expand Down Expand Up @@ -1147,7 +1162,6 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
_Path.push_back(center);
return;
}
IM_ASSERT(a_min <= a_max);

if (num_segments > 0)
{
Expand All @@ -1158,22 +1172,34 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
// Automatic segment count
if (radius <= _Data->ArcFastRadiusCutoff)
{
const bool a_reverse = a_min >= a_max;

if (a_reverse)
ImSwap(a_min, a_max); // Swap for sample calculation

// We are going to use precomputed values for mid samples.
// 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));
const int a_max_sample = (int)( IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f));
const int a_mid_samples = ImMax(a_max_sample - a_min_sample, 0);
int a_min_sample = (int)ImCeil(IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f));
int a_max_sample = (int)( IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f));
int a_mid_samples = ImMax(a_max_sample - a_min_sample, 0);

const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
const float a_max_segment_angle = a_max_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
const bool a_emit_start = (a_min_segment_angle - a_min) > 0.0f;
const bool a_emit_end = (a_max - a_max_segment_angle) > 0.0f;

_Path.reserve(_Path.Size + (a_mid_samples + 1 + (a_emit_start ? 1 : 0) + (a_emit_end ? 1 : 0)));

if (a_reverse)
{
// Swap back for drawing
ImSwap(a_min, a_max);
ImSwap(a_max_sample, a_min_sample);
}

if (a_emit_start)
_Path.push_back(ImVec2(center.x + ImCos(a_min) * radius, center.y + ImSin(a_min) * radius));
if (a_max_sample >= a_min_sample)
_PathArcToFastEx(center, radius, a_min_sample, a_max_sample, 0);
_PathArcToFastEx(center, radius, a_min_sample, a_max_sample, 0);
if (a_emit_end)
_Path.push_back(ImVec2(center.x + ImCos(a_max) * radius, center.y + ImSin(a_max) * radius));
}
Expand Down
1 change: 1 addition & 0 deletions imgui_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ static inline float ImPow(float x, float y) { return powf(x, y); }
static inline double ImPow(double x, double y) { return pow(x, y); }
static inline float ImLog(float x) { return logf(x); } // DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision
static inline double ImLog(double x) { return log(x); }
static inline int ImAbs(int x) { return x < 0 ? -x : x; }
static inline float ImAbs(float x) { return fabsf(x); }
static inline double ImAbs(double x) { return fabs(x); }
static inline float ImSign(float x) { return (x < 0.0f) ? -1.0f : ((x > 0.0f) ? 1.0f : 0.0f); } // Sign operator - returns -1, 0 or 1 based on sign of argument
Expand Down

0 comments on commit dec6346

Please sign in to comment.