@@ -3,6 +3,10 @@ import defaults from '../core/core.defaults';
33import Arc from '../elements/element.arc' ;
44import { isArray , valueOrDefault } from '../helpers/helpers.core' ;
55
6+ /**
7+ * @typedef { import("../core/core.controller").default } Chart
8+ */
9+
610const PI = Math . PI ;
711const DOUBLE_PI = PI * 2 ;
812const HALF_PI = PI / 2 ;
@@ -94,13 +98,45 @@ defaults.set('doughnut', {
9498 }
9599} ) ;
96100
101+ function getRatioAndOffset ( rotation , circumference , cutout ) {
102+ let ratioX = 1 ;
103+ let ratioY = 1 ;
104+ let offsetX = 0 ;
105+ let offsetY = 0 ;
106+ // If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc
107+ if ( circumference < DOUBLE_PI ) {
108+ let startAngle = rotation % DOUBLE_PI ;
109+ startAngle += startAngle >= PI ? - DOUBLE_PI : startAngle < - PI ? DOUBLE_PI : 0 ;
110+ const endAngle = startAngle + circumference ;
111+ const startX = Math . cos ( startAngle ) ;
112+ const startY = Math . sin ( startAngle ) ;
113+ const endX = Math . cos ( endAngle ) ;
114+ const endY = Math . sin ( endAngle ) ;
115+ const contains0 = ( startAngle <= 0 && endAngle >= 0 ) || endAngle >= DOUBLE_PI ;
116+ const contains90 = ( startAngle <= HALF_PI && endAngle >= HALF_PI ) || endAngle >= DOUBLE_PI + HALF_PI ;
117+ const contains180 = startAngle === - PI || endAngle >= PI ;
118+ const contains270 = ( startAngle <= - HALF_PI && endAngle >= - HALF_PI ) || endAngle >= PI + HALF_PI ;
119+ const minX = contains180 ? - 1 : Math . min ( startX , startX * cutout , endX , endX * cutout ) ;
120+ const minY = contains270 ? - 1 : Math . min ( startY , startY * cutout , endY , endY * cutout ) ;
121+ const maxX = contains0 ? 1 : Math . max ( startX , startX * cutout , endX , endX * cutout ) ;
122+ const maxY = contains90 ? 1 : Math . max ( startY , startY * cutout , endY , endY * cutout ) ;
123+ ratioX = ( maxX - minX ) / 2 ;
124+ ratioY = ( maxY - minY ) / 2 ;
125+ offsetX = - ( maxX + minX ) / 2 ;
126+ offsetY = - ( maxY + minY ) / 2 ;
127+ }
128+ return { ratioX, ratioY, offsetX, offsetY} ;
129+ }
130+
97131class DoughnutController extends DatasetController {
98132
99133 constructor ( chart , datasetIndex ) {
100134 super ( chart , datasetIndex ) ;
101135
102136 this . innerRadius = undefined ;
103137 this . outerRadius = undefined ;
138+ this . offsetX = undefined ;
139+ this . offsetY = undefined ;
104140 }
105141
106142 linkScales ( ) { }
@@ -131,61 +167,31 @@ class DoughnutController extends DatasetController {
131167 return ringIndex ;
132168 }
133169
170+ /**
171+ * @param {string } mode
172+ */
134173 update ( mode ) {
135174 const me = this ;
136175 const chart = me . chart ;
137- const chartArea = chart . chartArea ;
138- const opts = chart . options ;
139- let ratioX = 1 ;
140- let ratioY = 1 ;
141- let offsetX = 0 ;
142- let offsetY = 0 ;
176+ const { chartArea, options} = chart ;
143177 const meta = me . _cachedMeta ;
144178 const arcs = meta . data ;
145- const cutout = opts . cutoutPercentage / 100 || 0 ;
146- const circumference = opts . circumference ;
179+ const cutout = options . cutoutPercentage / 100 || 0 ;
147180 const chartWeight = me . _getRingWeight ( me . index ) ;
148-
149- // If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc
150- if ( circumference < DOUBLE_PI ) {
151- let startAngle = opts . rotation % DOUBLE_PI ;
152- startAngle += startAngle >= PI ? - DOUBLE_PI : startAngle < - PI ? DOUBLE_PI : 0 ;
153- const endAngle = startAngle + circumference ;
154- const startX = Math . cos ( startAngle ) ;
155- const startY = Math . sin ( startAngle ) ;
156- const endX = Math . cos ( endAngle ) ;
157- const endY = Math . sin ( endAngle ) ;
158- const contains0 = ( startAngle <= 0 && endAngle >= 0 ) || endAngle >= DOUBLE_PI ;
159- const contains90 = ( startAngle <= HALF_PI && endAngle >= HALF_PI ) || endAngle >= DOUBLE_PI + HALF_PI ;
160- const contains180 = startAngle === - PI || endAngle >= PI ;
161- const contains270 = ( startAngle <= - HALF_PI && endAngle >= - HALF_PI ) || endAngle >= PI + HALF_PI ;
162- const minX = contains180 ? - 1 : Math . min ( startX , startX * cutout , endX , endX * cutout ) ;
163- const minY = contains270 ? - 1 : Math . min ( startY , startY * cutout , endY , endY * cutout ) ;
164- const maxX = contains0 ? 1 : Math . max ( startX , startX * cutout , endX , endX * cutout ) ;
165- const maxY = contains90 ? 1 : Math . max ( startY , startY * cutout , endY , endY * cutout ) ;
166- ratioX = ( maxX - minX ) / 2 ;
167- ratioY = ( maxY - minY ) / 2 ;
168- offsetX = - ( maxX + minX ) / 2 ;
169- offsetY = - ( maxY + minY ) / 2 ;
170- }
171-
172- for ( let i = 0 , ilen = arcs . length ; i < ilen ; ++ i ) {
173- arcs [ i ] . _options = me . _resolveDataElementOptions ( i , mode ) ;
174- }
175-
176- chart . borderWidth = me . getMaxBorderWidth ( ) ;
177- const maxWidth = ( chartArea . right - chartArea . left - chart . borderWidth ) / ratioX ;
178- const maxHeight = ( chartArea . bottom - chartArea . top - chart . borderWidth ) / ratioY ;
179- chart . outerRadius = Math . max ( Math . min ( maxWidth , maxHeight ) / 2 , 0 ) ;
180- chart . innerRadius = Math . max ( chart . outerRadius * cutout , 0 ) ;
181- chart . radiusLength = ( chart . outerRadius - chart . innerRadius ) / ( me . _getVisibleDatasetWeightTotal ( ) || 1 ) ;
182- chart . offsetX = offsetX * chart . outerRadius ;
183- chart . offsetY = offsetY * chart . outerRadius ;
181+ const { ratioX, ratioY, offsetX, offsetY} = getRatioAndOffset ( options . rotation , options . circumference , cutout ) ;
182+ const borderWidth = me . getMaxBorderWidth ( ) ;
183+ const maxWidth = ( chartArea . right - chartArea . left - borderWidth ) / ratioX ;
184+ const maxHeight = ( chartArea . bottom - chartArea . top - borderWidth ) / ratioY ;
185+ const outerRadius = Math . max ( Math . min ( maxWidth , maxHeight ) / 2 , 0 ) ;
186+ const innerRadius = Math . max ( outerRadius * cutout , 0 ) ;
187+ const radiusLength = ( outerRadius - innerRadius ) / me . _getVisibleDatasetWeightTotal ( ) ;
188+ me . offsetX = offsetX * outerRadius ;
189+ me . offsetY = offsetY * outerRadius ;
184190
185191 meta . total = me . calculateTotal ( ) ;
186192
187- me . outerRadius = chart . outerRadius - chart . radiusLength * me . _getRingWeightOffset ( me . index ) ;
188- me . innerRadius = Math . max ( me . outerRadius - chart . radiusLength * chartWeight , 0 ) ;
193+ me . outerRadius = outerRadius - radiusLength * me . _getRingWeightOffset ( me . index ) ;
194+ me . innerRadius = Math . max ( me . outerRadius - radiusLength * chartWeight , 0 ) ;
189195
190196 me . updateElements ( arcs , 0 , mode ) ;
191197 }
@@ -209,8 +215,12 @@ class DoughnutController extends DatasetController {
209215 const animationOpts = opts . animation ;
210216 const centerX = ( chartArea . left + chartArea . right ) / 2 ;
211217 const centerY = ( chartArea . top + chartArea . bottom ) / 2 ;
212- const innerRadius = reset && animationOpts . animateScale ? 0 : me . innerRadius ;
213- const outerRadius = reset && animationOpts . animateScale ? 0 : me . outerRadius ;
218+ const animateScale = reset && animationOpts . animateScale ;
219+ const innerRadius = animateScale ? 0 : me . innerRadius ;
220+ const outerRadius = animateScale ? 0 : me . outerRadius ;
221+ const firstOpts = me . _resolveDataElementOptions ( start , mode ) ;
222+ const sharedOptions = me . _getSharedOptions ( mode , arcs [ start ] , firstOpts ) ;
223+ const includeOptions = me . _includeOptions ( mode , sharedOptions ) ;
214224 let startAngle = opts . rotation ;
215225 let i ;
216226
@@ -222,21 +232,23 @@ class DoughnutController extends DatasetController {
222232 const index = start + i ;
223233 const circumference = me . _circumference ( index , reset ) ;
224234 const arc = arcs [ i ] ;
225- const options = arc . _options || { } ;
226235 const properties = {
227- x : centerX + chart . offsetX ,
228- y : centerY + chart . offsetY ,
236+ x : centerX + me . offsetX ,
237+ y : centerY + me . offsetY ,
229238 startAngle,
230239 endAngle : startAngle + circumference ,
231240 circumference,
232241 outerRadius,
233- innerRadius,
234- options
242+ innerRadius
235243 } ;
244+ if ( includeOptions ) {
245+ properties . options = me . _resolveDataElementOptions ( index , mode ) ;
246+ }
236247 startAngle += circumference ;
237248
238249 me . _updateElement ( arc , index , properties , mode ) ;
239250 }
251+ me . _updateSharedOptions ( sharedOptions , mode ) ;
240252 }
241253
242254 calculateTotal ( ) {
@@ -252,10 +264,6 @@ class DoughnutController extends DatasetController {
252264 }
253265 }
254266
255- /* if (total === 0) {
256- total = NaN;
257- }*/
258-
259267 return total ;
260268 }
261269
@@ -267,7 +275,6 @@ class DoughnutController extends DatasetController {
267275 return 0 ;
268276 }
269277
270- // gets the max border or hover width to properly scale pie charts
271278 getMaxBorderWidth ( arcs ) {
272279 const me = this ;
273280 let max = 0 ;
@@ -321,16 +328,16 @@ class DoughnutController extends DatasetController {
321328 /**
322329 * @private
323330 */
324- _getRingWeight ( dataSetIndex ) {
325- return Math . max ( valueOrDefault ( this . chart . data . datasets [ dataSetIndex ] . weight , 1 ) , 0 ) ;
331+ _getRingWeight ( datasetIndex ) {
332+ return Math . max ( valueOrDefault ( this . chart . data . datasets [ datasetIndex ] . weight , 1 ) , 0 ) ;
326333 }
327334
328335 /**
329- * Returns the sum of all visibile data set weights. This value can be 0.
336+ * Returns the sum of all visibile data set weights.
330337 * @private
331338 */
332339 _getVisibleDatasetWeightTotal ( ) {
333- return this . _getRingWeightOffset ( this . chart . data . datasets . length ) ;
340+ return this . _getRingWeightOffset ( this . chart . data . datasets . length ) || 1 ;
334341 }
335342}
336343
0 commit comments