Make PathArcTo accept counter-clockwise angles (#4030, #3491)

This commit is contained in:
thedmd 2021-04-14 20:07:23 +02:00 committed by ocornut
parent 5fed6bdc72
commit 690a90bd35
3 changed files with 47 additions and 30 deletions

View File

@ -54,6 +54,7 @@ Other Changes:
- LabelText: Fixed clipping of multi-line value text when label is single-line. (#4004) - LabelText: Fixed clipping of multi-line value text when label is single-line. (#4004)
- LabelText: Fixed vertical alignment of single-line value text when label is multi-line. (#4004) - LabelText: Fixed vertical alignment of single-line value text when label is multi-line. (#4004)
- Popups: Added 'OpenPopup(ImGuiID id)' overload to facilitate calling from nested stacks. (#3993, #331) [@zlash] - Popups: Added 'OpenPopup(ImGuiID id)' overload to facilitate calling from nested stacks. (#3993, #331) [@zlash]
- ImDrawList: Fixed PathArcTo() regression from 1.82 preventing use of counter-clockwise angles. (#4030, #3491) [@thedmd]
- Demo: Improved popups demo and comments. - Demo: Improved popups demo and comments.
- Backends: SDL: Rework global mouse pos availability check listing supported platforms explicitly, - Backends: SDL: Rework global mouse pos availability check listing supported platforms explicitly,
effectively fixing mouse access on Raspberry Pi. (#2837, #3950) [@lethal-guitar, @hinxx] effectively fixing mouse access on Raspberry Pi. (#2837, #3950) [@lethal-guitar, @hinxx]

View File

@ -1037,7 +1037,6 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_
_Path.push_back(center); _Path.push_back(center);
return; return;
} }
IM_ASSERT(a_min_sample <= a_max_sample);
// Calculate arc auto segment step size // Calculate arc auto segment step size
if (a_step <= 0) if (a_step <= 0)
@ -1046,17 +1045,7 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_
// Make sure we never do steps larger than one quarter of the circle // Make sure we never do steps larger than one quarter of the circle
a_step = ImClamp(a_step, 1, IM_DRAWLIST_ARCFAST_TABLE_SIZE / 4); a_step = ImClamp(a_step, 1, IM_DRAWLIST_ARCFAST_TABLE_SIZE / 4);
// Normalize a_min_sample to always start lie in [0..IM_DRAWLIST_ARCFAST_SAMPLE_MAX] range. const int sample_range = ImAbs(a_max_sample - a_min_sample);
if (a_min_sample < 0)
{
int normalized_sample = a_min_sample % IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
if (normalized_sample < 0)
normalized_sample += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
a_max_sample += (normalized_sample - a_min_sample);
a_min_sample = normalized_sample;
}
const int sample_range = a_max_sample - a_min_sample;
const int a_next_step = a_step; const int a_next_step = a_step;
int samples = sample_range + 1; int samples = sample_range + 1;
@ -1082,16 +1071,40 @@ void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_
ImVec2* out_ptr = _Path.Data + (_Path.Size - samples); ImVec2* out_ptr = _Path.Data + (_Path.Size - samples);
int sample_index = a_min_sample; 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 (sample_index < 0 || sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX)
{ {
// a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more sample_index = sample_index % IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
if (sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX) if (sample_index < 0)
sample_index -= IM_DRAWLIST_ARCFAST_SAMPLE_MAX; sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
}
const ImVec2 s = _Data->ArcFastVtx[sample_index]; if (a_max_sample >= a_min_sample)
out_ptr->x = center.x + s.x * radius; {
out_ptr->y = center.y + s.y * radius; for (int a = a_min_sample; a <= a_max_sample; a += a_step, sample_index += a_step, a_step = a_next_step)
out_ptr++; {
// 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) if (extra_max_sample)
@ -1116,7 +1129,6 @@ void ImDrawList::_PathArcToN(const ImVec2& center, float radius, float a_min, fl
_Path.push_back(center); _Path.push_back(center);
return; return;
} }
IM_ASSERT(a_min <= a_max);
// Note that we are adding a point at both a_min and 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! // If you are trying to draw a full closed circle you don't want the overlapping points!
@ -1136,7 +1148,6 @@ void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_
_Path.push_back(center); _Path.push_back(center);
return; return;
} }
IM_ASSERT(a_min_of_12 <= a_max_of_12);
_PathArcToFastEx(center, radius, a_min_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, a_max_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0); _PathArcToFastEx(center, radius, a_min_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, a_max_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0);
} }
@ -1147,7 +1158,6 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
_Path.push_back(center); _Path.push_back(center);
return; return;
} }
IM_ASSERT(a_min <= a_max);
if (num_segments > 0) if (num_segments > 0)
{ {
@ -1158,28 +1168,33 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
// Automatic segment count // Automatic segment count
if (radius <= _Data->ArcFastRadiusCutoff) if (radius <= _Data->ArcFastRadiusCutoff)
{ {
const bool a_is_reverse = a_max < a_min;
// We are going to use precomputed values for mid samples. // We are going to use precomputed values for mid samples.
// Determine first and last sample in lookup table that belong to the arc. // 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 float a_min_sample_f = 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 float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f);
const int a_mid_samples = ImMax(a_max_sample - a_min_sample, 0);
const int a_min_sample = a_is_reverse ? (int)ImFloorSigned(a_min_sample_f) : (int)ImCeil(a_min_sample_f);
const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloorSigned(a_max_sample_f);
const int a_mid_samples = a_is_reverse ? ImMax(a_min_sample - a_max_sample, 0) : 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_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 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_start = (a_min_segment_angle - a_min) != 0.0f;
const bool a_emit_end = (a_max - a_max_segment_angle) > 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))); _Path.reserve(_Path.Size + (a_mid_samples + 1 + (a_emit_start ? 1 : 0) + (a_emit_end ? 1 : 0)));
if (a_emit_start) if (a_emit_start)
_Path.push_back(ImVec2(center.x + ImCos(a_min) * radius, center.y + ImSin(a_min) * radius)); _Path.push_back(ImVec2(center.x + ImCos(a_min) * radius, center.y + ImSin(a_min) * radius));
if (a_max_sample >= a_min_sample) if (a_mid_samples > 0)
_PathArcToFastEx(center, radius, a_min_sample, a_max_sample, 0); _PathArcToFastEx(center, radius, a_min_sample, a_max_sample, 0);
if (a_emit_end) if (a_emit_end)
_Path.push_back(ImVec2(center.x + ImCos(a_max) * radius, center.y + ImSin(a_max) * radius)); _Path.push_back(ImVec2(center.x + ImCos(a_max) * radius, center.y + ImSin(a_max) * radius));
} }
else else
{ {
const float arc_length = a_max - a_min; const float arc_length = ImAbs(a_max - a_min);
const int circle_segment_count = _CalcCircleAutoSegmentCount(radius); const int circle_segment_count = _CalcCircleAutoSegmentCount(radius);
const int arc_segment_count = ImMax((int)ImCeil(circle_segment_count * arc_length / (IM_PI * 2.0f)), (int)(2.0f * IM_PI / arc_length)); const int arc_segment_count = ImMax((int)ImCeil(circle_segment_count * arc_length / (IM_PI * 2.0f)), (int)(2.0f * IM_PI / arc_length));
_PathArcToN(center, radius, a_min, a_max, arc_segment_count); _PathArcToN(center, radius, a_min, a_max, arc_segment_count);

View File

@ -371,6 +371,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 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 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 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 float ImAbs(float x) { return fabsf(x); }
static inline double ImAbs(double x) { return fabs(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 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