@ -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 ;