Skip to content

Commit 8ed1957

Browse files
committed
Merge branch 'master' into frame-extend-with-nulls
2 parents 66f8bb7 + c883e57 commit 8ed1957

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+1930
-998
lines changed

src/components/annotations/annotation_defaults.js

+32-45
Original file line numberDiff line numberDiff line change
@@ -39,69 +39,56 @@ module.exports = function handleAnnotationDefaults(annIn, annOut, fullLayout, op
3939
var borderWidth = coerce('borderwidth');
4040
var showArrow = coerce('showarrow');
4141

42-
if(showArrow) {
43-
coerce('arrowcolor', borderOpacity ? annOut.bordercolor : Color.defaultLine);
44-
coerce('arrowhead');
45-
coerce('arrowsize');
46-
coerce('arrowwidth', ((borderOpacity && borderWidth) || 1) * 2);
47-
coerce('ax');
48-
coerce('ay');
49-
coerce('axref');
50-
coerce('ayref');
51-
52-
// if you have one part of arrow length you should have both
53-
Lib.noneOrAll(annIn, annOut, ['ax', 'ay']);
54-
}
55-
5642
coerce('text', showArrow ? ' ' : 'new text');
5743
coerce('textangle');
5844
Lib.coerceFont(coerce, 'font', fullLayout.font);
5945

6046
// positioning
61-
var axLetters = ['x', 'y'];
47+
var axLetters = ['x', 'y'],
48+
arrowPosDflt = [-10, -30],
49+
gdMock = {_fullLayout: fullLayout};
6250
for(var i = 0; i < 2; i++) {
63-
var axLetter = axLetters[i],
64-
tdMock = {_fullLayout: fullLayout};
51+
var axLetter = axLetters[i];
6552

6653
// xref, yref
67-
var axRef = Axes.coerceRef(annIn, annOut, tdMock, axLetter);
68-
69-
// TODO: should be refactored in conjunction with Axes axref, ayref
70-
var aaxRef = Axes.coerceARef(annIn, annOut, tdMock, axLetter);
54+
var axRef = Axes.coerceRef(annIn, annOut, gdMock, axLetter, '', 'paper');
7155

7256
// x, y
73-
var defaultPosition = 0.5;
74-
if(axRef !== 'paper') {
75-
var ax = Axes.getFromId(tdMock, axRef);
76-
defaultPosition = ax.range[0] + defaultPosition * (ax.range[1] - ax.range[0]);
77-
78-
// convert date or category strings to numbers
79-
if(['date', 'category'].indexOf(ax.type) !== -1 &&
80-
typeof annIn[axLetter] === 'string') {
81-
var newval;
82-
if(ax.type === 'date') {
83-
newval = Lib.dateTime2ms(annIn[axLetter]);
84-
if(newval !== false) annIn[axLetter] = newval;
85-
86-
if(aaxRef === axRef) {
87-
var newvalB = Lib.dateTime2ms(annIn['a' + axLetter]);
88-
if(newvalB !== false) annIn['a' + axLetter] = newvalB;
89-
}
90-
}
91-
else if((ax._categories || []).length) {
92-
newval = ax._categories.indexOf(annIn[axLetter]);
93-
if(newval !== -1) annIn[axLetter] = newval;
94-
}
57+
Axes.coercePosition(annOut, gdMock, coerce, axRef, axLetter, 0.5);
58+
59+
if(showArrow) {
60+
var arrowPosAttr = 'a' + axLetter,
61+
// axref, ayref
62+
aaxRef = Axes.coerceRef(annIn, annOut, gdMock, arrowPosAttr, 'pixel');
63+
64+
// for now the arrow can only be on the same axis or specified as pixels
65+
// TODO: sometime it might be interesting to allow it to be on *any* axis
66+
// but that would require updates to drawing & autorange code and maybe more
67+
if(aaxRef !== 'pixel' && aaxRef !== axRef) {
68+
aaxRef = annOut[arrowPosAttr] = 'pixel';
9569
}
70+
71+
// ax, ay
72+
var aDflt = (aaxRef === 'pixel') ? arrowPosDflt[i] : 0.4;
73+
Axes.coercePosition(annOut, gdMock, coerce, aaxRef, arrowPosAttr, aDflt);
9674
}
97-
coerce(axLetter, defaultPosition);
9875

9976
// xanchor, yanchor
100-
if(!showArrow) coerce(axLetter + 'anchor');
77+
else coerce(axLetter + 'anchor');
10178
}
10279

10380
// if you have one coordinate you should have both
10481
Lib.noneOrAll(annIn, annOut, ['x', 'y']);
10582

83+
if(showArrow) {
84+
coerce('arrowcolor', borderOpacity ? annOut.bordercolor : Color.defaultLine);
85+
coerce('arrowhead');
86+
coerce('arrowsize');
87+
coerce('arrowwidth', ((borderOpacity && borderWidth) || 1) * 2);
88+
89+
// if you have one part of arrow length you should have both
90+
Lib.noneOrAll(annIn, annOut, ['ax', 'ay']);
91+
}
92+
10693
return annOut;
10794
};

src/components/annotations/attributes.js

+24-10
Original file line numberDiff line numberDiff line change
@@ -141,27 +141,27 @@ module.exports = {
141141
description: 'Sets the width (in px) of annotation arrow.'
142142
},
143143
ax: {
144-
valType: 'number',
145-
dflt: -10,
144+
valType: 'any',
146145
role: 'info',
147146
description: [
148147
'Sets the x component of the arrow tail about the arrow head.',
149148
'If `axref` is `pixel`, a positive (negative) ',
150149
'component corresponds to an arrow pointing',
151150
'from right to left (left to right).',
152-
'If `axref` is an axis, this is a value on that axis.'
151+
'If `axref` is an axis, this is an absolute value on that axis,',
152+
'like `x`, NOT a relative value.'
153153
].join(' ')
154154
},
155155
ay: {
156-
valType: 'number',
157-
dflt: -30,
156+
valType: 'any',
158157
role: 'info',
159158
description: [
160159
'Sets the y component of the arrow tail about the arrow head.',
161160
'If `ayref` is `pixel`, a positive (negative) ',
162161
'component corresponds to an arrow pointing',
163162
'from bottom to top (top to bottom).',
164-
'If `ayref` is an axis, this is a value on that axis.'
163+
'If `ayref` is an axis, this is an absolute value on that axis,',
164+
'like `y`, NOT a relative value.'
165165
].join(' ')
166166
},
167167
axref: {
@@ -216,11 +216,18 @@ module.exports = {
216216
].join(' ')
217217
},
218218
x: {
219-
valType: 'number',
219+
valType: 'any',
220220
role: 'info',
221221
description: [
222222
'Sets the annotation\'s x position.',
223-
'Note that dates and categories are converted to numbers.'
223+
'If the axis `type` is *log*, then you must take the',
224+
'log of your desired range.',
225+
'If the axis `type` is *date*, it should be date strings,',
226+
'like date data, though Date objects and unix milliseconds',
227+
'will be accepted and converted to strings.',
228+
'If the axis `type` is *category*, it should be numbers,',
229+
'using the scale where each category is assigned a serial',
230+
'number from zero in the order it appears.'
224231
].join(' ')
225232
},
226233
xanchor: {
@@ -259,11 +266,18 @@ module.exports = {
259266
].join(' ')
260267
},
261268
y: {
262-
valType: 'number',
269+
valType: 'any',
263270
role: 'info',
264271
description: [
265272
'Sets the annotation\'s y position.',
266-
'Note that dates and categories are converted to numbers.'
273+
'If the axis `type` is *log*, then you must take the',
274+
'log of your desired range.',
275+
'If the axis `type` is *date*, it should be date strings,',
276+
'like date data, though Date objects and unix milliseconds',
277+
'will be accepted and converted to strings.',
278+
'If the axis `type` is *category*, it should be numbers,',
279+
'using the scale where each category is assigned a serial',
280+
'number from zero in the order it appears.'
267281
].join(' ')
268282
},
269283
yanchor: {

src/components/annotations/calc_autorange.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,14 @@ function annAutorange(gd) {
6969
}
7070

7171
if(xa && xa.autorange) {
72-
Axes.expand(xa, [xa.l2c(ann.x)], {
72+
Axes.expand(xa, [xa.l2c(xa.r2l(ann.x))], {
7373
ppadplus: rightSize,
7474
ppadminus: leftSize
7575
});
7676
}
7777

7878
if(ya && ya.autorange) {
79-
Axes.expand(ya, [ya.l2c(ann.y)], {
79+
Axes.expand(ya, [ya.l2c(ya.r2l(ann.y))], {
8080
ppadplus: bottomSize,
8181
ppadminus: topSize
8282
});

src/components/annotations/draw.js

+28-28
Original file line numberDiff line numberDiff line change
@@ -172,12 +172,19 @@ function drawOne(gd, index, opt, value) {
172172
continue;
173173
}
174174

175-
var axOld = Axes.getFromId(gd, Axes.coerceRef(oldRef, {}, gd, axLetter)),
176-
axNew = Axes.getFromId(gd, Axes.coerceRef(optionsIn, {}, gd, axLetter)),
175+
var axOld = Axes.getFromId(gd, Axes.coerceRef(oldRef, {}, gd, axLetter, '', 'paper')),
176+
axNew = Axes.getFromId(gd, Axes.coerceRef(optionsIn, {}, gd, axLetter, '', 'paper')),
177177
position = optionsIn[axLetter],
178178
axTypeOld = oldPrivate['_' + axLetter + 'type'];
179179

180180
if(optionsEdit[axLetter + 'ref'] !== undefined) {
181+
182+
// TODO: include ax / ay / axref / ayref here if not 'pixel'
183+
// or even better, move all of this machinery out of here and into
184+
// streambed as extra attributes to a regular relayout call
185+
// we should do this after v2.0 when it can work equivalently for
186+
// annotations, shapes, and images.
187+
181188
var autoAnchor = optionsIn[axLetter + 'anchor'] === 'auto',
182189
plotSize = (axLetter === 'x' ? gs.w : gs.h),
183190
halfSizeFrac = (oldPrivate['_' + axLetter + 'size'] || 0) /
@@ -186,18 +193,11 @@ function drawOne(gd, index, opt, value) {
186193
// go to the same fraction of the axis length
187194
// whether or not these axes share a domain
188195

189-
// first convert to fraction of the axis
190-
position = (position - axOld.range[0]) /
191-
(axOld.range[1] - axOld.range[0]);
192-
193-
// then convert to new data coordinates at the same fraction
194-
position = axNew.range[0] +
195-
position * (axNew.range[1] - axNew.range[0]);
196+
position = axNew.fraction2r(axOld.r2fraction(position));
196197
}
197198
else if(axOld) { // data -> paper
198199
// first convert to fraction of the axis
199-
position = (position - axOld.range[0]) /
200-
(axOld.range[1] - axOld.range[0]);
200+
position = axOld.r2fraction(position);
201201

202202
// next scale the axis to the whole plot
203203
position = axOld.domain[0] +
@@ -225,8 +225,7 @@ function drawOne(gd, index, opt, value) {
225225
(axNew.domain[1] - axNew.domain[0]);
226226

227227
// finally convert to data coordinates
228-
position = axNew.range[0] +
229-
position * (axNew.range[1] - axNew.range[0]);
228+
position = axNew.fraction2r(position);
230229
}
231230
}
232231

@@ -358,20 +357,21 @@ function drawOne(gd, index, opt, value) {
358357
// outside the visible plot (as long as the axis
359358
// isn't autoranged - then we need to draw it
360359
// anyway to get its bounding box)
361-
if(!ax.autorange && ((options[axLetter] - ax.range[0]) *
362-
(options[axLetter] - ax.range[1]) > 0)) {
360+
var posFraction = ax.r2fraction(options[axLetter]);
361+
if(!ax.autorange && (posFraction < 0 || posFraction > 1)) {
363362
if(options['a' + axLetter + 'ref'] === axRef) {
364-
if((options['a' + axLetter] - ax.range[0]) *
365-
(options['a' + axLetter] - ax.range[1]) > 0) {
363+
posFraction = ax.r2fraction(options['a' + axLetter]);
364+
if(posFraction < 0 || posFraction > 1) {
366365
annotationIsOffscreen = true;
367366
}
368-
} else {
367+
}
368+
else {
369369
annotationIsOffscreen = true;
370370
}
371371

372372
if(annotationIsOffscreen) return;
373373
}
374-
annPosPx[axLetter] = ax._offset + ax.l2p(options[axLetter]);
374+
annPosPx[axLetter] = ax._offset + ax.r2p(options[axLetter]);
375375
alignPosition = 0.5;
376376
}
377377
else {
@@ -384,7 +384,7 @@ function drawOne(gd, index, opt, value) {
384384

385385
var alignShift = 0;
386386
if(options['a' + axLetter + 'ref'] === axRef) {
387-
annPosPx['aa' + axLetter] = ax._offset + ax.l2p(options['a' + axLetter]);
387+
annPosPx['aa' + axLetter] = ax._offset + ax.r2p(options['a' + axLetter]);
388388
} else {
389389
if(options.showarrow) {
390390
alignShift = options['a' + axLetter];
@@ -584,22 +584,22 @@ function drawOne(gd, index, opt, value) {
584584
ann.call(Lib.setTranslate, xcenter, ycenter);
585585

586586
update[annbase + '.x'] = xa ?
587-
(options.x + dx / xa._m) :
587+
xa.p2r(xa.r2p(options.x) + dx) :
588588
((arrowX + dx - gs.l) / gs.w);
589589
update[annbase + '.y'] = ya ?
590-
(options.y + dy / ya._m) :
590+
ya.p2r(ya.r2p(options.y) + dy) :
591591
(1 - ((arrowY + dy - gs.t) / gs.h));
592592

593593
if(options.axref === options.xref) {
594594
update[annbase + '.ax'] = xa ?
595-
(options.ax + dx / xa._m) :
596-
((arrowX + dx - gs.l) / gs.w);
595+
xa.p2r(xa.r2p(options.ax) + dx) :
596+
((arrowX + dx - gs.l) / gs.w);
597597
}
598598

599599
if(options.ayref === options.yref) {
600600
update[annbase + '.ay'] = ya ?
601-
(options.ay + dy / ya._m) :
602-
(1 - ((arrowY + dy - gs.t) / gs.h));
601+
ya.p2r(ya.r2p(options.ay) + dy) :
602+
(1 - ((arrowY + dy - gs.t) / gs.h));
603603
}
604604

605605
anng.attr({
@@ -645,13 +645,13 @@ function drawOne(gd, index, opt, value) {
645645
var csr = 'pointer';
646646
if(options.showarrow) {
647647
if(options.axref === options.xref) {
648-
update[annbase + '.ax'] = xa.p2l(xa.l2p(options.ax) + dx);
648+
update[annbase + '.ax'] = xa.p2r(xa.r2p(options.ax) + dx);
649649
} else {
650650
update[annbase + '.ax'] = options.ax + dx;
651651
}
652652

653653
if(options.ayref === options.yref) {
654-
update[annbase + '.ay'] = ya.p2l(ya.l2p(options.ay) + dy);
654+
update[annbase + '.ay'] = ya.p2r(ya.r2p(options.ay) + dy);
655655
} else {
656656
update[annbase + '.ay'] = options.ay + dy;
657657
}

src/components/drawing/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,14 @@ drawing.getPx = function(s, styleAttr) {
7575
return Number(s.style(styleAttr).replace(/px$/, ''));
7676
};
7777

78-
drawing.crispRound = function(td, lineWidth, dflt) {
78+
drawing.crispRound = function(gd, lineWidth, dflt) {
7979
// for lines that disable antialiasing we want to
8080
// make sure the width is an integer, and at least 1 if it's nonzero
8181

8282
if(!lineWidth || !isNumeric(lineWidth)) return dflt || 0;
8383

8484
// but not for static plots - these don't get antialiased anyway.
85-
if(td._context.staticPlot) return lineWidth;
85+
if(gd._context.staticPlot) return lineWidth;
8686

8787
if(lineWidth < 1) return 1;
8888
return Math.round(lineWidth);

src/components/images/attributes.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ module.exports = {
9090
},
9191

9292
x: {
93-
valType: 'number',
93+
valType: 'any',
9494
role: 'info',
9595
dflt: 0,
9696
description: [
@@ -102,7 +102,7 @@ module.exports = {
102102
},
103103

104104
y: {
105-
valType: 'number',
105+
valType: 'any',
106106
role: 'info',
107107
dflt: 0,
108108
description: [

0 commit comments

Comments
 (0)