|
@@ -374,18 +374,22 @@ ImDrawListSharedData::ImDrawListSharedData()
|
|
|
const float a = ((float)i * 2 * IM_PI) / (float)IM_ARRAYSIZE(ArcFastVtx);
|
|
|
ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a));
|
|
|
}
|
|
|
+ ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);
|
|
|
}
|
|
|
|
|
|
void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
|
|
|
{
|
|
|
if (CircleSegmentMaxError == max_error)
|
|
|
return;
|
|
|
+
|
|
|
+ IM_ASSERT(max_error > 0.0f);
|
|
|
CircleSegmentMaxError = max_error;
|
|
|
for (int i = 0; i < IM_ARRAYSIZE(CircleSegmentCounts); i++)
|
|
|
{
|
|
|
const float radius = (float)i;
|
|
|
CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : 0);
|
|
|
}
|
|
|
+ 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.
|
|
@@ -1026,32 +1030,86 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 0: East, 3: South, 6: West, 9: North, 12: East
|
|
|
-void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12)
|
|
|
+void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step)
|
|
|
{
|
|
|
if (radius <= 0.0f)
|
|
|
{
|
|
|
_Path.push_back(center);
|
|
|
return;
|
|
|
}
|
|
|
- IM_ASSERT(a_min_of_12 <= a_max_of_12);
|
|
|
+ IM_ASSERT(a_min_sample <= a_max_sample);
|
|
|
|
|
|
- // 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
|
|
|
+ // Calculate arc auto segment step size
|
|
|
+ if (a_step <= 0)
|
|
|
+ a_step = IM_DRAWLIST_ARCFAST_SAMPLE_MAX / _CalcCircleAutoSegmentCount(radius);
|
|
|
+
|
|
|
+ // 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);
|
|
|
+
|
|
|
+ // Normalize a_min_sample to always start lie in [0..IM_DRAWLIST_ARCFAST_SAMPLE_MAX] range.
|
|
|
+ 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;
|
|
|
+
|
|
|
+ int samples = sample_range + 1;
|
|
|
+ bool extra_max_sample = false;
|
|
|
+ if (a_step > 1)
|
|
|
+ {
|
|
|
+ samples = sample_range / a_step + 1;
|
|
|
+ const int overstep = sample_range % a_step;
|
|
|
+
|
|
|
+ if (overstep > 0)
|
|
|
+ {
|
|
|
+ extra_max_sample = true;
|
|
|
+ samples++;
|
|
|
|
|
|
- _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++)
|
|
|
+ // When we have overstep to avoid awkwardly looking one long line and one tiny one at the end,
|
|
|
+ // distribute first step range evenly between them by reducing first step size.
|
|
|
+ if (sample_range > 0)
|
|
|
+ a_step -= (a_step - overstep) / 2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ _Path.resize(_Path.Size + samples);
|
|
|
+ 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)
|
|
|
+ {
|
|
|
+ // 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++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (extra_max_sample)
|
|
|
{
|
|
|
- const ImVec2& c = _Data->ArcFastVtx[a % IM_ARRAYSIZE(_Data->ArcFastVtx)];
|
|
|
- _Path.push_back(ImVec2(center.x + c.x * radius, center.y + c.y * radius));
|
|
|
+ int normalized_max_sample = a_max_sample % IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
|
|
|
+ if (normalized_max_sample < 0)
|
|
|
+ normalized_max_sample += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
|
|
|
+
|
|
|
+ const ImVec2 s = _Data->ArcFastVtx[normalized_max_sample];
|
|
|
+ out_ptr->x = center.x + s.x * radius;
|
|
|
+ out_ptr->y = center.y + s.y * radius;
|
|
|
+ out_ptr++;
|
|
|
}
|
|
|
+
|
|
|
+ IM_ASSERT_PARANOID(_Path.Data + _Path.Size == out_ptr);
|
|
|
}
|
|
|
|
|
|
-void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments)
|
|
|
+void ImDrawList::_PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments)
|
|
|
{
|
|
|
if (radius <= 0.0f)
|
|
|
{
|
|
@@ -1070,6 +1128,64 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// 0: East, 3: South, 6: West, 9: North, 12: East
|
|
|
+void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12)
|
|
|
+{
|
|
|
+ if (radius <= 0.0f)
|
|
|
+ {
|
|
|
+ _Path.push_back(center);
|
|
|
+ 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);
|
|
|
+}
|
|
|
+
|
|
|
+void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments)
|
|
|
+{
|
|
|
+ if (radius <= 0.0f)
|
|
|
+ {
|
|
|
+ _Path.push_back(center);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ IM_ASSERT(a_min <= a_max);
|
|
|
+
|
|
|
+ if (num_segments > 0)
|
|
|
+ {
|
|
|
+ _PathArcToN(center, radius, a_min, a_max, num_segments);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Automatic segment count
|
|
|
+ if (radius <= _Data->ArcFastRadiusCutoff)
|
|
|
+ {
|
|
|
+ // 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);
|
|
|
+
|
|
|
+ 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_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);
|
|
|
+ if (a_emit_end)
|
|
|
+ _Path.push_back(ImVec2(center.x + ImCos(a_max) * radius, center.y + ImSin(a_max) * radius));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ const float arc_length = a_max - a_min;
|
|
|
+ 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));
|
|
|
+ _PathArcToN(center, radius, a_min, a_max, arc_segment_count);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t)
|
|
|
{
|
|
|
float u = 1.0f - t;
|