diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 24f06adc781..35277e990eb 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -659,13 +659,11 @@ function cleanAxRef(container, attr) { } } +// Make a few changes to the data right away +// before it gets used for anything function cleanData(data, existingData) { - // make a few changes to the data right away - // before it gets used for anything - /* - * Enforce unique IDs - */ + // Enforce unique IDs var suids = [], // seen uids --- so we can weed out incoming repeats uids = data.concat(Array.isArray(existingData) ? existingData : []) .filter(function(trace) { return 'uid' in trace; }) @@ -673,11 +671,13 @@ function cleanData(data, existingData) { for(var tracei = 0; tracei < data.length; tracei++) { var trace = data[tracei]; + var i; // assign uids to each trace and detect collisions. if(!('uid' in trace) || suids.indexOf(trace.uid) !== -1) { - var newUid, i; - for(i=0; i<100; i++) { + var newUid; + + for(i = 0; i < 100; i++) { newUid = Lib.randstr(uids); if(suids.indexOf(newUid)===-1) break; } @@ -765,6 +765,27 @@ function cleanData(data, existingData) { if(cont.colorscale === 'YIOrRd') cont.colorscale = 'YlOrRd'; } + // fix typo in surface 'highlight*' definitions + if(trace.type === 'surface' && Lib.isPlainObject(trace.contours)) { + var dims = ['x', 'y', 'z']; + + for(i = 0; i < dims.length; i++) { + var opts = trace.contours[dims[i]]; + + if(!Lib.isPlainObject(opts)) continue; + + if(opts.highlightColor) { + opts.highlightcolor = opts.highlightColor; + delete opts.highlightColor; + } + + if(opts.highlightWidth) { + opts.highlightwidth = opts.highlightWidth; + delete opts.highlightWidth; + } + } + } + // prune empty containers made before the new nestedProperty if(emptyContainer(trace, 'line')) delete trace.line; if('marker' in trace) { diff --git a/src/plots/gl3d/layout/axis_attributes.js b/src/plots/gl3d/layout/axis_attributes.js index f311e63d4c4..6c948c1c1cb 100644 --- a/src/plots/gl3d/layout/axis_attributes.js +++ b/src/plots/gl3d/layout/axis_attributes.js @@ -8,7 +8,7 @@ 'use strict'; - +var Color = require('../../../components/color'); var axesAttrs = require('../../cartesian/layout_attributes'); var extendFlat = require('../../../lib/extend').extendFlat; @@ -43,7 +43,7 @@ module.exports = { spikecolor: { valType: 'color', role: 'style', - dflt: 'rgb(0,0,0)', + dflt: Color.defaultLine, description: 'Sets the color of the spikes.' }, showbackground: { diff --git a/src/plots/gl3d/layout/axis_defaults.js b/src/plots/gl3d/layout/axis_defaults.js index 37ee79dcd23..8332efa8bd9 100644 --- a/src/plots/gl3d/layout/axis_defaults.js +++ b/src/plots/gl3d/layout/axis_defaults.js @@ -8,6 +8,7 @@ 'use strict'; + var colorMix = require('tinycolor2').mix; var Lib = require('../../../lib'); diff --git a/src/traces/surface/attributes.js b/src/traces/surface/attributes.js index 57270b2799b..3cfd0b408e1 100644 --- a/src/traces/surface/attributes.js +++ b/src/traces/surface/attributes.js @@ -9,6 +9,7 @@ 'use strict'; +var Color = require('../../components/color'); var colorscaleAttrs = require('../../components/colorscale/attributes'); var extendFlat = require('../../lib/extend').extendFlat; @@ -19,8 +20,12 @@ function makeContourProjAttr(axLetter) { role: 'info', dflt: false, description: [ - 'Sets whether or not the dynamic contours are projected', - 'along the', axLetter, 'axis.' + 'Determines whether or not these contour lines are projected', + 'on the', axLetter, 'axis walls.', + 'If `highlight` is set to *true* (the default), the projected', + 'lines are shown on hover.', + 'If `show` is set to *true*, the projected lines are shown', + 'in permanence.' ].join(' ') }; } @@ -32,8 +37,8 @@ function makeContourAttr(axLetter) { role: 'info', dflt: false, description: [ - 'Sets whether or not dynamic contours are shown along the', - axLetter, 'axis' + 'Determines whether or not contour lines about the', axLetter, + 'dimension are drawn.' ].join(' ') }, project: { @@ -44,36 +49,49 @@ function makeContourAttr(axLetter) { color: { valType: 'color', role: 'style', - dflt: '#000' + dflt: Color.defaultLine, + description: 'Sets the color of the contour lines.' }, usecolormap: { valType: 'boolean', role: 'info', - dflt: false + dflt: false, + description: [ + 'An alternate to *color*.', + 'Determines whether or not the contour lines are colored using', + 'the trace *colorscale*.' + ].join(' ') }, width: { valType: 'number', role: 'style', min: 1, max: 16, - dflt: 2 + dflt: 2, + description: 'Sets the width of the contour lines.' }, highlight: { valType: 'boolean', role: 'info', - dflt: false + dflt: true, + description: [ + 'Determines whether or not contour lines about the', axLetter, + 'dimension are highlighted on hover.' + ].join(' ') }, - highlightColor: { + highlightcolor: { valType: 'color', role: 'style', - dflt: '#000' + dflt: Color.defaultLine, + description: 'Sets the color of the highlighted contour lines.' }, - highlightWidth: { + highlightwidth: { valType: 'number', role: 'style', min: 1, max: 16, - dflt: 2 + dflt: 2, + description: 'Sets the width of the highlighted contour lines.' } }; } @@ -102,6 +120,7 @@ module.exports = { 'used for setting a color scale independent of `z`.' ].join(' ') }, + cauto: colorscaleAttrs.zauto, cmin: colorscaleAttrs.zmin, cmax: colorscaleAttrs.zmax, @@ -110,6 +129,7 @@ module.exports = { {dflt: false}), reversescale: colorscaleAttrs.reversescale, showscale: colorscaleAttrs.showscale, + contours: { x: makeContourAttr('x'), y: makeContourAttr('y'), @@ -118,8 +138,15 @@ module.exports = { hidesurface: { valType: 'boolean', role: 'info', - dflt: false + dflt: false, + description: [ + 'Determines whether or not a surface is drawn.', + 'For example, set `hidesurface` to *false*', + '`contours.x.show` to *true* and', + '`contours.y.show` to *true* to draw a wire frame plot.' + ].join(' ') }, + lighting: { ambient: { valType: 'number', @@ -163,7 +190,8 @@ module.exports = { role: 'style', min: 0, max: 1, - dflt: 1 + dflt: 1, + description: 'Sets the opacity of the surface.' }, _nestedModules: { // nested module coupling diff --git a/src/traces/surface/convert.js b/src/traces/surface/convert.js index 0cb3f1d4671..dea88dea6c6 100644 --- a/src/traces/surface/convert.js +++ b/src/traces/surface/convert.js @@ -280,13 +280,11 @@ proto.update = function(data) { } var highlightEnable = [true, true, true]; - var contourEnable = [true, true, true]; var axis = ['x', 'y', 'z']; for(i = 0; i < 3; ++i) { var contourParams = data.contours[axis[i]]; highlightEnable[i] = contourParams.highlight; - contourEnable[i] = contourParams.show; params.showContour[i] = contourParams.show || contourParams.highlight; if(!params.showContour[i]) continue; @@ -301,6 +299,7 @@ proto.update = function(data) { this.showContour[i] = true; params.levels[i] = contourLevels[i]; surface.highlightColor[i] = params.contourColor[i] = str2RgbaArray(contourParams.color); + if(contourParams.usecolormap) { surface.highlightTint[i] = params.contourTint[i] = 0; } @@ -313,8 +312,8 @@ proto.update = function(data) { } if(contourParams.highlight) { - params.dynamicColor[i] = str2RgbaArray(contourParams.highlightColor); - params.dynamicWidth[i] = contourParams.highlightWidth; + params.dynamicColor[i] = str2RgbaArray(contourParams.highlightcolor); + params.dynamicWidth[i] = contourParams.highlightwidth; } } @@ -322,9 +321,8 @@ proto.update = function(data) { surface.update(params); - surface.highlightEnable = highlightEnable; - surface.contourEnable = contourEnable; surface.visible = data.visible; + surface.enableDynamic = highlightEnable; surface.snapToData = true; diff --git a/src/traces/surface/defaults.js b/src/traces/surface/defaults.js index 0fa8c456401..47307c75fff 100644 --- a/src/traces/surface/defaults.js +++ b/src/traces/surface/defaults.js @@ -82,8 +82,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } if(highlight) { - coerce(contourDim + '.highlightColor'); - coerce(contourDim + '.highlightWidth'); + coerce(contourDim + '.highlightcolor'); + coerce(contourDim + '.highlightwidth'); } } @@ -94,6 +94,9 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout mapLegacy(traceIn, 'zauto', 'cauto'); } + // TODO if contours.?.usecolormap are false and hidesurface is true + // the colorbar shouldn't be shown by default + colorscaleDefaults( traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'c'} ); diff --git a/test/image/baselines/gl3d_contour-lines.png b/test/image/baselines/gl3d_contour-lines.png index fd75d20ff94..589e628c79f 100644 Binary files a/test/image/baselines/gl3d_contour-lines.png and b/test/image/baselines/gl3d_contour-lines.png differ diff --git a/test/jasmine/tests/plot_api_test.js b/test/jasmine/tests/plot_api_test.js index 6d183f0b14d..520f4fee006 100644 --- a/test/jasmine/tests/plot_api_test.js +++ b/test/jasmine/tests/plot_api_test.js @@ -689,5 +689,71 @@ describe('Test plot api', function() { Plotly.plot(gd, data); expect(gd.data[0].marker.colorscale).toBe('YlOrRd'); }); + + it('should rename \'highlightColor\' to \'highlightcolor\')', function() { + var data = [{ + type: 'surface', + contours: { + x: { highlightColor: 'red' }, + y: { highlightcolor: 'blue' } + } + }, { + type: 'surface' + }, { + type: 'surface', + contours: false + }, { + type: 'surface', + contours: { + stuff: {}, + x: false, + y: [] + } + }]; + + spyOn(Plots.subplotsRegistry.gl3d, 'plot'); + + Plotly.plot(gd, data); + + expect(Plots.subplotsRegistry.gl3d.plot).toHaveBeenCalled(); + + var contours = gd.data[0].contours; + + expect(contours.x.highlightColor).toBeUndefined(); + expect(contours.x.highlightcolor).toEqual('red'); + expect(contours.y.highlightcolor).toEqual('blue'); + expect(contours.z).toBeUndefined(); + + expect(gd.data[1].contours).toBeUndefined(); + expect(gd.data[2].contours).toBe(false); + expect(gd.data[3].contours).toEqual({ stuff: {}, x: false, y: [] }); + }); + + it('should rename \'highlightWidth\' to \'highlightwidth\')', function() { + var data = [{ + type: 'surface', + contours: { + z: { highlightwidth: 'red' }, + y: { highlightWidth: 'blue' } + } + }, { + type: 'surface' + }]; + + spyOn(Plots.subplotsRegistry.gl3d, 'plot'); + + Plotly.plot(gd, data); + + expect(Plots.subplotsRegistry.gl3d.plot).toHaveBeenCalled(); + + var contours = gd.data[0].contours; + + expect(contours.x).toBeUndefined(); + expect(contours.y.highlightwidth).toEqual('blue'); + expect(contours.z.highlightWidth).toBeUndefined(); + expect(contours.z.highlightwidth).toEqual('red'); + + expect(gd.data[1].contours).toBeUndefined(); + }); }); }); diff --git a/test/jasmine/tests/surface_test.js b/test/jasmine/tests/surface_test.js index 1936de871b1..8e512734355 100644 --- a/test/jasmine/tests/surface_test.js +++ b/test/jasmine/tests/surface_test.js @@ -1,5 +1,7 @@ var Surface = require('@src/traces/surface'); +var Lib = require('@src/lib'); + describe('Test surface', function() { 'use strict'; @@ -37,13 +39,28 @@ describe('Test surface', function() { traceIn = { z: [[1,2,3], [2,1,2]], contours: { - x: { show: true } + x: {}, + y: { show: true }, + z: { show: false, highlight: false } } }; + var fullOpts = { + show: false, + highlight: true, + project: { x: false, y: false, z: false }, + highlightcolor: '#444', + highlightwidth: 2 + }; + supplyDefaults(traceIn, traceOut, defaultColor, layout); - expect(traceOut.contours.x.project).toEqual({ x: false, y: false, z: false }); - expect(traceOut.contours.y).toEqual({ show: false, highlight: false }); + expect(traceOut.contours.x).toEqual(fullOpts); + expect(traceOut.contours.y).toEqual(Lib.extendDeep({}, fullOpts, { + show: true, + color: '#444', + width: 2, + usecolormap: false + })); expect(traceOut.contours.z).toEqual({ show: false, highlight: false }); }); @@ -56,7 +73,7 @@ describe('Test surface', function() { }; supplyDefaults(traceIn, traceOut, defaultColor, layout); - expect(traceOut.contours.x.color).toEqual('#000'); + expect(traceOut.contours.x.color).toEqual('#444'); expect(traceOut.contours.x.width).toEqual(2); expect(traceOut.contours.x.usecolormap).toEqual(false);