From 5f573c0585222ac66b800fba3ce5249c9ebeac8e Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 18 Dec 2015 13:33:12 -0500 Subject: [PATCH 1/6] make surface calc step its own module (to be used in heatmapgl too) --- src/traces/surface/calc.js | 18 ++++++++++++++++++ src/traces/surface/index.js | 7 +------ 2 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 src/traces/surface/calc.js diff --git a/src/traces/surface/calc.js b/src/traces/surface/calc.js new file mode 100644 index 00000000000..3c05d5921b2 --- /dev/null +++ b/src/traces/surface/calc.js @@ -0,0 +1,18 @@ +/** +* Copyright 2012-2015, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var Colorscale = require('../../components/colorscale'); + + +// Compute auto-z and autocolorscale if applicable +module.exports = function calc(gd, trace) { + Colorscale.calc(trace, trace.z, '', 'z'); +}; diff --git a/src/traces/surface/index.js b/src/traces/surface/index.js index b8520b0dba3..fb7449a8be5 100644 --- a/src/traces/surface/index.js +++ b/src/traces/surface/index.js @@ -32,9 +32,4 @@ Surface.supplyDefaults = require('./defaults'); Surface.colorbar = require('../heatmap/colorbar'); -Surface.calc = function(gd, trace) { - - // auto-z and autocolorscale if applicable - Plotly.Colorscale.calc(trace, trace.z, '', 'z'); - -}; +Surface.calc = require('./calc'); From 777709e2b3d2caab9e4b178b0a44eaf4d5b19d8e Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 18 Dec 2015 14:54:11 -0500 Subject: [PATCH 2/6] move hasColorbar check in colorbar/ --- src/components/colorbar/has_colorbar.js | 18 ++++++++++++++ src/components/colorscale/index.js | 4 ---- test/jasmine/tests/colorbar_test.js | 31 +++++++++++++++++++++++++ test/jasmine/tests/colorscale_test.js | 26 --------------------- 4 files changed, 49 insertions(+), 30 deletions(-) create mode 100644 src/components/colorbar/has_colorbar.js create mode 100644 test/jasmine/tests/colorbar_test.js diff --git a/src/components/colorbar/has_colorbar.js b/src/components/colorbar/has_colorbar.js new file mode 100644 index 00000000000..ec43b725102 --- /dev/null +++ b/src/components/colorbar/has_colorbar.js @@ -0,0 +1,18 @@ +/** +* Copyright 2012-2015, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + + +module.exports = function hasColorbar(container) { + return ( + typeof container.colorbar === 'object' && + container.colorbar !== null + ); +}; diff --git a/src/components/colorscale/index.js b/src/components/colorscale/index.js index 3ad9cef61fb..377bcd52f00 100644 --- a/src/components/colorscale/index.js +++ b/src/components/colorscale/index.js @@ -109,10 +109,6 @@ colorscale.hasColorscale = function(trace, containerStr) { ); }; -colorscale.hasColorbar = function(container) { - return typeof container.colorbar==='object' && container.colorbar!==null; -}; - colorscale.handleDefaults = function(traceIn, traceOut, layout, coerce, opts) { var prefix = opts.prefix, cLetter = opts.cLetter, diff --git a/test/jasmine/tests/colorbar_test.js b/test/jasmine/tests/colorbar_test.js new file mode 100644 index 00000000000..4b97ee01412 --- /dev/null +++ b/test/jasmine/tests/colorbar_test.js @@ -0,0 +1,31 @@ +var Colorbar = require('@src/components/colorbar'); + +describe('Test colorbar:', function () { + 'use strict'; + + describe('hasColorbar', function() { + var hasColorbar = Colorbar.hasColorbar, + trace; + + it('should return true when marker colorbar is defined', function() { + trace = { + marker: { + colorbar: {}, + line: { + colorbar: {} + } + } + }; + expect(hasColorbar(trace.marker)).toBe(true); + expect(hasColorbar(trace.marker.line)).toBe(true); + + trace = { + marker: { + line: {} + } + }; + expect(hasColorbar(trace.marker)).toBe(false); + expect(hasColorbar(trace.marker.line)).toBe(false); + }); + }); +}); diff --git a/test/jasmine/tests/colorscale_test.js b/test/jasmine/tests/colorscale_test.js index c9beb0910b1..4ee27437c85 100644 --- a/test/jasmine/tests/colorscale_test.js +++ b/test/jasmine/tests/colorscale_test.js @@ -180,32 +180,6 @@ describe('Test colorscale:', function () { }); }); - describe('hasColorbar', function() { - var hasColorbar = Plotly.Colorscale.hasColorbar, - trace; - - it('should return true when marker colorbar is defined', function() { - trace = { - marker: { - colorbar: {}, - line: { - colorbar: {} - } - } - }; - expect(hasColorbar(trace.marker)).toBe(true); - expect(hasColorbar(trace.marker.line)).toBe(true); - - trace = { - marker: { - line: {} - } - }; - expect(hasColorbar(trace.marker)).toBe(false); - expect(hasColorbar(trace.marker.line)).toBe(false); - }); - }); - describe('handleDefaults (heatmap-like version)', function() { var handleDefaults = Plotly.Colorscale.handleDefaults, layout = { From 2a600d1170feecb2c5c1f80d3bc0d412a44f6fd6 Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 18 Dec 2015 14:56:17 -0500 Subject: [PATCH 3/6] split colorscale methods into seperate files --- src/components/colorscale/calc.js | 63 ++++++ src/components/colorscale/default_scale.js | 12 + src/components/colorscale/defaults.js | 62 +++++ src/components/colorscale/flip_scale.js | 23 ++ src/components/colorscale/get_scale.js | 38 ++++ src/components/colorscale/has_colorscale.js | 44 ++++ src/components/colorscale/index.js | 213 +----------------- src/components/colorscale/is_valid_scale.js | 19 ++ .../colorscale/is_valid_scale_array.js | 33 +++ .../colorscale/make_scale_function.js | 41 ++++ 10 files changed, 345 insertions(+), 203 deletions(-) create mode 100644 src/components/colorscale/calc.js create mode 100644 src/components/colorscale/default_scale.js create mode 100644 src/components/colorscale/defaults.js create mode 100644 src/components/colorscale/flip_scale.js create mode 100644 src/components/colorscale/get_scale.js create mode 100644 src/components/colorscale/has_colorscale.js create mode 100644 src/components/colorscale/is_valid_scale.js create mode 100644 src/components/colorscale/is_valid_scale_array.js create mode 100644 src/components/colorscale/make_scale_function.js diff --git a/src/components/colorscale/calc.js b/src/components/colorscale/calc.js new file mode 100644 index 00000000000..a574840b6c6 --- /dev/null +++ b/src/components/colorscale/calc.js @@ -0,0 +1,63 @@ +/** +* Copyright 2012-2015, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var Lib = require('../../lib'); + +var scales = require('./scales'); +var flipScale = require('./flip_scale'); + + +module.exports = function calc(trace, vals, containerStr, cLetter) { + var container, inputContainer; + + if(containerStr) { + container = Lib.nestedProperty(trace, containerStr).get(); + inputContainer = Lib.nestedProperty(trace._input, containerStr).get(); + } else { + container = trace; + inputContainer = trace._input; + } + + var auto = container[cLetter + 'auto'], + min = container[cLetter + 'min'], + max = container[cLetter + 'max'], + scl = container.colorscale; + + if(auto!==false || min===undefined) { + min = Lib.aggNums(Math.min, null, vals); + } + + if(auto!==false || max===undefined) { + max = Lib.aggNums(Math.max, null, vals); + } + + if(min === max) { + min -= 0.5; + max += 0.5; + } + + container[cLetter + 'min'] = min; + container[cLetter + 'max'] = max; + + inputContainer[cLetter + 'min'] = min; + inputContainer[cLetter + 'max'] = max; + + if(container.autocolorscale) { + if(min * max < 0) scl = scales.RdBu; + else if(min >= 0) scl = scales.Reds; + else scl = scales.Blues; + + // reversescale is handled at the containerOut level + inputContainer.colorscale = scl; + if(container.reversescale) scl = flipScale(scl); + container.colorscale = scl; + } +}; diff --git a/src/components/colorscale/default_scale.js b/src/components/colorscale/default_scale.js new file mode 100644 index 00000000000..8c7d7f984ba --- /dev/null +++ b/src/components/colorscale/default_scale.js @@ -0,0 +1,12 @@ +/** +* Copyright 2012-2015, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +var scales = require('./scales'); + + +module.exports = scales.RdBu; diff --git a/src/components/colorscale/defaults.js b/src/components/colorscale/defaults.js new file mode 100644 index 00000000000..79193baf665 --- /dev/null +++ b/src/components/colorscale/defaults.js @@ -0,0 +1,62 @@ +/** +* Copyright 2012-2015, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var isNumeric = require('fast-isnumeric'); + +var Lib = require('../../lib'); + +var hasColorbar = require('../colorbar/has_colorbar'); +var colorbarDefaults = require('../colorbar/defaults'); +var isValidScale = require('./is_valid_scale'); +var flipScale = require('./flip_scale'); + + +module.exports = function colorScaleDefaults(traceIn, traceOut, layout, coerce, opts) { + var prefix = opts.prefix, + cLetter = opts.cLetter, + containerStr = prefix.slice(0, prefix.length-1), + containerIn = prefix ? + Lib.nestedProperty(traceIn, containerStr).get() || {} : + traceIn, + containerOut = prefix ? + Lib.nestedProperty(traceOut, containerStr).get() || {} : + traceOut, + minIn = containerIn[cLetter + 'min'], + maxIn = containerIn[cLetter + 'max'], + sclIn = containerIn.colorscale; + + var validMinMax = isNumeric(minIn) && isNumeric(maxIn) && (minIn < maxIn); + coerce(prefix + cLetter + 'auto', !validMinMax); + coerce(prefix + cLetter + 'min'); + coerce(prefix + cLetter + 'max'); + + // handles both the trace case (autocolorscale is false by default) and + // the marker and marker.line case (autocolorscale is true by default) + var autoColorscaleDftl; + if(sclIn!==undefined) autoColorscaleDftl = !isValidScale(sclIn); + coerce(prefix + 'autocolorscale', autoColorscaleDftl); + var sclOut = coerce(prefix + 'colorscale'); + + // reversescale is handled at the containerOut level + var reverseScale = coerce(prefix + 'reversescale'); + if(reverseScale) containerOut.colorscale = flipScale(sclOut); + + // ... until Scatter.colorbar can handle marker line colorbars + if(prefix === 'marker.line.') return; + + // handle both the trace case where the dflt is listed in attributes and + // the marker case where the dflt is determined by hasColorbar + var showScaleDftl; + if(prefix) showScaleDftl = hasColorbar(containerIn); + var showScale = coerce(prefix + 'showscale', showScaleDftl); + + if(showScale) colorbarDefaults(containerIn, containerOut, layout); +}; diff --git a/src/components/colorscale/flip_scale.js b/src/components/colorscale/flip_scale.js new file mode 100644 index 00000000000..d2440599289 --- /dev/null +++ b/src/components/colorscale/flip_scale.js @@ -0,0 +1,23 @@ +/** +* Copyright 2012-2015, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +module.exports = function flipScale(scl) { + var N = scl.length, + sclNew = new Array(N), + si; + + for(var i = N-1, j = 0; i >= 0; i--, j++) { + si = scl[i]; + sclNew[j] = [1 - si[0], si[1]]; + } + + return sclNew; +}; diff --git a/src/components/colorscale/get_scale.js b/src/components/colorscale/get_scale.js new file mode 100644 index 00000000000..154cad63acb --- /dev/null +++ b/src/components/colorscale/get_scale.js @@ -0,0 +1,38 @@ +/** +* Copyright 2012-2015, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var scales = require('./scales'); +var defaultScale = require('./default_scale'); +var isValidScaleArray = require('./is_valid_scale_array'); + + +module.exports = function getScale(scl, dflt) { + if(!dflt) dflt = defaultScale; + if(!scl) return dflt; + + function parseScale() { + try { + scl = scales[scl] || JSON.parse(scl); + } + catch(e) { + scl = dflt; + } + } + + if(typeof scl === 'string') { + parseScale(); + // occasionally scl is double-JSON encoded... + if(typeof scl === 'string') parseScale(); + } + + if(!isValidScaleArray(scl)) return dflt; + return scl; +}; diff --git a/src/components/colorscale/has_colorscale.js b/src/components/colorscale/has_colorscale.js new file mode 100644 index 00000000000..753382b5d54 --- /dev/null +++ b/src/components/colorscale/has_colorscale.js @@ -0,0 +1,44 @@ +/** +* Copyright 2012-2015, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var isNumeric = require('fast-isnumeric'); + +var Lib = require('../../lib'); + +var isValidScale = require('./is_valid_scale'); + + +module.exports = function hasColorscale(trace, containerStr) { + var container = containerStr ? + Lib.nestedProperty(trace, containerStr).get() || {} : + trace, + color = container.color, + isArrayWithOneNumber = false; + + if(Array.isArray(color)) { + for(var i = 0; i < color.length; i++) { + if(isNumeric(color[i])) { + isArrayWithOneNumber = true; + break; + } + } + } + + return ( + (typeof container==='object' && container!==null) && ( + isArrayWithOneNumber || + container.showscale===true || + (isNumeric(container.cmin) && isNumeric(container.cmax)) || + isValidScale(container.colorscale) || + (typeof container.colorbar==='object' && container.colorbar!==null) + ) + ); +}; diff --git a/src/components/colorscale/index.js b/src/components/colorscale/index.js index 377bcd52f00..6a0523f2058 100644 --- a/src/components/colorscale/index.js +++ b/src/components/colorscale/index.js @@ -9,215 +9,22 @@ 'use strict'; -var Plotly = require('../../plotly'); -var d3 = require('d3'); -var tinycolor = require('tinycolor2'); -var isNumeric = require('fast-isnumeric'); +exports.scales = require('./scales'); -var colorscale = module.exports = {}; +exports.defaultScale = require('./default_scale'); -colorscale.scales = require('./scales'); -colorscale.defaultScale = colorscale.scales.RdBu; +exports.attributes = require('./attributes'); -colorscale.attributes = require('./attributes'); +exports.handleDefaults = require('./defaults'); -function isValidScaleArray(scl) { - var isValid = true, - highestVal = 0, - si; +exports.calc = require('./calc'); - if(!Array.isArray(scl)) return false; - else { - if(+scl[0][0]!==0 || +scl[scl.length-1][0]!==1) return false; - for (var i = 0; i < scl.length; i++) { - si = scl[i]; - if(si.length!==2 || +si[0]= 0; i--, j++) { - si = scl[i]; - sclNew[j] = [1 - si[0], si[1]]; - } - - return sclNew; -}; - -colorscale.hasColorscale = function(trace, containerStr) { - var container = containerStr ? - Plotly.Lib.nestedProperty(trace, containerStr).get() || {} : - trace, - color = container.color, - isArrayWithOneNumber = false; - - if(Array.isArray(color)) { - for(var i = 0; i < color.length; i++) { - if(isNumeric(color[i])) { - isArrayWithOneNumber = true; - break; - } - } - } - - return ( - (typeof container==='object' && container!==null) && ( - isArrayWithOneNumber || - container.showscale===true || - (isNumeric(container.cmin) && isNumeric(container.cmax)) || - colorscale.isValidScale(container.colorscale) || - (typeof container.colorbar==='object' && container.colorbar!==null) - ) - ); -}; - -colorscale.handleDefaults = function(traceIn, traceOut, layout, coerce, opts) { - var prefix = opts.prefix, - cLetter = opts.cLetter, - containerStr = prefix.slice(0, prefix.length-1), - containerIn = prefix ? - Plotly.Lib.nestedProperty(traceIn, containerStr).get() || {} : - traceIn, - containerOut = prefix ? - Plotly.Lib.nestedProperty(traceOut, containerStr).get() || {} : - traceOut, - minIn = containerIn[cLetter + 'min'], - maxIn = containerIn[cLetter + 'max'], - sclIn = containerIn.colorscale; - - var validMinMax, autoColorscaleDftl, showScaleDftl, sclOut, reverseScale, showScale; - - validMinMax = isNumeric(minIn) && isNumeric(maxIn) && minIn < maxIn; - coerce(prefix + cLetter + 'auto', !validMinMax); - coerce(prefix + cLetter + 'min'); - coerce(prefix + cLetter + 'max'); - - // handles both the trace case (autocolorscale is false by default) and - // the marker and marker.line case (autocolorscale is true by default) - if(sclIn!==undefined) autoColorscaleDftl = !colorscale.isValidScale(sclIn); - coerce(prefix + 'autocolorscale', autoColorscaleDftl); - sclOut = coerce(prefix + 'colorscale'); - - // reversescale is handled at the containerOut level - reverseScale = coerce(prefix + 'reversescale'); - if(reverseScale) containerOut.colorscale = colorscale.flipScale(sclOut); - - // ... until Scatter.colorbar can handle marker line colorbars - if(prefix === 'marker.line.') return; - - // handle both the trace case where the dflt is listed in attributes and - // the marker case where the dflt is determined by hasColorbar - if(prefix) showScaleDftl = colorscale.hasColorbar(containerIn); - showScale = coerce(prefix + 'showscale', showScaleDftl); - - if(showScale) Plotly.Colorbar.supplyDefaults(containerIn, containerOut, layout); -}; - -colorscale.calc = function(trace, vals, containerStr, cLetter) { - var container, inputContainer; - - if(containerStr) { - container = Plotly.Lib.nestedProperty(trace, containerStr).get(); - inputContainer = Plotly.Lib.nestedProperty(trace._input, containerStr).get(); - } else { - container = trace; - inputContainer = trace._input; - } - - var auto = container[cLetter + 'auto'], - min = container[cLetter + 'min'], - max = container[cLetter + 'max'], - scl = container.colorscale; - - if(auto!==false || min===undefined) { - min = Plotly.Lib.aggNums(Math.min, null, vals); - } - - if(auto!==false || max===undefined) { - max = Plotly.Lib.aggNums(Math.max, null, vals); - } - - if(min === max) { - min -= 0.5; - max += 0.5; - } - - container[cLetter + 'min'] = min; - container[cLetter + 'max'] = max; - - inputContainer[cLetter + 'min'] = min; - inputContainer[cLetter + 'max'] = max; - - if(container.autocolorscale) { - if(min * max < 0) scl = colorscale.scales.RdBu; - else if(min >= 0) scl = colorscale.scales.Reds; - else scl = colorscale.scales.Blues; - - // reversescale is handled at the containerOut level - inputContainer.colorscale = scl; - if(container.reversescale) scl = colorscale.flipScale(scl); - container.colorscale = scl; - } -}; - -colorscale.makeScaleFunction = function(scl, cmin, cmax) { - var N = scl.length, - domain = new Array(N), - range = new Array(N), - si; - - for(var i = 0; i < N; i++) { - si = scl[i]; - domain[i] = cmin + si[0] * (cmax - cmin); - range[i] = si[1]; - } - - var sclFunc = d3.scale.linear() - .domain(domain) - .interpolate(d3.interpolateRgb) - .range(range); - - return function(v) { - if(isNumeric(v)) return sclFunc(v); - else if(tinycolor(v).isValid()) return v; - else return Plotly.Color.defaultLine; - }; -}; +exports.makeScaleFunction = require('./make_scale_function'); diff --git a/src/components/colorscale/is_valid_scale.js b/src/components/colorscale/is_valid_scale.js new file mode 100644 index 00000000000..1d4513ba258 --- /dev/null +++ b/src/components/colorscale/is_valid_scale.js @@ -0,0 +1,19 @@ +/** +* Copyright 2012-2015, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var scales = require('./scales'); +var isValidScaleArray = require('./is_valid_scale_array'); + + +module.exports = function isValidScale(scl) { + if(scales[scl] !== undefined) return true; + else return isValidScaleArray(scl); +}; diff --git a/src/components/colorscale/is_valid_scale_array.js b/src/components/colorscale/is_valid_scale_array.js new file mode 100644 index 00000000000..c11ff6e0540 --- /dev/null +++ b/src/components/colorscale/is_valid_scale_array.js @@ -0,0 +1,33 @@ +/** +* Copyright 2012-2015, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var tinycolor = require('tinycolor2'); + + +module.exports = function isValidScaleArray(scl) { + var isValid = true, + highestVal = 0, + si; + + if(!Array.isArray(scl)) return false; + else { + if(+scl[0][0]!==0 || +scl[scl.length-1][0]!==1) return false; + for (var i = 0; i < scl.length; i++) { + si = scl[i]; + if(si.length!==2 || +si[0] Date: Fri, 18 Dec 2015 15:28:50 -0500 Subject: [PATCH 4/6] require colorscale methods directly --- src/lib/coerce.js | 4 ++-- src/traces/bar/calc.js | 10 ++++++---- src/traces/bar/style_defaults.js | 11 ++++++----- src/traces/choropleth/defaults.js | 20 ++++++++++++-------- src/traces/choropleth/index.js | 16 +++++----------- src/traces/choropleth/plot.js | 9 ++++++--- src/traces/contour/colorbar.js | 3 ++- src/traces/contour/style.js | 3 ++- src/traces/contour/style_defaults.js | 4 ++-- src/traces/heatmap/calc.js | 3 ++- src/traces/heatmap/colorbar.js | 3 ++- src/traces/heatmap/defaults.js | 6 ++---- src/traces/heatmap/plot.js | 3 ++- src/traces/histogram2d/defaults.js | 4 ++-- src/traces/scatter/colorbar.js | 3 ++- src/traces/surface/calc.js | 4 ++-- src/traces/surface/defaults.js | 10 ++++++---- test/jasmine/tests/colorscale_test.js | 13 +++++++------ 18 files changed, 70 insertions(+), 59 deletions(-) diff --git a/src/lib/coerce.js b/src/lib/coerce.js index c65ff0aad03..ba9c3fa6421 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -9,11 +9,11 @@ 'use strict'; -var Plotly = require('../plotly'); var isNumeric = require('fast-isnumeric'); var tinycolor = require('tinycolor2'); var nestedProperty = require('./nested_property'); +var getColorscale = require('../components/colorscale/get_scale'); var colorscaleNames = Object.keys(require('../components/colorscale/scales')); @@ -140,7 +140,7 @@ exports.valObjects = { requiredOpts: [], otherOpts: ['dflt'], coerceFunction: function(v, propOut, dflt) { - propOut.set(Plotly.Colorscale.getScale(v, dflt)); + propOut.set(getColorscale(v, dflt)); } }, angle: { diff --git a/src/traces/bar/calc.js b/src/traces/bar/calc.js index 0ebdf4b10df..531526c3a64 100644 --- a/src/traces/bar/calc.js +++ b/src/traces/bar/calc.js @@ -12,6 +12,8 @@ var isNumeric = require('fast-isnumeric'); var Plotly = require('../../plotly'); +var hasColorscale = require('../../components/colorscale/has_colorscale'); +var colorscaleCalc = require('../../components/colorscale/calc'); module.exports = function calc(gd, trace) { @@ -44,11 +46,11 @@ module.exports = function calc(gd, trace) { } // auto-z and autocolorscale if applicable - if(Plotly.Colorscale.hasColorscale(trace, 'marker')) { - Plotly.Colorscale.calc(trace, trace.marker.color, 'marker', 'c'); + if(hasColorscale(trace, 'marker')) { + colorscaleCalc(trace, trace.marker.color, 'marker', 'c'); } - if(Plotly.Colorscale.hasColorscale(trace, 'marker.line')) { - Plotly.Colorscale.calc(trace, trace.marker.line.color, 'marker.line', 'c'); + if(hasColorscale(trace, 'marker.line')) { + colorscaleCalc(trace, trace.marker.line.color, 'marker.line', 'c'); } return cd; diff --git a/src/traces/bar/style_defaults.js b/src/traces/bar/style_defaults.js index e4a0065ade8..859c04e6a4f 100644 --- a/src/traces/bar/style_defaults.js +++ b/src/traces/bar/style_defaults.js @@ -10,22 +10,23 @@ 'use strict'; var Color = require('../../components/color'); -var Colorscale = require('../../components/colorscale'); +var hasColorscale = require('../../components/colorscale/has_colorscale'); +var colorscaleDefaults = require('../../components/colorscale/defaults'); module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, defaultColor, layout) { coerce('marker.color', defaultColor); - if(Colorscale.hasColorscale(traceIn, 'marker')) { - Colorscale.handleDefaults( + if(hasColorscale(traceIn, 'marker')) { + colorscaleDefaults( traceIn, traceOut, layout, coerce, {prefix: 'marker.', cLetter: 'c'} ); } coerce('marker.line.color', Color.defaultLine); - if(Colorscale.hasColorscale(traceIn, 'marker.line')) { - Colorscale.handleDefaults( + if(hasColorscale(traceIn, 'marker.line')) { + colorscaleDefaults( traceIn, traceOut, layout, coerce, {prefix: 'marker.line.', cLetter: 'c'} ); } diff --git a/src/traces/choropleth/defaults.js b/src/traces/choropleth/defaults.js index 8ad77bec025..a3fcddc20c7 100644 --- a/src/traces/choropleth/defaults.js +++ b/src/traces/choropleth/defaults.js @@ -9,24 +9,28 @@ 'use strict'; -var Plotly = require('../../plotly'); -var Choropleth = require('./'); +var Lib = require('../../lib'); + +var colorscaleDefaults = require('../../components/colorscale/defaults'); +var attributes = require('./attributes'); -module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { - var locations, len, z; +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { function coerce(attr, dflt) { - return Plotly.Lib.coerce(traceIn, traceOut, Choropleth.attributes, attr, dflt); + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); } - locations = coerce('locations'); + var locations = coerce('locations'); + + var len; if(locations) len = locations.length; + if(!locations || !len) { traceOut.visible = false; return; } - z = coerce('z'); + var z = coerce('z'); if(!Array.isArray(z)) { traceOut.visible = false; return; @@ -41,7 +45,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('marker.line.color'); coerce('marker.line.width'); - Plotly.Colorscale.handleDefaults( + colorscaleDefaults( traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'} ); diff --git a/src/traces/choropleth/index.js b/src/traces/choropleth/index.js index c66037d950f..87b2c153078 100644 --- a/src/traces/choropleth/index.js +++ b/src/traces/choropleth/index.js @@ -11,9 +11,7 @@ var Plotly = require('../../plotly'); -var Choropleth = module.exports = {}; - -Plotly.Plots.register(Choropleth, 'choropleth', ['geo', 'noOpacity'], { +Plotly.Plots.register(exports, 'choropleth', ['geo', 'noOpacity'], { description: [ 'The data that describes the choropleth value-to-color mapping', 'is set in `z`.', @@ -22,14 +20,10 @@ Plotly.Plots.register(Choropleth, 'choropleth', ['geo', 'noOpacity'], { ].join(' ') }); -Choropleth.attributes = require('./attributes'); - -Choropleth.supplyDefaults = require('./defaults'); - -Choropleth.colorbar = require('../heatmap/colorbar'); +exports.attributes = require('./attributes'); -Choropleth.calc = function(gd, trace) { +exports.supplyDefaults = require('./defaults'); - Plotly.Colorscale.calc(trace, trace.z, '', 'z'); +exports.colorbar = require('../heatmap/colorbar'); -}; +exports.calc = require('../surface/calc'); diff --git a/src/traces/choropleth/plot.js b/src/traces/choropleth/plot.js index 4cf8b4f9393..d23fb38f154 100644 --- a/src/traces/choropleth/plot.js +++ b/src/traces/choropleth/plot.js @@ -12,9 +12,12 @@ var Plotly = require('../../plotly'); var d3 = require('d3'); +var Color = require('../../components/color'); var getTopojsonFeatures = require('../../lib/topojson_utils').getTopojsonFeatures; var locationToFeature = require('../../lib/geo_location_utils').locationToFeature; var arrayToCalcItem = require('../../lib/array_to_calc_item'); +var getColorscale = require('../../components/colorscale/get_scale'); +var makeScaleFunction = require('../../components/colorscale/make_scale_function'); var constants = require('../../constants/geo_constants'); @@ -130,14 +133,14 @@ plotChoropleth.style = function(geo) { markerLine = marker.line || {}, zmin = trace.zmin, zmax = trace.zmax, - scl = Plotly.Colorscale.getScale(trace.colorscale), - sclFunc = Plotly.Colorscale.makeScaleFunction(scl, zmin, zmax); + scl = getColorscale(trace.colorscale), + sclFunc = makeScaleFunction(scl, zmin, zmax); s.selectAll('path.choroplethlocation') .each(function(d) { d3.select(this) .attr('fill', function(d) { return sclFunc(d.z); }) - .call(Plotly.Color.stroke, d.mlc || markerLine.color) + .call(Color.stroke, d.mlc || markerLine.color) .call(Plotly.Drawing.dashLine, '', d.mlw || markerLine.width); }); }); diff --git a/src/traces/contour/colorbar.js b/src/traces/contour/colorbar.js index 20e72141786..b9797203961 100644 --- a/src/traces/contour/colorbar.js +++ b/src/traces/contour/colorbar.js @@ -12,6 +12,7 @@ var d3 = require('d3'); var Plotly = require('../../plotly'); +var getColorscale = require('../../components/colorscale/get_scale'); module.exports = function colorbar(gd, cd) { @@ -33,7 +34,7 @@ module.exports = function colorbar(gd, cd) { line = trace.line, cs = contours.size||1, nc = Math.floor((contours.end + cs/10 - contours.start)/cs)+1, - scl = Plotly.Colorscale.getScale(trace.colorscale), + scl = getColorscale(trace.colorscale), extraLevel = contours.coloring==='lines' ? 0 : 1, colormap = d3.scale.linear().interpolate(d3.interpolateRgb), colorDomain = scl.map(function(si){ diff --git a/src/traces/contour/style.js b/src/traces/contour/style.js index f9e382a7a04..79ba2f4768e 100644 --- a/src/traces/contour/style.js +++ b/src/traces/contour/style.js @@ -12,6 +12,7 @@ var d3 = require('d3'); var Plotly = require('../../plotly'); +var getColorscale = require('../../components/colorscale/get_scale'); var heatmapStyle = require('../heatmap/style'); @@ -26,7 +27,7 @@ module.exports = function style(gd) { colorLines = contours.coloring==='lines', cs = contours.size||1, nc = Math.floor((contours.end + cs/10 - contours.start)/cs) + 1, - scl = Plotly.Colorscale.getScale(trace.colorscale), + scl = getColorscale(trace.colorscale), extraLevel = colorLines ? 0 : 1, colormap = d3.scale.linear() .domain(scl.map(function(si){ diff --git a/src/traces/contour/style_defaults.js b/src/traces/contour/style_defaults.js index f02bc7b5819..69cef30c052 100644 --- a/src/traces/contour/style_defaults.js +++ b/src/traces/contour/style_defaults.js @@ -9,7 +9,7 @@ 'use strict'; -var Colorscale = require('../../components/colorscale'); +var colorscaleDefaults = require('../../components/colorscale/defaults'); module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, layout) { @@ -27,7 +27,7 @@ module.exports = function handleStyleDefaults(traceIn, traceOut, coerce, layout) coerce('line.smoothing'); if((traceOut.contours || {}).coloring !== 'none') { - Colorscale.handleDefaults( + colorscaleDefaults( traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'} ); } diff --git a/src/traces/heatmap/calc.js b/src/traces/heatmap/calc.js index d2f4b7a7e80..73687c521c6 100644 --- a/src/traces/heatmap/calc.js +++ b/src/traces/heatmap/calc.js @@ -15,6 +15,7 @@ var Plotly = require('../../plotly'); var Lib = require('../../lib'); var histogram2dCalc = require('../histogram2d/calc'); +var colorscaleCalc = require('../../components/colorscale/calc'); var hasColumns = require('./has_columns'); var convertColumnXYZ = require('./convert_column_xyz'); var maxRowLength = require('./max_row_length'); @@ -120,7 +121,7 @@ module.exports = function calc(gd, trace) { var cd0 = {x: xArray, y: yArray, z: z}; // auto-z and autocolorscale if applicable - Plotly.Colorscale.calc(trace, z, '', 'z'); + colorscaleCalc(trace, z, '', 'z'); if(isContour && trace.contours && trace.contours.coloring==='heatmap') { var hmType = trace.type === 'contour' ? 'heatmap' : 'histogram2d'; diff --git a/src/traces/heatmap/colorbar.js b/src/traces/heatmap/colorbar.js index 4fec7470c38..9606dad5e89 100644 --- a/src/traces/heatmap/colorbar.js +++ b/src/traces/heatmap/colorbar.js @@ -14,11 +14,12 @@ var isNumeric = require('fast-isnumeric'); var Plotly = require('../../plotly'); var Lib = require('../../lib'); +var getColorscale = require('../../components/colorscale/get_scale'); module.exports = function colorbar(gd, cd) { var trace = cd[0].trace, cbId = 'cb' + trace.uid, - scl = Plotly.Colorscale.getScale(trace.colorscale), + scl = getColorscale(trace.colorscale), zmin = trace.zmin, zmax = trace.zmax; diff --git a/src/traces/heatmap/defaults.js b/src/traces/heatmap/defaults.js index 39a4d9802c4..ac08b5dbaaf 100644 --- a/src/traces/heatmap/defaults.js +++ b/src/traces/heatmap/defaults.js @@ -10,10 +10,10 @@ 'use strict'; var Lib = require('../../lib'); -var Colorscale = require('../../components/colorscale'); var hasColumns = require('./has_columns'); var handleXYZDefaults = require('./xyz_defaults'); +var colorscaleDefaults = require('../../components/colorscale/defaults'); var attributes = require('./attributes'); @@ -32,7 +32,5 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('zsmooth'); coerce('connectgaps', hasColumns(traceOut) && (traceOut.zsmooth !== false)); - Colorscale.handleDefaults( - traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'} - ); + colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'}); }; diff --git a/src/traces/heatmap/plot.js b/src/traces/heatmap/plot.js index 6f05a034da6..95581af9cec 100644 --- a/src/traces/heatmap/plot.js +++ b/src/traces/heatmap/plot.js @@ -14,6 +14,7 @@ var tinycolor = require('tinycolor2'); var Plotly = require('../../plotly'); var Lib = require('../../lib'); +var getColorscale = require('../../components/colorscale/get_scale'); var maxRowLength = require('./max_row_length'); @@ -45,7 +46,7 @@ function plotOne(gd, plotinfo, cd) { var z = cd[0].z, min = trace.zmin, max = trace.zmax, - scl = Plotly.Colorscale.getScale(trace.colorscale), + scl = getColorscale(trace.colorscale), x = cd[0].x, y = cd[0].y, isContour = Plotly.Plots.traceIs(trace, 'contour'), diff --git a/src/traces/histogram2d/defaults.js b/src/traces/histogram2d/defaults.js index 53addb64cb1..4dce4652e5c 100644 --- a/src/traces/histogram2d/defaults.js +++ b/src/traces/histogram2d/defaults.js @@ -10,9 +10,9 @@ 'use strict'; var Lib = require('../../lib'); -var Colorscale = require('../../components/colorscale'); var handleSampleDefaults = require('./sample_defaults'); +var colorscaleDefaults = require('../../components/colorscale/defaults'); var attributes = require('./attributes'); @@ -25,7 +25,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, layout) { coerce('zsmooth'); - Colorscale.handleDefaults( + colorscaleDefaults( traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'} ); }; diff --git a/src/traces/scatter/colorbar.js b/src/traces/scatter/colorbar.js index 2092907bd2d..41355cf8a4e 100644 --- a/src/traces/scatter/colorbar.js +++ b/src/traces/scatter/colorbar.js @@ -14,6 +14,7 @@ var isNumeric = require('fast-isnumeric'); var Plotly = require('../../plotly'); var Lib = require('../../lib'); +var getColorscale = require('../../components/colorscale/get_scale'); module.exports = function colorbar(gd, cd) { @@ -31,7 +32,7 @@ module.exports = function colorbar(gd, cd) { return; } - var scl = Plotly.Colorscale.getScale(marker.colorscale), + var scl = getColorscale(marker.colorscale), vals = marker.color, cmin = marker.cmin, cmax = marker.cmax; diff --git a/src/traces/surface/calc.js b/src/traces/surface/calc.js index 3c05d5921b2..bc5411d564f 100644 --- a/src/traces/surface/calc.js +++ b/src/traces/surface/calc.js @@ -9,10 +9,10 @@ 'use strict'; -var Colorscale = require('../../components/colorscale'); +var colorscaleCalc = require('../../components/colorscale/calc'); // Compute auto-z and autocolorscale if applicable module.exports = function calc(gd, trace) { - Colorscale.calc(trace, trace.z, '', 'z'); + colorscaleCalc(trace, trace.z, '', 'z'); }; diff --git a/src/traces/surface/defaults.js b/src/traces/surface/defaults.js index f3f3f3c5e04..aa503710909 100644 --- a/src/traces/surface/defaults.js +++ b/src/traces/surface/defaults.js @@ -9,15 +9,17 @@ 'use strict'; -var Plotly = require('../../plotly'); -var Surface = require('./'); +var Lib = require('../../lib'); + +var colorscaleDefaults = require('../../components/colorscale/defaults'); +var attributes = require('./attributes'); module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { var i, j; function coerce(attr, dflt) { - return Plotly.Lib.coerce(traceIn, traceOut, Surface.attributes, attr, dflt); + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); } var z = coerce('z'); @@ -83,7 +85,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } } - Plotly.Colorscale.handleDefaults( + colorscaleDefaults( traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'} ); }; diff --git a/test/jasmine/tests/colorscale_test.js b/test/jasmine/tests/colorscale_test.js index 4ee27437c85..fc50044e0a5 100644 --- a/test/jasmine/tests/colorscale_test.js +++ b/test/jasmine/tests/colorscale_test.js @@ -1,10 +1,11 @@ var Plotly = require('@src/plotly'); +var Colorscale = require('@src/components/colorscale'); describe('Test colorscale:', function () { 'use strict'; describe('isValidScale', function() { - var isValidScale = Plotly.Colorscale.isValidScale, + var isValidScale = Colorscale.isValidScale, scl; it('should accept colorscale strings', function() { @@ -53,7 +54,7 @@ describe('Test colorscale:', function () { }); describe('flipScale', function() { - var flipScale = Plotly.Colorscale.flipScale, + var flipScale = Colorscale.flipScale, scl; it('should flip a colorscale', function() { @@ -66,7 +67,7 @@ describe('Test colorscale:', function () { }); describe('hasColorscale', function() { - var hasColorscale = Plotly.Colorscale.hasColorscale, + var hasColorscale = Colorscale.hasColorscale, trace; it('should return false when marker is not defined', function() { @@ -181,7 +182,7 @@ describe('Test colorscale:', function () { }); describe('handleDefaults (heatmap-like version)', function() { - var handleDefaults = Plotly.Colorscale.handleDefaults, + var handleDefaults = Colorscale.handleDefaults, layout = { font: Plotly.Plots.layoutAttributes.font }, @@ -251,7 +252,7 @@ describe('Test colorscale:', function () { }); describe('handleDefaults (scatter-like version)', function() { - var handleDefaults = Plotly.Colorscale.handleDefaults, + var handleDefaults = Colorscale.handleDefaults, layout = { font: Plotly.Plots.layoutAttributes.font }, @@ -302,7 +303,7 @@ describe('Test colorscale:', function () { }); describe('calc', function() { - var calcColorscale = Plotly.Colorscale.calc; + var calcColorscale = Colorscale.calc; var trace, z; beforeEach(function() { From accf041e322cf16a247956773d6706f15fe2ec54 Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 18 Dec 2015 15:30:57 -0500 Subject: [PATCH 5/6] split in up colorbar method: - rename Plotly.Colorbar -> Colorbar.draw --- src/components/colorbar/defaults.js | 60 +++ src/components/colorbar/draw.js | 533 ++++++++++++++++++++++++++ src/components/colorbar/index.js | 566 +--------------------------- 3 files changed, 597 insertions(+), 562 deletions(-) create mode 100644 src/components/colorbar/defaults.js create mode 100644 src/components/colorbar/draw.js diff --git a/src/components/colorbar/defaults.js b/src/components/colorbar/defaults.js new file mode 100644 index 00000000000..0084292d0a8 --- /dev/null +++ b/src/components/colorbar/defaults.js @@ -0,0 +1,60 @@ +/** +* Copyright 2012-2015, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var Axes = require('../../plots/cartesian/axes'); +var Lib = require('../../lib'); + +var attributes = require('./attributes'); + + +module.exports = function colorbarDefaults(containerIn, containerOut, layout) { + var colorbarOut = containerOut.colorbar = {}, + colorbarIn = containerIn.colorbar || {}; + + function coerce(attr, dflt) { + return Lib.coerce(colorbarIn, colorbarOut, attributes, attr, dflt); + } + + var thicknessmode = coerce('thicknessmode'); + coerce('thickness', (thicknessmode === 'fraction') ? + 30 / (layout.width - layout.margin.l - layout.margin.r) : + 30 + ); + + var lenmode = coerce('lenmode'); + coerce('len', (lenmode === 'fraction') ? + 1 : + layout.height - layout.margin.t - layout.margin.b + ); + + coerce('x'); + coerce('xanchor'); + coerce('xpad'); + coerce('y'); + coerce('yanchor'); + coerce('ypad'); + Lib.noneOrAll(colorbarIn, colorbarOut, ['x', 'y']); + + coerce('outlinecolor'); + coerce('outlinewidth'); + coerce('bordercolor'); + coerce('borderwidth'); + coerce('bgcolor'); + + Axes.handleTickValueDefaults(colorbarIn, colorbarOut, coerce, 'linear'); + + Axes.handleTickDefaults(colorbarIn, colorbarOut, coerce, 'linear', + {outerTicks: false, font: layout.font, noHover: true}); + + coerce('title'); + Lib.coerceFont(coerce, 'titlefont', layout.font); + coerce('titleside'); +}; diff --git a/src/components/colorbar/draw.js b/src/components/colorbar/draw.js new file mode 100644 index 00000000000..e5484966a93 --- /dev/null +++ b/src/components/colorbar/draw.js @@ -0,0 +1,533 @@ +/** +* Copyright 2012-2015, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var d3 = require('d3'); + +var Plotly = require('../../plotly'); + +var attributes = require('./attributes'); + + +module.exports = function draw(gd, id) { + // opts: options object, containing everything from attributes + // plus a few others that are the equivalent of the colorbar "data" + var opts = {}; + Object.keys(attributes).forEach(function(k) { + opts[k] = null; + }); + // fillcolor can be a d3 scale, domain is z values, range is colors + // or leave it out for no fill, + // or set to a string constant for single-color fill + opts.fillcolor = null; + // line.color has the same options as fillcolor + opts.line = {color: null, width: null, dash: null}; + // levels of lines to draw. + // note that this DOES NOT determine the extent of the bar + // that's given by the domain of fillcolor + // (or line.color if no fillcolor domain) + opts.levels = {start: null, end: null, size: null}; + // separate fill levels (for example, heatmap coloring of a + // contour map) if this is omitted, fillcolors will be + // evaluated halfway between levels + opts.filllevels = null; + + function component(){ + var fullLayout = gd._fullLayout; + if((typeof opts.fillcolor !== 'function') && + (typeof opts.line.color !== 'function')) { + fullLayout._infolayer.selectAll('g.'+id).remove(); + return; + } + var zrange = d3.extent(((typeof opts.fillcolor === 'function') ? + opts.fillcolor : opts.line.color).domain()), + linelevels = [], + filllevels = [], + l, + linecolormap = typeof opts.line.color === 'function' ? + opts.line.color : function(){ return opts.line.color; }, + fillcolormap = typeof opts.fillcolor === 'function' ? + opts.fillcolor : function(){ return opts.fillcolor; }; + + var l0 = opts.levels.end + opts.levels.size/100, + ls = opts.levels.size, + zr0 = (1.001 * zrange[0] - 0.001 * zrange[1]), + zr1 = (1.001 * zrange[1] - 0.001 * zrange[0]); + for(l = opts.levels.start; (l - l0) * ls < 0; l += ls) { + if(l > zr0 && l < zr1) linelevels.push(l); + } + + if(typeof opts.fillcolor === 'function') { + if(opts.filllevels) { + l0 = opts.filllevels.end + opts.filllevels.size / 100; + ls = opts.filllevels.size; + for(l = opts.filllevels.start; (l - l0) * ls < 0; l += ls) { + if(l > zrange[0] && l < zrange[1]) filllevels.push(l); + } + } + else { + filllevels = linelevels.map(function(v){ + return v-opts.levels.size / 2; + }); + filllevels.push(filllevels[filllevels.length - 1] + + opts.levels.size); + } + } + else if(opts.fillcolor && typeof opts.fillcolor==='string') { + // doesn't matter what this value is, with a single value + // we'll make a single fill rect covering the whole bar + filllevels = [0]; + } + + if(opts.levels.size<0) { + linelevels.reverse(); + filllevels.reverse(); + } + + // now make a Plotly Axes object to scale with and draw ticks + // TODO: does not support orientation other than right + + // we calculate pixel sizes based on the specified graph size, + // not the actual (in case something pushed the margins around) + // which is a little odd but avoids an odd iterative effect + // when the colorbar itself is pushing the margins. + // but then the fractional size is calculated based on the + // actual graph size, so that the axes will size correctly. + var originalPlotHeight = fullLayout.height - fullLayout.margin.t - fullLayout.margin.b, + originalPlotWidth = fullLayout.width - fullLayout.margin.l - fullLayout.margin.r, + thickPx = Math.round(opts.thickness * + (opts.thicknessmode==='fraction' ? originalPlotWidth : 1)), + thickFrac = thickPx / fullLayout._size.w, + lenPx = Math.round(opts.len * + (opts.lenmode==='fraction' ? originalPlotHeight : 1)), + lenFrac = lenPx / fullLayout._size.h, + xpadFrac = opts.xpad/fullLayout._size.w, + yExtraPx = (opts.borderwidth + opts.outlinewidth)/2, + ypadFrac = opts.ypad / fullLayout._size.h, + + // x positioning: do it initially just for left anchor, + // then fix at the end (since we don't know the width yet) + xLeft = Math.round(opts.x*fullLayout._size.w + opts.xpad), + // for dragging... this is getting a little muddled... + xLeftFrac = opts.x - thickFrac * + ({middle: 0.5, right: 1}[opts.xanchor]||0), + + // y positioning we can do correctly from the start + yBottomFrac = opts.y + lenFrac * + (({top:-0.5, bottom:0.5}[opts.yanchor]||0)-0.5), + yBottomPx = Math.round(fullLayout._size.h * (1-yBottomFrac)), + yTopPx = yBottomPx-lenPx, + titleEl, + cbAxisIn = { + type: 'linear', + range: zrange, + tickmode: opts.tickmode, + nticks: opts.nticks, + tick0: opts.tick0, + dtick: opts.dtick, + tickvals: opts.tickvals, + ticktext: opts.ticktext, + ticks: opts.ticks, + ticklen: opts.ticklen, + tickwidth: opts.tickwidth, + tickcolor: opts.tickcolor, + showticklabels: opts.showticklabels, + tickfont: opts.tickfont, + tickangle: opts.tickangle, + tickformat: opts.tickformat, + exponentformat: opts.exponentformat, + showexponent: opts.showexponent, + showtickprefix: opts.showtickprefix, + tickprefix: opts.tickprefix, + showticksuffix: opts.showticksuffix, + ticksuffix: opts.ticksuffix, + title: opts.title, + titlefont: opts.titlefont, + anchor: 'free', + position: 1 + }, + cbAxisOut = {}, + axisOptions = { + letter: 'y', + font: fullLayout.font, + noHover: true + }; + + // Coerce w.r.t. Axes layoutAttributes: + // re-use axes.js logic without updating _fullData + function coerce(attr, dflt) { + return Plotly.Lib.coerce(cbAxisIn, cbAxisOut, + Plotly.Axes.layoutAttributes, + attr, dflt); + } + + // Prepare the Plotly axis object + Plotly.Axes.handleAxisDefaults(cbAxisIn, cbAxisOut, + coerce, axisOptions); + Plotly.Axes.handleAxisPositioningDefaults(cbAxisIn, cbAxisOut, + coerce, axisOptions); + + cbAxisOut._id = 'y' + id; + cbAxisOut._td = gd; + + // position can't go in through supplyDefaults + // because that restricts it to [0,1] + cbAxisOut.position = opts.x+xpadFrac+thickFrac; + + // save for other callers to access this axis + component.axis = cbAxisOut; + + if(['top','bottom'].indexOf(opts.titleside)!==-1) { + cbAxisOut.titleside = opts.titleside; + cbAxisOut.titlex = opts.x + xpadFrac; + cbAxisOut.titley = yBottomFrac + + (opts.titleside==='top' ? lenFrac-ypadFrac : ypadFrac); + } + + if(opts.line.color && opts.tickmode === 'auto') { + cbAxisOut.tickmode = 'linear'; + cbAxisOut.tick0 = opts.levels.start; + var dtick = opts.levels.size; + // expand if too many contours, so we don't get too many ticks + var autoNtick = Plotly.Lib.constrain( + (yBottomPx-yTopPx)/50, 4, 15) + 1, + dtFactor = (zrange[1]-zrange[0]) / + ((opts.nticks||autoNtick)*dtick); + if(dtFactor>1) { + var dtexp = Math.pow(10,Math.floor( + Math.log(dtFactor)/Math.LN10)); + dtick *= dtexp*Plotly.Lib.roundUp(dtFactor/dtexp,[2,5,10]); + // if the contours are at round multiples, reset tick0 + // so they're still at round multiples. Otherwise, + // keep the first label on the first contour level + if((Math.abs(opts.levels.start)/ + opts.levels.size+1e-6)%1 < 2e-6) { + cbAxisOut.tick0 = 0; + } + } + cbAxisOut.dtick = dtick; + } + + // set domain after init, because we may want to + // allow it outside [0,1] + cbAxisOut.domain = [ + yBottomFrac+ypadFrac, + yBottomFrac+lenFrac-ypadFrac + ]; + cbAxisOut.setScale(); + + // now draw the elements + var container = fullLayout._infolayer.selectAll('g.'+id).data([0]); + container.enter().append('g').classed(id,true) + .each(function(){ + var s = d3.select(this); + s.append('rect').classed('cbbg',true); + s.append('g').classed('cbfills',true); + s.append('g').classed('cblines',true); + s.append('g').classed('cbaxis',true).classed('crisp',true); + s.append('g').classed('cbtitleunshift',true) + .append('g').classed('cbtitle',true); + s.append('rect').classed('cboutline',true); + }); + container.attr('transform','translate('+Math.round(fullLayout._size.l)+ + ','+Math.round(fullLayout._size.t)+')'); + // TODO: this opposite transform is a hack until we make it + // more rational which items get this offset + var titleCont = container.select('.cbtitleunshift') + .attr('transform', 'translate(-'+ + Math.round(fullLayout._size.l) + ',-' + + Math.round(fullLayout._size.t) + ')'); + + cbAxisOut._axislayer = container.select('.cbaxis'); + var titleHeight = 0; + if(['top','bottom'].indexOf(opts.titleside)!==-1) { + // draw the title so we know how much room it needs + // when we squish the axis + Plotly.Titles.draw(gd, cbAxisOut._id + 'title'); + } + + function drawAxis(){ + if(['top','bottom'].indexOf(opts.titleside)!==-1) { + // squish the axis top to make room for the title + var titleGroup = container.select('.cbtitle'), + titleText = titleGroup.select('text'), + titleTrans = + [-opts.outlinewidth/2, opts.outlinewidth/2], + mathJaxNode = titleGroup + .select('.h'+cbAxisOut._id+'title-math-group') + .node(), + lineSize = 15.6; + if(titleText.node()) { + lineSize = + parseInt(titleText.style('font-size'), 10) * 1.3; + } + if(mathJaxNode) { + titleHeight = Plotly.Drawing.bBox(mathJaxNode).height; + if(titleHeight>lineSize) { + // not entirely sure how mathjax is doing + // vertical alignment, but this seems to work. + titleTrans[1] -= (titleHeight-lineSize)/2; + } + } + else if(titleText.node() && + !titleText.classed('js-placeholder')) { + titleHeight = Plotly.Drawing.bBox( + titleGroup.node()).height; + } + if(titleHeight) { + // buffer btwn colorbar and title + // TODO: configurable + titleHeight += 5; + + if(opts.titleside==='top') { + cbAxisOut.domain[1] -= titleHeight/fullLayout._size.h; + titleTrans[1] *= -1; + } + else { + cbAxisOut.domain[0] += titleHeight/fullLayout._size.h; + var nlines = Math.max(1, + titleText.selectAll('tspan.line').size()); + titleTrans[1] += (1-nlines)*lineSize; + } + + titleGroup.attr('transform', + 'translate('+titleTrans+')'); + + cbAxisOut.setScale(); + } + } + + container.selectAll('.cbfills,.cblines,.cbaxis') + .attr('transform','translate(0,'+ + Math.round(fullLayout._size.h*(1-cbAxisOut.domain[1]))+')'); + + var fills = container.select('.cbfills') + .selectAll('rect.cbfill') + .data(filllevels); + fills.enter().append('rect') + .classed('cbfill',true) + .style('stroke','none'); + fills.exit().remove(); + fills.each(function(d,i) { + var z = [ + (i===0) ? zrange[0] : + (filllevels[i]+filllevels[i-1])/2, + (i===filllevels.length-1) ? zrange[1] : + (filllevels[i]+filllevels[i+1])/2 + ] + .map(cbAxisOut.c2p) + .map(Math.round); + + // offset the side adjoining the next rectangle so they + // overlap, to prevent antialiasing gaps + if(i!==filllevels.length-1) { + z[1] += (z[1]>z[0]) ? 1 : -1; + } + d3.select(this).attr({ + x: xLeft, + width: Math.max(thickPx,2), + y: d3.min(z), + height: Math.max(d3.max(z)-d3.min(z),2) + }) + .style('fill',fillcolormap(d)); + }); + + var lines = container.select('.cblines') + .selectAll('path.cbline') + .data(opts.line.color && opts.line.width ? + linelevels : []); + lines.enter().append('path') + .classed('cbline',true); + lines.exit().remove(); + lines.each(function(d) { + d3.select(this) + .attr('d','M'+xLeft+',' + + (Math.round(cbAxisOut.c2p(d))+(opts.line.width/2)%1) + + 'h'+thickPx) + .call(Plotly.Drawing.lineGroupStyle, + opts.line.width, linecolormap(d), opts.line.dash); + }); + + // force full redraw of labels and ticks + cbAxisOut._axislayer.selectAll('g.'+cbAxisOut._id+'tick,path') + .remove(); + + cbAxisOut._pos = xLeft+thickPx + + (opts.outlinewidth||0)/2 - (opts.ticks==='outside' ? 1 : 0); + cbAxisOut.side = 'right'; + + return Plotly.Axes.doTicks(gd, cbAxisOut); + } + + function positionCB(){ + // wait for the axis & title to finish rendering before + // continuing positioning + // TODO: why are we redrawing multiple times now with this? + // I guess autoMargin doesn't like being post-promise? + var innerWidth = thickPx + opts.outlinewidth/2 + + Plotly.Drawing.bBox(cbAxisOut._axislayer.node()).width; + titleEl = titleCont.select('text'); + if(titleEl.node() && !titleEl.classed('js-placeholder')) { + var mathJaxNode = titleCont + .select('.h'+cbAxisOut._id+'title-math-group') + .node(), + titleWidth; + if(mathJaxNode && + ['top','bottom'].indexOf(opts.titleside)!==-1) { + titleWidth = Plotly.Drawing.bBox(mathJaxNode).width; + } + else { + // note: the formula below works for all titlesides, + // (except for top/bottom mathjax, above) + // but the weird fullLayout._size.l is because the titleunshift + // transform gets removed by Drawing.bBox + titleWidth = + Plotly.Drawing.bBox(titleCont.node()).right - + xLeft - fullLayout._size.l; + } + innerWidth = Math.max(innerWidth,titleWidth); + } + + var outerwidth = 2*opts.xpad + innerWidth + + opts.borderwidth + opts.outlinewidth/2, + outerheight = yBottomPx-yTopPx; + + container.select('.cbbg').attr({ + x: xLeft-opts.xpad - + (opts.borderwidth + opts.outlinewidth)/2, + y: yTopPx - yExtraPx, + width: Math.max(outerwidth,2), + height: Math.max(outerheight + 2*yExtraPx,2) + }) + .call(Plotly.Color.fill, opts.bgcolor) + .call(Plotly.Color.stroke, opts.bordercolor) + .style({'stroke-width': opts.borderwidth}); + + container.selectAll('.cboutline').attr({ + x: xLeft, + y: yTopPx + opts.ypad + + (opts.titleside==='top' ? titleHeight : 0), + width: Math.max(thickPx,2), + height: Math.max(outerheight - 2*opts.ypad - titleHeight, 2) + }) + .call(Plotly.Color.stroke, opts.outlinecolor) + .style({ + fill: 'None', + 'stroke-width': opts.outlinewidth + }); + + // fix positioning for xanchor!='left' + var xoffset = ({center:0.5, right:1}[opts.xanchor]||0) * + outerwidth; + container.attr('transform', + 'translate('+(fullLayout._size.l-xoffset)+','+fullLayout._size.t+')'); + + //auto margin adjustment + Plotly.Plots.autoMargin(gd, id,{ + x: opts.x, + y: opts.y, + l: outerwidth*({right:1, center:0.5}[opts.xanchor]||0), + r: outerwidth*({left:1, center:0.5}[opts.xanchor]||0), + t: outerheight*({bottom:1, middle:0.5}[opts.yanchor]||0), + b: outerheight*({top:1, middle:0.5}[opts.yanchor]||0) + }); + } + + var cbDone = Plotly.Lib.syncOrAsync([ + Plotly.Plots.previousPromises, + drawAxis, + Plotly.Plots.previousPromises, + positionCB + ], gd); + + if(cbDone && cbDone.then) (gd._promises || []).push(cbDone); + + // dragging... + if(gd._context.editable) { + var t0, + xf, + yf; + + Plotly.Fx.dragElement({ + element: container.node(), + prepFn: function() { + t0 = container.attr('transform'); + Plotly.Fx.setCursor(container); + }, + moveFn: function(dx, dy) { + var gs = gd._fullLayout._size; + + container.attr('transform', + t0+' ' + 'translate('+dx+','+dy+')'); + + xf = Plotly.Fx.dragAlign(xLeftFrac + (dx/gs.w), thickFrac, + 0, 1, opts.xanchor); + yf = Plotly.Fx.dragAlign(yBottomFrac - (dy/gs.h), lenFrac, + 0, 1, opts.yanchor); + + var csr = Plotly.Fx.dragCursors(xf, yf, + opts.xanchor, opts.yanchor); + Plotly.Fx.setCursor(container, csr); + }, + doneFn: function(dragged) { + Plotly.Fx.setCursor(container); + + if(dragged && xf!==undefined && yf!==undefined) { + var idNum = id.substr(2), + traceNum; + gd._fullData.some(function(trace) { + if(trace.uid===idNum) { + traceNum = trace.index; + return true; + } + }); + + Plotly.restyle(gd, + {'colorbar.x': xf, 'colorbar.y': yf}, + traceNum); + } + } + }); + } + return cbDone; + } + + // setter/getters for every item defined in opts + Object.keys(opts).forEach(function (name) { + component[name] = function(v) { + // getter + if(!arguments.length) return opts[name]; + + // setter - for multi-part properties, + // set only the parts that are provided + opts[name] = Plotly.Lib.isPlainObject(opts[name]) ? + Plotly.Lib.extendFlat(opts[name], v) : + v; + + return component; + }; + }); + + // or use .options to set multiple options at once via a dictionary + component.options = function(o) { + Object.keys(o).forEach(function(name) { + // in case something random comes through + // that's not an option, ignore it + if(typeof component[name]==='function') { + component[name](o[name]); + } + }); + return component; + }; + + component._opts = opts; + + return component; +}; diff --git a/src/components/colorbar/index.js b/src/components/colorbar/index.js index 63b327768af..0fe5485a08b 100644 --- a/src/components/colorbar/index.js +++ b/src/components/colorbar/index.js @@ -9,569 +9,11 @@ 'use strict'; -var Plotly = require('../../plotly'); -var d3 = require('d3'); -var isNumeric = require('fast-isnumeric'); +exports.attributes = require('./attributes'); -var colorbar = module.exports = function(td, id) { - // opts: options object, containing everything from attributes - // plus a few others that are the equivalent of the colorbar "data" - var opts = {}; - Object.keys(colorbar.attributes).forEach(function(k) { - opts[k] = null; - }); - // fillcolor can be a d3 scale, domain is z values, range is colors - // or leave it out for no fill, - // or set to a string constant for single-color fill - opts.fillcolor = null; - // line.color has the same options as fillcolor - opts.line = {color: null, width: null, dash: null}; - // levels of lines to draw. - // note that this DOES NOT determine the extent of the bar - // that's given by the domain of fillcolor - // (or line.color if no fillcolor domain) - opts.levels = {start: null, end: null, size: null}; - // separate fill levels (for example, heatmap coloring of a - // contour map) if this is omitted, fillcolors will be - // evaluated halfway between levels - opts.filllevels = null; +exports.supplyDefaults = require('./defaults'); - function component(){ - var fullLayout = td._fullLayout; - if((typeof opts.fillcolor !== 'function') && - (typeof opts.line.color !== 'function')) { - fullLayout._infolayer.selectAll('g.'+id).remove(); - return; - } - var zrange = d3.extent(((typeof opts.fillcolor === 'function') ? - opts.fillcolor : opts.line.color).domain()), - linelevels = [], - filllevels = [], - l, - linecolormap = typeof opts.line.color === 'function' ? - opts.line.color : function(){ return opts.line.color; }, - fillcolormap = typeof opts.fillcolor === 'function' ? - opts.fillcolor : function(){ return opts.fillcolor; }; +exports.draw = require('./draw'); - var l0 = opts.levels.end + opts.levels.size/100, - ls = opts.levels.size, - zr0 = (1.001 * zrange[0] - 0.001 * zrange[1]), - zr1 = (1.001 * zrange[1] - 0.001 * zrange[0]); - for(l = opts.levels.start; (l - l0) * ls < 0; l += ls) { - if(l > zr0 && l < zr1) linelevels.push(l); - } - - if(typeof opts.fillcolor === 'function') { - if(opts.filllevels) { - l0 = opts.filllevels.end + opts.filllevels.size / 100; - ls = opts.filllevels.size; - for(l = opts.filllevels.start; (l - l0) * ls < 0; l += ls) { - if(l > zrange[0] && l < zrange[1]) filllevels.push(l); - } - } - else { - filllevels = linelevels.map(function(v){ - return v-opts.levels.size / 2; - }); - filllevels.push(filllevels[filllevels.length - 1] + - opts.levels.size); - } - } - else if(opts.fillcolor && typeof opts.fillcolor==='string') { - // doesn't matter what this value is, with a single value - // we'll make a single fill rect covering the whole bar - filllevels = [0]; - } - - if(opts.levels.size<0) { - linelevels.reverse(); - filllevels.reverse(); - } - - // now make a Plotly Axes object to scale with and draw ticks - // TODO: does not support orientation other than right - - // we calculate pixel sizes based on the specified graph size, - // not the actual (in case something pushed the margins around) - // which is a little odd but avoids an odd iterative effect - // when the colorbar itself is pushing the margins. - // but then the fractional size is calculated based on the - // actual graph size, so that the axes will size correctly. - var originalPlotHeight = fullLayout.height - fullLayout.margin.t - fullLayout.margin.b, - originalPlotWidth = fullLayout.width - fullLayout.margin.l - fullLayout.margin.r, - thickPx = Math.round(opts.thickness * - (opts.thicknessmode==='fraction' ? originalPlotWidth : 1)), - thickFrac = thickPx / fullLayout._size.w, - lenPx = Math.round(opts.len * - (opts.lenmode==='fraction' ? originalPlotHeight : 1)), - lenFrac = lenPx / fullLayout._size.h, - xpadFrac = opts.xpad/fullLayout._size.w, - yExtraPx = (opts.borderwidth + opts.outlinewidth)/2, - ypadFrac = opts.ypad / fullLayout._size.h, - - // x positioning: do it initially just for left anchor, - // then fix at the end (since we don't know the width yet) - xLeft = Math.round(opts.x*fullLayout._size.w + opts.xpad), - // for dragging... this is getting a little muddled... - xLeftFrac = opts.x - thickFrac * - ({middle: 0.5, right: 1}[opts.xanchor]||0), - - // y positioning we can do correctly from the start - yBottomFrac = opts.y + lenFrac * - (({top:-0.5, bottom:0.5}[opts.yanchor]||0)-0.5), - yBottomPx = Math.round(fullLayout._size.h * (1-yBottomFrac)), - yTopPx = yBottomPx-lenPx, - titleEl, - cbAxisIn = { - type: 'linear', - range: zrange, - tickmode: opts.tickmode, - nticks: opts.nticks, - tick0: opts.tick0, - dtick: opts.dtick, - tickvals: opts.tickvals, - ticktext: opts.ticktext, - ticks: opts.ticks, - ticklen: opts.ticklen, - tickwidth: opts.tickwidth, - tickcolor: opts.tickcolor, - showticklabels: opts.showticklabels, - tickfont: opts.tickfont, - tickangle: opts.tickangle, - tickformat: opts.tickformat, - exponentformat: opts.exponentformat, - showexponent: opts.showexponent, - showtickprefix: opts.showtickprefix, - tickprefix: opts.tickprefix, - showticksuffix: opts.showticksuffix, - ticksuffix: opts.ticksuffix, - title: opts.title, - titlefont: opts.titlefont, - anchor: 'free', - position: 1 - }, - cbAxisOut = {}, - axisOptions = { - letter: 'y', - font: fullLayout.font, - noHover: true - }; - - // Coerce w.r.t. Axes layoutAttributes: - // re-use axes.js logic without updating _fullData - function coerce(attr, dflt) { - return Plotly.Lib.coerce(cbAxisIn, cbAxisOut, - Plotly.Axes.layoutAttributes, - attr, dflt); - } - - // Prepare the Plotly axis object - Plotly.Axes.handleAxisDefaults(cbAxisIn, cbAxisOut, - coerce, axisOptions); - Plotly.Axes.handleAxisPositioningDefaults(cbAxisIn, cbAxisOut, - coerce, axisOptions); - - cbAxisOut._id = 'y' + id; - cbAxisOut._td = td; - - // position can't go in through supplyDefaults - // because that restricts it to [0,1] - cbAxisOut.position = opts.x+xpadFrac+thickFrac; - - // save for other callers to access this axis - component.axis = cbAxisOut; - - if(['top','bottom'].indexOf(opts.titleside)!==-1) { - cbAxisOut.titleside = opts.titleside; - cbAxisOut.titlex = opts.x + xpadFrac; - cbAxisOut.titley = yBottomFrac + - (opts.titleside==='top' ? lenFrac-ypadFrac : ypadFrac); - } - - if(opts.line.color && opts.tickmode === 'auto') { - cbAxisOut.tickmode = 'linear'; - cbAxisOut.tick0 = opts.levels.start; - var dtick = opts.levels.size; - // expand if too many contours, so we don't get too many ticks - var autoNtick = Plotly.Lib.constrain( - (yBottomPx-yTopPx)/50, 4, 15) + 1, - dtFactor = (zrange[1]-zrange[0]) / - ((opts.nticks||autoNtick)*dtick); - if(dtFactor>1) { - var dtexp = Math.pow(10,Math.floor( - Math.log(dtFactor)/Math.LN10)); - dtick *= dtexp*Plotly.Lib.roundUp(dtFactor/dtexp,[2,5,10]); - // if the contours are at round multiples, reset tick0 - // so they're still at round multiples. Otherwise, - // keep the first label on the first contour level - if((Math.abs(opts.levels.start)/ - opts.levels.size+1e-6)%1 < 2e-6) { - cbAxisOut.tick0 = 0; - } - } - cbAxisOut.dtick = dtick; - } - - // set domain after init, because we may want to - // allow it outside [0,1] - cbAxisOut.domain = [ - yBottomFrac+ypadFrac, - yBottomFrac+lenFrac-ypadFrac - ]; - cbAxisOut.setScale(); - - // now draw the elements - var container = fullLayout._infolayer.selectAll('g.'+id).data([0]); - container.enter().append('g').classed(id,true) - .each(function(){ - var s = d3.select(this); - s.append('rect').classed('cbbg',true); - s.append('g').classed('cbfills',true); - s.append('g').classed('cblines',true); - s.append('g').classed('cbaxis',true).classed('crisp',true); - s.append('g').classed('cbtitleunshift',true) - .append('g').classed('cbtitle',true); - s.append('rect').classed('cboutline',true); - }); - container.attr('transform','translate('+Math.round(fullLayout._size.l)+ - ','+Math.round(fullLayout._size.t)+')'); - // TODO: this opposite transform is a hack until we make it - // more rational which items get this offset - var titleCont = container.select('.cbtitleunshift') - .attr('transform', 'translate(-'+ - Math.round(fullLayout._size.l) + ',-' + - Math.round(fullLayout._size.t) + ')'); - - cbAxisOut._axislayer = container.select('.cbaxis'); - var titleHeight = 0; - if(['top','bottom'].indexOf(opts.titleside)!==-1) { - // draw the title so we know how much room it needs - // when we squish the axis - Plotly.Titles.draw(td, cbAxisOut._id + 'title'); - } - - function drawAxis(){ - if(['top','bottom'].indexOf(opts.titleside)!==-1) { - // squish the axis top to make room for the title - var titleGroup = container.select('.cbtitle'), - titleText = titleGroup.select('text'), - titleTrans = - [-opts.outlinewidth/2, opts.outlinewidth/2], - mathJaxNode = titleGroup - .select('.h'+cbAxisOut._id+'title-math-group') - .node(), - lineSize = 15.6; - if(titleText.node()) { - lineSize = - parseInt(titleText.style('font-size'), 10) * 1.3; - } - if(mathJaxNode) { - titleHeight = Plotly.Drawing.bBox(mathJaxNode).height; - if(titleHeight>lineSize) { - // not entirely sure how mathjax is doing - // vertical alignment, but this seems to work. - titleTrans[1] -= (titleHeight-lineSize)/2; - } - } - else if(titleText.node() && - !titleText.classed('js-placeholder')) { - titleHeight = Plotly.Drawing.bBox( - titleGroup.node()).height; - } - if(titleHeight) { - // buffer btwn colorbar and title - // TODO: configurable - titleHeight += 5; - - if(opts.titleside==='top') { - cbAxisOut.domain[1] -= titleHeight/fullLayout._size.h; - titleTrans[1] *= -1; - } - else { - cbAxisOut.domain[0] += titleHeight/fullLayout._size.h; - var nlines = Math.max(1, - titleText.selectAll('tspan.line').size()); - titleTrans[1] += (1-nlines)*lineSize; - } - - titleGroup.attr('transform', - 'translate('+titleTrans+')'); - - cbAxisOut.setScale(); - } - } - - container.selectAll('.cbfills,.cblines,.cbaxis') - .attr('transform','translate(0,'+ - Math.round(fullLayout._size.h*(1-cbAxisOut.domain[1]))+')'); - - var fills = container.select('.cbfills') - .selectAll('rect.cbfill') - .data(filllevels); - fills.enter().append('rect') - .classed('cbfill',true) - .style('stroke','none'); - fills.exit().remove(); - fills.each(function(d,i) { - var z = [ - (i===0) ? zrange[0] : - (filllevels[i]+filllevels[i-1])/2, - (i===filllevels.length-1) ? zrange[1] : - (filllevels[i]+filllevels[i+1])/2 - ] - .map(cbAxisOut.c2p) - .map(Math.round); - - // offset the side adjoining the next rectangle so they - // overlap, to prevent antialiasing gaps - if(i!==filllevels.length-1) { - z[1] += (z[1]>z[0]) ? 1 : -1; - } - d3.select(this).attr({ - x: xLeft, - width: Math.max(thickPx,2), - y: d3.min(z), - height: Math.max(d3.max(z)-d3.min(z),2) - }) - .style('fill',fillcolormap(d)); - }); - - var lines = container.select('.cblines') - .selectAll('path.cbline') - .data(opts.line.color && opts.line.width ? - linelevels : []); - lines.enter().append('path') - .classed('cbline',true); - lines.exit().remove(); - lines.each(function(d) { - d3.select(this) - .attr('d','M'+xLeft+',' + - (Math.round(cbAxisOut.c2p(d))+(opts.line.width/2)%1) + - 'h'+thickPx) - .call(Plotly.Drawing.lineGroupStyle, - opts.line.width, linecolormap(d), opts.line.dash); - }); - - // force full redraw of labels and ticks - cbAxisOut._axislayer.selectAll('g.'+cbAxisOut._id+'tick,path') - .remove(); - - cbAxisOut._pos = xLeft+thickPx + - (opts.outlinewidth||0)/2 - (opts.ticks==='outside' ? 1 : 0); - cbAxisOut.side = 'right'; - - return Plotly.Axes.doTicks(td, cbAxisOut); - } - - function positionCB(){ - // wait for the axis & title to finish rendering before - // continuing positioning - // TODO: why are we redrawing multiple times now with this? - // I guess autoMargin doesn't like being post-promise? - var innerWidth = thickPx + opts.outlinewidth/2 + - Plotly.Drawing.bBox(cbAxisOut._axislayer.node()).width; - titleEl = titleCont.select('text'); - if(titleEl.node() && !titleEl.classed('js-placeholder')) { - var mathJaxNode = titleCont - .select('.h'+cbAxisOut._id+'title-math-group') - .node(), - titleWidth; - if(mathJaxNode && - ['top','bottom'].indexOf(opts.titleside)!==-1) { - titleWidth = Plotly.Drawing.bBox(mathJaxNode).width; - } - else { - // note: the formula below works for all titlesides, - // (except for top/bottom mathjax, above) - // but the weird fullLayout._size.l is because the titleunshift - // transform gets removed by Drawing.bBox - titleWidth = - Plotly.Drawing.bBox(titleCont.node()).right - - xLeft - fullLayout._size.l; - } - innerWidth = Math.max(innerWidth,titleWidth); - } - - var outerwidth = 2*opts.xpad + innerWidth + - opts.borderwidth + opts.outlinewidth/2, - outerheight = yBottomPx-yTopPx; - - container.select('.cbbg').attr({ - x: xLeft-opts.xpad - - (opts.borderwidth + opts.outlinewidth)/2, - y: yTopPx - yExtraPx, - width: Math.max(outerwidth,2), - height: Math.max(outerheight + 2*yExtraPx,2) - }) - .call(Plotly.Color.fill, opts.bgcolor) - .call(Plotly.Color.stroke, opts.bordercolor) - .style({'stroke-width': opts.borderwidth}); - - container.selectAll('.cboutline').attr({ - x: xLeft, - y: yTopPx + opts.ypad + - (opts.titleside==='top' ? titleHeight : 0), - width: Math.max(thickPx,2), - height: Math.max(outerheight - 2*opts.ypad - titleHeight, 2) - }) - .call(Plotly.Color.stroke, opts.outlinecolor) - .style({ - fill: 'None', - 'stroke-width': opts.outlinewidth - }); - - // fix positioning for xanchor!='left' - var xoffset = ({center:0.5, right:1}[opts.xanchor]||0) * - outerwidth; - container.attr('transform', - 'translate('+(fullLayout._size.l-xoffset)+','+fullLayout._size.t+')'); - - //auto margin adjustment - Plotly.Plots.autoMargin(td, id,{ - x: opts.x, - y: opts.y, - l: outerwidth*({right:1, center:0.5}[opts.xanchor]||0), - r: outerwidth*({left:1, center:0.5}[opts.xanchor]||0), - t: outerheight*({bottom:1, middle:0.5}[opts.yanchor]||0), - b: outerheight*({top:1, middle:0.5}[opts.yanchor]||0) - }); - } - - var cbDone = Plotly.Lib.syncOrAsync([ - Plotly.Plots.previousPromises, - drawAxis, - Plotly.Plots.previousPromises, - positionCB - ], td); - if(cbDone && cbDone.then) (td._promises || []).push(cbDone); - - // dragging... - if(td._context.editable) { - var t0, - xf, - yf; - - Plotly.Fx.dragElement({ - element: container.node(), - prepFn: function() { - t0 = container.attr('transform'); - Plotly.Fx.setCursor(container); - }, - moveFn: function(dx, dy) { - var gs = td._fullLayout._size; - - container.attr('transform', - t0+' ' + 'translate('+dx+','+dy+')'); - - xf = Plotly.Fx.dragAlign(xLeftFrac + (dx/gs.w), thickFrac, - 0, 1, opts.xanchor); - yf = Plotly.Fx.dragAlign(yBottomFrac - (dy/gs.h), lenFrac, - 0, 1, opts.yanchor); - - var csr = Plotly.Fx.dragCursors(xf, yf, - opts.xanchor, opts.yanchor); - Plotly.Fx.setCursor(container, csr); - }, - doneFn: function(dragged) { - Plotly.Fx.setCursor(container); - - if(dragged && xf!==undefined && yf!==undefined) { - var idNum = id.substr(2), - traceNum; - td._fullData.some(function(trace) { - if(trace.uid===idNum) { - traceNum = trace.index; - return true; - } - }); - - Plotly.restyle(td, - {'colorbar.x': xf, 'colorbar.y': yf}, - traceNum); - } - } - }); - } - return cbDone; - } - - // setter/getters for every item defined in opts - Object.keys(opts).forEach(function (name) { - component[name] = function(v) { - // getter - if(!arguments.length) return opts[name]; - - // setter - for multi-part properties, - // set only the parts that are provided - opts[name] = Plotly.Lib.isPlainObject(opts[name]) ? - Plotly.Lib.extendFlat(opts[name], v) : - v; - - return component; - }; - }); - - // or use .options to set multiple options at once via a dictionary - component.options = function(o) { - Object.keys(o).forEach(function(name) { - // in case something random comes through - // that's not an option, ignore it - if(typeof component[name]==='function') { - component[name](o[name]); - } - }); - return component; - }; - - component._opts = opts; - - return component; -}; - -colorbar.attributes = require('./attributes'); - -colorbar.supplyDefaults = function(containerIn, containerOut, layout) { - var colorbarOut = containerOut.colorbar = {}, - colorbarIn = containerIn.colorbar || {}; - - function coerce(attr, dflt) { - return Plotly.Lib.coerce(colorbarIn, colorbarOut, - colorbar.attributes, attr, dflt); - } - - var thicknessmode = coerce('thicknessmode'); - coerce('thickness', thicknessmode === 'fraction' ? - 30 / (layout.width - layout.margin.l - layout.margin.r) : - 30 - ); - - var lenmode = coerce('lenmode'); - coerce('len', lenmode === 'fraction' ? - 1 : - layout.height - layout.margin.t - layout.margin.b - ); - - coerce('x'); - coerce('xanchor'); - coerce('xpad'); - coerce('y'); - coerce('yanchor'); - coerce('ypad'); - Plotly.Lib.noneOrAll(colorbarIn, colorbarOut, ['x', 'y']); - - coerce('outlinecolor'); - coerce('outlinewidth'); - coerce('bordercolor'); - coerce('borderwidth'); - coerce('bgcolor'); - - Plotly.Axes.handleTickValueDefaults(colorbarIn, colorbarOut, coerce, 'linear'); - - Plotly.Axes.handleTickDefaults(colorbarIn, colorbarOut, coerce, 'linear', - {outerTicks: false, font: layout.font, noHover: true}); - - coerce('title'); - Plotly.Lib.coerceFont(coerce, 'titlefont', layout.font); - coerce('titleside'); -}; +exports.hasColorbar = require('./has_colorbar'); From 9e1732e8dd959b8d7fe63b4c8b5ab725b4bc28ed Mon Sep 17 00:00:00 2001 From: etpinard Date: Fri, 18 Dec 2015 15:31:46 -0500 Subject: [PATCH 6/6] require colorbar draw method directly --- src/traces/contour/colorbar.js | 8 ++++---- src/traces/heatmap/colorbar.js | 11 +++++++---- src/traces/scatter/colorbar.js | 3 ++- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/traces/contour/colorbar.js b/src/traces/contour/colorbar.js index b9797203961..bedc569f0d0 100644 --- a/src/traces/contour/colorbar.js +++ b/src/traces/contour/colorbar.js @@ -13,21 +13,21 @@ var d3 = require('d3'); var Plotly = require('../../plotly'); var getColorscale = require('../../components/colorscale/get_scale'); +var drawColorbar = require('../../components/colorbar/draw'); module.exports = function colorbar(gd, cd) { var trace = cd[0].trace, - cbId = 'cb'+trace.uid; + cbId = 'cb' + trace.uid; - gd._fullLayout._infolayer.selectAll('.'+cbId).remove(); + gd._fullLayout._infolayer.selectAll('.' + cbId).remove(); if(trace.showscale===false){ Plotly.Plots.autoMargin(gd, cbId); return; } - // instantiate the colorbar (will be drawn and styled in contour.style) - var cb = Plotly.Colorbar(gd, cbId); + var cb = drawColorbar(gd, cbId); cd[0].t.cb = cb; var contours = trace.contours, diff --git a/src/traces/heatmap/colorbar.js b/src/traces/heatmap/colorbar.js index 9606dad5e89..c2c9dca1734 100644 --- a/src/traces/heatmap/colorbar.js +++ b/src/traces/heatmap/colorbar.js @@ -15,6 +15,8 @@ var isNumeric = require('fast-isnumeric'); var Plotly = require('../../plotly'); var Lib = require('../../lib'); var getColorscale = require('../../components/colorscale/get_scale'); +var drawColorbar = require('../../components/colorbar/draw'); + module.exports = function colorbar(gd, cd) { var trace = cd[0].trace, @@ -23,16 +25,17 @@ module.exports = function colorbar(gd, cd) { zmin = trace.zmin, zmax = trace.zmax; - if(!isNumeric(zmin)) zmin = Plotly.Lib.aggNums(Math.min, null, trace.z); - if(!isNumeric(zmax)) zmax = Plotly.Lib.aggNums(Math.max, null, trace.z); + if(!isNumeric(zmin)) zmin = Lib.aggNums(Math.min, null, trace.z); + if(!isNumeric(zmax)) zmax = Lib.aggNums(Math.max, null, trace.z); + + gd._fullLayout._infolayer.selectAll('.' + cbId).remove(); - gd._fullLayout._infolayer.selectAll('.'+cbId).remove(); if(!trace.showscale){ Plotly.Plots.autoMargin(gd, cbId); return; } - var cb = cd[0].t.cb = Plotly.Colorbar(gd, cbId); + var cb = cd[0].t.cb = drawColorbar(gd, cbId); cb.fillcolor(d3.scale.linear() .domain(scl.map(function(v){ return zmin + v[0]*(zmax-zmin); })) .range(scl.map(function(v){ return v[1]; }))) diff --git a/src/traces/scatter/colorbar.js b/src/traces/scatter/colorbar.js index 41355cf8a4e..2993f33dbe0 100644 --- a/src/traces/scatter/colorbar.js +++ b/src/traces/scatter/colorbar.js @@ -15,6 +15,7 @@ var isNumeric = require('fast-isnumeric'); var Plotly = require('../../plotly'); var Lib = require('../../lib'); var getColorscale = require('../../components/colorscale/get_scale'); +var drawColorbar = require('../../components/colorbar/draw'); module.exports = function colorbar(gd, cd) { @@ -40,7 +41,7 @@ module.exports = function colorbar(gd, cd) { if(!isNumeric(cmin)) cmin = Plotly.Lib.aggNums(Math.min, null, vals); if(!isNumeric(cmax)) cmax = Plotly.Lib.aggNums(Math.max, null, vals); - var cb = cd[0].t.cb = Plotly.Colorbar(gd, cbId); + var cb = cd[0].t.cb = drawColorbar(gd, cbId); cb.fillcolor(d3.scale.linear() .domain(scl.map(function(v){ return cmin + v[0] * (cmax - cmin); }))