@@ -22,6 +22,7 @@ var eventData = require('./event_data');
2222module . exports = function plot ( gd , cdpie ) {
2323 var fullLayout = gd . _fullLayout ;
2424
25+ prerenderTitles ( cdpie , gd ) ;
2526 scalePies ( cdpie , fullLayout . _size ) ;
2627
2728 var pieGroups = Lib . makeTraceGroups ( fullLayout . _pielayer , cdpie , 'trace' ) . each ( function ( cd ) {
@@ -309,11 +310,8 @@ module.exports = function plot(gd, cdpie) {
309310 } ) ;
310311
311312 // add the title
312- var hasTitle = trace . title &&
313- ( ( trace . titleposition === 'inhole' && trace . hole > 0 ) ||
314- ( trace . titleposition === 'outside' ) ) ;
315313 var titleTextGroup = d3 . select ( this ) . selectAll ( 'g.titletext' )
316- . data ( hasTitle ? [ 0 ] : [ ] ) ;
314+ . data ( trace . title ? [ 0 ] : [ ] ) ;
317315
318316 titleTextGroup . enter ( ) . append ( 'g' )
319317 . classed ( 'titletext' , true ) ;
@@ -329,30 +327,23 @@ module.exports = function plot(gd, cdpie) {
329327 . attr ( {
330328 'class' : 'titletext' ,
331329 transform : '' ,
332- 'text-anchor' : 'middle'
330+ 'text-anchor' : 'middle' ,
333331 } )
334332 . call ( Drawing . font , trace . titlefont )
335333 . call ( svgTextUtils . convertToTspans , gd ) ;
336334
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.
341335 var transform ;
342336
343- if ( trace . titleposition === 'outside ' ) {
344- transform = positionTitleOutside ( titleBB , cd0 , fullLayout . _size ) ;
337+ if ( trace . titleposition === 'middle center ' ) {
338+ transform = positionTitleInside ( cd0 ) ;
345339 } else {
346- transform = positionTitleInside ( titleBB , cd0 ) ;
340+ transform = positionTitleOutside ( cd0 , fullLayout . _size ) ;
347341 }
348342
349343 titleText . attr ( 'transform' ,
350344 'translate(' + transform . x + ',' + transform . y + ')' +
351345 ( 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 + ')' ) ;
356347 } ) ;
357348
358349 // now make sure no labels overlap (at least within one pie)
@@ -418,6 +409,28 @@ module.exports = function plot(gd, cdpie) {
418409 } , 0 ) ;
419410} ;
420411
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+ }
421434
422435function transformInsideText ( textBB , pt , cd0 ) {
423436 var textDiameter = Math . sqrt ( textBB . width * textBB . width + textBB . height * textBB . height ) ;
@@ -501,59 +514,76 @@ function transformOutsideText(textBB, pt) {
501514 } ;
502515}
503516
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 ) ;
506520 return {
507521 x : cd0 . cx ,
508522 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
510526 } ;
511527}
512528
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 ;
515531 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+ } ;
516544
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 ;
517549 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 ;
530570 }
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 ;
535573 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
539579 } ;
540580}
541581
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 ) ;
557587}
558588
559589function getMaxPull ( trace ) {
@@ -687,14 +717,19 @@ function scalePies(cdpie, plotSize) {
687717 pieBoxWidth = plotSize . w * ( trace . domain . x [ 1 ] - trace . domain . x [ 0 ] ) ;
688718 pieBoxHeight = plotSize . h * ( trace . domain . y [ 1 ] - trace . domain . y [ 0 ] ) ;
689719 // 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+ }
691723
692724 maxPull = getMaxPull ( trace ) ;
693725
694726 cd0 . r = Math . min ( pieBoxWidth , pieBoxHeight ) / ( 2 + 2 * maxPull ) ;
695727
696728 cd0 . cx = plotSize . l + plotSize . w * ( trace . domain . x [ 1 ] + trace . domain . x [ 0 ] ) / 2 ;
697729 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+ }
698733
699734 if ( trace . scalegroup && scaleGroups . indexOf ( trace . scalegroup ) === - 1 ) {
700735 scaleGroups . push ( trace . scalegroup ) ;
0 commit comments