@@ -31,6 +31,7 @@ pub struct Pie<'a, Coord, Label: Display> {
3131 label_style : TextStyle < ' a > ,
3232 label_offset : f64 ,
3333 percentage_style : Option < TextStyle < ' a > > ,
34+ donut_hole : f64 , // radius of the hole in case of a donut chart
3435}
3536
3637impl < ' a , Label : Display > Pie < ' a , ( i32 , i32 ) , Label > {
@@ -62,6 +63,7 @@ impl<'a, Label: Display> Pie<'a, (i32, i32), Label> {
6263 label_style,
6364 label_offset : radius_5pct,
6465 percentage_style : None ,
66+ donut_hole : 0.0 ,
6567 }
6668 }
6769
@@ -91,6 +93,15 @@ impl<'a, Label: Display> Pie<'a, (i32, i32), Label> {
9193 pub fn percentages < T : Into < TextStyle < ' a > > > ( & mut self , label_style : T ) {
9294 self . percentage_style = Some ( label_style. into ( ) ) ;
9395 }
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 : f64 ) {
101+ if hole_radius > 0.0 && hole_radius < * self . radius {
102+ self . donut_hole = hole_radius;
103+ }
104+ }
94105}
95106
96107impl < ' 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)
118129 . get ( index)
119130 . ok_or_else ( || DrawingErrorKind :: FontError ( Box :: new ( PieError :: LengthMismatch ) ) ) ?;
120131 // 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+ } ;
122137 let ratio = slice / self . total ;
123138 let theta_final = ratio * 2.0 * PI + offset_theta; // end radian for the wedge
124139
125140 // calculate middle for labels before mutating offset
126141 let middle_theta = ratio * PI + offset_theta;
127142
143+ let slice_start = offset_theta;
144+
128145 // calculate every fraction of radian for the wedge, offsetting for every iteration, clockwise
129146 //
130147 // a custom Range such as `for theta in offset_theta..=theta_final` would be more elegant
@@ -138,6 +155,19 @@ impl<'a, DB: DrawingBackend, Label: Display> Drawable<DB> for Pie<'a, (i32, i32)
138155 // final point of the wedge may not fall exactly on a radian, so add it extra
139156 let final_coord = theta_to_ordinal_coord ( * self . radius , theta_final, self . center ) ;
140157 points. push ( final_coord) ;
158+
159+ if self . donut_hole > 0.0 {
160+ while offset_theta >= slice_start {
161+ let coord = theta_to_ordinal_coord ( self . donut_hole , offset_theta, self . center ) ;
162+ points. push ( coord) ;
163+ offset_theta -= radian_increment;
164+ }
165+ // final point of the wedge may not fall exactly on a radian, so add it extra
166+ let final_coord_inner =
167+ theta_to_ordinal_coord ( self . donut_hole , slice_start, self . center ) ;
168+ points. push ( final_coord_inner) ;
169+ }
170+
141171 // next wedge calculation will start from previous wedges's last radian
142172 offset_theta = theta_final;
143173
@@ -163,8 +193,9 @@ impl<'a, DB: DrawingBackend, Label: Display> Drawable<DB> for Pie<'a, (i32, i32)
163193 let label_size = backend. estimate_text_size ( & perc_label, percentage_style) ?;
164194 let text_x_mid = ( label_size. 0 as f64 / 2.0 ) . round ( ) as i32 ;
165195 let text_y_mid = ( label_size. 1 as f64 / 2.0 ) . round ( ) as i32 ;
196+ let perc_radius = ( self . radius + self . donut_hole ) / 2.0 ;
166197 let perc_coord = theta_to_ordinal_coord (
167- self . radius / 2.0 ,
198+ perc_radius ,
168199 middle_theta,
169200 & ( self . center . 0 - text_x_mid, self . center . 1 - text_y_mid) ,
170201 ) ;
0 commit comments