[483] | 1 | define(["dojo/_base/lang", "dojo/_base/array" ,"dojo/_base/declare", |
---|
| 2 | "./Base", "./_PlotEvents", "./common", |
---|
| 3 | "dojox/gfx", "dojox/gfx/matrix", "dojox/lang/functional", "dojox/lang/utils","dojo/has"], |
---|
| 4 | function(lang, arr, declare, Base, PlotEvents, dc, g, m, df, du, has){ |
---|
| 5 | |
---|
| 6 | /*===== |
---|
| 7 | declare("dojox.charting.plot2d.__PieCtorArgs", dojox.charting.plot2d.__DefaultCtorArgs, { |
---|
| 8 | // summary: |
---|
| 9 | // Specialized keyword arguments object for use in defining parameters on a Pie chart. |
---|
| 10 | |
---|
| 11 | // labels: Boolean? |
---|
| 12 | // Whether or not to draw labels for each pie slice. Default is true. |
---|
| 13 | labels: true, |
---|
| 14 | |
---|
| 15 | // ticks: Boolean? |
---|
| 16 | // Whether or not to draw ticks to labels within each slice. Default is false. |
---|
| 17 | ticks: false, |
---|
| 18 | |
---|
| 19 | // fixed: Boolean? |
---|
| 20 | // Whether a fixed precision must be applied to data values for display. Default is true. |
---|
| 21 | fixed: true, |
---|
| 22 | |
---|
| 23 | // precision: Number? |
---|
| 24 | // The precision at which to round data values for display. Default is 0. |
---|
| 25 | precision: 1, |
---|
| 26 | |
---|
| 27 | // labelOffset: Number? |
---|
| 28 | // The amount in pixels by which to offset labels. Default is 20. |
---|
| 29 | labelOffset: 20, |
---|
| 30 | |
---|
| 31 | // labelStyle: String? |
---|
| 32 | // Options as to where to draw labels. Values include "default", and "columns". Default is "default". |
---|
| 33 | labelStyle: "default", // default/columns |
---|
| 34 | |
---|
| 35 | // omitLabels: Boolean? |
---|
| 36 | // Whether labels of slices small to the point of not being visible are omitted. Default false. |
---|
| 37 | omitLabels: false, |
---|
| 38 | |
---|
| 39 | // htmlLabels: Boolean? |
---|
| 40 | // Whether or not to use HTML to render slice labels. Default is true. |
---|
| 41 | htmlLabels: true, |
---|
| 42 | |
---|
| 43 | // radGrad: String? |
---|
| 44 | // The type of radial gradient to use in rendering. Default is "native". |
---|
| 45 | radGrad: "native", |
---|
| 46 | |
---|
| 47 | // fanSize: Number? |
---|
| 48 | // The amount for a radial gradient. Default is 5. |
---|
| 49 | fanSize: 5, |
---|
| 50 | |
---|
| 51 | // startAngle: Number? |
---|
| 52 | // Where to being rendering gradients in slices, in degrees. Default is 0. |
---|
| 53 | startAngle: 0, |
---|
| 54 | |
---|
| 55 | // radius: Number? |
---|
| 56 | // The size of the radial gradient. Default is 0. |
---|
| 57 | radius: 0, |
---|
| 58 | |
---|
| 59 | // shadow: dojox.gfx.Stroke? |
---|
| 60 | // An optional stroke to use to draw any shadows for a series on a plot. |
---|
| 61 | shadow: {}, |
---|
| 62 | |
---|
| 63 | // fill: dojox.gfx.Fill? |
---|
| 64 | // Any fill to be used for elements on the plot. |
---|
| 65 | fill: {}, |
---|
| 66 | |
---|
| 67 | // filter: dojox.gfx.Filter? |
---|
| 68 | // An SVG filter to be used for elements on the plot. gfx SVG renderer must be used and dojox/gfx/svgext must |
---|
| 69 | // be required for this to work. |
---|
| 70 | filter: {}, |
---|
| 71 | |
---|
| 72 | // styleFunc: Function? |
---|
| 73 | // A function that returns a styling object for the a given data item. |
---|
| 74 | styleFunc: null |
---|
| 75 | }); |
---|
| 76 | =====*/ |
---|
| 77 | |
---|
| 78 | var FUDGE_FACTOR = 0.2; // use to overlap fans |
---|
| 79 | |
---|
| 80 | return declare("dojox.charting.plot2d.Pie", [Base, PlotEvents], { |
---|
| 81 | // summary: |
---|
| 82 | // The plot that represents a typical pie chart. |
---|
| 83 | defaultParams: { |
---|
| 84 | labels: true, |
---|
| 85 | ticks: false, |
---|
| 86 | fixed: true, |
---|
| 87 | precision: 1, |
---|
| 88 | labelOffset: 20, |
---|
| 89 | labelStyle: "default", // default/columns |
---|
| 90 | htmlLabels: true, // use HTML to draw labels |
---|
| 91 | radGrad: "native", // or "linear", or "fan" |
---|
| 92 | fanSize: 5, // maximum fan size in degrees |
---|
| 93 | startAngle: 0 // start angle for slices in degrees |
---|
| 94 | }, |
---|
| 95 | optionalParams: { |
---|
| 96 | radius: 0, |
---|
| 97 | omitLabels: false, |
---|
| 98 | // theme components |
---|
| 99 | stroke: {}, |
---|
| 100 | outline: {}, |
---|
| 101 | shadow: {}, |
---|
| 102 | fill: {}, |
---|
| 103 | filter: {}, |
---|
| 104 | styleFunc: null, |
---|
| 105 | font: "", |
---|
| 106 | fontColor: "", |
---|
| 107 | labelWiring: {} |
---|
| 108 | }, |
---|
| 109 | |
---|
| 110 | constructor: function(chart, kwArgs){ |
---|
| 111 | // summary: |
---|
| 112 | // Create a pie plot. |
---|
| 113 | this.opt = lang.clone(this.defaultParams); |
---|
| 114 | du.updateWithObject(this.opt, kwArgs); |
---|
| 115 | du.updateWithPattern(this.opt, kwArgs, this.optionalParams); |
---|
| 116 | this.axes = []; |
---|
| 117 | this.run = null; |
---|
| 118 | this.dyn = []; |
---|
| 119 | }, |
---|
| 120 | clear: function(){ |
---|
| 121 | // summary: |
---|
| 122 | // Clear out all of the information tied to this plot. |
---|
| 123 | // returns: dojox/charting/plot2d/Pie |
---|
| 124 | // A reference to this plot for functional chaining. |
---|
| 125 | this.inherited(arguments); |
---|
| 126 | this.dyn = []; |
---|
| 127 | this.run = null; |
---|
| 128 | return this; // dojox/charting/plot2d/Pie |
---|
| 129 | }, |
---|
| 130 | setAxis: function(axis){ |
---|
| 131 | // summary: |
---|
| 132 | // Dummy method, since axes are irrelevant with a Pie chart. |
---|
| 133 | // returns: dojox/charting/plot2d/Pie |
---|
| 134 | // The reference to this plot for functional chaining. |
---|
| 135 | return this; // dojox/charting/plot2d/Pie |
---|
| 136 | }, |
---|
| 137 | addSeries: function(run){ |
---|
| 138 | // summary: |
---|
| 139 | // Add a series of data to this plot. |
---|
| 140 | // returns: dojox/charting/plot2d/Pie |
---|
| 141 | // The reference to this plot for functional chaining. |
---|
| 142 | this.run = run; |
---|
| 143 | return this; // dojox/charting/plot2d/Pie |
---|
| 144 | }, |
---|
| 145 | getSeriesStats: function(){ |
---|
| 146 | // summary: |
---|
| 147 | // Returns default stats (irrelevant for this type of plot). |
---|
| 148 | // returns: Object |
---|
| 149 | // {hmin, hmax, vmin, vmax} min/max in both directions. |
---|
| 150 | return lang.delegate(dc.defaultStats); // Object |
---|
| 151 | }, |
---|
| 152 | getRequiredColors: function(){ |
---|
| 153 | // summary: |
---|
| 154 | // Return the number of colors needed to draw this plot. |
---|
| 155 | return this.run ? this.run.data.length : 0; |
---|
| 156 | }, |
---|
| 157 | render: function(dim, offsets){ |
---|
| 158 | // summary: |
---|
| 159 | // Render the plot on the chart. |
---|
| 160 | // dim: Object |
---|
| 161 | // An object of the form { width, height }. |
---|
| 162 | // offsets: Object |
---|
| 163 | // An object of the form { l, r, t, b }. |
---|
| 164 | // returns: dojox/charting/plot2d/Pie |
---|
| 165 | // A reference to this plot for functional chaining. |
---|
| 166 | if(!this.dirty){ return this; } |
---|
| 167 | this.resetEvents(); |
---|
| 168 | this.dirty = false; |
---|
| 169 | this._eventSeries = {}; |
---|
| 170 | this.cleanGroup(); |
---|
| 171 | var s = this.group, t = this.chart.theme; |
---|
| 172 | |
---|
| 173 | if(!this.run || !this.run.data.length){ |
---|
| 174 | return this; |
---|
| 175 | } |
---|
| 176 | |
---|
| 177 | // calculate the geometry |
---|
| 178 | var rx = (dim.width - offsets.l - offsets.r) / 2, |
---|
| 179 | ry = (dim.height - offsets.t - offsets.b) / 2, |
---|
| 180 | r = Math.min(rx, ry), |
---|
| 181 | labelFont = "font" in this.opt ? this.opt.font : t.series.font, |
---|
| 182 | size, |
---|
| 183 | startAngle = m._degToRad(this.opt.startAngle), |
---|
| 184 | start = startAngle, filteredRun, slices, labels, shift, labelR, |
---|
| 185 | run = this.run.data, |
---|
| 186 | events = this.events(); |
---|
| 187 | |
---|
| 188 | this.dyn = []; |
---|
| 189 | |
---|
| 190 | if("radius" in this.opt){ |
---|
| 191 | r = this.opt.radius; |
---|
| 192 | labelR = r - this.opt.labelOffset; |
---|
| 193 | } |
---|
| 194 | var circle = { |
---|
| 195 | cx: offsets.l + rx, |
---|
| 196 | cy: offsets.t + ry, |
---|
| 197 | r: r |
---|
| 198 | }; |
---|
| 199 | |
---|
| 200 | // draw shadow |
---|
| 201 | if(this.opt.shadow || t.shadow){ |
---|
| 202 | var shadow = this.opt.shadow || t.shadow; |
---|
| 203 | var scircle = lang.clone(circle); |
---|
| 204 | scircle.cx += shadow.dx; |
---|
| 205 | scircle.cy += shadow.dy; |
---|
| 206 | s.createCircle(scircle).setFill(shadow.color).setStroke(shadow); |
---|
| 207 | } |
---|
| 208 | if(s.setFilter && (this.opt.filter || t.filter)){ |
---|
| 209 | s.createCircle(circle).setFill(t.series.stroke).setFilter(this.opt.filter || t.filter); |
---|
| 210 | } |
---|
| 211 | |
---|
| 212 | if(typeof run[0] == "number"){ |
---|
| 213 | filteredRun = df.map(run, "x ? Math.max(x, 0) : 0"); |
---|
| 214 | if(df.every(filteredRun, "<= 0")){ |
---|
| 215 | s.createCircle(circle).setStroke(t.series.stroke); |
---|
| 216 | this.dyn = arr.map(filteredRun, function(){ |
---|
| 217 | return { }; |
---|
| 218 | }); |
---|
| 219 | return this; |
---|
| 220 | }else{ |
---|
| 221 | slices = df.map(filteredRun, "/this", df.foldl(filteredRun, "+", 0)); |
---|
| 222 | if(this.opt.labels){ |
---|
| 223 | labels = arr.map(slices, function(x){ |
---|
| 224 | return x > 0 ? this._getLabel(x * 100) + "%" : ""; |
---|
| 225 | }, this); |
---|
| 226 | } |
---|
| 227 | } |
---|
| 228 | }else{ |
---|
| 229 | filteredRun = df.map(run, "x ? Math.max(x.y, 0) : 0"); |
---|
| 230 | if(df.every(filteredRun, "<= 0")){ |
---|
| 231 | s.createCircle(circle).setStroke(t.series.stroke); |
---|
| 232 | this.dyn = arr.map(filteredRun, function(){ |
---|
| 233 | return { }; |
---|
| 234 | }); |
---|
| 235 | return this; |
---|
| 236 | }else{ |
---|
| 237 | slices = df.map(filteredRun, "/this", df.foldl(filteredRun, "+", 0)); |
---|
| 238 | if(this.opt.labels){ |
---|
| 239 | labels = arr.map(slices, function(x, i){ |
---|
| 240 | if(x < 0){ return ""; } |
---|
| 241 | var v = run[i]; |
---|
| 242 | return "text" in v ? v.text : this._getLabel(x * 100) + "%"; |
---|
| 243 | }, this); |
---|
| 244 | } |
---|
| 245 | } |
---|
| 246 | } |
---|
| 247 | var themes = df.map(run, function(v, i){ |
---|
| 248 | var tMixin = [this.opt, this.run]; |
---|
| 249 | if(v !== null && typeof v != "number"){ |
---|
| 250 | tMixin.push(v); |
---|
| 251 | } |
---|
| 252 | if(this.opt.styleFunc){ |
---|
| 253 | tMixin.push(this.opt.styleFunc(v)); |
---|
| 254 | } |
---|
| 255 | return t.next("slice", tMixin, true); |
---|
| 256 | }, this); |
---|
| 257 | |
---|
| 258 | if(this.opt.labels){ |
---|
| 259 | size = labelFont ? g.normalizedLength(g.splitFontString(labelFont).size) : 0; |
---|
| 260 | shift = df.foldl1(df.map(labels, function(label, i){ |
---|
| 261 | var font = themes[i].series.font; |
---|
| 262 | return g._base._getTextBox(label, {font: font}).w; |
---|
| 263 | }, this), "Math.max(a, b)") / 2; |
---|
| 264 | if(this.opt.labelOffset < 0){ |
---|
| 265 | r = Math.min(rx - 2 * shift, ry - size) + this.opt.labelOffset; |
---|
| 266 | } |
---|
| 267 | labelR = r - this.opt.labelOffset; |
---|
| 268 | } |
---|
| 269 | |
---|
| 270 | // draw slices |
---|
| 271 | var eventSeries = new Array(slices.length); |
---|
| 272 | arr.some(slices, function(slice, i){ |
---|
| 273 | if(slice < 0){ |
---|
| 274 | // degenerated slice |
---|
| 275 | return false; // continue |
---|
| 276 | } |
---|
| 277 | if(slice == 0){ |
---|
| 278 | this.dyn.push({fill: null, stroke: null}); |
---|
| 279 | return false; |
---|
| 280 | } |
---|
| 281 | var v = run[i], theme = themes[i], specialFill, o; |
---|
| 282 | if(slice >= 1){ |
---|
| 283 | // whole pie |
---|
| 284 | specialFill = this._plotFill(theme.series.fill, dim, offsets); |
---|
| 285 | specialFill = this._shapeFill(specialFill, |
---|
| 286 | { |
---|
| 287 | x: circle.cx - circle.r, y: circle.cy - circle.r, |
---|
| 288 | width: 2 * circle.r, height: 2 * circle.r |
---|
| 289 | }); |
---|
| 290 | specialFill = this._pseudoRadialFill(specialFill, {x: circle.cx, y: circle.cy}, circle.r); |
---|
| 291 | var shape = s.createCircle(circle).setFill(specialFill).setStroke(theme.series.stroke); |
---|
| 292 | this.dyn.push({fill: specialFill, stroke: theme.series.stroke}); |
---|
| 293 | |
---|
| 294 | if(events){ |
---|
| 295 | o = { |
---|
| 296 | element: "slice", |
---|
| 297 | index: i, |
---|
| 298 | run: this.run, |
---|
| 299 | shape: shape, |
---|
| 300 | x: i, |
---|
| 301 | y: typeof v == "number" ? v : v.y, |
---|
| 302 | cx: circle.cx, |
---|
| 303 | cy: circle.cy, |
---|
| 304 | cr: r |
---|
| 305 | }; |
---|
| 306 | this._connectEvents(o); |
---|
| 307 | eventSeries[i] = o; |
---|
| 308 | } |
---|
| 309 | |
---|
| 310 | return false; // we continue because we want to collect null data points for legend |
---|
| 311 | } |
---|
| 312 | // calculate the geometry of the slice |
---|
| 313 | var end = start + slice * 2 * Math.PI; |
---|
| 314 | if(i + 1 == slices.length){ |
---|
| 315 | end = startAngle + 2 * Math.PI; |
---|
| 316 | } |
---|
| 317 | var step = end - start, |
---|
| 318 | x1 = circle.cx + r * Math.cos(start), |
---|
| 319 | y1 = circle.cy + r * Math.sin(start), |
---|
| 320 | x2 = circle.cx + r * Math.cos(end), |
---|
| 321 | y2 = circle.cy + r * Math.sin(end); |
---|
| 322 | // draw the slice |
---|
| 323 | var fanSize = m._degToRad(this.opt.fanSize); |
---|
| 324 | if(theme.series.fill && theme.series.fill.type === "radial" && this.opt.radGrad === "fan" && step > fanSize){ |
---|
| 325 | var group = s.createGroup(), nfans = Math.ceil(step / fanSize), delta = step / nfans; |
---|
| 326 | specialFill = this._shapeFill(theme.series.fill, |
---|
| 327 | {x: circle.cx - circle.r, y: circle.cy - circle.r, width: 2 * circle.r, height: 2 * circle.r}); |
---|
| 328 | for(var j = 0; j < nfans; ++j){ |
---|
| 329 | var fansx = j == 0 ? x1 : circle.cx + r * Math.cos(start + (j - FUDGE_FACTOR) * delta), |
---|
| 330 | fansy = j == 0 ? y1 : circle.cy + r * Math.sin(start + (j - FUDGE_FACTOR) * delta), |
---|
| 331 | fanex = j == nfans - 1 ? x2 : circle.cx + r * Math.cos(start + (j + 1 + FUDGE_FACTOR) * delta), |
---|
| 332 | faney = j == nfans - 1 ? y2 : circle.cy + r * Math.sin(start + (j + 1 + FUDGE_FACTOR) * delta); |
---|
| 333 | group.createPath(). |
---|
| 334 | moveTo(circle.cx, circle.cy). |
---|
| 335 | lineTo(fansx, fansy). |
---|
| 336 | arcTo(r, r, 0, delta > Math.PI, true, fanex, faney). |
---|
| 337 | lineTo(circle.cx, circle.cy). |
---|
| 338 | closePath(). |
---|
| 339 | setFill(this._pseudoRadialFill(specialFill, {x: circle.cx, y: circle.cy}, r, start + (j + 0.5) * delta, start + (j + 0.5) * delta)); |
---|
| 340 | } |
---|
| 341 | group.createPath(). |
---|
| 342 | moveTo(circle.cx, circle.cy). |
---|
| 343 | lineTo(x1, y1). |
---|
| 344 | arcTo(r, r, 0, step > Math.PI, true, x2, y2). |
---|
| 345 | lineTo(circle.cx, circle.cy). |
---|
| 346 | closePath(). |
---|
| 347 | setStroke(theme.series.stroke); |
---|
| 348 | shape = group; |
---|
| 349 | }else{ |
---|
| 350 | shape = s.createPath(). |
---|
| 351 | moveTo(circle.cx, circle.cy). |
---|
| 352 | lineTo(x1, y1). |
---|
| 353 | arcTo(r, r, 0, step > Math.PI, true, x2, y2). |
---|
| 354 | lineTo(circle.cx, circle.cy). |
---|
| 355 | closePath(). |
---|
| 356 | setStroke(theme.series.stroke); |
---|
| 357 | specialFill = theme.series.fill; |
---|
| 358 | if(specialFill && specialFill.type === "radial"){ |
---|
| 359 | specialFill = this._shapeFill(specialFill, {x: circle.cx - circle.r, y: circle.cy - circle.r, width: 2 * circle.r, height: 2 * circle.r}); |
---|
| 360 | if(this.opt.radGrad === "linear"){ |
---|
| 361 | specialFill = this._pseudoRadialFill(specialFill, {x: circle.cx, y: circle.cy}, r, start, end); |
---|
| 362 | } |
---|
| 363 | }else if(specialFill && specialFill.type === "linear"){ |
---|
| 364 | specialFill = this._plotFill(specialFill, dim, offsets); |
---|
| 365 | specialFill = this._shapeFill(specialFill, shape.getBoundingBox()); |
---|
| 366 | } |
---|
| 367 | shape.setFill(specialFill); |
---|
| 368 | } |
---|
| 369 | this.dyn.push({fill: specialFill, stroke: theme.series.stroke}); |
---|
| 370 | |
---|
| 371 | if(events){ |
---|
| 372 | o = { |
---|
| 373 | element: "slice", |
---|
| 374 | index: i, |
---|
| 375 | run: this.run, |
---|
| 376 | shape: shape, |
---|
| 377 | x: i, |
---|
| 378 | y: typeof v == "number" ? v : v.y, |
---|
| 379 | cx: circle.cx, |
---|
| 380 | cy: circle.cy, |
---|
| 381 | cr: r |
---|
| 382 | }; |
---|
| 383 | this._connectEvents(o); |
---|
| 384 | eventSeries[i] = o; |
---|
| 385 | } |
---|
| 386 | |
---|
| 387 | start = end; |
---|
| 388 | |
---|
| 389 | return false; // continue |
---|
| 390 | }, this); |
---|
| 391 | // draw labels |
---|
| 392 | if(this.opt.labels){ |
---|
| 393 | var isRtl = has("dojo-bidi") && this.chart.isRightToLeft(); |
---|
| 394 | if(this.opt.labelStyle == "default"){ // inside or outside based on labelOffset |
---|
| 395 | start = startAngle; |
---|
| 396 | arr.some(slices, function(slice, i){ |
---|
| 397 | if(slice <= 0){ |
---|
| 398 | // degenerated slice |
---|
| 399 | return false; // continue |
---|
| 400 | } |
---|
| 401 | var theme = themes[i]; |
---|
| 402 | if(slice >= 1){ |
---|
| 403 | // whole pie |
---|
| 404 | this.renderLabel(s, circle.cx, circle.cy + size / 2, labels[i], theme, this.opt.labelOffset > 0); |
---|
| 405 | return true; // stop iteration |
---|
| 406 | } |
---|
| 407 | // calculate the geometry of the slice |
---|
| 408 | var end = start + slice * 2 * Math.PI; |
---|
| 409 | if(i + 1 == slices.length){ |
---|
| 410 | end = startAngle + 2 * Math.PI; |
---|
| 411 | } |
---|
| 412 | if(this.opt.omitLabels && end-start < 0.001){ |
---|
| 413 | return false; // continue |
---|
| 414 | } |
---|
| 415 | var labelAngle = (start + end) / 2, |
---|
| 416 | x = circle.cx + labelR * Math.cos(labelAngle), |
---|
| 417 | y = circle.cy + labelR * Math.sin(labelAngle) + size / 2; |
---|
| 418 | // draw the label |
---|
| 419 | this.renderLabel(s, isRtl ? dim.width - x : x, y, labels[i], theme, this.opt.labelOffset > 0); |
---|
| 420 | start = end; |
---|
| 421 | return false; // continue |
---|
| 422 | }, this); |
---|
| 423 | }else if(this.opt.labelStyle == "columns"){ |
---|
| 424 | start = startAngle; |
---|
| 425 | var omitLabels = this.opt.omitLabels; |
---|
| 426 | //calculate label angles |
---|
| 427 | var labeledSlices = []; |
---|
| 428 | arr.forEach(slices, function(slice, i){ |
---|
| 429 | var end = start + slice * 2 * Math.PI; |
---|
| 430 | if(i + 1 == slices.length){ |
---|
| 431 | end = startAngle + 2 * Math.PI; |
---|
| 432 | } |
---|
| 433 | var labelAngle = (start + end) / 2; |
---|
| 434 | labeledSlices.push({ |
---|
| 435 | angle: labelAngle, |
---|
| 436 | left: Math.cos(labelAngle) < 0, |
---|
| 437 | theme: themes[i], |
---|
| 438 | index: i, |
---|
| 439 | omit: omitLabels?end - start < 0.001:false |
---|
| 440 | }); |
---|
| 441 | start = end; |
---|
| 442 | }); |
---|
| 443 | //calculate label radius to each slice |
---|
| 444 | var labelHeight = g._base._getTextBox("a",{ font: labelFont }).h; |
---|
| 445 | this._getProperLabelRadius(labeledSlices, labelHeight, circle.r * 1.1); |
---|
| 446 | //draw label and wiring |
---|
| 447 | arr.forEach(labeledSlices, function(slice, i){ |
---|
| 448 | if(!slice.omit){ |
---|
| 449 | var leftColumn = circle.cx - circle.r * 2, |
---|
| 450 | rightColumn = circle.cx + circle.r * 2, |
---|
| 451 | labelWidth = g._base._getTextBox(labels[i], {font: slice.theme.series.font}).w, |
---|
| 452 | x = circle.cx + slice.labelR * Math.cos(slice.angle), |
---|
| 453 | y = circle.cy + slice.labelR * Math.sin(slice.angle), |
---|
| 454 | jointX = (slice.left) ? (leftColumn + labelWidth) : (rightColumn - labelWidth), |
---|
| 455 | labelX = (slice.left) ? leftColumn : jointX; |
---|
| 456 | var wiring = s.createPath().moveTo(circle.cx + circle.r * Math.cos(slice.angle), circle.cy + circle.r * Math.sin(slice.angle)); |
---|
| 457 | if(Math.abs(slice.labelR * Math.cos(slice.angle)) < circle.r * 2 - labelWidth){ |
---|
| 458 | wiring.lineTo(x, y); |
---|
| 459 | } |
---|
| 460 | wiring.lineTo(jointX, y).setStroke(slice.theme.series.labelWiring); |
---|
| 461 | this.renderLabel(s, isRtl ? dim.width - labelWidth - labelX : labelX, y, labels[i], slice.theme, false, "left"); |
---|
| 462 | } |
---|
| 463 | },this); |
---|
| 464 | } |
---|
| 465 | } |
---|
| 466 | // post-process events to restore the original indexing |
---|
| 467 | var esi = 0; |
---|
| 468 | this._eventSeries[this.run.name] = df.map(run, function(v){ |
---|
| 469 | return v <= 0 ? null : eventSeries[esi++]; |
---|
| 470 | }); |
---|
| 471 | // chart mirroring starts |
---|
| 472 | if(has("dojo-bidi")){ |
---|
| 473 | this._checkOrientation(this.group, dim, offsets); |
---|
| 474 | } |
---|
| 475 | // chart mirroring ends |
---|
| 476 | return this; // dojox/charting/plot2d/Pie |
---|
| 477 | }, |
---|
| 478 | _getProperLabelRadius: function(slices, labelHeight, minRidius){ |
---|
| 479 | var leftCenterSlice, rightCenterSlice, |
---|
| 480 | leftMinSIN = 1, rightMinSIN = 1; |
---|
| 481 | if(slices.length == 1){ |
---|
| 482 | slices[0].labelR = minRidius; |
---|
| 483 | return; |
---|
| 484 | } |
---|
| 485 | for(var i = 0; i < slices.length; i++){ |
---|
| 486 | var tempSIN = Math.abs(Math.sin(slices[i].angle)); |
---|
| 487 | if(slices[i].left){ |
---|
| 488 | if(leftMinSIN >= tempSIN){ |
---|
| 489 | leftMinSIN = tempSIN; |
---|
| 490 | leftCenterSlice = slices[i]; |
---|
| 491 | } |
---|
| 492 | }else{ |
---|
| 493 | if(rightMinSIN >= tempSIN){ |
---|
| 494 | rightMinSIN = tempSIN; |
---|
| 495 | rightCenterSlice = slices[i]; |
---|
| 496 | } |
---|
| 497 | } |
---|
| 498 | } |
---|
| 499 | leftCenterSlice.labelR = rightCenterSlice.labelR = minRidius; |
---|
| 500 | this._calculateLabelR(leftCenterSlice, slices, labelHeight); |
---|
| 501 | this._calculateLabelR(rightCenterSlice, slices, labelHeight); |
---|
| 502 | }, |
---|
| 503 | _calculateLabelR: function(firstSlice, slices, labelHeight){ |
---|
| 504 | var i = firstSlice.index,length = slices.length, |
---|
| 505 | currentLabelR = firstSlice.labelR, nextLabelR; |
---|
| 506 | while(!(slices[i%length].left ^ slices[(i+1)%length].left)){ |
---|
| 507 | if(!slices[(i + 1) % length].omit){ |
---|
| 508 | nextLabelR = (Math.sin(slices[i % length].angle) * currentLabelR + ((slices[i % length].left) ? (-labelHeight) : labelHeight)) / |
---|
| 509 | Math.sin(slices[(i + 1) % length].angle); |
---|
| 510 | currentLabelR = (nextLabelR < firstSlice.labelR) ? firstSlice.labelR : nextLabelR; |
---|
| 511 | slices[(i + 1) % length].labelR = currentLabelR; |
---|
| 512 | } |
---|
| 513 | i++; |
---|
| 514 | } |
---|
| 515 | i = firstSlice.index; |
---|
| 516 | var j = (i == 0)?length-1 : i - 1; |
---|
| 517 | while(!(slices[i].left ^ slices[j].left)){ |
---|
| 518 | if(!slices[j].omit){ |
---|
| 519 | nextLabelR = (Math.sin(slices[i].angle) * currentLabelR + ((slices[i].left) ? labelHeight : (-labelHeight))) / |
---|
| 520 | Math.sin(slices[j].angle); |
---|
| 521 | currentLabelR = (nextLabelR < firstSlice.labelR) ? firstSlice.labelR : nextLabelR; |
---|
| 522 | slices[j].labelR = currentLabelR; |
---|
| 523 | } |
---|
| 524 | i--;j--; |
---|
| 525 | i = (i < 0)?i+slices.length:i; |
---|
| 526 | j = (j < 0)?j+slices.length:j; |
---|
| 527 | } |
---|
| 528 | } |
---|
| 529 | }); |
---|
| 530 | }); |
---|