| /* | 
 | * Licensed to the Apache Software Foundation (ASF) under one | 
 | * or more contributor license agreements.  See the NOTICE file | 
 | * distributed with this work for additional information | 
 | * regarding copyright ownership.  The ASF licenses this file | 
 | * to you under the Apache License, Version 2.0 (the | 
 | * "License"); you may not use this file except in compliance | 
 | * with the License.  You may obtain a copy of the License at | 
 | * | 
 | *   http://www.apache.org/licenses/LICENSE-2.0 | 
 | * | 
 | * Unless required by applicable law or agreed to in writing, | 
 | * software distributed under the License is distributed on an | 
 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
 | * KIND, either express or implied.  See the License for the | 
 | * specific language governing permissions and limitations | 
 | * under the License. | 
 | */ | 
 |  | 
 | import { parsePercent, linearMap } from '../../util/number'; | 
 | import * as layout from '../../util/layout'; | 
 | import * as zrUtil from 'zrender/src/core/util'; | 
 | import GlobalModel from '../../model/Global'; | 
 | import ExtensionAPI from '../../core/ExtensionAPI'; | 
 | import PieSeriesModel from './PieSeries'; | 
 | import { SectorShape } from 'zrender/src/graphic/shape/Sector'; | 
 |  | 
 | const PI2 = Math.PI * 2; | 
 | const RADIAN = Math.PI / 180; | 
 |  | 
 | function getViewRect(seriesModel: PieSeriesModel, api: ExtensionAPI) { | 
 |     return layout.getLayoutRect( | 
 |         seriesModel.getBoxLayoutParams(), { | 
 |             width: api.getWidth(), | 
 |             height: api.getHeight() | 
 |         } | 
 |     ); | 
 | } | 
 |  | 
 | export function getBasicPieLayout(seriesModel: PieSeriesModel, api: ExtensionAPI): | 
 |     Pick<SectorShape, 'cx' | 'cy' | 'r' | 'r0'> { | 
 |     const viewRect = getViewRect(seriesModel, api); | 
 |  | 
 |     // center can be string or number when coordinateSystem is specified | 
 |     let center = seriesModel.get('center'); | 
 |     let radius = seriesModel.get('radius'); | 
 |  | 
 |     if (!zrUtil.isArray(radius)) { | 
 |         radius = [0, radius]; | 
 |     } | 
 |     const width = parsePercent(viewRect.width, api.getWidth()); | 
 |     const height = parsePercent(viewRect.height, api.getHeight()); | 
 |     const size = Math.min(width, height); | 
 |     const r0 = parsePercent(radius[0], size / 2); | 
 |     const r = parsePercent(radius[1], size / 2); | 
 |  | 
 |     let cx: number; | 
 |     let cy: number; | 
 |     const coordSys = seriesModel.coordinateSystem; | 
 |     if (coordSys) { | 
 |         // percentage is not allowed when coordinate system is specified | 
 |         const point = coordSys.dataToPoint(center); | 
 |         cx = point[0] || 0; | 
 |         cy = point[1] || 0; | 
 |     } | 
 |     else { | 
 |         if (!zrUtil.isArray(center)) { | 
 |             center = [center, center]; | 
 |         } | 
 |         cx = parsePercent(center[0], width) + viewRect.x; | 
 |         cy = parsePercent(center[1], height) + viewRect.y; | 
 |     } | 
 |  | 
 |     return { | 
 |         cx, | 
 |         cy, | 
 |         r0, | 
 |         r | 
 |     }; | 
 | } | 
 |  | 
 | export default function pieLayout( | 
 |     seriesType: 'pie', | 
 |     ecModel: GlobalModel, | 
 |     api: ExtensionAPI | 
 | ) { | 
 |     ecModel.eachSeriesByType(seriesType, function (seriesModel: PieSeriesModel) { | 
 |         const data = seriesModel.getData(); | 
 |         const valueDim = data.mapDimension('value'); | 
 |         const viewRect = getViewRect(seriesModel, api); | 
 |  | 
 |         const { cx, cy, r, r0 } = getBasicPieLayout(seriesModel, api); | 
 |  | 
 |         const startAngle = -seriesModel.get('startAngle') * RADIAN; | 
 |  | 
 |         const minAngle = seriesModel.get('minAngle') * RADIAN; | 
 |  | 
 |         let validDataCount = 0; | 
 |         data.each(valueDim, function (value: number) { | 
 |             !isNaN(value) && validDataCount++; | 
 |         }); | 
 |  | 
 |         const sum = data.getSum(valueDim); | 
 |         // Sum may be 0 | 
 |         let unitRadian = Math.PI / (sum || validDataCount) * 2; | 
 |  | 
 |         const clockwise = seriesModel.get('clockwise'); | 
 |  | 
 |         const roseType = seriesModel.get('roseType'); | 
 |         const stillShowZeroSum = seriesModel.get('stillShowZeroSum'); | 
 |  | 
 |         // [0...max] | 
 |         const extent = data.getDataExtent(valueDim); | 
 |         extent[0] = 0; | 
 |  | 
 |         // In the case some sector angle is smaller than minAngle | 
 |         let restAngle = PI2; | 
 |         let valueSumLargerThanMinAngle = 0; | 
 |  | 
 |         let currentAngle = startAngle; | 
 |         const dir = clockwise ? 1 : -1; | 
 |  | 
 |         data.setLayout({ viewRect, r }); | 
 |  | 
 |         data.each(valueDim, function (value: number, idx: number) { | 
 |             let angle; | 
 |             if (isNaN(value)) { | 
 |                 data.setItemLayout(idx, { | 
 |                     angle: NaN, | 
 |                     startAngle: NaN, | 
 |                     endAngle: NaN, | 
 |                     clockwise: clockwise, | 
 |                     cx: cx, | 
 |                     cy: cy, | 
 |                     r0: r0, | 
 |                     r: roseType | 
 |                         ? NaN | 
 |                         : r | 
 |                 }); | 
 |                 return; | 
 |             } | 
 |  | 
 |             // FIXME 兼容 2.0 但是 roseType 是 area 的时候才是这样? | 
 |             if (roseType !== 'area') { | 
 |                 angle = (sum === 0 && stillShowZeroSum) | 
 |                     ? unitRadian : (value * unitRadian); | 
 |             } | 
 |             else { | 
 |                 angle = PI2 / validDataCount; | 
 |             } | 
 |  | 
 |             if (angle < minAngle) { | 
 |                 angle = minAngle; | 
 |                 restAngle -= minAngle; | 
 |             } | 
 |             else { | 
 |                 valueSumLargerThanMinAngle += value; | 
 |             } | 
 |  | 
 |             const endAngle = currentAngle + dir * angle; | 
 |             data.setItemLayout(idx, { | 
 |                 angle: angle, | 
 |                 startAngle: currentAngle, | 
 |                 endAngle: endAngle, | 
 |                 clockwise: clockwise, | 
 |                 cx: cx, | 
 |                 cy: cy, | 
 |                 r0: r0, | 
 |                 r: roseType | 
 |                     ? linearMap(value, extent, [r0, r]) | 
 |                     : r | 
 |             }); | 
 |  | 
 |             currentAngle = endAngle; | 
 |         }); | 
 |  | 
 |         // Some sector is constrained by minAngle | 
 |         // Rest sectors needs recalculate angle | 
 |         if (restAngle < PI2 && validDataCount) { | 
 |             // Average the angle if rest angle is not enough after all angles is | 
 |             // Constrained by minAngle | 
 |             if (restAngle <= 1e-3) { | 
 |                 const angle = PI2 / validDataCount; | 
 |                 data.each(valueDim, function (value: number, idx: number) { | 
 |                     if (!isNaN(value)) { | 
 |                         const layout = data.getItemLayout(idx); | 
 |                         layout.angle = angle; | 
 |                         layout.startAngle = startAngle + dir * idx * angle; | 
 |                         layout.endAngle = startAngle + dir * (idx + 1) * angle; | 
 |                     } | 
 |                 }); | 
 |             } | 
 |             else { | 
 |                 unitRadian = restAngle / valueSumLargerThanMinAngle; | 
 |                 currentAngle = startAngle; | 
 |                 data.each(valueDim, function (value: number, idx: number) { | 
 |                     if (!isNaN(value)) { | 
 |                         const layout = data.getItemLayout(idx); | 
 |                         const angle = layout.angle === minAngle | 
 |                             ? minAngle : value * unitRadian; | 
 |                         layout.startAngle = currentAngle; | 
 |                         layout.endAngle = currentAngle + dir * angle; | 
 |                         currentAngle += dir * angle; | 
 |                     } | 
 |                 }); | 
 |             } | 
 |         } | 
 |     }); | 
 | } |