@@ -5,6 +5,10 @@ import defaults from '../core/core.defaults';
55import Arc from '../elements/element.arc' ;
66import { isArray , valueOrDefault } from '../helpers/helpers.core' ;
77
8+ /**
9+ * @typedef { import("../core/core.controller").default } Chart
10+ */
11+
812const PI = Math . PI ;
913const DOUBLE_PI = PI * 2 ;
1014const HALF_PI = PI / 2 ;
@@ -96,13 +100,45 @@ defaults.set('doughnut', {
96100 }
97101} ) ;
98102
103+ function getRatioAndOffset ( rotation , circumference , cutout ) {
104+ let ratioX = 1 ;
105+ let ratioY = 1 ;
106+ let offsetX = 0 ;
107+ let offsetY = 0 ;
108+ // If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc
109+ if ( circumference < DOUBLE_PI ) {
110+ let startAngle = rotation % DOUBLE_PI ;
111+ startAngle += startAngle >= PI ? - DOUBLE_PI : startAngle < - PI ? DOUBLE_PI : 0 ;
112+ const endAngle = startAngle + circumference ;
113+ const startX = Math . cos ( startAngle ) ;
114+ const startY = Math . sin ( startAngle ) ;
115+ const endX = Math . cos ( endAngle ) ;
116+ const endY = Math . sin ( endAngle ) ;
117+ const contains0 = ( startAngle <= 0 && endAngle >= 0 ) || endAngle >= DOUBLE_PI ;
118+ const contains90 = ( startAngle <= HALF_PI && endAngle >= HALF_PI ) || endAngle >= DOUBLE_PI + HALF_PI ;
119+ const contains180 = startAngle === - PI || endAngle >= PI ;
120+ const contains270 = ( startAngle <= - HALF_PI && endAngle >= - HALF_PI ) || endAngle >= PI + HALF_PI ;
121+ const minX = contains180 ? - 1 : Math . min ( startX , startX * cutout , endX , endX * cutout ) ;
122+ const minY = contains270 ? - 1 : Math . min ( startY , startY * cutout , endY , endY * cutout ) ;
123+ const maxX = contains0 ? 1 : Math . max ( startX , startX * cutout , endX , endX * cutout ) ;
124+ const maxY = contains90 ? 1 : Math . max ( startY , startY * cutout , endY , endY * cutout ) ;
125+ ratioX = ( maxX - minX ) / 2 ;
126+ ratioY = ( maxY - minY ) / 2 ;
127+ offsetX = - ( maxX + minX ) / 2 ;
128+ offsetY = - ( maxY + minY ) / 2 ;
129+ }
130+ return { ratioX, ratioY, offsetX, offsetY} ;
131+ }
132+
99133class DoughnutController extends DatasetController {
100134
101135 constructor ( chart , datasetIndex ) {
102136 super ( chart , datasetIndex ) ;
103137
104138 this . innerRadius = undefined ;
105139 this . outerRadius = undefined ;
140+ this . offsetX = undefined ;
141+ this . offsetY = undefined ;
106142 }
107143
108144 linkScales ( ) { }
@@ -133,62 +169,33 @@ class DoughnutController extends DatasetController {
133169 return ringIndex ;
134170 }
135171
172+ /**
173+ * @param {string } mode
174+ */
136175 update ( mode ) {
137- var me = this ;
138- var chart = me . chart ;
139- var chartArea = chart . chartArea ;
140- var opts = chart . options ;
141- var ratioX = 1 ;
142- var ratioY = 1 ;
143- var offsetX = 0 ;
144- var offsetY = 0 ;
145- var meta = me . _cachedMeta ;
146- var arcs = meta . data ;
147- var cutout = opts . cutoutPercentage / 100 || 0 ;
148- var circumference = opts . circumference ;
149- var chartWeight = me . _getRingWeight ( me . index ) ;
150- var maxWidth , maxHeight , i , ilen ;
151-
152- // If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc
153- if ( circumference < DOUBLE_PI ) {
154- var startAngle = opts . rotation % DOUBLE_PI ;
155- startAngle += startAngle >= PI ? - DOUBLE_PI : startAngle < - PI ? DOUBLE_PI : 0 ;
156- var endAngle = startAngle + circumference ;
157- var startX = Math . cos ( startAngle ) ;
158- var startY = Math . sin ( startAngle ) ;
159- var endX = Math . cos ( endAngle ) ;
160- var endY = Math . sin ( endAngle ) ;
161- var contains0 = ( startAngle <= 0 && endAngle >= 0 ) || endAngle >= DOUBLE_PI ;
162- var contains90 = ( startAngle <= HALF_PI && endAngle >= HALF_PI ) || endAngle >= DOUBLE_PI + HALF_PI ;
163- var contains180 = startAngle === - PI || endAngle >= PI ;
164- var contains270 = ( startAngle <= - HALF_PI && endAngle >= - HALF_PI ) || endAngle >= PI + HALF_PI ;
165- var minX = contains180 ? - 1 : Math . min ( startX , startX * cutout , endX , endX * cutout ) ;
166- var minY = contains270 ? - 1 : Math . min ( startY , startY * cutout , endY , endY * cutout ) ;
167- var maxX = contains0 ? 1 : Math . max ( startX , startX * cutout , endX , endX * cutout ) ;
168- var maxY = contains90 ? 1 : Math . max ( startY , startY * cutout , endY , endY * cutout ) ;
169- ratioX = ( maxX - minX ) / 2 ;
170- ratioY = ( maxY - minY ) / 2 ;
171- offsetX = - ( maxX + minX ) / 2 ;
172- offsetY = - ( maxY + minY ) / 2 ;
173- }
174-
175- for ( i = 0 , ilen = arcs . length ; i < ilen ; ++ i ) {
176- arcs [ i ] . _options = me . _resolveDataElementOptions ( i , mode ) ;
177- }
178-
179- chart . borderWidth = me . getMaxBorderWidth ( ) ;
180- maxWidth = ( chartArea . right - chartArea . left - chart . borderWidth ) / ratioX ;
181- maxHeight = ( chartArea . bottom - chartArea . top - chart . borderWidth ) / ratioY ;
182- chart . outerRadius = Math . max ( Math . min ( maxWidth , maxHeight ) / 2 , 0 ) ;
183- chart . innerRadius = Math . max ( chart . outerRadius * cutout , 0 ) ;
184- chart . radiusLength = ( chart . outerRadius - chart . innerRadius ) / ( me . _getVisibleDatasetWeightTotal ( ) || 1 ) ;
185- chart . offsetX = offsetX * chart . outerRadius ;
186- chart . offsetY = offsetY * chart . outerRadius ;
176+ const me = this ;
177+ /** @type {Chart } */
178+ const chart = me . chart ;
179+ const { chartArea, options} = chart ;
180+ const meta = me . _cachedMeta ;
181+ /** @type {Arc[] } */
182+ const arcs = meta . data ;
183+ const cutout = options . cutoutPercentage / 100 || 0 ;
184+ const chartWeight = me . _getRingWeight ( me . index ) ;
185+ const { ratioX, ratioY, offsetX, offsetY} = getRatioAndOffset ( options . rotation , options . circumference , cutout ) ;
186+ const borderWidth = me . getMaxBorderWidth ( ) ;
187+ const maxWidth = ( chartArea . right - chartArea . left - borderWidth ) / ratioX ;
188+ const maxHeight = ( chartArea . bottom - chartArea . top - borderWidth ) / ratioY ;
189+ const outerRadius = Math . max ( Math . min ( maxWidth , maxHeight ) / 2 , 0 ) ;
190+ const innerRadius = Math . max ( outerRadius * cutout , 0 ) ;
191+ const radiusLength = ( outerRadius - innerRadius ) / ( me . _getVisibleDatasetWeightTotal ( ) || 1 ) ;
192+ me . offsetX = offsetX * outerRadius ;
193+ me . offsetY = offsetY * outerRadius ;
187194
188195 meta . total = me . calculateTotal ( ) ;
189196
190- me . outerRadius = chart . outerRadius - chart . radiusLength * me . _getRingWeightOffset ( me . index ) ;
191- me . innerRadius = Math . max ( me . outerRadius - chart . radiusLength * chartWeight , 0 ) ;
197+ me . outerRadius = outerRadius - radiusLength * me . _getRingWeightOffset ( me . index ) ;
198+ me . innerRadius = Math . max ( me . outerRadius - radiusLength * chartWeight , 0 ) ;
192199
193200 me . updateElements ( arcs , 0 , mode ) ;
194201 }
@@ -203,9 +210,15 @@ class DoughnutController extends DatasetController {
203210 return reset && opts . animation . animateRotate ? 0 : meta . data [ i ] . hidden ? 0 : me . calculateCircumference ( meta . _parsed [ i ] * opts . circumference / DOUBLE_PI ) ;
204211 }
205212
213+ /**
214+ * @param {Arc[] } arcs
215+ * @param {number } start
216+ * @param {string } mode
217+ */
206218 updateElements ( arcs , start , mode ) {
207219 const me = this ;
208220 const reset = mode === 'reset' ;
221+ /** @type {Chart } */
209222 const chart = me . chart ;
210223 const chartArea = chart . chartArea ;
211224 const opts = chart . options ;
@@ -214,6 +227,9 @@ class DoughnutController extends DatasetController {
214227 const centerY = ( chartArea . top + chartArea . bottom ) / 2 ;
215228 const innerRadius = reset && animationOpts . animateScale ? 0 : me . innerRadius ;
216229 const outerRadius = reset && animationOpts . animateScale ? 0 : me . outerRadius ;
230+ const firstOpts = me . _resolveDataElementOptions ( start , mode ) ;
231+ const sharedOptions = me . _getSharedOptions ( mode , arcs [ start ] , firstOpts ) ;
232+ const includeOptions = me . _includeOptions ( mode , sharedOptions ) ;
217233 let startAngle = opts . rotation ;
218234 let i ;
219235
@@ -225,21 +241,23 @@ class DoughnutController extends DatasetController {
225241 const index = start + i ;
226242 const circumference = me . _circumference ( index , reset ) ;
227243 const arc = arcs [ i ] ;
228- const options = arc . _options || { } ;
229244 const properties = {
230- x : centerX + chart . offsetX ,
231- y : centerY + chart . offsetY ,
245+ x : centerX + me . offsetX ,
246+ y : centerY + me . offsetY ,
232247 startAngle,
233248 endAngle : startAngle + circumference ,
234249 circumference,
235250 outerRadius,
236- innerRadius,
237- options
251+ innerRadius
238252 } ;
253+ if ( includeOptions ) {
254+ properties . options = me . _resolveDataElementOptions ( index , mode ) ;
255+ }
239256 startAngle += circumference ;
240257
241258 me . _updateElement ( arc , index , properties , mode ) ;
242259 }
260+ me . _updateSharedOptions ( sharedOptions , mode ) ;
243261 }
244262
245263 calculateTotal ( ) {
@@ -255,13 +273,12 @@ class DoughnutController extends DatasetController {
255273 }
256274 }
257275
258- /* if (total === 0) {
259- total = NaN;
260- }*/
261-
262276 return total ;
263277 }
264278
279+ /**
280+ * @param {number } value
281+ */
265282 calculateCircumference ( value ) {
266283 var total = this . _cachedMeta . total ;
267284 if ( total > 0 && ! isNaN ( value ) ) {
@@ -270,7 +287,10 @@ class DoughnutController extends DatasetController {
270287 return 0 ;
271288 }
272289
273- // gets the max border or hover width to properly scale pie charts
290+ /**
291+ * gets the max border or hover width to properly scale pie charts
292+ * @param {Arc[] } [arcs]
293+ */
274294 getMaxBorderWidth ( arcs ) {
275295 var me = this ;
276296 var max = 0 ;
@@ -307,6 +327,7 @@ class DoughnutController extends DatasetController {
307327
308328 /**
309329 * Get radius length offset of the dataset in relation to the visible datasets weights. This allows determining the inner and outer radius correctly
330+ * @param {number } datasetIndex
310331 * @private
311332 */
312333 _getRingWeightOffset ( datasetIndex ) {
@@ -322,10 +343,11 @@ class DoughnutController extends DatasetController {
322343 }
323344
324345 /**
346+ * @param {number } datasetIndex
325347 * @private
326348 */
327- _getRingWeight ( dataSetIndex ) {
328- return Math . max ( valueOrDefault ( this . chart . data . datasets [ dataSetIndex ] . weight , 1 ) , 0 ) ;
349+ _getRingWeight ( datasetIndex ) {
350+ return Math . max ( valueOrDefault ( this . chart . data . datasets [ datasetIndex ] . weight , 1 ) , 0 ) ;
329351 }
330352
331353 /**
0 commit comments