1/* * 2 * 3 * (c) 2010-2024 Torstein Honsi 4 * 5 * License: www.highcharts.com/license 6 * 7 * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! 8 * 9 * */ 10'use strict'; 11import CU from '../CenteredUtilities.js'; 12const { getStartAndEndRadians } = CU; 13import ColumnSeries from '../Column/ColumnSeries.js'; 14import H from '../../Core/Globals.js'; 15const { noop } = H; 16import PiePoint from './PiePoint.js'; 17import PieSeriesDefaults from './PieSeriesDefaults.js'; 18import Series from '../../Core/Series/Series.js'; 19import SeriesRegistry from '../../Core/Series/SeriesRegistry.js'; 20import Symbols from '../../Core/Renderer/SVG/Symbols.js'; 21import U from '../../Core/Utilities.js'; 22const { clamp, extend, fireEvent, merge, pick } = U; 23/* * 24 * 25 * Class 26 * 27 * */ 28/** 29 * Pie series type. 30 * 31 * @private 32 * @class 33 * @name Highcharts.seriesTypes.pie 34 * 35 * @augments Highcharts.Series 36 */ 37class PieSeries extends Series { 38 /* * 39 * 40 * Functions 41 * 42 * */ 43 /* eslint-disable valid-jsdoc */ 44 /** 45 * Animates the pies in. 46 * @private 47 */ 48 animate(init) { 49 const series = this, points = series.points, startAngleRad = series.startAngleRad; 50 if (!init) { 51 points.forEach(function (point) { 52 const graphic = point.graphic, args = point.shapeArgs; 53 if (graphic && args) { 54 // Start values 55 graphic.attr({ 56 // Animate from inner radius (#779) 57 r: pick(point.startR, (series.center && series.center[3] / 2)), 58 start: startAngleRad, 59 end: startAngleRad 60 }); 61 // Animate 62 graphic.animate({ 63 r: args.r, 64 start: args.start, 65 end: args.end 66 }, series.options.animation); 67 } 68 }); 69 } 70 } 71 /** 72 * Called internally to draw auxiliary graph in pie-like series in 73 * situtation when the default graph is not sufficient enough to present 74 * the data well. Auxiliary graph is saved in the same object as 75 * regular graph. 76 * @private 77 */ 78 drawEmpty() { 79 const start = this.startAngleRad, end = this.endAngleRad, options = this.options; 80 let centerX, centerY; 81 // Draw auxiliary graph if there're no visible points. 82 if (this.total === 0 && this.center) { 83 centerX = this.center[0]; 84 centerY = this.center[1]; 85 if (!this.graph) { 86 this.graph = this.chart.renderer 87 .arc(centerX, centerY, this.center[1] / 2, 0, start, end) 88 .addClass('highcharts-empty-series') 89 .add(this.group); 90 } 91 this.graph.attr({ 92 d: Symbols.arc(centerX, centerY, this.center[2] / 2, 0, { 93 start, 94 end, 95 innerR: this.center[3] / 2 96 }) 97 }); 98 if (!this.chart.styledMode) { 99 this.graph.attr({ 100 'stroke-width': options.borderWidth, 101 fill: options.fillColor || 'none', 102 stroke: options.color || "#cccccc" /* Palette.neutralColor20 */ 103 }); 104 } 105 } 106 else if (this.graph) { // Destroy the graph object. 107 this.graph = this.graph.destroy(); 108 } 109 } 110 /** 111 * Slices in pie chart are initialized in DOM, but it's shapes and 112 * animations are normally run in `drawPoints()`. 113 * @private 114 */ 115 drawPoints() { 116 const renderer = this.chart.renderer; 117 this.points.forEach(function (point) { 118 // When updating a series between 2d and 3d or cartesian and 119 // polar, the shape type changes. 120 if (point.graphic && point.hasNewShapeType()) { 121 point.graphic = point.graphic.destroy(); 122 } 123 if (!point.graphic) { 124 point.graphic = renderer[point.shapeType](point.shapeArgs) 125 .add(point.series.group); 126 point.delayedRendering = true; 127 } 128 }); 129 } 130 /** 131 * Extend the generatePoints method by adding total and percentage 132 * properties to each point 133 * @private 134 */ 135 generatePoints() { 136 super.generatePoints(); 137 this.updateTotals(); 138 } 139 /** 140 * Utility for getting the x value from a given y, used for anticollision 141 * logic in data labels. 142 * @private 143 */ 144 getX(y, left, point, dataLabel) { 145 const center = this.center, 146 // Variable pie has individual radius 147 radius = this.radii ? 148 this.radii[point.index] || 0 : 149 center[2] / 2, labelPosition = dataLabel.dataLabelPosition, distance = labelPosition?.distance || 0; 150 const angle = Math.asin(clamp((y - center[1]) / (radius + distance), -1, 1)); 151 const x = center[0] + 152 (left ? -1 : 1) * 153 (Math.cos(angle) * (radius + distance)) + 154 (distance > 0 ? 155 (left ? -1 : 1) * (dataLabel.padding || 0) : 156 0); 157 return x; 158 } 159 /** 160 * Define hasData function for non-cartesian series. Returns true if the 161 * series has points at all. 162 * @private 163 */ 164 hasData() { 165 return !!this.dataTable.rowCount; 166 } 167 /** 168 * Draw the data points 169 * @private 170 */ 171 redrawPoints() { 172 const series = this, chart = series.chart; 173 let groupTranslation, graphic, pointAttr, shapeArgs; 174 this.drawEmpty(); 175 // Apply the drop-shadow to the group because otherwise each element 176 // would cast a shadow on others 177 if (series.group && !chart.styledMode) { 178 series.group.shadow(series.options.shadow); 179 } 180 // Draw the slices
vendor: 6,905 bytes, lines 181-365
181 series.points.forEach(function (point) { 182 const animateTo = {}; 183 graphic = point.graphic; 184 if (!point.isNull && graphic) { 185 shapeArgs = point.shapeArgs; 186 // If the point is sliced, use special translation, else use 187 // plot area translation 188 groupTranslation = point.getTranslate(); 189 if (!chart.styledMode) { 190 pointAttr = series.pointAttribs(point, (point.selected && 'select')); 191 } 192 // Draw the slice 193 if (!point.delayedRendering) { 194 graphic 195 .setRadialReference(series.center); 196 if (!chart.styledMode) { 197 merge(true, animateTo, pointAttr); 198 } 199 merge(true, animateTo, shapeArgs, groupTranslation); 200 graphic.animate(animateTo); 201 } 202 else { 203 graphic 204 .setRadialReference(series.center) 205 .attr(shapeArgs) 206 .attr(groupTranslation); 207 if (!chart.styledMode) { 208 graphic 209 .attr(pointAttr) 210 .attr({ 'stroke-linejoin': 'round' }); 211 } 212 point.delayedRendering = false; 213 } 214 graphic 215 .attr({ 216 visibility: point.visible ? 'inherit' : 'hidden' 217 }); 218 graphic.addClass(point.getClassName(), true); 219 } 220 else if (graphic) { 221 point.graphic = graphic.destroy(); 222 } 223 }); 224 } 225 /** 226 * Utility for sorting data labels. 227 * @private 228 */ 229 sortByAngle(points, sign) { 230 points.sort(function (a, b) { 231 return ((typeof a.angle !== 'undefined') && 232 (b.angle - a.angle) * sign); 233 }); 234 } 235 /** 236 * Do translation for pie slices 237 * @private 238 */ 239 translate(positions) { 240 fireEvent(this, 'translate'); 241 this.generatePoints(); 242 const series = this, precision = 1000, // Issue #172 243 options = series.options, slicedOffset = options.slicedOffset, radians = getStartAndEndRadians(options.startAngle, options.endAngle), startAngleRad = series.startAngleRad = radians.start, endAngleRad = series.endAngleRad = radians.end, circ = endAngleRad - startAngleRad, // 2 * Math.PI, 244 points = series.points, ignoreHiddenPoint = options.ignoreHiddenPoint, len = points.length; 245 let start, end, angle, 246 // The x component of the radius vector for a given point 247 radiusX, radiusY, i, point, cumulative = 0; 248 // Get positions - either an integer or a percentage string must be 249 // given. If positions are passed as a parameter, we're in a 250 // recursive loop for adjusting space for data labels. 251 if (!positions) { 252 /** 253 * The series center position, read only. This applies only to 254 * circular chart types like pie and sunburst. It is an array of 255 * `[centerX, centerY, diameter, innerDiameter]`. 256 * 257 * @name Highcharts.Series#center 258 * @type {Array<number>} 259 */ 260 series.center = positions = series.getCenter(); 261 } 262 // Calculate the geometry for each point 263 for (i = 0; i < len; i++) { 264 point = points[i]; 265 // Set start and end angle 266 start = startAngleRad + (cumulative * circ); 267 if (point.isValid() && 268 (!ignoreHiddenPoint || point.visible)) { 269 cumulative += point.percentage / 100; 270 } 271 end = startAngleRad + (cumulative * circ); 272 // Set the shape 273 const shapeArgs = { 274 x: positions[0], 275 y: positions[1], 276 r: positions[2] / 2, 277 innerR: positions[3] / 2, 278 start: Math.round(start * precision) / precision, 279 end: Math.round(end * precision) / precision 280 }; 281 point.shapeType = 'arc'; 282 point.shapeArgs = shapeArgs; 283 // The angle must stay within -90 and 270 (#2645) 284 angle = (end + start) / 2; 285 if (angle > 1.5 * Math.PI) { 286 angle -= 2 * Math.PI; 287 } 288 else if (angle < -Math.PI / 2) { 289 angle += 2 * Math.PI; 290 } 291 // Center for the sliced out slice 292 point.slicedTranslation = { 293 translateX: Math.round(Math.cos(angle) * slicedOffset), 294 translateY: Math.round(Math.sin(angle) * slicedOffset) 295 }; 296 // Set the anchor point for tooltips 297 radiusX = Math.cos(angle) * positions[2] / 2; 298 radiusY = Math.sin(angle) * positions[2] / 2; 299 point.tooltipPos = [ 300 positions[0] + radiusX * 0.7, 301 positions[1] + radiusY * 0.7 302 ]; 303 point.half = angle < -Math.PI / 2 || angle > Math.PI / 2 ? 304 1 : 305 0; 306 point.angle = angle; 307 } 308 fireEvent(series, 'afterTranslate'); 309 } 310 /** 311 * Recompute total chart sum and update percentages of points. 312 * @private 313 */ 314 updateTotals() { 315 const points = this.points, len = points.length, ignoreHiddenPoint = this.options.ignoreHiddenPoint; 316 let i, point, total = 0; 317 // Get the total sum 318 for (i = 0; i < len; i++) { 319 point = points[i]; 320 if (point.isValid() && 321 (!ignoreHiddenPoint || point.visible)) { 322 total += point.y; 323 } 324 } 325 this.total = total; 326 // Set each point's properties 327 for (i = 0; i < len; i++) { 328 point = points[i]; 329 point.percentage = 330 (total > 0 && (point.visible || !ignoreHiddenPoint)) ? 331 point.y / total * 100 : 332 0; 333 point.total = total; 334 } 335 } 336} 337/* * 338 * 339 * Static Properties 340 * 341 * */ 342PieSeries.defaultOptions = merge(Series.defaultOptions, PieSeriesDefaults); 343extend(PieSeries.prototype, { 344 axisTypes: [], 345 directTouch: true, 346 drawGraph: void 0, 347 drawTracker: ColumnSeries.prototype.drawTracker, 348 getCenter: CU.getCenter, 349 getSymbol: noop, 350 invertible: false, 351 isCartesian: false, 352 noSharedTooltip: true, 353 pointAttribs: ColumnSeries.prototype.pointAttribs, 354 pointClass: PiePoint, 355 requireSorting: false, 356 searchPoint: noop, 357 trackerGroups: ['group', 'dataLabelsGroup'] 358}); 359SeriesRegistry.registerSeriesType('pie', PieSeries); 360/* * 361 * 362 * Default Export 363 * 364 * */ 365export default PieSeries;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.