@@ -16,6 +16,7 @@ var Color = require('../../components/color');
1616var Drawing = require ( '../../components/drawing' ) ;
1717var Lib = require ( '../../lib' ) ;
1818var svgTextUtils = require ( '../../lib/svg_text_utils' ) ;
19+ var recordMinTextSize = require ( '../bar/plot' ) . recordMinTextSize ;
1920
2021var helpers = require ( './helpers' ) ;
2122var eventData = require ( './event_data' ) ;
@@ -52,7 +53,7 @@ function plot(gd, cdModule) {
5253 ] ;
5354 var hasOutsideText = false ;
5455
55- slices . each ( function ( pt ) {
56+ slices . each ( function ( pt , i ) {
5657 if ( pt . hidden ) {
5758 d3 . select ( this ) . selectAll ( 'path,g' ) . remove ( ) ;
5859 return ;
@@ -131,7 +132,7 @@ function plot(gd, cdModule) {
131132 formatSliceLabel ( gd , pt , cd0 ) ;
132133 var textPosition = helpers . castOption ( trace . textposition , pt . pts ) ;
133134 var sliceTextGroup = sliceTop . selectAll ( 'g.slicetext' )
134- . data ( pt . text && ( textPosition !== 'none' ) ? [ 0 ] : [ ] ) ;
135+ . data ( pt . text && ( textPosition !== 'none' ) ? [ pt ] : [ ] ) ;
135136
136137 sliceTextGroup . enter ( ) . append ( 'g' )
137138 . classed ( 'slicetext' , true ) ;
@@ -144,15 +145,19 @@ function plot(gd, cdModule) {
144145 s . attr ( 'data-notex' , 1 ) ;
145146 } ) ;
146147
148+ var font = Lib . extendFlat ( { } , textPosition === 'outside' ?
149+ determineOutsideTextFont ( trace , pt , fullLayout . font ) :
150+ determineInsideTextFont ( trace , pt , fullLayout . font ) , { }
151+ ) ;
152+ font . size = Math . max ( font . size , fullLayout . uniformtext . minsize || 0 ) ;
153+
147154 sliceText . text ( pt . text )
148155 . attr ( {
149156 'class' : 'slicetext' ,
150157 transform : '' ,
151158 'text-anchor' : 'middle'
152159 } )
153- . call ( Drawing . font , textPosition === 'outside' ?
154- determineOutsideTextFont ( trace , pt , gd . _fullLayout . font ) :
155- determineInsideTextFont ( trace , pt , gd . _fullLayout . font ) )
160+ . call ( Drawing . font , font )
156161 . call ( svgTextUtils . convertToTspans , gd ) ;
157162
158163 // position the text relative to the slice
@@ -164,36 +169,39 @@ function plot(gd, cdModule) {
164169 } else {
165170 transform = transformInsideText ( textBB , pt , cd0 ) ;
166171 if ( textPosition === 'auto' && transform . scale < 1 ) {
167- sliceText . call ( Drawing . font , trace . outsidetextfont ) ;
168- if ( trace . outsidetextfont . family !== trace . insidetextfont . family ||
169- trace . outsidetextfont . size !== trace . insidetextfont . size ) {
172+ var newFont = Lib . extendFlat ( { } , trace . outsidetextfont , { } ) ;
173+ newFont . size = Math . max ( newFont . size , fullLayout . uniformtext . minsize || 0 ) ;
174+
175+ sliceText . call ( Drawing . font , newFont ) ;
176+ if ( newFont . family !== font . family || newFont . size !== font . size ) {
177+ // recompute bounding box
170178 textBB = Drawing . bBox ( sliceText . node ( ) ) ;
171179 }
172180 transform = transformOutsideText ( textBB , pt ) ;
173181 }
174182 }
175183
176- var translateX = cx + pt . pxmid [ 0 ] * transform . rCenter + ( transform . x || 0 ) ;
177- var translateY = cy + pt . pxmid [ 1 ] * transform . rCenter + ( transform . y || 0 ) ;
184+ var pxtxt = pt . pxtxt || pt . pxmid ;
185+ transform . targetX = cx + pxtxt [ 0 ] * transform . rCenter + ( transform . x || 0 ) ;
186+ transform . targetY = cy + pxtxt [ 1 ] * transform . rCenter + ( transform . y || 0 ) ;
187+ computeTransform ( transform , textBB ) ;
178188
179189 // save some stuff to use later ensure no labels overlap
180190 if ( transform . outside ) {
181- pt . yLabelMin = translateY - textBB . height / 2 ;
182- pt . yLabelMid = translateY ;
183- pt . yLabelMax = translateY + textBB . height / 2 ;
191+ var targetY = transform . targetY ;
192+ pt . yLabelMin = targetY - textBB . height / 2 ;
193+ pt . yLabelMid = targetY ;
194+ pt . yLabelMax = targetY + textBB . height / 2 ;
184195 pt . labelExtraX = 0 ;
185196 pt . labelExtraY = 0 ;
186197 hasOutsideText = true ;
187198 }
188199
189- sliceText . attr ( 'transform' ,
190- 'translate(' + translateX + ',' + translateY + ')' +
191- ( transform . scale < 1 ? ( 'scale(' + transform . scale + ')' ) : '' ) +
192- ( transform . rotate ? ( 'rotate(' + transform . rotate + ')' ) : '' ) +
193- 'translate(' +
194- ( - ( textBB . left + textBB . right ) / 2 ) + ',' +
195- ( - ( textBB . top + textBB . bottom ) / 2 ) +
196- ')' ) ;
200+ transform . fontSize = font . size ;
201+ recordMinTextSize ( trace . type , transform , fullLayout ) ;
202+ cd [ i ] . transform = transform ;
203+
204+ sliceText . attr ( 'transform' , Lib . getTextTransform ( transform , true ) ) ;
197205 } ) ;
198206 } ) ;
199207
@@ -298,8 +306,10 @@ function plotTextLines(slices, trace) {
298306 // first move the text to its new location
299307 var sliceText = sliceTop . select ( 'g.slicetext text' ) ;
300308
301- sliceText . attr ( 'transform' , 'translate(' + pt . labelExtraX + ',' + pt . labelExtraY + ')' +
302- sliceText . attr ( 'transform' ) ) ;
309+ pt . transform . targetX += pt . labelExtraX ;
310+ pt . transform . targetY += pt . labelExtraY ;
311+
312+ sliceText . attr ( 'transform' , Lib . getTextTransform ( pt . transform ) ) ;
303313
304314 // then add a line to the new location
305315 var lineStartX = pt . cxFinal + pt . pxmid [ 0 ] ;
@@ -549,59 +559,126 @@ function prerenderTitles(cdModule, gd) {
549559
550560function transformInsideText ( textBB , pt , cd0 ) {
551561 var textDiameter = Math . sqrt ( textBB . width * textBB . width + textBB . height * textBB . height ) ;
552- var textAspect = textBB . width / textBB . height ;
553562 var halfAngle = pt . halfangle ;
563+ var midAngle = pt . midangle ;
554564 var ring = pt . ring ;
555565 var rInscribed = pt . rInscribed ;
556566 var r = cd0 . r || pt . rpx1 ;
567+ var orientation = cd0 . trace . insidetextorientation ;
568+ var allTransforms = [ ] ;
569+
570+ var isCircle = ( ring === 1 ) && ( Math . abs ( pt . startangle - pt . stopangle ) === Math . PI * 2 ) ;
571+
572+ if ( isCircle || orientation === 'auto' || orientation === 'h' ) {
573+ // max size text can be inserted inside without rotating it
574+ // this inscribes the text rectangle in a circle, which is then inscribed
575+ // in the slice, so it will be an underestimate, which some day we may want
576+ // to improve so this case can get more use
577+ var transform = {
578+ scale : rInscribed * r * 2 / textDiameter ,
579+
580+ // and the center position and rotation in this case
581+ rCenter : 1 - rInscribed ,
582+ rotate : 0
583+ } ;
557584
558- // max size text can be inserted inside without rotating it
559- // this inscribes the text rectangle in a circle, which is then inscribed
560- // in the slice, so it will be an underestimate, which some day we may want
561- // to improve so this case can get more use
562- var transform = {
563- scale : rInscribed * r * 2 / textDiameter ,
585+ if ( transform . scale >= 1 ) return transform ;
564586
565- // and the center position and rotation in this case
566- rCenter : 1 - rInscribed ,
567- rotate : 0
568- } ;
587+ allTransforms . push ( transform ) ;
588+ }
569589
570- if ( transform . scale >= 1 ) return transform ;
590+ if ( orientation === 'h' ) {
591+ // max size if text is placed (horizontally) at the top or bottom of the arc
571592
572- // max size if text is rotated radially
573- var Qr = textAspect + 1 / ( 2 * Math . tan ( halfAngle ) ) ;
574- var maxHalfHeightRotRadial = r * Math . min (
575- 1 / ( Math . sqrt ( Qr * Qr + 0.5 ) + Qr ) ,
576- ring / ( Math . sqrt ( textAspect * textAspect + ring / 2 ) + textAspect )
593+ var considerCrossing = function ( angle , key ) {
594+ if ( isCrossing ( pt , angle ) ) {
595+ var dStart = Math . abs ( angle - pt . startangle ) ;
596+ var dStop = Math . abs ( angle - pt . stopangle ) ;
597+
598+ var closestEdge = dStart < dStop ? dStart : dStop ;
599+
600+ var newT ;
601+ if ( key === 'tan' ) {
602+ newT = calcTanTransform ( textBB , r , ring , closestEdge , 0 ) ;
603+ } else { // case of 'rad'
604+ newT = calcRadTransform ( textBB , r , ring , closestEdge , Math . PI / 2 ) ;
605+ }
606+ newT . _repos = getCoords ( r , angle ) ;
607+
608+ allTransforms . push ( newT ) ;
609+ }
610+ } ;
611+
612+ for ( var i = 3 ; i >= - 3 ; i -- ) { // to cover all cases with trace.rotation added
613+ considerCrossing ( Math . PI * ( i + 0.0 ) , 'tan' ) ;
614+ considerCrossing ( Math . PI * ( i + 0.5 ) , 'rad' ) ;
615+ }
616+ }
617+
618+ if ( orientation === 'auto' || orientation === 'r' ) {
619+ allTransforms . push ( calcRadTransform ( textBB , r , ring , halfAngle , midAngle ) ) ;
620+ }
621+
622+ if ( orientation === 'auto' || orientation === 't' ) {
623+ allTransforms . push ( calcTanTransform ( textBB , r , ring , halfAngle , midAngle ) ) ;
624+ }
625+
626+ var maxScaleTransform = allTransforms . sort ( function ( a , b ) {
627+ return b . scale - a . scale ;
628+ } ) [ 0 ] ;
629+
630+ if ( maxScaleTransform . _repos ) {
631+ pt . pxtxt = maxScaleTransform . _repos ;
632+ }
633+
634+ return maxScaleTransform ;
635+ }
636+
637+ function isCrossing ( pt , angle ) {
638+ var start = pt . startangle ;
639+ var stop = pt . stopangle ;
640+ return (
641+ ( start > angle && angle > stop ) ||
642+ ( start < angle && angle < stop )
577643 ) ;
578- var radialTransform = {
579- scale : maxHalfHeightRotRadial * 2 / textBB . height ,
580- rCenter : Math . cos ( maxHalfHeightRotRadial / r ) -
581- maxHalfHeightRotRadial * textAspect / r ,
582- rotate : ( 180 / Math . PI * pt . midangle + 720 ) % 180 - 90
644+ }
645+
646+ function calcRadTransform ( textBB , r , ring , halfAngle , midAngle ) {
647+ // max size if text is rotated radially
648+ var a = textBB . width / textBB . height ;
649+ var s = calcMaxHalfSize ( a , halfAngle , r , ring ) ;
650+ return {
651+ scale : s * 2 / textBB . height ,
652+ rCenter : calcRCenter ( a , s / r ) ,
653+ rotate : calcRotate ( midAngle )
583654 } ;
655+ }
584656
657+ function calcTanTransform ( textBB , r , ring , halfAngle , midAngle ) {
585658 // max size if text is rotated tangentially
586- var aspectInv = 1 / textAspect ;
587- var Qt = aspectInv + 1 / ( 2 * Math . tan ( halfAngle ) ) ;
588- var maxHalfWidthTangential = r * Math . min (
589- 1 / ( Math . sqrt ( Qt * Qt + 0.5 ) + Qt ) ,
590- ring / ( Math . sqrt ( aspectInv * aspectInv + ring / 2 ) + aspectInv )
591- ) ;
592- var tangentialTransform = {
593- scale : maxHalfWidthTangential * 2 / textBB . width ,
594- rCenter : Math . cos ( maxHalfWidthTangential / r ) -
595- maxHalfWidthTangential / textAspect / r ,
596- rotate : ( 180 / Math . PI * pt . midangle + 810 ) % 180 - 90
659+ var a = textBB . height / textBB . width ;
660+ var s = calcMaxHalfSize ( a , halfAngle , r , ring ) ;
661+ return {
662+ scale : s * 2 / textBB . width ,
663+ rCenter : calcRCenter ( a , s / r ) ,
664+ rotate : calcRotate ( midAngle + Math . PI / 2 )
597665 } ;
598- // if we need a rotated transform, pick the biggest one
599- // even if both are bigger than 1
600- var rotatedTransform = tangentialTransform . scale > radialTransform . scale ?
601- tangentialTransform : radialTransform ;
666+ }
667+
668+ function calcRCenter ( a , b ) {
669+ return Math . cos ( b ) - a * b ;
670+ }
671+
672+ function calcRotate ( t ) {
673+ return ( 180 / Math . PI * t + 720 ) % 180 - 90 ;
674+ }
602675
603- if ( transform . scale < 1 && rotatedTransform . scale > transform . scale ) return rotatedTransform ;
604- return transform ;
676+ function calcMaxHalfSize ( a , halfAngle , r , ring ) {
677+ var q = a + 1 / ( 2 * Math . tan ( halfAngle ) ) ;
678+ return r * Math . min (
679+ 1 / ( Math . sqrt ( q * q + 0.5 ) + q ) ,
680+ ring / ( Math . sqrt ( a * a + ring / 2 ) + a )
681+ ) ;
605682}
606683
607684function getInscribedRadiusFraction ( pt , cd0 ) {
@@ -921,6 +998,7 @@ function groupScale(cdModule, scaleGroups) {
921998
922999function setCoords ( cd ) {
9231000 var cd0 = cd [ 0 ] ;
1001+ var r = cd0 . r ;
9241002 var trace = cd0 . trace ;
9251003 var currentAngle = trace . rotation * Math . PI / 180 ;
9261004 var angleFactor = 2 * Math . PI / cd0 . vTotal ;
@@ -941,24 +1019,21 @@ function setCoords(cd) {
9411019 lastPt = 'px0' ;
9421020 }
9431021
944- function getCoords ( angle ) {
945- return [ cd0 . r * Math . sin ( angle ) , - cd0 . r * Math . cos ( angle ) ] ;
946- }
947-
948- currentCoords = getCoords ( currentAngle ) ;
1022+ currentCoords = getCoords ( r , currentAngle ) ;
9491023
9501024 for ( i = 0 ; i < cd . length ; i ++ ) {
9511025 cdi = cd [ i ] ;
9521026 if ( cdi . hidden ) continue ;
9531027
9541028 cdi [ firstPt ] = currentCoords ;
9551029
1030+ cdi . startangle = currentAngle ;
9561031 currentAngle += angleFactor * cdi . v / 2 ;
957- cdi . pxmid = getCoords ( currentAngle ) ;
1032+ cdi . pxmid = getCoords ( r , currentAngle ) ;
9581033 cdi . midangle = currentAngle ;
959-
9601034 currentAngle += angleFactor * cdi . v / 2 ;
961- currentCoords = getCoords ( currentAngle ) ;
1035+ currentCoords = getCoords ( r , currentAngle ) ;
1036+ cdi . stopangle = currentAngle ;
9621037
9631038 cdi [ lastPt ] = currentCoords ;
9641039
@@ -970,6 +1045,10 @@ function setCoords(cd) {
9701045 }
9711046}
9721047
1048+ function getCoords ( r , angle ) {
1049+ return [ r * Math . sin ( angle ) , - r * Math . cos ( angle ) ] ;
1050+ }
1051+
9731052function formatSliceLabel ( gd , pt , cd0 ) {
9741053 var fullLayout = gd . _fullLayout ;
9751054 var trace = cd0 . trace ;
@@ -1024,6 +1103,24 @@ function formatSliceLabel(gd, pt, cd0) {
10241103 }
10251104 }
10261105}
1106+
1107+ function computeTransform (
1108+ transform , // inout
1109+ textBB // in
1110+ ) {
1111+ var rotate = transform . rotate ;
1112+ var scale = transform . scale ;
1113+ if ( scale > 1 ) scale = 1 ;
1114+
1115+ var a = rotate * Math . PI / 180 ;
1116+ var cosA = Math . cos ( a ) ;
1117+ var sinA = Math . sin ( a ) ;
1118+ var midX = ( textBB . left + textBB . right ) / 2 ;
1119+ var midY = ( textBB . top + textBB . bottom ) / 2 ;
1120+ transform . textX = midX * cosA - midY * sinA ;
1121+ transform . textY = midX * sinA + midY * cosA ;
1122+ }
1123+
10271124module . exports = {
10281125 plot : plot ,
10291126 formatSliceLabel : formatSliceLabel ,
@@ -1033,4 +1130,5 @@ module.exports = {
10331130 prerenderTitles : prerenderTitles ,
10341131 layoutAreas : layoutAreas ,
10351132 attachFxHandlers : attachFxHandlers ,
1133+ computeTransform : computeTransform
10361134} ;
0 commit comments