diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index 40a1e1e5e96..1c1915ff3b9 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -521,6 +521,9 @@ function autoShiftMonthBins(binStart, data, dtick, dataMin, calendar) {
axes.prepTicks = function(ax, opts) {
var rng = Lib.simpleMap(ax.range, ax.r2l, undefined, undefined, opts);
+ ax._dtickInit = ax.dtick;
+ ax._tick0Init = ax.tick0;
+
// calculate max number of (auto) ticks to display based on plot size
if(ax.tickmode === 'auto' || !ax.dtick) {
var nt = ax.nticks;
@@ -545,7 +548,7 @@ axes.prepTicks = function(ax, opts) {
if(ax.tickmode === 'array') nt *= 100;
- ax._roughDTick = (Math.abs(rng[1] - rng[0]) - (ax._lBreaks || 0)) / nt;
+ ax._roughDTick = Math.abs(rng[1] - rng[0]) / nt;
axes.autoTicks(ax, ax._roughDTick);
// check for a forced minimum dtick
@@ -568,6 +571,10 @@ axes.prepTicks = function(ax, opts) {
autoTickRound(ax);
};
+function nMonths(dtick) {
+ return +(dtick.substring(1));
+}
+
// calculate the ticks: text, values, positioning
// if ticks are set to automatic, determine the right values (tick0,dtick)
// in any case, set tickround to # of digits to round tick labels to,
@@ -580,15 +587,17 @@ axes.calcTicks = function calcTicks(ax, opts) {
// in case we're missing some ticktext, we can break out for array ticks
if(ax.tickmode === 'array') return arrayTicks(ax);
- // find the first tick
- ax._tmin = axes.tickFirst(ax, opts);
-
// add a tiny bit so we get ticks which may have rounded out
var exRng = expandRange(rng);
var startTick = exRng[0];
var endTick = exRng[1];
// check for reversed axis
var axrev = (rng[1] < rng[0]);
+ var minRange = Math.min(rng[0], rng[1]);
+ var maxRange = Math.max(rng[0], rng[1]);
+
+ // find the first tick
+ ax._tmin = axes.tickFirst(ax, opts);
// No visible ticks? Quit.
// I've only seen this on category axes with all categories off the edge.
@@ -601,112 +610,13 @@ axes.calcTicks = function calcTicks(ax, opts) {
}
var isDLog = (ax.type === 'log') && !(isNumeric(ax.dtick) || ax.dtick.charAt(0) === 'L');
+ var isMDate = (ax.type === 'date') && !(isNumeric(ax.dtick) || ax.dtick.charAt(0) === 'M');
- var tickVals;
- function generateTicks() {
- var xPrevious = null;
- var maxTicks = Math.max(1000, ax._length || 0);
- tickVals = [];
- for(var x = ax._tmin;
- (axrev) ? (x >= endTick) : (x <= endTick);
- x = axes.tickIncrement(x, ax.dtick, axrev, ax.calendar)) {
- // prevent infinite loops - no more than one tick per pixel,
- // and make sure each value is different from the previous
- if(tickVals.length > maxTicks || x === xPrevious) break;
- xPrevious = x;
-
- var minor = false;
- if(isDLog && (x !== (x | 0))) {
- minor = true;
- }
-
- tickVals.push({
- minor: minor,
- value: x
- });
- }
- }
-
- generateTicks();
-
+ var tickformat = axes.getTickFormat(ax);
var isPeriod = ax.ticklabelmode === 'period';
- var addedPreTick0Label = false;
- if(isPeriod && tickVals[0]) {
- // add one label to show pre tick0 period
- tickVals.unshift({
- minor: false,
- value: axes.tickIncrement(tickVals[0].value, ax.dtick, !axrev, ax.caldendar)
- });
- addedPreTick0Label = true;
- }
-
- if(ax.rangebreaks) {
- // replace ticks inside breaks that would get a tick
- // and reduce ticks
- var len = tickVals.length;
- if(len) {
- var tf = 0;
- if(ax.tickmode === 'auto') {
- tf =
- (ax._id.charAt(0) === 'y' ? 2 : 6) *
- (ax.tickfont ? ax.tickfont.size : 12);
- }
-
- var newTickVals = [];
- var prevPos;
-
- var dir = axrev ? 1 : -1;
- var first = axrev ? 0 : len - 1;
- var last = axrev ? len - 1 : 0;
- for(var q = first; dir * q <= dir * last; q += dir) {
- var tickVal = tickVals[q];
- if(ax.maskBreaks(tickVal.value) === BADNUM) {
- tickVal.value = moveOutsideBreak(tickVal.value, ax);
-
- if(ax._rl && (
- ax._rl[0] === tickVal.value ||
- ax._rl[1] === tickVal.value
- )) continue;
- }
-
- var pos = ax.c2p(tickVal.value);
-
- if(pos === prevPos) {
- if(newTickVals[newTickVals.length - 1].value < tickVal.value) {
- newTickVals[newTickVals.length - 1] = tickVal;
- }
- } else if(prevPos === undefined || Math.abs(pos - prevPos) > tf) {
- prevPos = pos;
- newTickVals.push(tickVal);
- }
- }
- tickVals = newTickVals.reverse();
- }
- }
-
- // If same angle over a full circle, the last tick vals is a duplicate.
- // TODO must do something similar for angular date axes.
- if(isAngular(ax) && Math.abs(rng[1] - rng[0]) === 360) {
- tickVals.pop();
- }
-
- // save the last tick as well as first, so we can
- // show the exponent only on the last one
- ax._tmax = (tickVals[tickVals.length - 1] || {}).value;
-
- // for showing the rest of a date when the main tick label is only the
- // latter part: ax._prevDateHead holds what we showed most recently.
- // Start with it cleared and mark that we're in calcTicks (ie calculating a
- // whole string of these so we should care what the previous date head was!)
- ax._prevDateHead = '';
- ax._inCalcTicks = true;
-
- var minRange = Math.min(rng[0], rng[1]);
- var maxRange = Math.max(rng[0], rng[1]);
-
var definedDelta;
- var tickformat = axes.getTickFormat(ax);
if(isPeriod && tickformat) {
+ var noDtick = ax._dtickInit !== ax.dtick;
if(
!(/%[fLQsSMX]/.test(tickformat))
// %f: microseconds as a decimal number [000000, 999999]
@@ -721,11 +631,15 @@ axes.calcTicks = function calcTicks(ax, opts) {
/%[HI]/.test(tickformat)
// %H: hour (24-hour clock) as a decimal number [00,23]
// %I: hour (12-hour clock) as a decimal number [01,12]
- ) definedDelta = ONEHOUR;
- else if(
+ ) {
+ definedDelta = ONEHOUR;
+ if(noDtick && !isMDate && ax.dtick < ONEHOUR) ax.dtick = ONEHOUR;
+ } else if(
/%p/.test(tickformat) // %p: either AM or PM
- ) definedDelta = HALFDAY;
- else if(
+ ) {
+ definedDelta = HALFDAY;
+ if(noDtick && !isMDate && ax.dtick < HALFDAY) ax.dtick = HALFDAY;
+ } else if(
/%[Aadejuwx]/.test(tickformat)
// %A: full weekday name
// %a: abbreviated weekday name
@@ -735,60 +649,101 @@ axes.calcTicks = function calcTicks(ax, opts) {
// %u: Monday-based (ISO 8601) weekday as a decimal number [1,7]
// %w: Sunday-based weekday as a decimal number [0,6]
// %x: the locale’s date, such as %-m/%-d/%Y
- ) definedDelta = ONEDAY;
- else if(
+ ) {
+ definedDelta = ONEDAY;
+ if(noDtick && !isMDate && ax.dtick < ONEDAY) ax.dtick = ONEDAY;
+ } else if(
/%[UVW]/.test(tickformat)
// %U: Sunday-based week of the year as a decimal number [00,53]
// %V: ISO 8601 week of the year as a decimal number [01, 53]
// %W: Monday-based week of the year as a decimal number [00,53]
- ) definedDelta = ONEWEEK;
- else if(
+ ) {
+ definedDelta = ONEWEEK;
+ if(noDtick && !isMDate && ax.dtick < ONEWEEK) ax.dtick = ONEWEEK;
+ } else if(
/%[Bbm]/.test(tickformat)
// %B: full month name
// %b: abbreviated month name
// %m: month as a decimal number [01,12]
- ) definedDelta = ONEAVGMONTH;
- else if(
+ ) {
+ definedDelta = ONEAVGMONTH;
+ if(noDtick && (
+ isMDate ? nMonths(ax.dtick) < 1 : ax.dtick < ONEMINMONTH)
+ ) ax.dtick = 'M1';
+ } else if(
/%[q]/.test(tickformat)
// %q: quarter of the year as a decimal number [1,4]
- ) definedDelta = ONEAVGQUARTER;
- else if(
+ ) {
+ definedDelta = ONEAVGQUARTER;
+ if(noDtick && (
+ isMDate ? nMonths(ax.dtick) < 3 : ax.dtick < ONEMINQUARTER)
+ ) ax.dtick = 'M3';
+ } else if(
/%[Yy]/.test(tickformat)
// %Y: year with century as a decimal number, such as 1999
// %y: year without century as a decimal number [00,99]
- ) definedDelta = ONEAVGYEAR;
+ ) {
+ definedDelta = ONEAVGYEAR;
+ if(noDtick && (
+ isMDate ? nMonths(ax.dtick) < 12 : ax.dtick < ONEMINYEAR)
+ ) ax.dtick = 'M12';
+ }
}
}
- var ticksOut = [];
- var i;
- var prevText;
- for(i = 0; i < tickVals.length; i++) {
- var _minor = tickVals[i].minor;
- var _value = tickVals[i].value;
+ var maxTicks = Math.max(1000, ax._length || 0);
+ var tickVals = [];
+ var xPrevious = null;
+ var x = ax._tmin;
- var t = axes.tickText(
- ax,
- _value,
- false, // hover
- _minor // noSuffixPrefix
- );
-
- if(isPeriod && prevText === t.text) continue;
- prevText = t.text;
+ if(ax.rangebreaks && ax._tick0Init !== ax.tick0) {
+ // adjust tick0
+ x = moveOutsideBreak(x, ax);
+ if(!axrev) {
+ x = axes.tickIncrement(x, ax.dtick, !axrev, ax.calendar);
+ }
+ }
- ticksOut.push(t);
+ if(isPeriod) {
+ // add one item to label period before tick0
+ x = axes.tickIncrement(x, ax.dtick, !axrev, ax.calendar);
}
- if(isPeriod && addedPreTick0Label) {
- var removedPreTick0Label = false;
+ for(;
+ (axrev) ? (x >= endTick) : (x <= endTick);
+ x = axes.tickIncrement(x, ax.dtick, axrev, ax.calendar)
+ ) {
+ if(ax.rangebreaks) {
+ if(!axrev) {
+ if(x < startTick) continue;
+ if(ax.maskBreaks(x) === BADNUM && moveOutsideBreak(x, ax) >= maxRange) break;
+ }
+ }
+
+ // prevent infinite loops - no more than one tick per pixel,
+ // and make sure each value is different from the previous
+ if(tickVals.length > maxTicks || x === xPrevious) break;
+ xPrevious = x;
+
+ var minor = false;
+ if(isDLog && (x !== (x | 0))) {
+ minor = true;
+ }
+
+ tickVals.push({
+ minor: minor,
+ value: x
+ });
+ }
- for(i = 0; i < ticksOut.length; i++) {
- var v = ticksOut[i].x;
+ var i;
+ if(isPeriod) {
+ for(i = 0; i < tickVals.length; i++) {
+ var v = tickVals[i].value;
var a = i;
var b = i + 1;
- if(i < ticksOut.length - 1) {
+ if(i < tickVals.length - 1) {
a = i;
b = i + 1;
} else if(i > 0) {
@@ -799,8 +754,8 @@ axes.calcTicks = function calcTicks(ax, opts) {
b = i;
}
- var A = ticksOut[a].x;
- var B = ticksOut[b].x;
+ var A = tickVals[a].value;
+ var B = tickVals[b].value;
var actualDelta = Math.abs(B - A);
var delta = definedDelta || actualDelta;
var periodLength = 0;
@@ -833,48 +788,112 @@ axes.calcTicks = function calcTicks(ax, opts) {
periodLength = ONEHOUR;
}
- if(periodLength && ax.rangebreaks) {
- var nFirstHalf = 0;
- var nSecondHalf = 0;
- var nAll = 2 * 3 * 7; // number of samples
+ var inBetween;
+ if(periodLength >= actualDelta) {
+ // ensure new label positions remain between ticks
+ periodLength = actualDelta;
+ inBetween = true;
+ }
+
+ var endPeriod = v + periodLength;
+ if(ax.rangebreaks && periodLength > 0) {
+ var nAll = 84; // highly divisible 7 * 12
+ var n = 0;
for(var c = 0; c < nAll; c++) {
- var r = c / nAll;
- if(ax.maskBreaks(A * (1 - r) + B * r) !== BADNUM) {
- if(r < 0.5) {
- nFirstHalf++;
- } else {
- nSecondHalf++;
- }
- }
+ var r = (c + 0.5) / nAll;
+ if(ax.maskBreaks(v * (1 - r) + r * endPeriod) !== BADNUM) n++;
}
+ periodLength *= n / nAll;
- if(nSecondHalf) {
- periodLength *= (nFirstHalf + nSecondHalf) / nAll;
+ if(!periodLength) {
+ tickVals[i].drop = true;
}
+
+ if(inBetween && actualDelta > ONEWEEK) periodLength = actualDelta; // center monthly & longer periods
}
- if(periodLength <= actualDelta) { // i.e. to ensure new label positions remain between ticks
- v += periodLength / 2;
+ if(
+ periodLength > 0 || // not instant
+ i === 0 // taking care first tick added
+ ) {
+ tickVals[i].periodX = v + periodLength / 2;
+ }
+ }
+ }
+
+ if(ax.rangebreaks) {
+ var flip = ax._id.charAt(0) === 'y';
+
+ var fontSize = 1; // one pixel minimum
+ if(ax.tickmode === 'auto') {
+ fontSize = ax.tickfont ? ax.tickfont.size : 12;
+ }
+
+ var prevL = NaN;
+ for(i = tickVals.length - 1; i > -1; i--) {
+ if(tickVals[i].drop) {
+ tickVals.splice(i, 1);
+ continue;
}
- ticksOut[i].periodX = v;
+ tickVals[i].value = moveOutsideBreak(tickVals[i].value, ax);
- if(v > maxRange || v < minRange) { // hide label if outside the range
- ticksOut[i].text = ' '; // don't use an empty string here which can confuse automargin (issue 5132)
- removedPreTick0Label = true;
+ // avoid overlaps
+ var l = ax.c2p(tickVals[i].value);
+ if(flip ?
+ (prevL > l - fontSize) :
+ (prevL < l + fontSize)
+ ) { // ensure one pixel minimum
+ tickVals.splice(axrev ? i + 1 : i, 1);
+ } else {
+ prevL = l;
}
}
+ }
- if(removedPreTick0Label) {
- for(i = 0; i < ticksOut.length; i++) {
- if(ticksOut[i].periodX <= maxRange && ticksOut[i].periodX >= minRange) {
- // redo first visible tick
- ax._prevDateHead = '';
- ticksOut[i].text = axes.tickText(ax, ticksOut[i].x).text;
- break;
- }
+ // If same angle over a full circle, the last tick vals is a duplicate.
+ // TODO must do something similar for angular date axes.
+ if(isAngular(ax) && Math.abs(rng[1] - rng[0]) === 360) {
+ tickVals.pop();
+ }
+
+ // save the last tick as well as first, so we can
+ // show the exponent only on the last one
+ ax._tmax = (tickVals[tickVals.length - 1] || {}).value;
+
+ // for showing the rest of a date when the main tick label is only the
+ // latter part: ax._prevDateHead holds what we showed most recently.
+ // Start with it cleared and mark that we're in calcTicks (ie calculating a
+ // whole string of these so we should care what the previous date head was!)
+ ax._prevDateHead = '';
+ ax._inCalcTicks = true;
+
+ var ticksOut = [];
+ var t, p;
+ for(i = 0; i < tickVals.length; i++) {
+ var _minor = tickVals[i].minor;
+ var _value = tickVals[i].value;
+
+ t = axes.tickText(
+ ax,
+ _value,
+ false, // hover
+ _minor // noSuffixPrefix
+ );
+
+ p = tickVals[i].periodX;
+ if(p !== undefined) {
+ t.periodX = p;
+ if(p > maxRange || p < minRange) { // hide label if outside the range
+ if(p > maxRange) t.periodX = maxRange;
+ if(p < minRange) t.periodX = minRange;
+
+ t.text = ' '; // don't use an empty string here which can confuse automargin (issue 5132)
+ ax._prevDateHead = '';
}
}
+
+ ticksOut.push(t);
}
ax._inCalcTicks = false;
@@ -968,6 +987,7 @@ axes.autoTicks = function(ax, roughDTick) {
if(ax.type === 'date') {
ax.tick0 = Lib.dateTick0(ax.calendar, 0);
+
// the criteria below are all based on the rough spacing we calculate
// being > half of the final unit - so precalculate twice the rough val
var roughX2 = 2 * roughDTick;
@@ -2411,8 +2431,8 @@ axes.makeTransPeriodFn = function(ax) {
var axLetter = ax._id.charAt(0);
var offset = ax._offset;
return axLetter === 'x' ?
- function(d) { return 'translate(' + (offset + ax.l2p(d.periodX)) + ',0)'; } :
- function(d) { return 'translate(0,' + (offset + ax.l2p(d.periodX)) + ')'; };
+ function(d) { return 'translate(' + (offset + ax.l2p(d.periodX !== undefined ? d.periodX : d.x)) + ',0)'; } :
+ function(d) { return 'translate(0,' + (offset + ax.l2p(d.periodX !== undefined ? d.periodX : d.x)) + ')'; };
};
/**
diff --git a/test/image/baselines/axes_breaks-candlestick2.png b/test/image/baselines/axes_breaks-candlestick2.png
index e93fee533ae..326bf1dd071 100644
Binary files a/test/image/baselines/axes_breaks-candlestick2.png and b/test/image/baselines/axes_breaks-candlestick2.png differ
diff --git a/test/image/baselines/axes_breaks-dtick_auto.png b/test/image/baselines/axes_breaks-dtick_auto.png
index 18808479908..523b13a3170 100644
Binary files a/test/image/baselines/axes_breaks-dtick_auto.png and b/test/image/baselines/axes_breaks-dtick_auto.png differ
diff --git a/test/image/baselines/axes_breaks-finance.png b/test/image/baselines/axes_breaks-finance.png
index dffbeceebe9..d50156127af 100644
Binary files a/test/image/baselines/axes_breaks-finance.png and b/test/image/baselines/axes_breaks-finance.png differ
diff --git a/test/image/baselines/axes_breaks-night_autorange-reversed.png b/test/image/baselines/axes_breaks-night_autorange-reversed.png
index 464e78edb37..37fe6c40862 100644
Binary files a/test/image/baselines/axes_breaks-night_autorange-reversed.png and b/test/image/baselines/axes_breaks-night_autorange-reversed.png differ
diff --git a/test/image/baselines/axes_breaks-overlap.png b/test/image/baselines/axes_breaks-overlap.png
index eed0f683fd7..3cc73f6915b 100644
Binary files a/test/image/baselines/axes_breaks-overlap.png and b/test/image/baselines/axes_breaks-overlap.png differ
diff --git a/test/image/baselines/axes_breaks-rangeslider.png b/test/image/baselines/axes_breaks-rangeslider.png
index 10c47a94354..73600339ce6 100644
Binary files a/test/image/baselines/axes_breaks-rangeslider.png and b/test/image/baselines/axes_breaks-rangeslider.png differ
diff --git a/test/image/baselines/axes_breaks-reversed-without-pattern.png b/test/image/baselines/axes_breaks-reversed-without-pattern.png
index 497b91fd460..29fec4b988a 100644
Binary files a/test/image/baselines/axes_breaks-reversed-without-pattern.png and b/test/image/baselines/axes_breaks-reversed-without-pattern.png differ
diff --git a/test/image/baselines/axes_breaks-round-weekdays.png b/test/image/baselines/axes_breaks-round-weekdays.png
index 9f20ece5dfa..f4288baf687 100644
Binary files a/test/image/baselines/axes_breaks-round-weekdays.png and b/test/image/baselines/axes_breaks-round-weekdays.png differ
diff --git a/test/image/baselines/axes_breaks-values.png b/test/image/baselines/axes_breaks-values.png
index b3c9e48c6d8..c7c47d0e295 100644
Binary files a/test/image/baselines/axes_breaks-values.png and b/test/image/baselines/axes_breaks-values.png differ
diff --git a/test/image/baselines/axes_breaks-weekends-weeknights.png b/test/image/baselines/axes_breaks-weekends-weeknights.png
index 427913aa1f4..530bc27375f 100644
Binary files a/test/image/baselines/axes_breaks-weekends-weeknights.png and b/test/image/baselines/axes_breaks-weekends-weeknights.png differ
diff --git a/test/image/baselines/axes_breaks-weekends_autorange-reversed.png b/test/image/baselines/axes_breaks-weekends_autorange-reversed.png
index 33a32e12aa6..2892d889414 100644
Binary files a/test/image/baselines/axes_breaks-weekends_autorange-reversed.png and b/test/image/baselines/axes_breaks-weekends_autorange-reversed.png differ
diff --git a/test/image/baselines/axes_breaks.png b/test/image/baselines/axes_breaks.png
index 722307c1624..5f34b4c8e68 100644
Binary files a/test/image/baselines/axes_breaks.png and b/test/image/baselines/axes_breaks.png differ
diff --git a/test/image/baselines/date_axes_period_breaks_automargin.png b/test/image/baselines/date_axes_period_breaks_automargin.png
index 4ccf3e75f3f..2b7679051da 100644
Binary files a/test/image/baselines/date_axes_period_breaks_automargin.png and b/test/image/baselines/date_axes_period_breaks_automargin.png differ
diff --git a/test/image/baselines/period_positioning3.png b/test/image/baselines/period_positioning3.png
index 967ddbc0824..82f414d893b 100644
Binary files a/test/image/baselines/period_positioning3.png and b/test/image/baselines/period_positioning3.png differ
diff --git a/test/image/baselines/period_positioning4.png b/test/image/baselines/period_positioning4.png
index 25895a1608e..f885f15c384 100644
Binary files a/test/image/baselines/period_positioning4.png and b/test/image/baselines/period_positioning4.png differ
diff --git a/test/image/mocks/axes_breaks-contour1d.json b/test/image/mocks/axes_breaks-contour1d.json
index dbc9c4f052b..c218b2d504c 100644
--- a/test/image/mocks/axes_breaks-contour1d.json
+++ b/test/image/mocks/axes_breaks-contour1d.json
@@ -856,6 +856,7 @@
"text": "1D-z-array contour with rangebreaks"
},
"xaxis": {
+ "dtick": 1800000,
"rangebreaks": [
{
"pattern": "hour",
diff --git a/test/image/mocks/axes_breaks-contour2d.json b/test/image/mocks/axes_breaks-contour2d.json
index 94823f364c4..d4dcf9c7661 100644
--- a/test/image/mocks/axes_breaks-contour2d.json
+++ b/test/image/mocks/axes_breaks-contour2d.json
@@ -128,6 +128,7 @@
"text": "2D-z-array contour with rangebreaks"
},
"xaxis": {
+ "dtick": 1800000,
"rangebreaks": [
{
"pattern": "hour",
diff --git a/test/image/mocks/axes_breaks-heatmap1d.json b/test/image/mocks/axes_breaks-heatmap1d.json
index d1bb6cc6a9a..52f8d0f8675 100644
--- a/test/image/mocks/axes_breaks-heatmap1d.json
+++ b/test/image/mocks/axes_breaks-heatmap1d.json
@@ -856,6 +856,7 @@
"text": "1D-z-array heatmap with rangebreaks"
},
"xaxis": {
+ "dtick": 1800000,
"rangebreaks": [
{
"pattern": "hour",
diff --git a/test/image/mocks/axes_breaks-heatmap2d.json b/test/image/mocks/axes_breaks-heatmap2d.json
index 30fcad62abc..1e49b93f6cb 100644
--- a/test/image/mocks/axes_breaks-heatmap2d.json
+++ b/test/image/mocks/axes_breaks-heatmap2d.json
@@ -128,6 +128,7 @@
"text": "2D-z-array heatmap with rangebreaks"
},
"xaxis": {
+ "dtick": 1800000,
"rangebreaks": [
{
"pattern": "hour",
diff --git a/test/image/mocks/axes_breaks-histogram2d.json b/test/image/mocks/axes_breaks-histogram2d.json
index c9cf100821d..846ba0d5848 100644
--- a/test/image/mocks/axes_breaks-histogram2d.json
+++ b/test/image/mocks/axes_breaks-histogram2d.json
@@ -482,6 +482,7 @@
"2020-01-02 16:55",
"2020-01-03 09:55"
],
+ "dtick": 1800000,
"rangebreaks": [{
"pattern": "hour",
"bounds": [18, 9]
diff --git a/test/jasmine/tests/axes_test.js b/test/jasmine/tests/axes_test.js
index ad6d2509bc5..bd1c5c4a1cd 100644
--- a/test/jasmine/tests/axes_test.js
+++ b/test/jasmine/tests/axes_test.js
@@ -5039,12 +5039,14 @@ describe('Test axes', function() {
afterEach(destroyGraphDiv);
- function _assert(msg, exp) {
+ function _assert(msg, exp, autorange) {
var fullLayout = gd._fullLayout;
var xa = fullLayout.xaxis;
- expect(xa._vals.map(function(d) { return d.x; }))
- .withContext(msg).toEqual(exp.tickVals);
+ var vals = xa._vals.map(function(d) { return Lib.ms2DateTime(d.x); });
+ if(autorange === 'reversed') vals.reverse();
+
+ expect(vals).withContext(msg).toEqual(exp.tickVals);
}
it('should not include requested ticks that fall within rangebreaks', function(done) {
@@ -5066,7 +5068,7 @@ describe('Test axes', function() {
})
.then(function() {
_assert('base', {
- tickVals: [0, 50, 100, 150, 200]
+ tickVals: ['1970-01-01', '1970-01-01 00:00:00.05', '1970-01-01 00:00:00.1', '1970-01-01 00:00:00.15', '1970-01-01 00:00:00.2']
});
})
.then(function() {
@@ -5086,7 +5088,7 @@ describe('Test axes', function() {
})
.then(function() {
_assert('with two rangebreaks', {
- tickVals: [0, 5, 90, 95, 190, 195, 200]
+ tickVals: ['1970-01-01', '1970-01-01 00:00:00.089', '1970-01-01 00:00:00.1', '1970-01-01 00:00:00.189', '1970-01-01 00:00:00.2']
});
})
.catch(failTest)
@@ -5116,7 +5118,7 @@ describe('Test axes', function() {
}],
layout: {
width: 1600,
- height: 1600
+ height: 400
}
};
@@ -5147,8 +5149,8 @@ describe('Test axes', function() {
'1970-01-12 08:00', '1970-01-12 12:00', '1970-01-12 16:00',
'1970-01-13 08:00', '1970-01-13 12:00', '1970-01-13 16:00',
'1970-01-14 08:00', '1970-01-14 12:00', '1970-01-14 16:00'
- ].map(Lib.dateTime2ms)
- });
+ ]
+ }, autorange);
})
.then(function() {
fig.layout.xaxis = {
@@ -5178,8 +5180,8 @@ describe('Test axes', function() {
'1970-01-12 08:00', '1970-01-12 11:00', '1970-01-12 14:00',
'1970-01-13 08:00', '1970-01-13 11:00', '1970-01-13 14:00',
'1970-01-14 08:00', '1970-01-14 11:00', '1970-01-14 14:00'
- ].map(Lib.dateTime2ms)
- });
+ ]
+ }, autorange);
})
.then(function() {
fig.layout.xaxis = {
@@ -5209,8 +5211,8 @@ describe('Test axes', function() {
'1970-01-12 08:00', '1970-01-12 10:00', '1970-01-12 12:00', '1970-01-12 14:00', '1970-01-12 16:00',
'1970-01-13 08:00', '1970-01-13 10:00', '1970-01-13 12:00', '1970-01-13 14:00', '1970-01-13 16:00',
'1970-01-14 08:00', '1970-01-14 10:00', '1970-01-14 12:00', '1970-01-14 14:00', '1970-01-14 16:00'
- ].map(Lib.dateTime2ms)
- });
+ ]
+ }, autorange);
})
.catch(failTest)
.then(done);
@@ -5264,11 +5266,15 @@ describe('Test axes', function() {
function _assert(msg, expPositions, expLabels) {
var ax = gd._fullLayout.xaxis;
- var positions = ax._vals.map(function(d) { return ax.c2d(d.periodX); });
- expect(positions).withContext(msg).toEqual(expPositions);
-
+ var positions = ax._vals.map(function(d) { return ax.c2d(d.periodX !== undefined ? d.periodX : d.x); });
var labels = ax._vals.map(function(d) { return d.text; });
- expect(labels).withContext(msg).toEqual(expLabels);
+
+ for(var i = 0; i < labels.length; i++) {
+ expect(labels[i]).withContext(msg).toBe(expLabels[i]);
+ if(labels[i] !== ' ') {
+ expect(positions[i]).withContext(msg).toBe(expPositions[i]);
+ }
+ }
}
['%Y', '%y'].forEach(function(formatter, i) {
@@ -5473,7 +5479,7 @@ describe('Test axes', function() {
});
['%U', '%V', '%W'].forEach(function(formatter, i) {
- it('should move weekly labels by one day (i.e. to help center the labels) when *day of week* rangebreak is present', function(done) {
+ it('should position weekly labels in the middle when *day of week* rangebreak is present', function(done) {
Plotly.newPlot(gd, {
data: [{
hovertemplate: hovertemplate,
@@ -5522,13 +5528,13 @@ describe('Test axes', function() {
})
.then(function() {
_assert('', [
- ['2019-12-31 04:00', '2020-01-08 12:00', '2020-01-15 12:00', '2020-01-22 12:00', '2020-01-29 12:00'],
- ['2020-01-01 12:00', '2020-01-08 12:00', '2020-01-15 12:00', '2020-01-22 12:00', '2020-01-29 12:00'],
- ['2020-01-01 12:00', '2020-01-08 12:00', '2020-01-15 12:00', '2020-01-22 12:00', '2020-01-29 12:00']
+ ['2020-01-08 12:00', '2020-01-15 12:00', '2020-01-22 12:00', '2020-01-29 12:00'],
+ ['2020-01-08 12:00', '2020-01-15 12:00', '2020-01-22 12:00', '2020-01-29 12:00'],
+ ['2020-01-08 12:00', '2020-01-15 12:00', '2020-01-22 12:00', '2020-01-29 12:00']
][i], [
- [' ', 'Jan-W01', 'Jan-W02', 'Jan-W03', 'Jan-W04'],
- ['Dec-W01', 'Jan-W02', 'Jan-W03', 'Jan-W04', 'Jan-W05'],
- ['Dec-W52', 'Jan-W01', 'Jan-W02', 'Jan-W03', 'Jan-W04']
+ ['Jan-W01', 'Jan-W02', 'Jan-W03', 'Jan-W04'],
+ ['Jan-W02', 'Jan-W03', 'Jan-W04', 'Jan-W05'],
+ ['Jan-W01', 'Jan-W02', 'Jan-W03', 'Jan-W04']
][i]);
})
.catch(failTest)
@@ -5616,7 +5622,6 @@ describe('Test axes', function() {
});
});
-
[
{
formatter: '%H',
@@ -5630,28 +5635,28 @@ describe('Test axes', function() {
},
{
formatter: '%p',
- positions: ['2019-12-31 21:00', '2020-01-01 06:00', '2020-01-01 18:00', '2020-01-02 06:00'],
+ positions: ['2019-12-31 18:00', '2020-01-01 06:00', '2020-01-01 18:00', '2020-01-02 06:00'],
labels: [' ', 'Wed-AM', 'Wed-PM', ' ']
},
{
formatter: '%M',
- positions: ['2019-12-31 21:00', '2020-01-01 12:00', '2020-01-02 12:00'],
- labels: [' ', 'Wed-00', ' ']
+ positions: ['2019-12-31 21:00', '2020-01-01', '2020-01-01 03:00', '2020-01-01 06:00', '2020-01-01 09:00', '2020-01-01 12:00', '2020-01-01 15:00', '2020-01-01 18:00', '2020-01-01 21:00', '2020-01-02'],
+ labels: [' ', 'Wed-00', 'Wed-00', 'Wed-00', 'Wed-00', 'Wed-00', 'Wed-00', 'Wed-00', 'Wed-00', 'Thu-00']
},
{
formatter: '%S',
- positions: ['2019-12-31 21:00', '2020-01-01 12:00', '2020-01-02 12:00'],
- labels: [' ', 'Wed-00', ' ']
+ positions: ['2019-12-31 21:00', '2020-01-01', '2020-01-01 03:00', '2020-01-01 06:00', '2020-01-01 09:00', '2020-01-01 12:00', '2020-01-01 15:00', '2020-01-01 18:00', '2020-01-01 21:00', '2020-01-02'],
+ labels: [' ', 'Wed-00', 'Wed-00', 'Wed-00', 'Wed-00', 'Wed-00', 'Wed-00', 'Wed-00', 'Wed-00', 'Thu-00']
},
{
formatter: '%L',
- positions: ['2019-12-31 21:00', '2020-01-01 12:00', '2020-01-02 12:00'],
- labels: [' ', 'Wed-000', ' ']
+ positions: ['2019-12-31 21:00', '2020-01-01', '2020-01-01 03:00', '2020-01-01 06:00', '2020-01-01 09:00', '2020-01-01 12:00', '2020-01-01 15:00', '2020-01-01 18:00', '2020-01-01 21:00', '2020-01-02'],
+ labels: [' ', 'Wed-000', 'Wed-000', 'Wed-000', 'Wed-000', 'Wed-000', 'Wed-000', 'Wed-000', 'Wed-000', 'Thu-000']
},
{
formatter: '%f',
- positions: ['2019-12-31 21:00', '2020-01-01 12:00', '2020-01-02 12:00'],
- labels: [' ', 'Wed-0', ' ']
+ positions: ['2019-12-31 21:00', '2020-01-01', '2020-01-01 03:00', '2020-01-01 06:00', '2020-01-01 09:00', '2020-01-01 12:00', '2020-01-01 15:00', '2020-01-01 18:00', '2020-01-01 21:00', '2020-01-02'],
+ labels: [' ', 'Wed-0', 'Wed-0', 'Wed-0', 'Wed-0', 'Wed-0', 'Wed-0', 'Wed-0', 'Wed-0', 'Thu-0']
}
].forEach(function(t) {
it('should respect time tickformat that includes ' + t.formatter, function(done) {
@@ -5921,48 +5926,48 @@ describe('Test axes', function() {
[
{
range: ['2020-12-14 08:00', '2022-12-14 08:00'],
- positions: ['2020-12-06 10:26:47.1429', '2021-03-07 09:50:21.4286', '2021-06-06 16:26:47.1429', '2021-09-06 16:26:47.1429', '2021-12-07 09:50:21.4286', '2022-03-06 16:26:47.1429', '2022-06-06 16:26:47.1429', '2022-09-07 01:08:34.2857', '2022-12-07 01:08:34.2857'],
- labels: [' ', 'Mar 2021', 'Jun 2021', 'Sep 2021', 'Dec 2021', 'Mar 2022', 'Jun 2022', 'Sep 2022', 'Dec 2022']
+ positions: ['2021-01-06 07:45', '2021-04-06 07:45', '2021-07-06 07:45', '2021-10-06 07:45', '2022-01-06 07:45', '2022-04-06 07:45', '2022-07-06 07:45', '2022-10-06 07:45'],
+ labels: ['Jan 2021', 'Apr 2021', 'Jul 2021', 'Oct 2021', 'Jan 2022', 'Apr 2022', 'Jul 2022', 'Oct 2022']
},
{
range: ['2020-12-14 08:00', '2021-08-14 08:00'],
- positions: ['2020-12-06 04:17:08.5714', '2020-12-27 22:00', '2021-01-24 22:00', '2021-02-21 22:00', '2021-03-21 22:00', '2021-04-18 22:00', '2021-05-16 22:00', '2021-06-13 22:00', '2021-07-11 22:00', '2021-08-08 22:00'],
- labels: [' ', 'Dec 21
2020', 'Jan 18
2021', 'Feb 15', 'Mar 15', 'Apr 12', 'May 10', 'Jun 7', 'Jul 5', 'Aug 2']
+ positions: ['2021-01-16 18:00', '2021-02-15 06:00', '2021-03-16 18:00', '2021-04-16 06:00', '2021-05-16 18:00', '2021-06-16 06:00', '2021-07-16 18:00', '2021-08-16 18:00'],
+ labels: ['Jan 2021', 'Feb 2021', 'Mar 2021', 'Apr 2021', 'May 2021', 'Jun 2021', 'Jul 2021', ' ']
},
{
range: ['2020-12-14 08:00', '2021-04-14 08:00'],
- positions: ['2020-12-13 03:42:51.4286', '2020-12-21 11:42:51.4286', '2021-01-04 11:42:51.4286', '2021-01-18 11:42:51.4286', '2021-02-01 11:42:51.4286', '2021-02-15 11:42:51.4286', '2021-03-01 11:42:51.4286', '2021-03-15 11:42:51.4286', '2021-03-29 11:42:51.4286', '2021-04-12 11:42:51.4286'],
- labels: [' ', 'Dec 21
2020', 'Jan 4
2021', 'Jan 18', 'Feb 1', 'Feb 15', 'Mar 1', 'Mar 15', 'Mar 29', 'Apr 12']
+ positions: ['2020-12-21 12:00', '2021-01-04 12:00', '2021-01-18 12:00', '2021-02-01 12:00', '2021-02-15 12:00', '2021-03-01 12:00', '2021-03-15 12:00', '2021-03-29 12:00', '2021-04-12 12:00'],
+ labels: ['Dec 21
2020', 'Jan 4
2021', 'Jan 18', 'Feb 1', 'Feb 15', 'Mar 1', 'Mar 15', 'Mar 29', 'Apr 12']
},
{
range: ['2020-12-14 08:00', '2021-02-14 08:00'],
- positions: ['2020-12-13 03:42:51.4286', '2020-12-21 10:17:08.5714', '2020-12-28 10:17:08.5714', '2021-01-04 10:17:08.5714', '2021-01-11 10:17:08.5714', '2021-01-18 10:17:08.5714', '2021-01-25 10:17:08.5714', '2021-02-01 10:17:08.5714', '2021-02-08 11:42:51.4286', '2021-02-14 13:42:51.4286'],
- labels: [' ', 'Dec 21
2020', 'Dec 28', 'Jan 4
2021', 'Jan 11', 'Jan 18', 'Jan 25', 'Feb 1', 'Feb 8', ' ']
+ positions: ['2020-12-21 12:00', '2020-12-28 12:00', '2021-01-04 12:00', '2021-01-11 12:00', '2021-01-18 12:00', '2021-01-25 12:00', '2021-02-01 12:00', '2021-02-08 12:00'],
+ labels: ['Dec 21
2020', 'Dec 28', 'Jan 4
2021', 'Jan 11', 'Jan 18', 'Jan 25', 'Feb 1', 'Feb 8']
},
{
range: ['2020-12-14 08:00', '2021-01-14 08:00'],
- positions: ['2020-12-14 05:08:34.2857', '2020-12-16 12:17:08.5714', '2020-12-18 09:08:34.2857', '2020-12-22 12:17:08.5714', '2020-12-24 18:00', '2020-12-28 12:17:08.5714', '2020-12-30 12:17:08.5714', '2021-01-01 09:08:34.2857', '2021-01-05 12:17:08.5714', '2021-01-07 18:00', '2021-01-11 12:17:08.5714', '2021-01-13 12:17:08.5714'],
- labels: [' ', 'Dec 16
2020', 'Dec 18', 'Dec 22', 'Dec 24', 'Dec 28', 'Dec 30', 'Jan 1
2021', 'Jan 5', 'Jan 7', 'Jan 11', 'Jan 13']
+ positions: ['2020-12-21 12:00', '2020-12-28 12:00', '2021-01-04 12:00', '2021-01-11 12:00'],
+ labels: ['Dec 21
2020', 'Dec 28', 'Jan 4
2021', 'Jan 11']
},
{
range: ['2020-12-14 08:00', '2021-01-01 08:00'],
- positions: ['2020-12-14 05:08:34.2857', '2020-12-16 12:17:08.5714', '2020-12-18 09:08:34.2857', '2020-12-22 12:17:08.5714', '2020-12-24 18:00', '2020-12-28 12:17:08.5714', '2020-12-30 12:17:08.5714', '2021-01-01 12:17:08.5714'],
- labels: [' ', 'Dec 16
2020', 'Dec 18', 'Dec 22', 'Dec 24', 'Dec 28', 'Dec 30', ' ']
+ positions: ['2020-12-16 12:00', '2020-12-18 12:00', '2020-12-22 12:00', '2020-12-24 12:00', '2020-12-28 12:00', '2020-12-30 12:00', '2021-01-01 12:00'],
+ labels: ['Dec 16
2020', 'Dec 18', 'Dec 22', 'Dec 24', 'Dec 28', 'Dec 30', ' ']
},
{
range: ['2020-12-14 08:00', '2020-12-22 08:00'],
- positions: ['2020-12-14 04:51:25.7143', '2020-12-15 18:00', '2020-12-16 18:00', '2020-12-17 18:00', '2020-12-18 18:00', '2020-12-21 18:00', '2020-12-22 18:00'],
- labels: [' ', '06:00
Dec 15, 2020', '06:00
Dec 16, 2020', '06:00
Dec 17, 2020', '06:00
Dec 18, 2020', '06:00
Dec 21, 2020', ' ']
+ positions: ['2020-12-15 12:00', '2020-12-16 12:00', '2020-12-17 12:00', '2020-12-18 12:00', '2020-12-21 12:00', '2020-12-22 12:00'],
+ labels: ['Dec 15
2020', 'Dec 16', 'Dec 17', 'Dec 18', 'Dec 21', ' ']
},
{
range: ['2020-12-14 08:00', '2020-12-18 08:00'],
- positions: ['2020-12-14 06:00', '2020-12-14 12:00', '2020-12-15 06:00', '2020-12-15 12:00', '2020-12-16 06:00', '2020-12-16 12:00', '2020-12-17 06:00', '2020-12-17 12:00', '2020-12-18 06:00'],
- labels: [' ', '12:00
Dec 14, 2020', '06:00
Dec 15, 2020', '12:00', '06:00
Dec 16, 2020', '12:00', '06:00
Dec 17, 2020', '12:00', '06:00
Dec 18, 2020']
+ positions: ['2020-12-14 12:00', '2020-12-15 06:00', '2020-12-15 12:00', '2020-12-16 06:00', '2020-12-16 12:00', '2020-12-17 06:00', '2020-12-17 12:00', '2020-12-18 06:00'],
+ labels: ['12:00
Dec 14, 2020', '06:00
Dec 15, 2020', '12:00', '06:00
Dec 16, 2020', '12:00', '06:00
Dec 17, 2020', '12:00', '06:00
Dec 18, 2020']
},
{
range: ['2020-12-14 08:00', '2020-12-16 08:00'],
- positions: ['2020-12-14 06:00', '2020-12-14 09:00', '2020-12-14 12:00', '2020-12-14 15:00', '2020-12-15 06:00', '2020-12-15 09:00', '2020-12-15 12:00', '2020-12-15 15:00', '2020-12-16 06:00'],
- labels: [' ', '09:00
Dec 14, 2020', '12:00', '15:00', '06:00
Dec 15, 2020', '09:00', '12:00', '15:00', '06:00
Dec 16, 2020']
+ positions: ['2020-12-14 12:00', '2020-12-15 06:00', '2020-12-15 12:00', '2020-12-16 06:00'],
+ labels: ['12:00
Dec 14, 2020', '06:00
Dec 15, 2020', '12:00', '06:00
Dec 16, 2020']
}
].forEach(function(t) {
it('should position auto labels with rangebreaks | range:' + t.range, function(done) {