@@ -31,6 +31,7 @@ pub struct Pie<'a, Coord, Label: Display> {
31
31
label_style : TextStyle < ' a > ,
32
32
label_offset : f64 ,
33
33
percentage_style : Option < TextStyle < ' a > > ,
34
+ donut_hole : & ' a f64 , // radius of the hole in case of a donut chart
34
35
}
35
36
36
37
impl < ' a , Label : Display > Pie < ' a , ( i32 , i32 ) , Label > {
@@ -62,6 +63,7 @@ impl<'a, Label: Display> Pie<'a, (i32, i32), Label> {
62
63
label_style,
63
64
label_offset : radius_5pct,
64
65
percentage_style : None ,
66
+ donut_hole : & 0.0 ,
65
67
}
66
68
}
67
69
@@ -91,6 +93,15 @@ impl<'a, Label: Display> Pie<'a, (i32, i32), Label> {
91
93
pub fn percentages < T : Into < TextStyle < ' a > > > ( & mut self , label_style : T ) {
92
94
self . percentage_style = Some ( label_style. into ( ) ) ;
93
95
}
96
+
97
+ /// Enables creating a donut chart with a hole of the specified radius.
98
+ ///
99
+ /// The passed value must be greater than zero and lower than the chart overall radius, otherwise it'll be ignored.
100
+ pub fn donut_hole ( & mut self , hole_radius : & ' a f64 ) {
101
+ if hole_radius > & 0.0 && hole_radius < self . radius {
102
+ self . donut_hole = hole_radius;
103
+ }
104
+ }
94
105
}
95
106
96
107
impl < ' a , DB : DrawingBackend , Label : Display > Drawable < DB > for Pie < ' a , ( i32 , i32 ) , Label > {
@@ -118,13 +129,19 @@ impl<'a, DB: DrawingBackend, Label: Display> Drawable<DB> for Pie<'a, (i32, i32)
118
129
. get ( index)
119
130
. ok_or_else ( || DrawingErrorKind :: FontError ( Box :: new ( PieError :: LengthMismatch ) ) ) ?;
120
131
// start building wedge line against the previous edge
121
- let mut points = vec ! [ * self . center] ;
132
+ let mut points = if self . donut_hole == & 0.0 {
133
+ vec ! [ * self . center]
134
+ } else {
135
+ vec ! [ ]
136
+ } ;
122
137
let ratio = slice / self . total ;
123
138
let theta_final = ratio * 2.0 * PI + offset_theta; // end radian for the wedge
124
139
125
140
// calculate middle for labels before mutating offset
126
141
let middle_theta = ratio * PI + offset_theta;
127
142
143
+ let slice_start = offset_theta;
144
+
128
145
// calculate every fraction of radian for the wedge, offsetting for every iteration, clockwise
129
146
//
130
147
// a custom Range such as `for theta in offset_theta..=theta_final` would be more elegant
@@ -135,9 +152,18 @@ impl<'a, DB: DrawingBackend, Label: Display> Drawable<DB> for Pie<'a, (i32, i32)
135
152
points. push ( coord) ;
136
153
offset_theta += radian_increment;
137
154
}
155
+ if self . donut_hole > & 0.0 {
156
+ while offset_theta >= slice_start {
157
+ let coord = theta_to_ordinal_coord ( * self . donut_hole , offset_theta, self . center ) ;
158
+ points. push ( coord) ;
159
+ offset_theta -= radian_increment;
160
+ }
161
+ }
138
162
// final point of the wedge may not fall exactly on a radian, so add it extra
139
- let final_coord = theta_to_ordinal_coord ( * self . radius , theta_final, self . center ) ;
140
- points. push ( final_coord) ;
163
+ if self . donut_hole == & 0.0 {
164
+ let final_coord = theta_to_ordinal_coord ( * self . radius , theta_final, self . center ) ;
165
+ points. push ( final_coord) ;
166
+ }
141
167
// next wedge calculation will start from previous wedges's last radian
142
168
offset_theta = theta_final;
143
169
@@ -163,8 +189,13 @@ impl<'a, DB: DrawingBackend, Label: Display> Drawable<DB> for Pie<'a, (i32, i32)
163
189
let label_size = backend. estimate_text_size ( & perc_label, percentage_style) ?;
164
190
let text_x_mid = ( label_size. 0 as f64 / 2.0 ) . round ( ) as i32 ;
165
191
let text_y_mid = ( label_size. 1 as f64 / 2.0 ) . round ( ) as i32 ;
192
+ let perc_radius = if self . donut_hole == & 0.0 {
193
+ self . radius / 2.0
194
+ } else {
195
+ ( self . radius + self . donut_hole ) / 2.0
196
+ } ;
166
197
let perc_coord = theta_to_ordinal_coord (
167
- self . radius / 2.0 ,
198
+ perc_radius ,
168
199
middle_theta,
169
200
& ( self . center . 0 - text_x_mid, self . center . 1 - text_y_mid) ,
170
201
) ;
0 commit comments