diff --git a/src/traces/carpet/plot.js b/src/traces/carpet/plot.js
index e35963f492d..eb7b957985e 100644
--- a/src/traces/carpet/plot.js
+++ b/src/traces/carpet/plot.js
@@ -15,6 +15,8 @@ var map1dArray = require('./map_1d_array');
var makepath = require('./makepath');
var orientText = require('./orient_text');
var svgTextUtils = require('../../lib/svg_text_utils');
+var Lib = require('../../lib');
+var alignmentConstants = require('../../constants/alignment');
module.exports = function plot(gd, plotinfo, cdcarpet) {
for(var i = 0; i < cdcarpet.length; i++) {
@@ -58,10 +60,10 @@ function plotOne(gd, plotinfo, cd) {
drawGridLines(xa, ya, boundaryLayer, aax, 'a-boundary', aax._boundarylines);
drawGridLines(xa, ya, boundaryLayer, bax, 'b-boundary', bax._boundarylines);
- var maxAExtent = drawAxisLabels(gd, xa, ya, trace, t, labelLayer, aax._labels, 'a-label');
- var maxBExtent = drawAxisLabels(gd, xa, ya, trace, t, labelLayer, bax._labels, 'b-label');
+ var labelOrientationA = drawAxisLabels(gd, xa, ya, trace, t, labelLayer, aax._labels, 'a-label');
+ var labelOrientationB = drawAxisLabels(gd, xa, ya, trace, t, labelLayer, bax._labels, 'b-label');
- drawAxisTitles(gd, labelLayer, trace, t, xa, ya, maxAExtent, maxBExtent);
+ drawAxisTitles(gd, labelLayer, trace, t, xa, ya, labelOrientationA, labelOrientationB);
drawClipPath(trace, t, clipLayer, xa, ya);
}
@@ -131,8 +133,9 @@ function drawAxisLabels(gd, xaxis, yaxis, trace, t, layer, labels, labelClass) {
.classed(labelClass, true);
var maxExtent = 0;
+ var labelOrientation = {};
- labelJoin.each(function(label) {
+ labelJoin.each(function(label, i) {
// Most of the positioning is done in calc_labels. Only the parts that depend upon
// the screen space representation of the x and y axes are here:
var orientation;
@@ -142,6 +145,11 @@ function drawAxisLabels(gd, xaxis, yaxis, trace, t, layer, labels, labelClass) {
var angle = (label.axis.tickangle + 180.0) * Math.PI / 180.0;
orientation = orientText(trace, xaxis, yaxis, label.xy, [Math.cos(angle), Math.sin(angle)]);
}
+
+ if(!i) {
+ // TODO: offsetMultiplier? Not currently used anywhere...
+ labelOrientation = {angle: orientation.angle, flip: orientation.flip};
+ }
var direction = (label.endAnchor ? -1 : 1) * orientation.flip;
var labelEl = d3.select(this)
@@ -169,29 +177,40 @@ function drawAxisLabels(gd, xaxis, yaxis, trace, t, layer, labels, labelClass) {
labelJoin.exit().remove();
- return maxExtent;
+ labelOrientation.maxExtent = maxExtent;
+ return labelOrientation;
}
-function drawAxisTitles(gd, layer, trace, t, xa, ya, maxAExtent, maxBExtent) {
+function drawAxisTitles(gd, layer, trace, t, xa, ya, labelOrientationA, labelOrientationB) {
var a, b, xy, dxy;
a = 0.5 * (trace.a[0] + trace.a[trace.a.length - 1]);
b = trace.b[0];
xy = trace.ab2xy(a, b, true);
dxy = trace.dxyda_rough(a, b);
- drawAxisTitle(gd, layer, trace, t, xy, dxy, trace.aaxis, xa, ya, maxAExtent, 'a-title');
+ if(labelOrientationA.angle === undefined) {
+ Lib.extendFlat(labelOrientationA, orientText(trace, xa, ya, xy, trace.dxydb_rough(a, b)));
+ }
+ drawAxisTitle(gd, layer, trace, t, xy, dxy, trace.aaxis, xa, ya, labelOrientationA, 'a-title');
a = trace.a[0];
b = 0.5 * (trace.b[0] + trace.b[trace.b.length - 1]);
xy = trace.ab2xy(a, b, true);
dxy = trace.dxydb_rough(a, b);
- drawAxisTitle(gd, layer, trace, t, xy, dxy, trace.baxis, xa, ya, maxBExtent, 'b-title');
+ if(labelOrientationB.angle === undefined) {
+ Lib.extendFlat(labelOrientationB, orientText(trace, xa, ya, xy, trace.dxyda_rough(a, b)));
+ }
+ drawAxisTitle(gd, layer, trace, t, xy, dxy, trace.baxis, xa, ya, labelOrientationB, 'b-title');
}
-function drawAxisTitle(gd, layer, trace, t, xy, dxy, axis, xa, ya, offset, labelClass) {
+var lineSpacing = alignmentConstants.LINE_SPACING;
+var midShift = ((1 - alignmentConstants.MID_SHIFT) / lineSpacing) + 1;
+
+function drawAxisTitle(gd, layer, trace, t, xy, dxy, axis, xa, ya, labelOrientation, labelClass) {
var data = [];
if(axis.title) data.push(axis.title);
var titleJoin = layer.selectAll('text.' + labelClass).data(data);
+ var offset = labelOrientation.maxExtent;
titleJoin.enter().append('text')
.classed(labelClass, true);
@@ -205,14 +224,23 @@ function drawAxisTitle(gd, layer, trace, t, xy, dxy, axis, xa, ya, offset, label
}
// In addition to the size of the labels, add on some extra padding:
- offset += axis.titlefont.size + axis.titleoffset;
+ var titleSize = axis.titlefont.size;
+ offset += titleSize + axis.titleoffset;
+ var labelNorm = labelOrientation.angle + (labelOrientation.flip < 0 ? 180 : 0);
+ var angleDiff = (labelNorm - orientation.angle + 450) % 360;
+ var reverseTitle = angleDiff > 90 && angleDiff < 270;
var el = d3.select(this);
el.text(axis.title || '')
- .call(svgTextUtils.convertToTspans, gd)
- .attr('transform',
+ .call(svgTextUtils.convertToTspans, gd);
+
+ if(reverseTitle) {
+ offset = (-svgTextUtils.lineCount(el) + midShift) * lineSpacing * titleSize - offset;
+ }
+
+ el.attr('transform',
'translate(' + orientation.p[0] + ',' + orientation.p[1] + ') ' +
'rotate(' + orientation.angle + ') ' +
'translate(0,' + offset + ')'
diff --git a/test/image/baselines/cheaterslope.png b/test/image/baselines/cheaterslope.png
new file mode 100644
index 00000000000..5f71f40f1d8
Binary files /dev/null and b/test/image/baselines/cheaterslope.png differ
diff --git a/test/image/baselines/cheaterslope_noticklabels.png b/test/image/baselines/cheaterslope_noticklabels.png
new file mode 100644
index 00000000000..267ba49038d
Binary files /dev/null and b/test/image/baselines/cheaterslope_noticklabels.png differ
diff --git a/test/image/mocks/cheaterslope.json b/test/image/mocks/cheaterslope.json
new file mode 100644
index 00000000000..aff36f37929
--- /dev/null
+++ b/test/image/mocks/cheaterslope.json
@@ -0,0 +1,231 @@
+{
+ "data": [
+ {
+ "type": "carpet",
+ "carpet": "c1",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red"},
+ "baxis": {"smoothing": 1, "title": "B axis
title"},
+ "xaxis": "x",
+ "yaxis": "y",
+ "cheaterslope": -10
+ },
+ {
+ "type": "carpet",
+ "carpet": "c2",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red"},
+ "baxis": {"smoothing": 1, "title": "B axis
title"},
+ "xaxis": "x2",
+ "yaxis": "y",
+ "cheaterslope": -10
+ },
+ {
+ "type": "carpet",
+ "carpet": "c3",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red"},
+ "baxis": {"smoothing": 1, "title": "B axis
title"},
+ "xaxis": "x",
+ "yaxis": "y2",
+ "cheaterslope": -0.3
+ },
+ {
+ "type": "carpet",
+ "carpet": "c4",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red"},
+ "baxis": {"smoothing": 1, "title": "B axis
title"},
+ "xaxis": "x2",
+ "yaxis": "y2",
+ "cheaterslope": -0.3
+ },
+ {
+ "type": "carpet",
+ "carpet": "c5",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red"},
+ "baxis": {"smoothing": 1, "title": "B axis
title"},
+ "xaxis": "x",
+ "yaxis": "y3",
+ "cheaterslope": 0
+ },
+ {
+ "type": "carpet",
+ "carpet": "c6",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red"},
+ "baxis": {"smoothing": 1, "title": "B axis
title"},
+ "xaxis": "x2",
+ "yaxis": "y3",
+ "cheaterslope": 0
+ },
+ {
+ "type": "carpet",
+ "carpet": "c7",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red"},
+ "baxis": {"smoothing": 1, "title": "B axis
title"},
+ "xaxis": "x",
+ "yaxis": "y4",
+ "cheaterslope": 0.3
+ },
+ {
+ "type": "carpet",
+ "carpet": "c8",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red"},
+ "baxis": {"smoothing": 1, "title": "B axis
title"},
+ "xaxis": "x2",
+ "yaxis": "y4",
+ "cheaterslope": 0.3
+ },
+ {
+ "type": "carpet",
+ "carpet": "c9",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red"},
+ "baxis": {"smoothing": 1, "title": "B axis
title"},
+ "xaxis": "x",
+ "yaxis": "y5",
+ "cheaterslope": 10
+ },
+ {
+ "type": "carpet",
+ "carpet": "c10",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red"},
+ "baxis": {"smoothing": 1, "title": "B axis
title"},
+ "xaxis": "x2",
+ "yaxis": "y5",
+ "cheaterslope": 10
+ }
+ ],
+ "layout": {
+ "title": "Cheaterslope with titles",
+ "width": 600,
+ "height": 800,
+ "dragmode": "pan",
+ "margin": {
+ "t": 60,
+ "r": 30,
+ "b": 30,
+ "l": 30
+ },
+ "yaxis": {
+ "domain": [0, 0.19],
+ "zeroline": false,
+ "range": [-2, 5]
+ },
+ "yaxis2": {
+ "domain": [0.21, 0.39],
+ "zeroline": false,
+ "range": [-2, 5]
+ },
+ "yaxis3": {
+ "domain": [0.41, 0.59],
+ "zeroline": false,
+ "range": [-2, 5]
+ },
+ "yaxis4": {
+ "domain": [0.61, 0.79],
+ "zeroline": false,
+ "range": [-2, 5]
+ },
+ "yaxis5": {
+ "domain": [0.81, 1],
+ "zeroline": false,
+ "range": [-2, 5]
+ },
+ "xaxis": {
+ "domain": [0, 0.49],
+ "range": [-0.5, 1.2]
+ },
+ "xaxis2": {
+ "domain": [0.51, 1.0],
+ "range": [-0.5, 1.2]
+ },
+ "annotations": [
+ {
+ "x": 0,
+ "y": 4.5,
+ "text": "slope: -10",
+ "align": "left",
+ "xref": "x",
+ "yref": "y",
+ "showarrow": false,
+ "font": {
+ "size": 14
+ }
+ },
+ {
+ "x": 0,
+ "align": "left",
+ "y": 4.5,
+ "xref": "x",
+ "yref": "y2",
+ "text": "slope: -0.3",
+ "showarrow": false,
+ "font": {
+ "size": 14
+ }
+ },
+ {
+ "x": 0,
+ "y": 4.5,
+ "align": "left",
+ "xref": "x",
+ "yref": "y3",
+ "text": "slope: 0",
+ "showarrow": false,
+ "font": {
+ "size": 14
+ }
+ },
+ {
+ "x": 0,
+ "y": 4.5,
+ "align": "left",
+ "xref": "x",
+ "yref": "y4",
+ "text": "slope: 0.3",
+ "showarrow": false,
+ "font": {
+ "size": 14
+ }
+ },
+ {
+ "x": 0,
+ "y": 4.5,
+ "align": "left",
+ "xref": "x",
+ "yref": "y5",
+ "text": "slope: 10",
+ "showarrow": false,
+ "font": {
+ "size": 14
+ }
+ }
+ ]
+ }
+}
diff --git a/test/image/mocks/cheaterslope_noticklabels.json b/test/image/mocks/cheaterslope_noticklabels.json
new file mode 100644
index 00000000000..4311fc4ed29
--- /dev/null
+++ b/test/image/mocks/cheaterslope_noticklabels.json
@@ -0,0 +1,231 @@
+{
+ "data": [
+ {
+ "type": "carpet",
+ "carpet": "c1",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red", "showticklabels": "none"},
+ "baxis": {"smoothing": 1, "title": "B axis
title", "showticklabels": "none"},
+ "xaxis": "x",
+ "yaxis": "y",
+ "cheaterslope": -10
+ },
+ {
+ "type": "carpet",
+ "carpet": "c2",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red", "showticklabels": "none"},
+ "baxis": {"smoothing": 1, "title": "B axis
title", "showticklabels": "none"},
+ "xaxis": "x2",
+ "yaxis": "y",
+ "cheaterslope": -10
+ },
+ {
+ "type": "carpet",
+ "carpet": "c3",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red", "showticklabels": "none"},
+ "baxis": {"smoothing": 1, "title": "B axis
title", "showticklabels": "none"},
+ "xaxis": "x",
+ "yaxis": "y2",
+ "cheaterslope": -0.3
+ },
+ {
+ "type": "carpet",
+ "carpet": "c4",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red", "showticklabels": "none"},
+ "baxis": {"smoothing": 1, "title": "B axis
title", "showticklabels": "none"},
+ "xaxis": "x2",
+ "yaxis": "y2",
+ "cheaterslope": -0.3
+ },
+ {
+ "type": "carpet",
+ "carpet": "c5",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red", "showticklabels": "none"},
+ "baxis": {"smoothing": 1, "title": "B axis
title", "showticklabels": "none"},
+ "xaxis": "x",
+ "yaxis": "y3",
+ "cheaterslope": 0
+ },
+ {
+ "type": "carpet",
+ "carpet": "c6",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red", "showticklabels": "none"},
+ "baxis": {"smoothing": 1, "title": "B axis
title", "showticklabels": "none"},
+ "xaxis": "x2",
+ "yaxis": "y3",
+ "cheaterslope": 0
+ },
+ {
+ "type": "carpet",
+ "carpet": "c7",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red", "showticklabels": "none"},
+ "baxis": {"smoothing": 1, "title": "B axis
title", "showticklabels": "none"},
+ "xaxis": "x",
+ "yaxis": "y4",
+ "cheaterslope": 0.3
+ },
+ {
+ "type": "carpet",
+ "carpet": "c8",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red", "showticklabels": "none"},
+ "baxis": {"smoothing": 1, "title": "B axis
title", "showticklabels": "none"},
+ "xaxis": "x2",
+ "yaxis": "y4",
+ "cheaterslope": 0.3
+ },
+ {
+ "type": "carpet",
+ "carpet": "c9",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0, 0.8, 2], [1.2, 2, 3.2], [2, 2.8, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red", "showticklabels": "none"},
+ "baxis": {"smoothing": 1, "title": "B axis
title", "showticklabels": "none"},
+ "xaxis": "x",
+ "yaxis": "y5",
+ "cheaterslope": 10
+ },
+ {
+ "type": "carpet",
+ "carpet": "c10",
+ "a": [1, 2, 3],
+ "b": [1, 2, 3],
+ "y": [[0.8, 0, 2], [2, 1.2, 3.2], [2.8, 2, 4]],
+ "aaxis": {"smoothing": 1, "title": "A axis
title", "color": "red", "showticklabels": "none"},
+ "baxis": {"smoothing": 1, "title": "B axis
title", "showticklabels": "none"},
+ "xaxis": "x2",
+ "yaxis": "y5",
+ "cheaterslope": 10
+ }
+ ],
+ "layout": {
+ "title": "Cheaterslope with titles and no tick labels",
+ "width": 600,
+ "height": 800,
+ "dragmode": "pan",
+ "margin": {
+ "t": 60,
+ "r": 30,
+ "b": 30,
+ "l": 30
+ },
+ "yaxis": {
+ "domain": [0, 0.19],
+ "zeroline": false,
+ "range": [-2, 5]
+ },
+ "yaxis2": {
+ "domain": [0.21, 0.39],
+ "zeroline": false,
+ "range": [-2, 5]
+ },
+ "yaxis3": {
+ "domain": [0.41, 0.59],
+ "zeroline": false,
+ "range": [-2, 5]
+ },
+ "yaxis4": {
+ "domain": [0.61, 0.79],
+ "zeroline": false,
+ "range": [-2, 5]
+ },
+ "yaxis5": {
+ "domain": [0.81, 1],
+ "zeroline": false,
+ "range": [-2, 5]
+ },
+ "xaxis": {
+ "domain": [0, 0.49],
+ "range": [-0.5, 1.2]
+ },
+ "xaxis2": {
+ "domain": [0.51, 1.0],
+ "range": [-0.5, 1.2]
+ },
+ "annotations": [
+ {
+ "x": 0,
+ "y": 4.5,
+ "text": "slope: -10",
+ "align": "left",
+ "xref": "x",
+ "yref": "y",
+ "showarrow": false,
+ "font": {
+ "size": 14
+ }
+ },
+ {
+ "x": 0,
+ "align": "left",
+ "y": 4.5,
+ "xref": "x",
+ "yref": "y2",
+ "text": "slope: -0.3",
+ "showarrow": false,
+ "font": {
+ "size": 14
+ }
+ },
+ {
+ "x": 0,
+ "y": 4.5,
+ "align": "left",
+ "xref": "x",
+ "yref": "y3",
+ "text": "slope: 0",
+ "showarrow": false,
+ "font": {
+ "size": 14
+ }
+ },
+ {
+ "x": 0,
+ "y": 4.5,
+ "align": "left",
+ "xref": "x",
+ "yref": "y4",
+ "text": "slope: 0.3",
+ "showarrow": false,
+ "font": {
+ "size": 14
+ }
+ },
+ {
+ "x": 0,
+ "y": 4.5,
+ "align": "left",
+ "xref": "x",
+ "yref": "y5",
+ "text": "slope: 10",
+ "showarrow": false,
+ "font": {
+ "size": 14
+ }
+ }
+ ]
+ }
+}