diff --git a/package-lock.json b/package-lock.json index 3dab6a3e166..ea60b5991d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4770,9 +4770,9 @@ } }, "gl-scatter3d": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/gl-scatter3d/-/gl-scatter3d-1.0.15.tgz", - "integrity": "sha512-zRe+Y8E6g6X9Qwx1mMF0n91XOZEIibfF7EssHc9XQZj8XVC8w3q4VkMHJM5t5GxS2pd09gl9WbIAAPPl2dl73Q==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/gl-scatter3d/-/gl-scatter3d-1.0.16.tgz", + "integrity": "sha512-i4IytV9pX2r4HBPJsm6M1ZoirtoHzjUC8icz2YF81p6/QO42cYWFgW8Nkr6xD4fwlRkbI1VKa9/Equ0xf5oCZg==", "requires": { "gl-buffer": "^2.0.6", "gl-mat4": "^1.0.0", @@ -4783,6 +4783,13 @@ "is-string-blank": "^1.0.1", "typedarray-pool": "^1.0.2", "vectorize-text": "^3.0.0" + }, + "dependencies": { + "glsl-out-of-range": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/glsl-out-of-range/-/glsl-out-of-range-1.0.3.tgz", + "integrity": "sha512-3uSoD4aX4TjHx3uRJnJbUpegePR0tRPf9VWLS7EjDMbHHV+qrKjl8ov93ifG3kqzcxIOmaSXDK248EmM5uoQ/g==" + } } }, "gl-select-box": { diff --git a/package.json b/package.json index 20f0f38614a..ebac0650b22 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "gl-plot2d": "^1.3.1", "gl-plot3d": "^1.5.11", "gl-pointcloud2d": "^1.0.1", - "gl-scatter3d": "^1.0.15", + "gl-scatter3d": "^1.0.16", "gl-select-box": "^1.0.2", "gl-spikes2d": "^1.0.1", "gl-streamtube3d": "^1.1.1", diff --git a/src/plot_api/helpers.js b/src/plot_api/helpers.js index 7dbe5145365..4866edcd6ec 100644 --- a/src/plot_api/helpers.js +++ b/src/plot_api/helpers.js @@ -439,11 +439,14 @@ function commonPrefix(name1, name2, show1, show2) { function cleanTextPosition(textposition) { var posY = 'middle', posX = 'center'; - if(textposition.indexOf('top') !== -1) posY = 'top'; - else if(textposition.indexOf('bottom') !== -1) posY = 'bottom'; - if(textposition.indexOf('left') !== -1) posX = 'left'; - else if(textposition.indexOf('right') !== -1) posX = 'right'; + if(typeof textposition === 'string') { + if(textposition.indexOf('top') !== -1) posY = 'top'; + else if(textposition.indexOf('bottom') !== -1) posY = 'bottom'; + + if(textposition.indexOf('left') !== -1) posX = 'left'; + else if(textposition.indexOf('right') !== -1) posX = 'right'; + } return posY + ' ' + posX; } diff --git a/src/traces/scatter3d/attributes.js b/src/traces/scatter3d/attributes.js index f655b6031b5..16c5d67cbf0 100644 --- a/src/traces/scatter3d/attributes.js +++ b/src/traces/scatter3d/attributes.js @@ -160,7 +160,7 @@ var attrs = module.exports = overrideAll({ colorAttributes('marker') ), - textposition: extendFlat({}, scatterAttrs.textposition, {dflt: 'top center', arrayOk: false}), + textposition: extendFlat({}, scatterAttrs.textposition, {dflt: 'top center'}), textfont: { color: scatterAttrs.textfont.color, size: scatterAttrs.textfont.size, diff --git a/src/traces/scatter3d/convert.js b/src/traces/scatter3d/convert.js index 9c315bd32bb..3a96c327c94 100644 --- a/src/traces/scatter3d/convert.js +++ b/src/traces/scatter3d/convert.js @@ -133,14 +133,47 @@ function calculateErrorParams(errors) { return {capSize: capSize, color: color, lineWidth: lineWidth}; } +function parseAlignmentX(a) { + if(a === null || a === undefined) return 0; + + return (a.indexOf('left') > -1) ? -1 : + (a.indexOf('right') > -1) ? 1 : 0; +} + +function parseAlignmentY(a) { + if(a === null || a === undefined) return 0; + + return (a.indexOf('top') > -1) ? -1 : + (a.indexOf('bottom') > -1) ? 1 : 0; +} + function calculateTextOffset(tp) { // Read out text properties - var textOffset = [0, 0]; - if(Array.isArray(tp)) return [0, -1]; - if(tp.indexOf('bottom') >= 0) textOffset[1] += 1; - if(tp.indexOf('top') >= 0) textOffset[1] -= 1; - if(tp.indexOf('left') >= 0) textOffset[0] -= 1; - if(tp.indexOf('right') >= 0) textOffset[0] += 1; + + var defaultAlignmentX = 0; + var defaultAlignmentY = 0; + + var textOffset = [ + defaultAlignmentX, + defaultAlignmentY + ]; + + if(Array.isArray(tp)) { + for(var i = 0; i < tp.length; i++) { + textOffset[i] = [ + defaultAlignmentX, + defaultAlignmentY + ]; + if(tp[i]) { + textOffset[i][0] = parseAlignmentX(tp[i]); + textOffset[i][1] = parseAlignmentY(tp[i]); + } + } + } else { + textOffset[0] = parseAlignmentX(tp); + textOffset[1] = parseAlignmentY(tp); + } + return textOffset; } @@ -233,7 +266,7 @@ function convertPlotlyOptions(scene, data) { } if('textposition' in data) { - params.textOffset = calculateTextOffset(data.textposition); // arrayOk === false + params.textOffset = calculateTextOffset(data.textposition); params.textColor = formatColor(data.textfont, 1, len); params.textSize = formatParam(data.textfont.size, len, Lib.identity, 12); params.textFont = data.textfont.family; // arrayOk === false diff --git a/test/image/baselines/gl3d_scatter3d-different-align-texts.png b/test/image/baselines/gl3d_scatter3d-different-align-texts.png new file mode 100644 index 00000000000..f7e9fffd97e Binary files /dev/null and b/test/image/baselines/gl3d_scatter3d-different-align-texts.png differ diff --git a/test/image/mocks/gl3d_scatter3d-different-align-texts.json b/test/image/mocks/gl3d_scatter3d-different-align-texts.json new file mode 100644 index 00000000000..100e66a1101 --- /dev/null +++ b/test/image/mocks/gl3d_scatter3d-different-align-texts.json @@ -0,0 +1,25 @@ +{ + "data": [ + { + "x": [0, 1, 2, 3, 4, 5, 6, 7, 8], + "y": [0, 1, 0, 1, 0, 1, 0, 1, 0], + "z": [0, 1, 2, 3, 4, 5, 6, 7, 8], + "type": "scatter3d", + "mode":"lines+markers+text", + "text": ["middle center", "bottom", "right", "bottom right", "bottom left", "left", "top right", "top", "top left"], + "textposition": ["", "bottom", "right", "bottom right", "bottom left", "left", "top right", "top", "top left"] + } + ], + "layout": { + "title":"Texts in scatter3d could be aligned differently using arrays", + "width": 800, + "height": 600, + "scene":{ + "camera":{ + "eye":{ "x":-1.25,"y":1.25,"z":1.25 }, + "center":{ "x":0,"y":0,"z":0 }, + "up":{ "x":0,"y":0,"z":1 } + } + } + } +} diff --git a/test/jasmine/tests/gl3d_plot_interact_test.js b/test/jasmine/tests/gl3d_plot_interact_test.js index 7c29eba4bac..ffcaa7c9997 100644 --- a/test/jasmine/tests/gl3d_plot_interact_test.js +++ b/test/jasmine/tests/gl3d_plot_interact_test.js @@ -559,6 +559,31 @@ describe('Test gl3d plots', function() { .then(done); }); + it('@gl should only accept texts for textposition otherwise textposition is set to middle center before passing to webgl', function(done) { + Plotly.plot(gd, [{ + type: 'scatter3d', + mode: 'markers+text+lines', + x: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + y: [-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16], + z: [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1], + text: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'], + textposition: ['left top', 'right top', 'left bottom', 'right bottom', null, undefined, true, false, [], {}, NaN, Infinity, 0, 1.2] + }]) + .then(function() { + var AllTextpositions = gd._fullData[0].textposition; + + expect(AllTextpositions[0]).toBe('top left', 'is not top left'); + expect(AllTextpositions[1]).toBe('top right', 'is not top right'); + expect(AllTextpositions[2]).toBe('bottom left', 'is not bottom left'); + expect(AllTextpositions[3]).toBe('bottom right', 'is not bottom right'); + for(var i = 4; i < AllTextpositions.length; i++) { + expect(AllTextpositions[i]).toBe('middle center', 'is not middle center'); + } + }) + .catch(failTest) + .then(done); + }); + it('@gl axis ticks should not be set when axis _length is NaN', function(done) { Plotly.plot(gd, {