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 + } + } + ] + } +}