@@ -22,6 +22,7 @@ var eventData = require('./event_data');
22
22
module . exports = function plot ( gd , cdpie ) {
23
23
var fullLayout = gd . _fullLayout ;
24
24
25
+ prerenderTitles ( cdpie , gd ) ;
25
26
scalePies ( cdpie , fullLayout . _size ) ;
26
27
27
28
var pieGroups = Lib . makeTraceGroups ( fullLayout . _pielayer , cdpie , 'trace' ) . each ( function ( cd ) {
@@ -308,6 +309,43 @@ module.exports = function plot(gd, cdpie) {
308
309
} ) ;
309
310
} ) ;
310
311
312
+ // add the title
313
+ var titleTextGroup = d3 . select ( this ) . selectAll ( 'g.titletext' )
314
+ . data ( trace . title ? [ 0 ] : [ ] ) ;
315
+
316
+ titleTextGroup . enter ( ) . append ( 'g' )
317
+ . classed ( 'titletext' , true ) ;
318
+ titleTextGroup . exit ( ) . remove ( ) ;
319
+
320
+ titleTextGroup . each ( function ( ) {
321
+ var titleText = Lib . ensureSingle ( d3 . select ( this ) , 'text' , '' , function ( s ) {
322
+ // prohibit tex interpretation as above
323
+ s . attr ( 'data-notex' , 1 ) ;
324
+ } ) ;
325
+
326
+ titleText . text ( trace . title )
327
+ . attr ( {
328
+ 'class' : 'titletext' ,
329
+ transform : '' ,
330
+ 'text-anchor' : 'middle' ,
331
+ } )
332
+ . call ( Drawing . font , trace . titlefont )
333
+ . call ( svgTextUtils . convertToTspans , gd ) ;
334
+
335
+ var transform ;
336
+
337
+ if ( trace . titleposition === 'middle center' ) {
338
+ transform = positionTitleInside ( cd0 ) ;
339
+ } else {
340
+ transform = positionTitleOutside ( cd0 , fullLayout . _size ) ;
341
+ }
342
+
343
+ titleText . attr ( 'transform' ,
344
+ 'translate(' + transform . x + ',' + transform . y + ')' +
345
+ ( transform . scale < 1 ? ( 'scale(' + transform . scale + ')' ) : '' ) +
346
+ 'translate(' + transform . tx + ',' + transform . ty + ')' ) ;
347
+ } ) ;
348
+
311
349
// now make sure no labels overlap (at least within one pie)
312
350
if ( hasOutsideText ) scootLabels ( quadrants , trace ) ;
313
351
slices . each ( function ( pt ) {
@@ -371,6 +409,28 @@ module.exports = function plot(gd, cdpie) {
371
409
} , 0 ) ;
372
410
} ;
373
411
412
+ function prerenderTitles ( cdpie , gd ) {
413
+ var cd0 , trace ;
414
+ // Determine the width and height of the title for each pie.
415
+ for ( var i = 0 ; i < cdpie . length ; i ++ ) {
416
+ cd0 = cdpie [ i ] [ 0 ] ;
417
+ trace = cd0 . trace ;
418
+
419
+ if ( trace . title ) {
420
+ var dummyTitle = Drawing . tester . append ( 'text' )
421
+ . attr ( 'data-notex' , 1 )
422
+ . text ( trace . title )
423
+ . call ( Drawing . font , trace . titlefont )
424
+ . call ( svgTextUtils . convertToTspans , gd ) ;
425
+ var bBox = Drawing . bBox ( dummyTitle . node ( ) , true ) ;
426
+ cd0 . titleBox = {
427
+ width : bBox . width ,
428
+ height : bBox . height ,
429
+ } ;
430
+ dummyTitle . remove ( ) ;
431
+ }
432
+ }
433
+ }
374
434
375
435
function transformInsideText ( textBB , pt , cd0 ) {
376
436
var textDiameter = Math . sqrt ( textBB . width * textBB . width + textBB . height * textBB . height ) ;
@@ -454,6 +514,89 @@ function transformOutsideText(textBB, pt) {
454
514
} ;
455
515
}
456
516
517
+ function positionTitleInside ( cd0 ) {
518
+ var textDiameter =
519
+ Math . sqrt ( cd0 . titleBox . width * cd0 . titleBox . width + cd0 . titleBox . height * cd0 . titleBox . height ) ;
520
+ return {
521
+ x : cd0 . cx ,
522
+ y : cd0 . cy ,
523
+ scale : cd0 . trace . hole * cd0 . r * 2 / textDiameter ,
524
+ tx : 0 ,
525
+ ty : - cd0 . titleBox . height / 2 + cd0 . trace . titlefont . size
526
+ } ;
527
+ }
528
+
529
+ function positionTitleOutside ( cd0 , plotSize ) {
530
+ var scaleX = 1 , scaleY = 1 , maxWidth , maxPull ;
531
+ var trace = cd0 . trace ;
532
+ // position of the baseline point of the text box in the plot, before scaling.
533
+ // we anchored the text in the middle, so the baseline is on the bottom middle
534
+ // of the first line of text.
535
+ var topMiddle = {
536
+ x : cd0 . cx ,
537
+ y : cd0 . cy
538
+ } ;
539
+ // relative translation of the text box after scaling
540
+ var translate = {
541
+ tx : 0 ,
542
+ ty : 0
543
+ } ;
544
+
545
+ // we reason below as if the baseline is the top middle point of the text box.
546
+ // so we must add the font size to approximate the y-coord. of the top.
547
+ // note that this correction must happen after scaling.
548
+ translate . ty += trace . titlefont . size ;
549
+ maxPull = getMaxPull ( trace ) ;
550
+
551
+ if ( trace . titleposition . indexOf ( 'top' ) !== - 1 ) {
552
+ topMiddle . y -= ( 1 + maxPull ) * cd0 . r ;
553
+ translate . ty -= cd0 . titleBox . height ;
554
+ }
555
+ else if ( trace . titleposition . indexOf ( 'bottom' ) !== - 1 ) {
556
+ topMiddle . y += ( 1 + maxPull ) * cd0 . r ;
557
+ }
558
+
559
+ if ( trace . titleposition . indexOf ( 'left' ) !== - 1 ) {
560
+ // we start the text at the left edge of the pie
561
+ maxWidth = plotSize . w * ( trace . domain . x [ 1 ] - trace . domain . x [ 0 ] ) / 2 + cd0 . r ;
562
+ topMiddle . x -= ( 1 + maxPull ) * cd0 . r ;
563
+ translate . tx += cd0 . titleBox . width / 2 ;
564
+ } else if ( trace . titleposition . indexOf ( 'center' ) !== - 1 ) {
565
+ maxWidth = plotSize . w * ( trace . domain . x [ 1 ] - trace . domain . x [ 0 ] ) ;
566
+ } else if ( trace . titleposition . indexOf ( 'right' ) !== - 1 ) {
567
+ maxWidth = plotSize . w * ( trace . domain . x [ 1 ] - trace . domain . x [ 0 ] ) / 2 + cd0 . r ;
568
+ topMiddle . x += ( 1 + maxPull ) * cd0 . r ;
569
+ translate . tx -= cd0 . titleBox . width / 2 ;
570
+ }
571
+ scaleX = maxWidth / cd0 . titleBox . width ;
572
+ scaleY = getTitleSpace ( cd0 , plotSize ) / cd0 . titleBox . height ;
573
+ return {
574
+ x : topMiddle . x ,
575
+ y : topMiddle . y ,
576
+ scale : Math . min ( scaleX , scaleY ) ,
577
+ tx : translate . tx ,
578
+ ty : translate . ty
579
+ } ;
580
+ }
581
+
582
+ function getTitleSpace ( cd0 , plotSize ) {
583
+ var trace = cd0 . trace ;
584
+ var pieBoxHeight = plotSize . h * ( trace . domain . y [ 1 ] - trace . domain . y [ 0 ] ) ;
585
+ // use at most half of the plot for the title
586
+ return Math . min ( cd0 . titleBox . height , pieBoxHeight / 2 ) ;
587
+ }
588
+
589
+ function getMaxPull ( trace ) {
590
+ var maxPull = trace . pull , j ;
591
+ if ( Array . isArray ( maxPull ) ) {
592
+ maxPull = 0 ;
593
+ for ( j = 0 ; j < trace . pull . length ; j ++ ) {
594
+ if ( trace . pull [ j ] > maxPull ) maxPull = trace . pull [ j ] ;
595
+ }
596
+ }
597
+ return maxPull ;
598
+ }
599
+
457
600
function scootLabels ( quadrants , trace ) {
458
601
var xHalf , yHalf , equatorFirst , farthestX , farthestY ,
459
602
xDiffSign , yDiffSign , thisQuad , oppositeQuad ,
@@ -570,21 +713,23 @@ function scalePies(cdpie, plotSize) {
570
713
for ( i = 0 ; i < cdpie . length ; i ++ ) {
571
714
cd0 = cdpie [ i ] [ 0 ] ;
572
715
trace = cd0 . trace ;
716
+
573
717
pieBoxWidth = plotSize . w * ( trace . domain . x [ 1 ] - trace . domain . x [ 0 ] ) ;
574
718
pieBoxHeight = plotSize . h * ( trace . domain . y [ 1 ] - trace . domain . y [ 0 ] ) ;
575
-
576
- maxPull = trace . pull ;
577
- if ( Array . isArray ( maxPull ) ) {
578
- maxPull = 0 ;
579
- for ( j = 0 ; j < trace . pull . length ; j ++ ) {
580
- if ( trace . pull [ j ] > maxPull ) maxPull = trace . pull [ j ] ;
581
- }
719
+ // leave some space for the title, if it will be displayed outside
720
+ if ( trace . title && trace . titleposition !== 'middle center' ) {
721
+ pieBoxHeight -= getTitleSpace ( cd0 , plotSize ) ;
582
722
}
583
723
724
+ maxPull = getMaxPull ( trace ) ;
725
+
584
726
cd0 . r = Math . min ( pieBoxWidth , pieBoxHeight ) / ( 2 + 2 * maxPull ) ;
585
727
586
728
cd0 . cx = plotSize . l + plotSize . w * ( trace . domain . x [ 1 ] + trace . domain . x [ 0 ] ) / 2 ;
587
- cd0 . cy = plotSize . t + plotSize . h * ( 2 - trace . domain . y [ 1 ] - trace . domain . y [ 0 ] ) / 2 ;
729
+ cd0 . cy = plotSize . t + plotSize . h * ( 1 - trace . domain . y [ 0 ] ) - pieBoxHeight / 2 ;
730
+ if ( trace . title && trace . titleposition . indexOf ( 'bottom' ) !== - 1 ) {
731
+ cd0 . cy -= getTitleSpace ( cd0 , plotSize ) ;
732
+ }
588
733
589
734
if ( trace . scalegroup && scaleGroups . indexOf ( trace . scalegroup ) === - 1 ) {
590
735
scaleGroups . push ( trace . scalegroup ) ;
0 commit comments