@@ -598,7 +598,7 @@ function setupDragElement(gd, shapePath, shapeOptions, index, shapeLayer, editHe
598
598
}
599
599
600
600
function drawLabel ( gd , index , options , shapeGroup ) {
601
- if ( ! options . label ) return ;
601
+ if ( ! ( options . label && options . x0 && options . x1 ) ) return ;
602
602
603
603
// Remove existing label
604
604
shapeGroup . selectAll ( '.shape-label' ) . remove ( ) ;
@@ -634,32 +634,40 @@ function drawLabel(gd, index, options, shapeGroup) {
634
634
var shapey0 = y2p ( options . y0 ) ;
635
635
var shapey1 = y2p ( options . y1 ) ;
636
636
637
- // Calculate correct (x,y) for text
638
- var textPos = calcTextPosition ( shapex0 , shapey0 , shapex1 , shapey1 , options ) ;
639
- var textx = textPos . textx ;
640
- var texty = textPos . texty ;
641
-
642
- var textangle = options . label . textangle ;
643
-
644
637
// Handle 'auto' angle for lines
638
+ var textangle = options . label . textangle ;
645
639
if ( textangle === 'auto' ) {
646
640
textangle = calcTextAngle ( shapex0 , shapey0 , shapex1 , shapey1 ) ;
647
641
}
648
642
643
+ // Do an initial render just so we can get the bounding box height --
644
+ // this is not the final render
645
+ labelText . call ( function ( s ) {
646
+ s . call ( Drawing . font , font ) . attr ( { } ) ;
647
+ svgTextUtils . convertToTspans ( s , gd ) ;
648
+ return s ;
649
+ } ) ;
650
+ var textBB = Drawing . bBox ( labelText . node ( ) ) ;
651
+
652
+ // Calculate correct (x,y) for text
653
+ // We also determine true xanchor since xanchor depends on position when set to 'auto'
654
+ var textPos = calcTextPosition ( shapex0 , shapey0 , shapex1 , shapey1 , options , textangle , textBB ) ;
655
+ var textx = textPos . textx ;
656
+ var texty = textPos . texty ;
657
+ var xanchor = textPos . xanchor ;
658
+
649
659
function textLayout ( s ) {
650
- if ( options . x0 && options . y0 ) {
651
- s . call ( Drawing . font , font )
652
- . attr ( {
653
- 'text-anchor' : {
654
- left : 'start' ,
655
- center : 'middle' ,
656
- right : 'end'
657
- } [ options . label . xanchor ] ,
658
- 'y' : texty ,
659
- 'x' : textx ,
660
- 'transform' : 'rotate(' + textangle + ',' + textx + ',' + texty + ')'
661
- } ) ;
662
- }
660
+ s . call ( Drawing . font , font )
661
+ . attr ( {
662
+ 'text-anchor' : {
663
+ left : 'start' ,
664
+ center : 'middle' ,
665
+ right : 'end'
666
+ } [ xanchor ] ,
667
+ 'y' : texty ,
668
+ 'x' : textx ,
669
+ 'transform' : 'rotate(' + textangle + ',' + textx + ',' + texty + ')'
670
+ } ) ;
663
671
svgTextUtils . convertToTspans ( s , gd ) ;
664
672
return s ;
665
673
}
@@ -678,22 +686,22 @@ function calcTextAngle(shapex0, shapey0, shapex1, shapey1) {
678
686
return - 180 / Math . PI * Math . atan2 ( dy , dx ) ;
679
687
}
680
688
681
- function calcTextPosition ( shapex0 , shapey0 , shapex1 , shapey1 , shapeOptions ) {
689
+ function calcTextPosition ( shapex0 , shapey0 , shapex1 , shapey1 , shapeOptions , actualTextAngle , textBB ) {
682
690
var textPosition = shapeOptions . label . position ;
683
691
var textPadding = shapeOptions . label . padding ;
684
692
var shapeType = shapeOptions . type ;
685
- var textAngle = shapeOptions . label . textangle ;
693
+ var textAngleRad = Math . PI / 180 * actualTextAngle ;
694
+ var xanchor = shapeOptions . label . xanchor ;
695
+ var yanchor = shapeOptions . label . yanchor ;
686
696
687
- var textx , texty ;
697
+ var textx , texty , paddingX , paddingY ;
688
698
689
699
// Text position functions differently for lines vs. other shapes
690
700
if ( shapeType === 'line' ) {
691
701
// Handle special case for padding when angle is 'auto' for lines
692
702
// Padding should be treated as an orthogonal offset in this case
693
703
// Otherwise, padding is just a simple x and y offset
694
- var paddingX , paddingY ;
695
- if ( textAngle === 'auto' ) {
696
- var textAngleRad = Math . PI / 180 * calcTextAngle ( shapex0 , shapey0 , shapex1 , shapey1 ) ;
704
+ if ( shapeOptions . label . textangle === 'auto' ) {
697
705
paddingX = textPadding * Math . sin ( textAngleRad ) ;
698
706
paddingY = - textPadding * Math . cos ( textAngleRad ) ;
699
707
} else {
@@ -702,39 +710,69 @@ function calcTextPosition(shapex0, shapey0, shapex1, shapey1, shapeOptions) {
702
710
}
703
711
704
712
// Handle directional offset for top vs. bottom vs. center of line (default is 'top')
705
- var paddingMultiplier = textPosition . indexOf ( 'middle' ) !== - 1 ? 0 : textPosition . indexOf ( 'bottom' ) !== - 1 ? - 1 : 1 ;
713
+ var paddingMultiplier ;
714
+ if ( textPosition . indexOf ( 'middle' ) !== - 1 ) {
715
+ paddingMultiplier = 0 ;
716
+ if ( yanchor === 'auto' ) yanchor = 'middle' ;
717
+ } else if ( textPosition . indexOf ( 'bottom' ) !== - 1 ) {
718
+ paddingMultiplier = - 1 ;
719
+ if ( yanchor === 'auto' ) yanchor = 'top' ;
720
+ } else {
721
+ paddingMultiplier = 1 ;
722
+ if ( yanchor === 'auto' ) yanchor = 'bottom' ;
723
+ }
724
+ // var paddingMultiplier = textPosition.indexOf('middle') !== -1 ? 0 : textPosition.indexOf('bottom') !== -1 ? -1 : 1;
706
725
707
726
if ( textPosition . indexOf ( 'start' ) !== - 1 ) {
708
727
textx = shapex0 + paddingX * paddingMultiplier ;
709
728
texty = shapey0 + paddingY * paddingMultiplier ;
729
+ if ( xanchor === 'auto' ) xanchor = ( shapex1 >= shapex0 ) ? 'right' : 'left' ;
710
730
} else if ( textPosition . indexOf ( 'end' ) !== - 1 ) {
711
731
textx = shapex1 + paddingX * paddingMultiplier ;
712
732
texty = shapey1 + paddingY * paddingMultiplier ;
733
+ if ( xanchor === 'auto' ) xanchor = ( shapex1 >= shapex0 ) ? 'left' : 'right' ;
713
734
} else { // Default: center
714
735
textx = ( shapex0 + shapex1 ) / 2 + paddingX * paddingMultiplier ;
715
736
texty = ( shapey0 + shapey1 ) / 2 + paddingY * paddingMultiplier ;
737
+ if ( xanchor === 'auto' ) xanchor = 'center' ;
716
738
}
717
739
} else { // Text position for shapes that are not lines
718
740
// calc horizontal position
719
- if ( textPosition . indexOf ( 'top' ) !== - 1 ) {
720
- textx = Math . max ( shapex0 , shapex1 ) + textPadding ;
721
- } else if ( textPosition . indexOf ( 'bottom' ) !== - 1 ) {
722
- textx = Math . min ( shapex0 , shapex1 ) - textPadding ;
741
+ // Horizontal needs a little extra padding to look balanced
742
+ paddingX = textPadding + 3 ;
743
+ if ( textPosition . indexOf ( 'right' ) !== - 1 ) {
744
+ textx = Math . max ( shapex0 , shapex1 ) + paddingX ;
745
+ if ( xanchor === 'auto' ) xanchor = 'left' ;
746
+ } else if ( textPosition . indexOf ( 'left' ) !== - 1 ) {
747
+ textx = Math . min ( shapex0 , shapex1 ) - paddingX ;
748
+ if ( xanchor === 'auto' ) xanchor = 'right' ;
723
749
} else { // Default: center
724
750
textx = ( shapex0 + shapex1 ) / 2 ;
751
+ if ( xanchor === 'auto' ) xanchor = 'center' ;
725
752
}
726
753
727
754
// calc vertical position
755
+ paddingY = textPadding
728
756
if ( textPosition . indexOf ( 'top' ) !== - 1 ) {
729
- texty = Math . min ( shapey0 , shapey1 ) - textPadding ;
757
+ texty = Math . min ( shapey0 , shapey1 ) - paddingY ;
758
+ if ( yanchor === 'auto' ) yanchor = 'bottom' ;
730
759
} else if ( textPosition . indexOf ( 'bottom' ) !== - 1 ) {
731
- texty = Math . max ( shapey0 , shapey1 ) + textPadding ;
760
+ texty = Math . max ( shapey0 , shapey1 ) + paddingY ;
761
+ if ( yanchor === 'auto' ) yanchor = 'top' ;
732
762
} else { // Default: middle
733
763
texty = ( shapey0 + shapey1 ) / 2 ;
764
+ if ( yanchor === 'auto' ) yanchor = 'middle' ;
734
765
}
735
766
}
736
767
737
- return { textx : textx , texty : texty } ;
768
+ // Shift vertical (& horizontal) position according to `yanchor`
769
+ // This shiftFraction is only a rough approximation, but maybe good enough?
770
+ var shiftFraction = { middle : - 0.2 , bottom : 0.3 , top : - 0.7 } [ yanchor ] ;
771
+ var textHeight = textBB . height ;
772
+ var xshift = textHeight * Math . sin ( textAngleRad ) * shiftFraction ;
773
+ var yshift = - textHeight * Math . cos ( textAngleRad ) * shiftFraction ;
774
+
775
+ return { textx : textx + xshift , texty : texty + yshift , xanchor : xanchor } ;
738
776
}
739
777
740
778
function movePath ( pathIn , moveX , moveY ) {
0 commit comments