@@ -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 ) {
@@ -309,11 +310,8 @@ module.exports = function plot(gd, cdpie) {
309
310
} ) ;
310
311
311
312
// add the title
312
- var hasTitle = trace . title &&
313
- ( ( trace . titleposition === 'inhole' && trace . hole > 0 ) ||
314
- ( trace . titleposition === 'outside' ) ) ;
315
313
var titleTextGroup = d3 . select ( this ) . selectAll ( 'g.titletext' )
316
- . data ( hasTitle ? [ 0 ] : [ ] ) ;
314
+ . data ( trace . title ? [ 0 ] : [ ] ) ;
317
315
318
316
titleTextGroup . enter ( ) . append ( 'g' )
319
317
. classed ( 'titletext' , true ) ;
@@ -329,30 +327,23 @@ module.exports = function plot(gd, cdpie) {
329
327
. attr ( {
330
328
'class' : 'titletext' ,
331
329
transform : '' ,
332
- 'text-anchor' : 'middle'
330
+ 'text-anchor' : 'middle' ,
333
331
} )
334
332
. call ( Drawing . font , trace . titlefont )
335
333
. call ( svgTextUtils . convertToTspans , gd ) ;
336
334
337
-
338
- var titleBB = Drawing . bBox ( titleText . node ( ) ) ;
339
- // translation and scaling for the title text box.
340
- // The translation is for the center point.
341
335
var transform ;
342
336
343
- if ( trace . titleposition === 'outside ' ) {
344
- transform = positionTitleOutside ( titleBB , cd0 , fullLayout . _size ) ;
337
+ if ( trace . titleposition === 'middle center ' ) {
338
+ transform = positionTitleInside ( cd0 ) ;
345
339
} else {
346
- transform = positionTitleInside ( titleBB , cd0 ) ;
340
+ transform = positionTitleOutside ( cd0 , fullLayout . _size ) ;
347
341
}
348
342
349
343
titleText . attr ( 'transform' ,
350
344
'translate(' + transform . x + ',' + transform . y + ')' +
351
345
( transform . scale < 1 ? ( 'scale(' + transform . scale + ')' ) : '' ) +
352
- 'translate(' +
353
- ( - ( titleBB . left + titleBB . right ) / 2 ) + ',' +
354
- ( - ( titleBB . top + titleBB . bottom ) / 2 ) +
355
- ')' ) ;
346
+ 'translate(' + transform . tx + ',' + transform . ty + ')' ) ;
356
347
} ) ;
357
348
358
349
// now make sure no labels overlap (at least within one pie)
@@ -418,6 +409,28 @@ module.exports = function plot(gd, cdpie) {
418
409
} , 0 ) ;
419
410
} ;
420
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
+ }
421
434
422
435
function transformInsideText ( textBB , pt , cd0 ) {
423
436
var textDiameter = Math . sqrt ( textBB . width * textBB . width + textBB . height * textBB . height ) ;
@@ -501,59 +514,76 @@ function transformOutsideText(textBB, pt) {
501
514
} ;
502
515
}
503
516
504
- function positionTitleInside ( titleBB , cd0 ) {
505
- var textDiameter = Math . sqrt ( titleBB . width * titleBB . width + titleBB . height * titleBB . height ) ;
517
+ function positionTitleInside ( cd0 ) {
518
+ var textDiameter =
519
+ Math . sqrt ( cd0 . titleBox . width * cd0 . titleBox . width + cd0 . titleBox . height * cd0 . titleBox . height ) ;
506
520
return {
507
521
x : cd0 . cx ,
508
522
y : cd0 . cy ,
509
- scale : cd0 . trace . hole * cd0 . r * 2 / textDiameter
523
+ scale : cd0 . trace . hole * cd0 . r * 2 / textDiameter ,
524
+ tx : 0 ,
525
+ ty : - cd0 . titleBox . height / 2 + cd0 . trace . titlefont . size
510
526
} ;
511
527
}
512
528
513
- function positionTitleOutside ( titleBB , cd0 , plotSize ) {
514
- var scaleX , scaleY , chartWidth , titleSpace , titleShift , maxPull ;
529
+ function positionTitleOutside ( cd0 , plotSize ) {
530
+ var scaleX = 1 , scaleY = 1 , maxWidth , maxPull ;
515
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
+ } ;
516
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 ;
517
549
maxPull = getMaxPull ( trace ) ;
518
- chartWidth = plotSize . w * ( trace . domain . x [ 1 ] - trace . domain . x [ 0 ] ) ;
519
- scaleX = chartWidth / titleBB . width ;
520
- if ( isSinglePie ( trace ) ) {
521
- titleShift = trace . titlefont . size / 2 ;
522
- // we need to leave enough free space for an outside label
523
- if ( trace . outsidetextfont ) titleShift += 3 * trace . outsidetextfont . size / 2 ;
524
- else titleShift += trace . titlefont . size / 4 ;
525
- return {
526
- x : cd0 . cx ,
527
- y : cd0 . cy - ( 1 + maxPull ) * cd0 . r - titleShift ,
528
- scale : scaleX
529
- } ;
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 ;
530
570
}
531
- titleSpace = getTitleSpace ( trace , plotSize ) ;
532
- // we previously left a free space of height titleSpace.
533
- // The text must fit in this space.
534
- scaleY = titleSpace / titleBB . height ;
571
+ scaleX = maxWidth / cd0 . titleBox . width ;
572
+ scaleY = getTitleSpace ( cd0 , plotSize ) / cd0 . titleBox . height ;
535
573
return {
536
- x : cd0 . cx ,
537
- y : cd0 . cy - ( 1 + maxPull ) * cd0 . r - ( titleSpace / 2 ) ,
538
- scale : Math . min ( scaleX , scaleY )
574
+ x : topMiddle . x ,
575
+ y : topMiddle . y ,
576
+ scale : Math . min ( scaleX , scaleY ) ,
577
+ tx : translate . tx ,
578
+ ty : translate . ty
539
579
} ;
540
580
}
541
581
542
- function isSinglePie ( trace ) {
543
- // check if there is a single pie per y-column
544
- if ( trace . domain . y [ 0 ] === 0 && trace . domain . y [ 1 ] === 1 ) return true ;
545
- return false ;
546
- }
547
-
548
- function getTitleSpace ( trace , plotSize ) {
549
- var chartHeight = plotSize . h * ( trace . domain . y [ 1 ] - trace . domain . y [ 0 ] ) ;
550
- // leave 3/2 * titlefont.size free space. We need at least titlefont.size
551
- // space, and the 1/2 * titlefont.size is a small buffer to avoid the text
552
- // touching the pie.
553
- var titleSpace = ( trace . title && trace . titleposition === 'outside' ) ?
554
- ( 3 * trace . titlefont . size / 2 ) : 0 ;
555
- if ( chartHeight > titleSpace ) return titleSpace ;
556
- else return chartHeight / 2 ;
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 pie
586
+ return Math . min ( cd0 . titleBox . height , pieBoxHeight / 2 ) ;
557
587
}
558
588
559
589
function getMaxPull ( trace ) {
@@ -687,14 +717,19 @@ function scalePies(cdpie, plotSize) {
687
717
pieBoxWidth = plotSize . w * ( trace . domain . x [ 1 ] - trace . domain . x [ 0 ] ) ;
688
718
pieBoxHeight = plotSize . h * ( trace . domain . y [ 1 ] - trace . domain . y [ 0 ] ) ;
689
719
// leave some space for the title, if it will be displayed outside
690
- if ( ! isSinglePie ( trace ) ) pieBoxHeight -= getTitleSpace ( trace , plotSize ) ;
720
+ if ( trace . title && trace . titleposition !== 'middle center' ) {
721
+ pieBoxHeight -= getTitleSpace ( cd0 , plotSize ) ;
722
+ }
691
723
692
724
maxPull = getMaxPull ( trace ) ;
693
725
694
726
cd0 . r = Math . min ( pieBoxWidth , pieBoxHeight ) / ( 2 + 2 * maxPull ) ;
695
727
696
728
cd0 . cx = plotSize . l + plotSize . w * ( trace . domain . x [ 1 ] + trace . domain . x [ 0 ] ) / 2 ;
697
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
+ }
698
733
699
734
if ( trace . scalegroup && scaleGroups . indexOf ( trace . scalegroup ) === - 1 ) {
700
735
scaleGroups . push ( trace . scalegroup ) ;
0 commit comments