@@ -73,6 +73,31 @@ function drawOne(gd, index) {
7373 drawRaw ( gd , options , index , false , xa , ya ) ;
7474}
7575
76+ // Convert pixels to the coordinates relevant for the axis referred to. For
77+ // example, for paper it would convert to a value normalized by the dimension of
78+ // the plot.
79+ // axDomainRef: if true and axa defined, draws relative to axis domain,
80+ // otherwise draws relative to data (if axa defined) or paper (if not).
81+ function shiftPosition ( axa , dAx , axLetter , gs , options ) {
82+ var optAx = options [ axLetter ] ;
83+ var axRef = options [ axLetter + 'ref' ] ;
84+ var vertical = axLetter . indexOf ( 'y' ) !== - 1 ;
85+ var axDomainRef = Axes . getRefType ( axRef ) === 'domain' ;
86+ var gsDim = vertical ? gs . h : gs . w ;
87+ if ( axa ) {
88+ if ( axDomainRef ) {
89+ // here optAx normalized to length of axis (e.g., normally in range
90+ // 0 to 1). But dAx is in pixels. So we normalize dAx to length of
91+ // axis before doing the math.
92+ return optAx + ( vertical ? - dAx : dAx ) / axa . _length ;
93+ } else {
94+ return axa . p2r ( axa . r2p ( optAx ) + dAx ) ;
95+ }
96+ } else {
97+ return optAx + ( vertical ? - dAx : dAx ) / gsDim ;
98+ }
99+ }
100+
76101/**
77102 * drawRaw: draw a single annotation, potentially with modifications
78103 *
@@ -296,13 +321,14 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
296321 var alignPosition ;
297322 var autoAlignFraction ;
298323 var textShift ;
324+ var axRefType = Axes . getRefType ( axRef ) ;
299325
300326 /*
301327 * calculate the *primary* pixel position
302328 * which is the arrowhead if there is one,
303329 * otherwise the text anchor point
304330 */
305- if ( ax ) {
331+ if ( ax && ( axRefType !== 'domain' ) ) {
306332 // check if annotation is off screen, to bypass DOM manipulations
307333 var posFraction = ax . r2fraction ( options [ axLetter ] ) ;
308334 if ( posFraction < 0 || posFraction > 1 ) {
@@ -318,12 +344,17 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
318344 basePx = ax . _offset + ax . r2p ( options [ axLetter ] ) ;
319345 autoAlignFraction = 0.5 ;
320346 } else {
347+ var axRefTypeEqDomain = axRefType === 'domain' ;
321348 if ( axLetter === 'x' ) {
322349 alignPosition = options [ axLetter ] ;
323- basePx = gs . l + gs . w * alignPosition ;
350+ basePx = axRefTypeEqDomain ?
351+ ax . _offset + ax . _length * alignPosition :
352+ basePx = gs . l + gs . w * alignPosition ;
324353 } else {
325354 alignPosition = 1 - options [ axLetter ] ;
326- basePx = gs . t + gs . h * alignPosition ;
355+ basePx = axRefTypeEqDomain ?
356+ ax . _offset + ax . _length * alignPosition :
357+ basePx = gs . t + gs . h * alignPosition ;
327358 }
328359 autoAlignFraction = options . showarrow ? 0.5 : alignPosition ;
329360 }
@@ -340,8 +371,29 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
340371 annSizeFromHeight * shiftFraction ( 0.5 , options . yanchor ) ;
341372
342373 if ( tailRef === axRef ) {
343- posPx . tail = ax . _offset + ax . r2p ( arrowLength ) ;
344- // tail is data-referenced: autorange pads the text in px from the tail
374+ // In the case tailRefType is 'domain' or 'paper', the arrow's
375+ // position is set absolutely, which is consistent with how
376+ // it behaves when its position is set in data ('range')
377+ // coordinates.
378+ var tailRefType = Axes . getRefType ( tailRef ) ;
379+ if ( tailRefType === 'domain' ) {
380+ if ( axLetter === 'y' ) {
381+ arrowLength = 1 - arrowLength ;
382+ }
383+ posPx . tail = ax . _offset + ax . _length * arrowLength ;
384+ } else if ( tailRefType === 'paper' ) {
385+ if ( axLetter === 'y' ) {
386+ arrowLength = 1 - arrowLength ;
387+ posPx . tail = gs . t + gs . h * arrowLength ;
388+ } else {
389+ posPx . tail = gs . l + gs . w * arrowLength ;
390+ }
391+ } else {
392+ // assumed tailRef is range or paper referenced
393+ posPx . tail = ax . _offset + ax . r2p ( arrowLength ) ;
394+ }
395+ // tail is range- or domain-referenced: autorange pads the
396+ // text in px from the tail
345397 textPadShift = textShift ;
346398 } else {
347399 posPx . tail = basePx + arrowLength ;
@@ -562,19 +614,20 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
562614 var ycenter = annxy0 [ 1 ] + dy ;
563615 annTextGroupInner . call ( Drawing . setTranslate , xcenter , ycenter ) ;
564616
565- modifyItem ( 'x' , xa ?
566- xa . p2r ( xa . r2p ( options . x ) + dx ) :
567- ( options . x + ( dx / gs . w ) ) ) ;
568- modifyItem ( 'y' , ya ?
569- ya . p2r ( ya . r2p ( options . y ) + dy ) :
570- ( options . y - ( dy / gs . h ) ) ) ;
617+ modifyItem ( 'x' ,
618+ shiftPosition ( xa , dx , 'x' , gs , options ) ) ;
619+ modifyItem ( 'y' ,
620+ shiftPosition ( ya , dy , 'y' , gs , options ) ) ;
571621
622+ // for these 2 calls to shiftPosition, it is assumed xa, ya are
623+ // defined, so gsDim will not be used, but we put it in
624+ // anyways for consistency
572625 if ( options . axref === options . xref ) {
573- modifyItem ( 'ax' , xa . p2r ( xa . r2p ( options . ax ) + dx ) ) ;
626+ modifyItem ( 'ax' , shiftPosition ( xa , dx , 'ax' , gs , options ) ) ;
574627 }
575628
576629 if ( options . ayref === options . yref ) {
577- modifyItem ( 'ay' , ya . p2r ( ya . r2p ( options . ay ) + dy ) ) ;
630+ modifyItem ( 'ay' , shiftPosition ( ya , dy , 'ay' , gs , options ) ) ;
578631 }
579632
580633 arrowGroup . attr ( 'transform' , 'translate(' + dx + ',' + dy + ')' ) ;
@@ -609,14 +662,17 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
609662 moveFn : function ( dx , dy ) {
610663 var csr = 'pointer' ;
611664 if ( options . showarrow ) {
665+ // for these 2 calls to shiftPosition, it is assumed xa, ya are
666+ // defined, so gsDim will not be used, but we put it in
667+ // anyways for consistency
612668 if ( options . axref === options . xref ) {
613- modifyItem ( 'ax' , xa . p2r ( xa . r2p ( options . ax ) + dx ) ) ;
669+ modifyItem ( 'ax' , shiftPosition ( xa , dx , 'ax' , gs , options ) ) ;
614670 } else {
615671 modifyItem ( 'ax' , options . ax + dx ) ;
616672 }
617673
618674 if ( options . ayref === options . yref ) {
619- modifyItem ( 'ay' , ya . p2r ( ya . r2p ( options . ay ) + dy ) ) ;
675+ modifyItem ( 'ay' , shiftPosition ( ya , dy , 'ay' , gs . w , options ) ) ;
620676 } else {
621677 modifyItem ( 'ay' , options . ay + dy ) ;
622678 }
@@ -625,7 +681,9 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
625681 } else if ( ! subplotId ) {
626682 var xUpdate , yUpdate ;
627683 if ( xa ) {
628- xUpdate = xa . p2r ( xa . r2p ( options . x ) + dx ) ;
684+ // shiftPosition will not execute code where xa was
685+ // undefined, so we use to calculate xUpdate too
686+ xUpdate = shiftPosition ( xa , dx , 'x' , gs , options ) ;
629687 } else {
630688 var widthFraction = options . _xsize / gs . w ;
631689 var xLeft = options . x + ( options . _xshift - options . xshift ) / gs . w - widthFraction / 2 ;
@@ -635,7 +693,9 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
635693 }
636694
637695 if ( ya ) {
638- yUpdate = ya . p2r ( ya . r2p ( options . y ) + dy ) ;
696+ // shiftPosition will not execute code where ya was
697+ // undefined, so we use to calculate yUpdate too
698+ yUpdate = shiftPosition ( ya , dy , 'y' , gs , options ) ;
639699 } else {
640700 var heightFraction = options . _ysize / gs . h ;
641701 var yBottom = options . y - ( options . _yshift + options . yshift ) / gs . h - heightFraction / 2 ;
0 commit comments