Skip to content

Add axis domain references to shapes, annotations, and layout images #5014

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 117 commits into from
Oct 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
117 commits
Select commit Hold shift + click to select a range
67d5f8e
First iteration of adding " domain" to x and y ref specifiers
nicholas-esterer Jul 2, 2020
83a7452
axis ref for axes without numbers now works
nicholas-esterer Jul 2, 2020
f9e05bc
handle case where axis id is undefined
nicholas-esterer Jul 2, 2020
f4c3a60
replaced let with var
nicholas-esterer Jul 6, 2020
36667f5
dflt cannot be a list in axes.coerceRef
nicholas-esterer Jul 6, 2020
ab7b973
Code linting
nicholas-esterer Jul 6, 2020
c6005d9
id might not be string in axis_ids.js:getId
nicholas-esterer Jul 6, 2020
6235f3a
code linting
nicholas-esterer Jul 6, 2020
10cbd78
Summarized methods compatible with " domain"
nicholas-esterer Jul 6, 2020
b546658
Looks like annotations can reference axis domains now, too
nicholas-esterer Jul 6, 2020
40a639c
code linting
nicholas-esterer Jul 7, 2020
7373e1c
Images can now be placed relative to axis domains
nicholas-esterer Jul 7, 2020
13f7203
code linting
nicholas-esterer Jul 7, 2020
b3258f6
Somehow Axes.coerceRef was missing an argument
nicholas-esterer Jul 7, 2020
e5139b0
Unlike shapes and attributes, requires default "paper" argument
nicholas-esterer Jul 7, 2020
547deaf
Shapes referring to axis domains now draggable
nicholas-esterer Jul 7, 2020
31b22be
Trying to make it possible to drag annotations referencing axis domain
nicholas-esterer Jul 8, 2020
c1ca50a
Can drag axis referenced annotations now
nicholas-esterer Jul 8, 2020
e139ab0
code linting
nicholas-esterer Jul 8, 2020
f4b37fd
axis domain referenced shapes don't affect autorange
nicholas-esterer Jul 10, 2020
6bb7b82
Domain referenced shapes and annotations can be dragged anywhere
nicholas-esterer Jul 16, 2020
d92c4da
Syntax fix
nicholas-esterer Jul 16, 2020
f5c5a17
More syntax repair
nicholas-esterer Jul 16, 2020
2225eb5
Domain referenced shapes work for log plots now
nicholas-esterer Jul 17, 2020
23d8888
fixed y direction for domain referenced shapes and annotations
nicholas-esterer Jul 17, 2020
50f75ee
Merge branch 'master' into issue-4958
nicholas-esterer Jul 17, 2020
aa316e8
Started adding tests for domain referenced shapes
nicholas-esterer Jul 22, 2020
37597ce
Merge branch 'master' into issue-4958
nicholas-esterer Jul 22, 2020
0289e6f
Added domain to xref and yref attribute values
nicholas-esterer Jul 23, 2020
360d322
Corrected domain reference regex
nicholas-esterer Jul 23, 2020
474f7d9
Missing "" around the regex
nicholas-esterer Jul 23, 2020
0891c0f
Generalization of description for axis placeable objects
nicholas-esterer Jul 27, 2020
0bb16db
Test correct position of domain referenced annotation
nicholas-esterer Jul 27, 2020
696f8df
Test domain referenced annotation on log plot
nicholas-esterer Jul 27, 2020
2e4ed19
Added tail to counterRegex
nicholas-esterer Aug 13, 2020
cf6e040
Use tail argument of counterRegex
nicholas-esterer Aug 13, 2020
6577c57
Reworked axis checking
nicholas-esterer Aug 14, 2020
4402dc7
' domain' appended to all items in axlist
nicholas-esterer Aug 17, 2020
896fbde
Changed extractInfoFromAxisRef to getRefType
nicholas-esterer Aug 17, 2020
647eef2
Replaced Axes.extractAxisFromAxisRef with AxisIds.ref2id
nicholas-esterer Aug 17, 2020
2f625a7
Added image test to domain_ref_tests.html
nicholas-esterer Aug 18, 2020
9c68c67
cleanId accepts domain referencing IDs
nicholas-esterer Aug 18, 2020
57c25bd
Doc string made ES5 compatible
nicholas-esterer Aug 18, 2020
dda98a7
Annotation arrows can be placed like in data coordinates
nicholas-esterer Aug 18, 2020
4972a2b
Width and height were switch when moving in paper coordinates!
nicholas-esterer Aug 18, 2020
e2b440f
More explicit selection based on axis type in calc_autorange
nicholas-esterer Aug 18, 2020
167da28
Use RefType variable naming convention now
nicholas-esterer Aug 18, 2020
2b29d19
Code linting
nicholas-esterer Aug 21, 2020
416cc3b
More linting
nicholas-esterer Aug 21, 2020
1d06c4f
No need to use axRefAxOnly anymore
nicholas-esterer Aug 21, 2020
fda38fc
Merge branch 'master' into issue-4958
nicholas-esterer Aug 21, 2020
a588f1e
hasOnlyAxRef returns false if axRef is falsy
nicholas-esterer Aug 21, 2020
e405e5b
image's coercePosition can just use axRef directly
nicholas-esterer Aug 21, 2020
63cbc29
Maintain 'paper' as image default axis reference
nicholas-esterer Aug 21, 2020
4bb72c1
Passes test_annotations now
nicholas-esterer Aug 25, 2020
55300b7
Updated valid axis IDs to include ' domain'
nicholas-esterer Aug 25, 2020
adbc6a5
Remove ' domain' from xref and yref
nicholas-esterer Aug 25, 2020
5d4cf74
Starting to write the jasmine tests for domain referenced shapes
nicholas-esterer Aug 25, 2020
0f204ad
Testing pixel_calc
nicholas-esterer Aug 25, 2020
c328243
Made a test for domain referencing shapes
nicholas-esterer Aug 26, 2020
e12dcb4
Starting to generalize the domain reference tests
nicholas-esterer Aug 27, 2020
117b3ed
Tests written for shape, annotation and image
nicholas-esterer Aug 28, 2020
68c807b
Trying to get the image tests to pass
nicholas-esterer Aug 31, 2020
60e053d
It seems the image tests are running correctly
nicholas-esterer Sep 1, 2020
f9d9c35
Working on the annotation tests
nicholas-esterer Sep 1, 2020
3896b08
Annotation arrows can now be placed using absolute paper coordinates
nicholas-esterer Sep 2, 2020
af7103f
Test values chosen so they are never beyond the plot boundaries
nicholas-esterer Sep 2, 2020
35ff5a7
Accept arrays for extra argument of Axes.coerceRef
nicholas-esterer Sep 2, 2020
9f0d331
Annotation tests work properly with log axes
nicholas-esterer Sep 2, 2020
11a8d3a
Tests are working in browser but not Jasmine?
nicholas-esterer Sep 3, 2020
e651d57
For annotation test no log ax or ay is pixel
nicholas-esterer Sep 4, 2020
27d8c43
Don't take log of range for images
nicholas-esterer Sep 4, 2020
0f1eae6
Shapes tests now run as Jasmine tests
nicholas-esterer Sep 4, 2020
83834f8
Static shape, annotation and image tests are passing
nicholas-esterer Sep 10, 2020
c9103df
Domain referenced shapes are visible outside the plotting area
nicholas-esterer Sep 14, 2020
9249684
fixed syntax, removed scraps
nicholas-esterer Sep 15, 2020
f9d4b03
Removed axis pairs and lambda-style function
nicholas-esterer Sep 15, 2020
e6c6235
Doubled all the hard-coded timeout intervals
nicholas-esterer Sep 15, 2020
44862fa
Clean up and syntax of test
nicholas-esterer Sep 15, 2020
95e328f
Merge branch 'master' into issue-4958
nicholas-esterer Sep 15, 2020
00edf56
Removing unused custom graph id fixes axes tests
nicholas-esterer Sep 15, 2020
93284df
Fixed click test
nicholas-esterer Sep 16, 2020
6e26db3
Starting to add interact tests for domain referenced objects
nicholas-esterer Sep 17, 2020
156b73b
Added test for interaction with domain reference objects
nicholas-esterer Sep 17, 2020
fd07290
Test dragging the whole annotation
nicholas-esterer Sep 18, 2020
bd2b34d
Domain refercing objects image test
nicholas-esterer Sep 18, 2020
09635ba
Fixed up docstring generation for axis placeable objects
nicholas-esterer Sep 21, 2020
6510415
Moved extra-iterable to dev-dependencies
nicholas-esterer Sep 21, 2020
aa2d6eb
Removed .js from require
nicholas-esterer Sep 21, 2020
4203088
PR review integration
nicholas-esterer Sep 21, 2020
b8dc27f
Using Array.isArray unbreaks the mathjax bundle test
nicholas-esterer Sep 22, 2020
099bf52
It's too complicated to use push like concat
nicholas-esterer Sep 22, 2020
f3531dc
Added domain_ref mock to mock_test
nicholas-esterer Sep 22, 2020
93890d0
Added domain_refs to mock_test but it doesn't pass the validator
nicholas-esterer Sep 22, 2020
6eba309
Made shiftPosition clearer and more modular
nicholas-esterer Sep 23, 2020
2d25857
Change function parameter name and syntax fix
nicholas-esterer Sep 23, 2020
35ab9da
Fixed domain_ref mock_test
nicholas-esterer Sep 23, 2020
759d37b
Changed order of arguments to shiftPosition
nicholas-esterer Sep 23, 2020
d70f4e9
DRY pixelToData
nicholas-esterer Sep 23, 2020
9055503
Made pixelToData shorter
nicholas-esterer Sep 24, 2020
e458c86
Syntax
nicholas-esterer Sep 24, 2020
df0560c
Don't coerce pixel, paper or domain references the same as range refe…
nicholas-esterer Sep 25, 2020
676d6c5
Improved 'axref' 'ayref' descriptions
nicholas-esterer Sep 28, 2020
69acd55
Added domain ref baseline for all types of axes
nicholas-esterer Sep 28, 2020
a54d354
newline
nicholas-esterer Sep 29, 2020
8b63e38
Removed some layout information from domain_ref_axis_types.json
nicholas-esterer Sep 29, 2020
7d2129c
Removed console.logs from tests
nicholas-esterer Sep 29, 2020
1c51d72
Removed domainRef argument from axes.coerceRef
nicholas-esterer Sep 29, 2020
71b0cc4
Syntax
nicholas-esterer Sep 30, 2020
a31a94a
Removed testnumber file
nicholas-esterer Oct 1, 2020
472c135
changed shape / annotation / image function prototypes
nicholas-esterer Oct 1, 2020
5d586dd
Use dicts in the function calls with many parameters
nicholas-esterer Oct 2, 2020
7f5300c
Fixed syntax error
nicholas-esterer Oct 2, 2020
dbf1bd4
Merge branch 'master' into issue-4958
nicholas-esterer Oct 2, 2020
35712fe
use xIsDomain, yIsDomain, no more axes.addAxRefDomainCoerceRefExtra
nicholas-esterer Oct 5, 2020
a3d561c
Just check that axRefType isn't range in axes.coercePosition
nicholas-esterer Oct 5, 2020
dcc27f4
Loop over component names in domain_ref_test
nicholas-esterer Oct 5, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ build/*

npm-debug.log*
*.sublime*
*~
tags
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate why we should add these two?

Copy link
Contributor Author

@nicholas-esterer nicholas-esterer Sep 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some text editors suffix backup files with ~ and use the file tags to store information so you can jump to function definitions. But taggers don't work that well with Javascript it seems, so we could get rid of the tags.


.*
!.circleci
Expand Down
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
"elliptic": "^6.5.3",
"eslint": "^7.10.0",
"espree": "^7.3.0",
"extra-iterable": "^2.5.13",
"falafel": "^2.2.4",
"fs-extra": "^9.0.1",
"fuse.js": "^6.4.1",
Expand Down
73 changes: 39 additions & 34 deletions src/components/annotations/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,34 @@ var ARROWPATHS = require('./arrow_paths');
var fontAttrs = require('../../plots/font_attributes');
var cartesianConstants = require('../../plots/cartesian/constants');
var templatedArray = require('../../plot_api/plot_template').templatedArray;
var axisPlaceableObjs = require('../../constants/axis_placeable_objects');

function arrowAxisRefDescription(axis) {
return [
'In order for absolute positioning of the arrow to work, *a' + axis +
'ref* must be exactly the same as *' + axis + 'ref*, otherwise *a' + axis +
'ref* will revert to *pixel* (explained next).',
'For relative positioning, *a' + axis + 'ref* can be set to *pixel*,',
'in which case the *a' + axis + '* value is specified in pixels',
'relative to *' + axis + '*.',
'Absolute positioning is useful',
'for trendline annotations which should continue to indicate',
'the correct trend when zoomed. Relative positioning is useful',
'for specifying the text offset for an annotated point.'
].join(' ');
}

function arrowCoordinateDescription(axis, lower, upper) {
return [
'Sets the', axis, 'component of the arrow tail about the arrow head.',
'If `a' + axis + 'ref` is `pixel`, a positive (negative)',
'component corresponds to an arrow pointing',
'from', upper, 'to', lower, '(' + lower, 'to', upper + ').',
'If `a' + axis + 'ref` is not `pixel` and is exactly the same as `' + axis + 'ref`,',
'this is an absolute value on that axis,',
'like `' + axis + '`, specified in the same coordinates as `' + axis + 'ref`.'
].join(' ');
}

module.exports = templatedArray('annotation', {
visible: {
Expand Down Expand Up @@ -254,25 +281,15 @@ module.exports = templatedArray('annotation', {
role: 'info',
editType: 'calc+arraydraw',
description: [
'Sets the x component of the arrow tail about the arrow head.',
'If `axref` is `pixel`, a positive (negative) ',
'component corresponds to an arrow pointing',
'from right to left (left to right).',
'If `axref` is an axis, this is an absolute value on that axis,',
'like `x`, NOT a relative value.'
arrowCoordinateDescription('x', 'left', 'right')
].join(' ')
},
ay: {
valType: 'any',
role: 'info',
editType: 'calc+arraydraw',
description: [
'Sets the y component of the arrow tail about the arrow head.',
'If `ayref` is `pixel`, a positive (negative) ',
'component corresponds to an arrow pointing',
'from bottom to top (top to bottom).',
'If `ayref` is an axis, this is an absolute value on that axis,',
'like `y`, NOT a relative value.'
arrowCoordinateDescription('y', 'top', 'bottom')
].join(' ')
},
axref: {
Expand All @@ -285,12 +302,10 @@ module.exports = templatedArray('annotation', {
role: 'info',
editType: 'calc',
description: [
'Indicates in what terms the tail of the annotation (ax,ay) ',
'is specified. If `pixel`, `ax` is a relative offset in pixels ',
'from `x`. If set to an x axis id (e.g. *x* or *x2*), `ax` is ',
'specified in the same terms as that axis. This is useful ',
'for trendline annotations which should continue to indicate ',
'the correct trend when zoomed.'
'Indicates in what coordinates the tail of the',
'annotation (ax,ay) is specified.',
axisPlaceableObjs.axisRefDescription('ax', 'left', 'right'),
arrowAxisRefDescription('x')
].join(' ')
},
ayref: {
Expand All @@ -303,12 +318,10 @@ module.exports = templatedArray('annotation', {
role: 'info',
editType: 'calc',
description: [
'Indicates in what terms the tail of the annotation (ax,ay) ',
'is specified. If `pixel`, `ay` is a relative offset in pixels ',
'from `y`. If set to a y axis id (e.g. *y* or *y2*), `ay` is ',
'specified in the same terms as that axis. This is useful ',
'for trendline annotations which should continue to indicate ',
'the correct trend when zoomed.'
'Indicates in what coordinates the tail of the',
'annotation (ax,ay) is specified.',
axisPlaceableObjs.axisRefDescription('ay', 'bottom', 'top'),
arrowAxisRefDescription('y')
].join(' ')
},
// positioning
Expand All @@ -322,11 +335,7 @@ module.exports = templatedArray('annotation', {
editType: 'calc',
description: [
'Sets the annotation\'s x coordinate axis.',
'If set to an x axis id (e.g. *x* or *x2*), the `x` position',
'refers to an x coordinate',
'If set to *paper*, the `x` position refers to the distance from',
'the left side of the plotting area in normalized coordinates',
'where 0 (1) corresponds to the left (right) side.'
axisPlaceableObjs.axisRefDescription('x', 'left', 'right'),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to xref could we use "domain" in axref as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes we can, but xref has to be exactly the same as axref or else axref reverts to pixel.

Copy link
Contributor

@archmoj archmoj Sep 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! Thanks for the info.
So let's update the description of axref and ayref as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(sorry wrong review item)

].join(' ')
},
x: {
Expand Down Expand Up @@ -385,11 +394,7 @@ module.exports = templatedArray('annotation', {
editType: 'calc',
description: [
'Sets the annotation\'s y coordinate axis.',
'If set to an y axis id (e.g. *y* or *y2*), the `y` position',
'refers to an y coordinate',
'If set to *paper*, the `y` position refers to the distance from',
'the bottom of the plotting area in normalized coordinates',
'where 0 (1) corresponds to the bottom (top).'
axisPlaceableObjs.axisRefDescription('y', 'bottom', 'top'),
].join(' ')
},
y: {
Expand Down
6 changes: 4 additions & 2 deletions src/components/annotations/calc_autorange.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ function annAutorange(gd) {
Lib.filterVisible(fullLayout.annotations).forEach(function(ann) {
var xa = Axes.getFromId(gd, ann.xref);
var ya = Axes.getFromId(gd, ann.yref);
var xRefType = Axes.getRefType(ann.xref);
var yRefType = Axes.getRefType(ann.yref);

ann._extremes = {};
if(xa) calcAxisExpansion(ann, xa);
if(ya) calcAxisExpansion(ann, ya);
if(xRefType === 'range') calcAxisExpansion(ann, xa);
if(yRefType === 'range') calcAxisExpansion(ann, ya);
});
}

Expand Down
3 changes: 2 additions & 1 deletion src/components/annotations/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ function handleAnnotationDefaults(annIn, annOut, fullLayout) {
if(showArrow) {
var arrowPosAttr = 'a' + axLetter;
// axref, ayref
var aaxRef = Axes.coerceRef(annIn, annOut, gdMock, arrowPosAttr, 'pixel');
var aaxRef = Axes.coerceRef(annIn, annOut, gdMock, arrowPosAttr, 'pixel',
['pixel', 'paper']);

// for now the arrow can only be on the same axis or specified as pixels
// TODO: sometime it might be interesting to allow it to be on *any* axis
Expand Down
94 changes: 77 additions & 17 deletions src/components/annotations/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,31 @@ function drawOne(gd, index) {
drawRaw(gd, options, index, false, xa, ya);
}

// Convert pixels to the coordinates relevant for the axis referred to. For
// example, for paper it would convert to a value normalized by the dimension of
// the plot.
// axDomainRef: if true and axa defined, draws relative to axis domain,
// otherwise draws relative to data (if axa defined) or paper (if not).
function shiftPosition(axa, dAx, axLetter, gs, options) {
var optAx = options[axLetter];
var axRef = options[axLetter + 'ref'];
var vertical = axLetter.indexOf('y') !== -1;
var axDomainRef = Axes.getRefType(axRef) === 'domain';
var gsDim = vertical ? gs.h : gs.w;
if(axa) {
if(axDomainRef) {
// here optAx normalized to length of axis (e.g., normally in range
// 0 to 1). But dAx is in pixels. So we normalize dAx to length of
// axis before doing the math.
return optAx + (vertical ? -dAx : dAx) / axa._length;
} else {
return axa.p2r(axa.r2p(optAx) + dAx);
}
} else {
return optAx + (vertical ? -dAx : dAx) / gsDim;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To improve performance, we may consider refactoring:

var delta = vertical ? -dAx : dAx;
return !axa ? optAx + delta / gsDim :
    axDomainRef ?
        optAx + delta / axa._length : // here optAx normalized to length of axis (e.g., normally in range 0 to 1). But dAx is in pixels. So we normalize dAx to length of axis before doing the math.
        axa.p2r(axa.r2p(optAx) + dAx);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think inline-ifs are good if they make the code clearer, but here I would not say they do that haha :-)

}

/**
* drawRaw: draw a single annotation, potentially with modifications
*
Expand Down Expand Up @@ -296,13 +321,14 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
var alignPosition;
var autoAlignFraction;
var textShift;
var axRefType = Axes.getRefType(axRef);

/*
* calculate the *primary* pixel position
* which is the arrowhead if there is one,
* otherwise the text anchor point
*/
if(ax) {
if(ax && (axRefType !== 'domain')) {
// check if annotation is off screen, to bypass DOM manipulations
var posFraction = ax.r2fraction(options[axLetter]);
if(posFraction < 0 || posFraction > 1) {
Expand All @@ -318,12 +344,17 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
basePx = ax._offset + ax.r2p(options[axLetter]);
autoAlignFraction = 0.5;
} else {
var axRefTypeEqDomain = axRefType === 'domain';
if(axLetter === 'x') {
alignPosition = options[axLetter];
basePx = gs.l + gs.w * alignPosition;
basePx = axRefTypeEqDomain ?
ax._offset + ax._length * alignPosition :
basePx = gs.l + gs.w * alignPosition;
} else {
alignPosition = 1 - options[axLetter];
basePx = gs.t + gs.h * alignPosition;
basePx = axRefTypeEqDomain ?
ax._offset + ax._length * alignPosition :
basePx = gs.t + gs.h * alignPosition;
}
autoAlignFraction = options.showarrow ? 0.5 : alignPosition;
}
Expand All @@ -340,8 +371,29 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
annSizeFromHeight * shiftFraction(0.5, options.yanchor);

if(tailRef === axRef) {
posPx.tail = ax._offset + ax.r2p(arrowLength);
// tail is data-referenced: autorange pads the text in px from the tail
// In the case tailRefType is 'domain' or 'paper', the arrow's
// position is set absolutely, which is consistent with how
// it behaves when its position is set in data ('range')
// coordinates.
var tailRefType = Axes.getRefType(tailRef);
if(tailRefType === 'domain') {
if(axLetter === 'y') {
arrowLength = 1 - arrowLength;
}
posPx.tail = ax._offset + ax._length * arrowLength;
} else if(tailRefType === 'paper') {
if(axLetter === 'y') {
arrowLength = 1 - arrowLength;
posPx.tail = gs.t + gs.h * arrowLength;
} else {
posPx.tail = gs.l + gs.w * arrowLength;
}
} else {
// assumed tailRef is range or paper referenced
posPx.tail = ax._offset + ax.r2p(arrowLength);
}
// tail is range- or domain-referenced: autorange pads the
// text in px from the tail
textPadShift = textShift;
} else {
posPx.tail = basePx + arrowLength;
Expand Down Expand Up @@ -562,19 +614,20 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
var ycenter = annxy0[1] + dy;
annTextGroupInner.call(Drawing.setTranslate, xcenter, ycenter);

modifyItem('x', xa ?
xa.p2r(xa.r2p(options.x) + dx) :
(options.x + (dx / gs.w)));
modifyItem('y', ya ?
ya.p2r(ya.r2p(options.y) + dy) :
(options.y - (dy / gs.h)));
modifyItem('x',
shiftPosition(xa, dx, 'x', gs, options));
modifyItem('y',
shiftPosition(ya, dy, 'y', gs, options));

// for these 2 calls to shiftPosition, it is assumed xa, ya are
// defined, so gsDim will not be used, but we put it in
// anyways for consistency
if(options.axref === options.xref) {
modifyItem('ax', xa.p2r(xa.r2p(options.ax) + dx));
modifyItem('ax', shiftPosition(xa, dx, 'ax', gs, options));
}

if(options.ayref === options.yref) {
modifyItem('ay', ya.p2r(ya.r2p(options.ay) + dy));
modifyItem('ay', shiftPosition(ya, dy, 'ay', gs, options));
}

arrowGroup.attr('transform', 'translate(' + dx + ',' + dy + ')');
Expand Down Expand Up @@ -609,14 +662,17 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
moveFn: function(dx, dy) {
var csr = 'pointer';
if(options.showarrow) {
// for these 2 calls to shiftPosition, it is assumed xa, ya are
// defined, so gsDim will not be used, but we put it in
// anyways for consistency
if(options.axref === options.xref) {
modifyItem('ax', xa.p2r(xa.r2p(options.ax) + dx));
modifyItem('ax', shiftPosition(xa, dx, 'ax', gs, options));
} else {
modifyItem('ax', options.ax + dx);
}

if(options.ayref === options.yref) {
modifyItem('ay', ya.p2r(ya.r2p(options.ay) + dy));
modifyItem('ay', shiftPosition(ya, dy, 'ay', gs.w, options));
} else {
modifyItem('ay', options.ay + dy);
}
Expand All @@ -625,7 +681,9 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
} else if(!subplotId) {
var xUpdate, yUpdate;
if(xa) {
xUpdate = xa.p2r(xa.r2p(options.x) + dx);
// shiftPosition will not execute code where xa was
// undefined, so we use to calculate xUpdate too
xUpdate = shiftPosition(xa, dx, 'x', gs, options);
} else {
var widthFraction = options._xsize / gs.w;
var xLeft = options.x + (options._xshift - options.xshift) / gs.w - widthFraction / 2;
Expand All @@ -635,7 +693,9 @@ function drawRaw(gd, options, index, subplotId, xa, ya) {
}

if(ya) {
yUpdate = ya.p2r(ya.r2p(options.y) + dy);
// shiftPosition will not execute code where ya was
// undefined, so we use to calculate yUpdate too
yUpdate = shiftPosition(ya, dy, 'y', gs, options);
} else {
var heightFraction = options._ysize / gs.h;
var yBottom = options.y - (options._yshift + options.yshift) / gs.h - heightFraction / 2;
Expand Down
Loading