From 94b20988b2d2c88f1408d49b0ccc8bfba7aa8f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Fri, 15 Jan 2016 09:50:06 -0500 Subject: [PATCH 01/10] split up scatter defaults in separate files: - require individual steps in scatter3d, scattergeo, scattergl defaults. --- src/traces/scatter/defaults.js | 72 ++++++++++++ src/traces/scatter/fillcolor_defaults.js | 37 ++++++ src/traces/scatter/index.js | 137 +---------------------- src/traces/scatter/line_defaults.js | 22 ++++ src/traces/scatter/marker_defaults.js | 61 ++++++++++ src/traces/scatter/text_defaults.js | 19 ++++ src/traces/scatter3d/defaults.js | 26 +++-- src/traces/scattergeo/defaults.js | 20 ++-- src/traces/scattergl/defaults.js | 19 ++-- 9 files changed, 251 insertions(+), 162 deletions(-) create mode 100644 src/traces/scatter/defaults.js create mode 100644 src/traces/scatter/fillcolor_defaults.js create mode 100644 src/traces/scatter/line_defaults.js create mode 100644 src/traces/scatter/marker_defaults.js create mode 100644 src/traces/scatter/text_defaults.js diff --git a/src/traces/scatter/defaults.js b/src/traces/scatter/defaults.js new file mode 100644 index 00000000000..7b045a705cc --- /dev/null +++ b/src/traces/scatter/defaults.js @@ -0,0 +1,72 @@ +/** +* Copyright 2012-2016, 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 attributes = require('./attributes'); +var constants = require('./constants'); +var subTypes = require('./subtypes'); +var handleXYDefaults = require('./xy_defaults'); +var handleMarkerDefaults = require('./marker_defaults'); +var handleLineDefaults = require('./line_defaults'); +var handleTextDefaults = require('./text_defaults'); +var handleFillColorDefaults = require('./fillcolor_defaults'); +var errorBarsSupplyDefaults = require('../../components/errorbars/defaults'); + + +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { + function coerce(attr, dflt) { + return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); + } + + var len = handleXYDefaults(traceIn, traceOut, coerce), + // TODO: default mode by orphan points... + defaultMode = len < constants.PTS_LINESONLY ? 'lines+markers' : 'lines'; + if(!len) { + traceOut.visible = false; + return; + } + + coerce('text'); + coerce('mode', defaultMode); + + if(subTypes.hasLines(traceOut)) { + handleLineDefaults(traceIn, traceOut, defaultColor, coerce); + lineShapeDefaults(traceIn, traceOut, coerce); + coerce('connectgaps'); + } + + if(subTypes.hasMarkers(traceOut)) { + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce); + } + + if(subTypes.hasText(traceOut)) { + handleTextDefaults(traceIn, traceOut, layout, coerce); + } + + if(subTypes.hasMarkers(traceOut) || subTypes.hasText(traceOut)) { + coerce('marker.maxdisplayed'); + } + + coerce('fill'); + if(traceOut.fill !== 'none') { + handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce); + if(!subTypes.hasLines(traceOut)) lineShapeDefaults(traceIn, traceOut, coerce); + } + + errorBarsSupplyDefaults(traceIn, traceOut, defaultColor, {axis: 'y'}); + errorBarsSupplyDefaults(traceIn, traceOut, defaultColor, {axis: 'x', inherit: 'y'}); +}; + +function lineShapeDefaults(traceIn, traceOut, coerce) { + var shape = coerce('line.shape'); + if(shape==='spline') coerce('line.smoothing'); +} diff --git a/src/traces/scatter/fillcolor_defaults.js b/src/traces/scatter/fillcolor_defaults.js new file mode 100644 index 00000000000..9871c09cd3b --- /dev/null +++ b/src/traces/scatter/fillcolor_defaults.js @@ -0,0 +1,37 @@ +/** +* Copyright 2012-2016, 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 Color = require('../../components/color'); + + +// common to 'scatter' and 'scattergl' +module.exports = function fillColorDefaults(traceIn, traceOut, defaultColor, coerce) { + var inheritColorFromMarker = false; + + if(traceOut.marker) { + // don't try to inherit a color array + var markerColor = traceOut.marker.color, + markerLineColor = (traceOut.marker.line || {}).color; + + if(markerColor && !Array.isArray(markerColor)) { + inheritColorFromMarker = markerColor; + } + else if(markerLineColor && !Array.isArray(markerLineColor)) { + inheritColorFromMarker = markerLineColor; + } + } + + coerce('fillcolor', Color.addOpacity( + (traceOut.line || {}).color || + inheritColorFromMarker || + defaultColor, 0.5 + )); +}; diff --git a/src/traces/scatter/index.js b/src/traces/scatter/index.js index 1a9291617f9..bbe12b500fb 100644 --- a/src/traces/scatter/index.js +++ b/src/traces/scatter/index.js @@ -37,6 +37,7 @@ scatter.moduleType = 'trace'; scatter.name = 'scatter'; scatter.categories = ['cartesian', 'symbols', 'markerColorscale', 'errorBarsOK', 'showLegend']; scatter.meta = { +Scatter.supplyDefaults = require('./defaults'); description: [ 'The scatter trace type encompasses line charts, scatter charts, text charts, and bubble charts.', 'The data visualized as scatter point or lines is set in `x` and `y`.', @@ -52,142 +53,6 @@ scatter.PTS_LINESONLY = 20; scatter.attributes = require('./attributes'); -var handleXYDefaults = require('./xy_defaults'); - -scatter.supplyDefaults = function(traceIn, traceOut, defaultColor, layout) { - function coerce(attr, dflt) { - return Lib.coerce(traceIn, traceOut, scatter.attributes, attr, dflt); - } - - var len = handleXYDefaults(traceIn, traceOut, coerce), - // TODO: default mode by orphan points... - defaultMode = len < scatter.PTS_LINESONLY ? 'lines+markers' : 'lines'; - if(!len) { - traceOut.visible = false; - return; - } - - coerce('text'); - coerce('mode', defaultMode); - - if(scatter.hasLines(traceOut)) { - scatter.lineDefaults(traceIn, traceOut, defaultColor, coerce); - lineShapeDefaults(traceIn, traceOut, coerce); - coerce('connectgaps'); - } - - if(scatter.hasMarkers(traceOut)) { - scatter.markerDefaults(traceIn, traceOut, defaultColor, layout, coerce); - } - - if(scatter.hasText(traceOut)) { - scatter.textDefaults(traceIn, traceOut, layout, coerce); - } - - if(scatter.hasMarkers(traceOut) || scatter.hasText(traceOut)) { - coerce('marker.maxdisplayed'); - } - - coerce('fill'); - if(traceOut.fill !== 'none') { - scatter.fillColorDefaults(traceIn, traceOut, defaultColor, coerce); - if(!scatter.hasLines(traceOut)) lineShapeDefaults(traceIn, traceOut, coerce); - } - - ErrorBars.supplyDefaults(traceIn, traceOut, defaultColor, {axis: 'y'}); - ErrorBars.supplyDefaults(traceIn, traceOut, defaultColor, {axis: 'x', inherit: 'y'}); -}; - -// common to 'scatter', 'scatter3d', 'scattergeo' and 'scattergl' -scatter.lineDefaults = function(traceIn, traceOut, defaultColor, coerce) { - var markerColor = (traceIn.marker || {}).color; - - // don't try to inherit a color array - coerce('line.color', (Array.isArray(markerColor) ? false : markerColor) || - defaultColor); - coerce('line.width'); - coerce('line.dash'); -}; - -function lineShapeDefaults(traceIn, traceOut, coerce) { - var shape = coerce('line.shape'); - if(shape==='spline') coerce('line.smoothing'); -} - -// common to 'scatter', 'scatter3d', 'scattergeo' and 'scattergl' -scatter.markerDefaults = function(traceIn, traceOut, defaultColor, layout, coerce) { - var isBubble = scatter.isBubble(traceIn), - lineColor = (traceIn.line || {}).color, - defaultMLC; - - if(lineColor) defaultColor = lineColor; - - coerce('marker.symbol'); - coerce('marker.opacity', isBubble ? 0.7 : 1); - coerce('marker.size'); - - coerce('marker.color', defaultColor); - if(Colorscale.hasColorscale(traceIn, 'marker')) { - Colorscale.handleDefaults( - traceIn, traceOut, layout, coerce, {prefix: 'marker.', cLetter: 'c'} - ); - } - - // if there's a line with a different color than the marker, use - // that line color as the default marker line color - // mostly this is for transparent markers to behave nicely - if(lineColor && traceOut.marker.color!==lineColor) { - defaultMLC = lineColor; - } - else if(isBubble) defaultMLC = Color.background; - else defaultMLC = Color.defaultLine; - - coerce('marker.line.color', defaultMLC); - if(Colorscale.hasColorscale(traceIn, 'marker.line')) { - Colorscale.handleDefaults( - traceIn, traceOut, layout, coerce, {prefix: 'marker.line.', cLetter: 'c'} - ); - } - - coerce('marker.line.width', isBubble ? 1 : 0); - - if(isBubble) { - coerce('marker.sizeref'); - coerce('marker.sizemin'); - coerce('marker.sizemode'); - } -}; - -// common to 'scatter', 'scatter3d' and 'scattergeo' -scatter.textDefaults = function(traceIn, traceOut, layout, coerce) { - coerce('textposition'); - Lib.coerceFont(coerce, 'textfont', layout.font); -}; - -// common to 'scatter' and 'scattergl' -scatter.fillColorDefaults = function(traceIn, traceOut, defaultColor, coerce) { - var inheritColorFromMarker = false; - - if(traceOut.marker) { - // don't try to inherit a color array - var markerColor = traceOut.marker.color, - markerLineColor = (traceOut.marker.line || {}).color; - - if(markerColor && !Array.isArray(markerColor)) { - inheritColorFromMarker = markerColor; - } - else if(markerLineColor && !Array.isArray(markerLineColor)) { - inheritColorFromMarker = markerLineColor; - } - } - - coerce('fillcolor', Color.addOpacity( - (traceOut.line || {}).color || - inheritColorFromMarker || - defaultColor, 0.5 - )); -}; - scatter.cleanData = function(fullData) { var i, tracei, diff --git a/src/traces/scatter/line_defaults.js b/src/traces/scatter/line_defaults.js new file mode 100644 index 00000000000..64f4104bd31 --- /dev/null +++ b/src/traces/scatter/line_defaults.js @@ -0,0 +1,22 @@ +/** +* Copyright 2012-2016, 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'; + + +// common to 'scatter', 'scatter3d', 'scattergeo' and 'scattergl' +module.exports = function lineDefaults(traceIn, traceOut, defaultColor, coerce) { + var markerColor = (traceIn.marker || {}).color; + + // don't try to inherit a color array + coerce('line.color', (Array.isArray(markerColor) ? false : markerColor) || + defaultColor); + coerce('line.width'); + coerce('line.dash'); +}; diff --git a/src/traces/scatter/marker_defaults.js b/src/traces/scatter/marker_defaults.js new file mode 100644 index 00000000000..ed6382fd94c --- /dev/null +++ b/src/traces/scatter/marker_defaults.js @@ -0,0 +1,61 @@ +/** +* Copyright 2012-2016, 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 Color = require('../../components/color'); +var hasColorscale = require('../../components/colorscale/has_colorscale'); +var colorscaleDefaults = require('../../components/colorscale/defaults'); + +var subTypes = require('./subtypes'); + + +// common to 'scatter', 'scatter3d', 'scattergeo' and 'scattergl' +module.exports = function markerDefaults(traceIn, traceOut, defaultColor, layout, coerce) { + var isBubble = subTypes.isBubble(traceIn), + lineColor = (traceIn.line || {}).color, + defaultMLC; + + if(lineColor) defaultColor = lineColor; + + coerce('marker.symbol'); + coerce('marker.opacity', isBubble ? 0.7 : 1); + coerce('marker.size'); + + coerce('marker.color', defaultColor); + if(hasColorscale(traceIn, 'marker')) { + colorscaleDefaults( + traceIn, traceOut, layout, coerce, {prefix: 'marker.', cLetter: 'c'} + ); + } + + // if there's a line with a different color than the marker, use + // that line color as the default marker line color + // mostly this is for transparent markers to behave nicely + if(lineColor && traceOut.marker.color!==lineColor) { + defaultMLC = lineColor; + } + else if(isBubble) defaultMLC = Color.background; + else defaultMLC = Color.defaultLine; + + coerce('marker.line.color', defaultMLC); + if(hasColorscale(traceIn, 'marker.line')) { + colorscaleDefaults( + traceIn, traceOut, layout, coerce, {prefix: 'marker.line.', cLetter: 'c'} + ); + } + + coerce('marker.line.width', isBubble ? 1 : 0); + + if(isBubble) { + coerce('marker.sizeref'); + coerce('marker.sizemin'); + coerce('marker.sizemode'); + } +}; diff --git a/src/traces/scatter/text_defaults.js b/src/traces/scatter/text_defaults.js new file mode 100644 index 00000000000..51225c9a8f5 --- /dev/null +++ b/src/traces/scatter/text_defaults.js @@ -0,0 +1,19 @@ +/** +* Copyright 2012-2016, 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'); + + +// common to 'scatter', 'scatter3d' and 'scattergeo' +module.exports = function(traceIn, traceOut, layout, coerce) { + coerce('textposition'); + Lib.coerceFont(coerce, 'textfont', layout.font); +}; diff --git a/src/traces/scatter3d/defaults.js b/src/traces/scatter3d/defaults.js index 23e3df9b166..43704afdd2c 100644 --- a/src/traces/scatter3d/defaults.js +++ b/src/traces/scatter3d/defaults.js @@ -9,9 +9,13 @@ 'use strict'; -var Scatter = require('../../traces/scatter'); var Lib = require('../../lib'); -var ErrorBars = require('../../components/errorbars'); + +var subTypes = require('../scatter/subtypes'); +var handleMarkerDefaults = require('../scatter/marker_defaults'); +var handleLineDefaults = require('../scatter/line_defaults'); +var handleTextDefaults = require('../scatter/text_defaults'); +var errorBarsSupplyDefaults = require('../../components/errorbars/defaults'); var attributes = require('./attributes'); @@ -31,16 +35,16 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('text'); coerce('mode'); - if(Scatter.hasLines(traceOut)) { - Scatter.lineDefaults(traceIn, traceOut, defaultColor, coerce); + if(subTypes.hasLines(traceOut)) { + handleLineDefaults(traceIn, traceOut, defaultColor, coerce); } - if(Scatter.hasMarkers(traceOut)) { - Scatter.markerDefaults(traceIn, traceOut, defaultColor, layout, coerce); + if(subTypes.hasMarkers(traceOut)) { + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce); } - if(Scatter.hasText(traceOut)) { - Scatter.textDefaults(traceIn, traceOut, layout, coerce); + if(subTypes.hasText(traceOut)) { + handleTextDefaults(traceIn, traceOut, layout, coerce); } var lineColor = (traceOut.line || {}).color , @@ -56,9 +60,9 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } } - ErrorBars.supplyDefaults(traceIn, traceOut, defaultColor, {axis: 'z'}); - ErrorBars.supplyDefaults(traceIn, traceOut, defaultColor, {axis: 'y', inherit: 'z'}); - ErrorBars.supplyDefaults(traceIn, traceOut, defaultColor, {axis: 'x', inherit: 'z'}); + errorBarsSupplyDefaults(traceIn, traceOut, defaultColor, {axis: 'z'}); + errorBarsSupplyDefaults(traceIn, traceOut, defaultColor, {axis: 'y', inherit: 'z'}); + errorBarsSupplyDefaults(traceIn, traceOut, defaultColor, {axis: 'x', inherit: 'z'}); }; function handleXYZDefaults(traceIn, traceOut, coerce) { diff --git a/src/traces/scattergeo/defaults.js b/src/traces/scattergeo/defaults.js index 073eee96503..beb6ded2c72 100644 --- a/src/traces/scattergeo/defaults.js +++ b/src/traces/scattergeo/defaults.js @@ -10,12 +10,16 @@ 'use strict'; var Lib = require('../../lib'); -var Scatter = require('../scatter'); + +var subTypes = require('../scatter/subtypes'); +var handleMarkerDefaults = require('../scatter/marker_defaults'); +var handleLineDefaults = require('../scatter/line_defaults'); +var handleTextDefaults = require('../scatter/text_defaults'); + var attributes = require('./attributes'); module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { - function coerce(attr, dflt) { return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); } @@ -29,16 +33,16 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('text'); coerce('mode'); - if(Scatter.hasLines(traceOut)) { - Scatter.lineDefaults(traceIn, traceOut, defaultColor, coerce); + if(subTypes.hasLines(traceOut)) { + handleLineDefaults(traceIn, traceOut, defaultColor, coerce); } - if(Scatter.hasMarkers(traceOut)) { - Scatter.markerDefaults(traceIn, traceOut, defaultColor, layout, coerce); + if(subTypes.hasMarkers(traceOut)) { + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce); } - if(Scatter.hasText(traceOut)) { - Scatter.textDefaults(traceIn, traceOut, layout, coerce); + if(subTypes.hasText(traceOut)) { + handleTextDefaults(traceIn, traceOut, layout, coerce); } coerce('hoverinfo', (layout._dataLength === 1) ? 'lon+lat+location+text' : undefined); diff --git a/src/traces/scattergl/defaults.js b/src/traces/scattergl/defaults.js index 2d7261b2a8b..8f2651b1ba6 100644 --- a/src/traces/scattergl/defaults.js +++ b/src/traces/scattergl/defaults.js @@ -10,10 +10,15 @@ 'use strict'; var Lib = require('../../lib'); -var Scatter = require('../scatter'); +var constants = require('../scatter/constants'); +var subTypes = require('../scatter/subtypes'); var handleXYDefaults = require('../scatter/xy_defaults'); +var handleMarkerDefaults = require('../scatter/marker_defaults'); +var handleLineDefaults = require('../scatter/line_defaults'); +var handleFillColorDefaults = require('../scatter/fillcolor_defaults'); var errorBarsSupplyDefaults = require('../../components/errorbars/defaults'); + var attributes = require('./attributes'); @@ -29,19 +34,19 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } coerce('text'); - coerce('mode', len < Scatter.PTS_LINESONLY ? 'lines+markers' : 'lines'); + coerce('mode', len < constants.PTS_LINESONLY ? 'lines+markers' : 'lines'); - if(Scatter.hasLines(traceOut)) { - Scatter.lineDefaults(traceIn, traceOut, defaultColor, coerce); + if(subTypes.hasLines(traceOut)) { + handleLineDefaults(traceIn, traceOut, defaultColor, coerce); } - if(Scatter.hasMarkers(traceOut)) { - Scatter.markerDefaults(traceIn, traceOut, defaultColor, layout, coerce); + if(subTypes.hasMarkers(traceOut)) { + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce); } coerce('fill'); if(traceOut.fill !== 'none') { - Scatter.fillColorDefaults(traceIn, traceOut, defaultColor, coerce); + handleFillColorDefaults(traceIn, traceOut, defaultColor, coerce); } errorBarsSupplyDefaults(traceIn, traceOut, defaultColor, {axis: 'y'}); From 60027887e7e98b2aad80c86099d563201f8206c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Fri, 15 Jan 2016 09:50:55 -0500 Subject: [PATCH 02/10] move scatter PTS_LINESONLY in constant into separate file: - use it in attributes and default step --- src/traces/scatter/attributes.js | 5 +++-- src/traces/scatter/constants.js | 14 ++++++++++++++ src/traces/scatter/index.js | 4 ---- 3 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 src/traces/scatter/constants.js diff --git a/src/traces/scatter/attributes.js b/src/traces/scatter/attributes.js index 75f8c8d7c95..aae4c38983a 100644 --- a/src/traces/scatter/attributes.js +++ b/src/traces/scatter/attributes.js @@ -10,7 +10,8 @@ var Drawing = require('../../components/drawing'); -var PTS_LINESONLY = 20; // TODO put in constants/ +var constants = require('./constants'); + module.exports = { x: { @@ -84,7 +85,7 @@ module.exports = { 'If the provided `mode` includes *text* then the `text` elements', 'appear at the coordinates. Otherwise, the `text` elements', 'appear on hover.', - 'If there are less than ' + PTS_LINESONLY + ' points,', + 'If there are less than ' + constants.PTS_LINESONLY + ' points,', 'then the default is *lines+markers*. Otherwise, *lines*.' ].join(' ') }, diff --git a/src/traces/scatter/constants.js b/src/traces/scatter/constants.js new file mode 100644 index 00000000000..8336b9d7162 --- /dev/null +++ b/src/traces/scatter/constants.js @@ -0,0 +1,14 @@ +/** +* Copyright 2012-2016, 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 = { + PTS_LINESONLY: 20 +}; diff --git a/src/traces/scatter/index.js b/src/traces/scatter/index.js index bbe12b500fb..f8facbae94e 100644 --- a/src/traces/scatter/index.js +++ b/src/traces/scatter/index.js @@ -47,10 +47,6 @@ Scatter.supplyDefaults = require('./defaults'); ].join(' ') }; -// traces with < this many points are by default shown -// with points and lines, > just get lines -scatter.PTS_LINESONLY = 20; - scatter.attributes = require('./attributes'); scatter.cleanData = function(fullData) { From b418b5fe6767ad763fa0bd754a8d834d88606319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Fri, 15 Jan 2016 09:53:56 -0500 Subject: [PATCH 03/10] rename getBubbleSizFn -> makeBubbleSizeFn + put it separate file --- src/components/drawing/index.js | 4 ++- src/traces/scatter/index.js | 26 -------------- src/traces/scatter/make_bubble_size_func.js | 39 +++++++++++++++++++++ src/traces/scatter3d/convert.js | 5 ++- src/traces/scattergl/convert.js | 3 +- 5 files changed, 46 insertions(+), 31 deletions(-) create mode 100644 src/traces/scatter/make_bubble_size_func.js diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js index aa13fa03144..50e52f50bb3 100644 --- a/src/components/drawing/index.js +++ b/src/components/drawing/index.js @@ -13,6 +13,8 @@ var Plotly = require('../../plotly'); var d3 = require('d3'); var isNumeric = require('fast-isnumeric'); +var makeBubbleSizeFn = require('../../traces/scatter/make_bubble_size_func'); + var drawing = module.exports = {}; // ----------------------------------------------------- @@ -175,7 +177,7 @@ drawing.pointStyle = function(s, trace) { // only scatter & box plots get marker path and opacity // bars, histograms don't if(Plotly.Plots.traceIs(trace, 'symbols')) { - var sizeFn = Plotly.Scatter.getBubbleSizeFn(trace); + var sizeFn = makeBubbleSizeFn(trace); s.attr('d', function(d) { var r; diff --git a/src/traces/scatter/index.js b/src/traces/scatter/index.js index f8facbae94e..65ab821fa7b 100644 --- a/src/traces/scatter/index.js +++ b/src/traces/scatter/index.js @@ -79,32 +79,6 @@ scatter.cleanData = function(fullData) { scatter.colorbar = require('./colorbar'); -// used in the drawing step for 'scatter' and 'scattegeo' and -// in the convert step for 'scatter3d' -scatter.getBubbleSizeFn = function(trace) { - var marker = trace.marker, - sizeRef = marker.sizeref || 1, - sizeMin = marker.sizemin || 0; - - // for bubble charts, allow scaling the provided value linearly - // and by area or diameter. - // Note this only applies to the array-value sizes - - var baseFn = marker.sizemode==='area' ? - function(v) { return Math.sqrt(v / sizeRef); } : - function(v) { return v / sizeRef; }; - - // TODO add support for position/negative bubbles? - // TODO add 'sizeoffset' attribute? - return function(v) { - var baseSize = baseFn(v / 2); - - // don't show non-numeric and negative sizes - return (isNumeric(baseSize) && baseSize>0) ? - Math.max(baseSize, sizeMin) : 0; - }; -}; - scatter.calc = function(gd, trace) { var xa = Axes.getFromId(gd,trace.xaxis||'x'), ya = Axes.getFromId(gd,trace.yaxis||'y'); diff --git a/src/traces/scatter/make_bubble_size_func.js b/src/traces/scatter/make_bubble_size_func.js new file mode 100644 index 00000000000..c87639dba50 --- /dev/null +++ b/src/traces/scatter/make_bubble_size_func.js @@ -0,0 +1,39 @@ +/** +* Copyright 2012-2016, 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'); + + +// used in the drawing step for 'scatter' and 'scattegeo' and +// in the convert step for 'scatter3d' +module.exports = function makeBubbleSizeFn(trace) { + var marker = trace.marker, + sizeRef = marker.sizeref || 1, + sizeMin = marker.sizemin || 0; + + // for bubble charts, allow scaling the provided value linearly + // and by area or diameter. + // Note this only applies to the array-value sizes + + var baseFn = marker.sizemode==='area' ? + function(v) { return Math.sqrt(v / sizeRef); } : + function(v) { return v / sizeRef; }; + + // TODO add support for position/negative bubbles? + // TODO add 'sizeoffset' attribute? + return function(v) { + var baseSize = baseFn(v / 2); + + // don't show non-numeric and negative sizes + return (isNumeric(baseSize) && baseSize>0) ? + Math.max(baseSize, sizeMin) : 0; + }; +}; diff --git a/src/traces/scatter3d/convert.js b/src/traces/scatter3d/convert.js index 67011faba0c..580de599d52 100644 --- a/src/traces/scatter3d/convert.js +++ b/src/traces/scatter3d/convert.js @@ -18,8 +18,7 @@ var triangulate = require('delaunay-triangulate'); var Lib = require('../../lib'); var str2RgbaArray = require('../../lib/str2rgbarray'); var formatColor = require('../../lib/gl_format_color'); - -var Scatter = require('../scatter'); +var makeBubbleSizeFn = require('../scatter/make_bubble_size_func'); var DASH_PATTERNS = require('../../constants/gl3d_dashes.json'); var MARKER_SYMBOLS = require('../../constants/gl_markers.json'); @@ -210,7 +209,7 @@ function convertPlotlyOptions(scene, data) { } if ('marker' in data) { - var sizeFn = Scatter.getBubbleSizeFn(data); + var sizeFn = makeBubbleSizeFn(data); params.scatterColor = formatColor(marker, 1, len); params.scatterSize = formatParam(marker.size, len, calculateSize, 20, sizeFn); diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 389da3b257f..7f480d8717c 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -24,6 +24,7 @@ var formatColor = require('../../lib/gl_format_color'); var Scatter = require('../scatter'); var ErrorBars = require('../../components/errorbars'); +var makeBubbleSizeFn = require('../scatter/make_bubble_size_func'); var MARKER_SYMBOLS = require('../../constants/gl_markers.json'); var DASHES = require('../../constants/gl2d_dashes.json'); @@ -418,7 +419,7 @@ proto.updateFancy = function(options) { this.scatterOptions.colors = new Array(pId * 4); this.scatterOptions.borderColors = new Array(pId * 4); - var markerSizeFunc = Scatter.getBubbleSizeFn(options), + var markerSizeFunc = makeBubbleSizeFn(options), markerOpts = options.marker, markerOpacity = markerOpts.opacity, traceOpacity = options.opacity, From f86673c2236f59ebb51859ca7bdf178feda21371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Fri, 15 Jan 2016 09:56:22 -0500 Subject: [PATCH 04/10] put hoverPoint and getTraceColor in separate files --- src/traces/scatter/get_trace_color.js | 51 +++++++++++++++ src/traces/scatter/hover.js | 69 ++++++++++++++++++++ src/traces/scatter/index.js | 92 +-------------------------- src/traces/scattergl/convert.js | 3 +- 4 files changed, 123 insertions(+), 92 deletions(-) create mode 100644 src/traces/scatter/get_trace_color.js create mode 100644 src/traces/scatter/hover.js diff --git a/src/traces/scatter/get_trace_color.js b/src/traces/scatter/get_trace_color.js new file mode 100644 index 00000000000..1eeec66afce --- /dev/null +++ b/src/traces/scatter/get_trace_color.js @@ -0,0 +1,51 @@ +/** +* Copyright 2012-2016, 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 Color = require('../../components/color'); +var subtypes = require('./subtypes'); + + +module.exports = function getTraceColor(trace, di) { + var lc, tc; + + // TODO: text modes + + if(trace.mode === 'lines') { + lc = trace.line.color; + return (lc && Color.opacity(lc)) ? + lc : trace.fillcolor; + } + else if(trace.mode === 'none') { + return trace.fill ? trace.fillcolor : ''; + } + else { + var mc = di.mcc || (trace.marker || {}).color, + mlc = di.mlcc || ((trace.marker || {}).line || {}).color; + + tc = (mc && Color.opacity(mc)) ? mc : + (mlc && Color.opacity(mlc) && + (di.mlw || ((trace.marker || {}).line || {}).width)) ? mlc : ''; + + if(tc) { + // make sure the points aren't TOO transparent + if(Color.opacity(tc) < 0.3) { + return Color.addOpacity(tc, 0.3); + } + else return tc; + } + else { + lc = (trace.line || {}).color; + return (lc && Color.opacity(lc) && + subtypes.hasLines(trace) && trace.line.width) ? + lc : trace.fillcolor; + } + } +}; diff --git a/src/traces/scatter/hover.js b/src/traces/scatter/hover.js new file mode 100644 index 00000000000..197d95c9fcc --- /dev/null +++ b/src/traces/scatter/hover.js @@ -0,0 +1,69 @@ +/** +* Copyright 2012-2016, 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 Fx = require('../../plots/cartesian/graph_interact'); +var ErrorBars = require('../../components/errorbars'); +var getTraceColor = require('./get_trace_color'); + + +module.exports = function hoverPoints(pointData, xval, yval, hovermode) { + var cd = pointData.cd, + trace = cd[0].trace, + xa = pointData.xa, + ya = pointData.ya, + dx = function(di){ + // scatter points: d.mrc is the calculated marker radius + // adjust the distance so if you're inside the marker it + // always will show up regardless of point size, but + // prioritize smaller points + var rad = Math.max(3, di.mrc||0); + return Math.max(Math.abs(xa.c2p(di.x)-xa.c2p(xval))-rad, 1-3/rad); + }, + dy = function(di){ + var rad = Math.max(3, di.mrc||0); + return Math.max(Math.abs(ya.c2p(di.y)-ya.c2p(yval))-rad, 1-3/rad); + }, + dxy = function(di) { + var rad = Math.max(3, di.mrc||0), + dx = Math.abs(xa.c2p(di.x)-xa.c2p(xval)), + dy = Math.abs(ya.c2p(di.y)-ya.c2p(yval)); + return Math.max(Math.sqrt(dx*dx + dy*dy)-rad, 1-3/rad); + }, + distfn = Fx.getDistanceFunction(hovermode, dx, dy, dxy); + + Fx.getClosest(cd, distfn, pointData); + + // skip the rest (for this trace) if we didn't find a close point + if(pointData.index===false) return; + + // the closest data point + var di = cd[pointData.index], + xc = xa.c2p(di.x, true), + yc = ya.c2p(di.y, true), + rad = di.mrc||1; + + pointData.color = getTraceColor(trace, di); + + pointData.x0 = xc - rad; + pointData.x1 = xc + rad; + pointData.xLabelVal = di.x; + + pointData.y0 = yc - rad; + pointData.y1 = yc + rad; + pointData.yLabelVal = di.y; + + if(di.tx) pointData.text = di.tx; + else if(trace.text) pointData.text = trace.text; + + ErrorBars.hoverInfo(di, trace, pointData); + + return [pointData]; +}; diff --git a/src/traces/scatter/index.js b/src/traces/scatter/index.js index 65ab821fa7b..af4ea77cb23 100644 --- a/src/traces/scatter/index.js +++ b/src/traces/scatter/index.js @@ -38,6 +38,7 @@ scatter.name = 'scatter'; scatter.categories = ['cartesian', 'symbols', 'markerColorscale', 'errorBarsOK', 'showLegend']; scatter.meta = { Scatter.supplyDefaults = require('./defaults'); +Scatter.hoverPoints = require('./hover'); description: [ 'The scatter trace type encompasses line charts, scatter charts, text charts, and bubble charts.', 'The data visualized as scatter point or lines is set in `x` and `y`.', @@ -602,94 +603,3 @@ scatter.style = function(gd) { s.selectAll('g.trace path.js-fill') .call(Drawing.fillGroupStyle); }; - -scatter.getTraceColor = function(trace, di) { - var lc, tc; - - // TODO: text modes - - if(trace.mode === 'lines') { - lc = trace.line.color; - return (lc && Color.opacity(lc)) ? - lc : trace.fillcolor; - } - else if(trace.mode === 'none') { - return trace.fill ? trace.fillcolor : ''; - } - else { - var mc = di.mcc || (trace.marker || {}).color, - mlc = di.mlcc || ((trace.marker || {}).line || {}).color; - - tc = (mc && Color.opacity(mc)) ? mc : - (mlc && Color.opacity(mlc) && - (di.mlw || ((trace.marker || {}).line || {}).width)) ? mlc : ''; - - if(tc) { - // make sure the points aren't TOO transparent - if(Color.opacity(tc) < 0.3) { - return Color.addOpacity(tc, 0.3); - } - else return tc; - } - else { - lc = (trace.line || {}).color; - return (lc && Color.opacity(lc) && - scatter.hasLines(trace) && trace.line.width) ? - lc : trace.fillcolor; - } - } -}; - -scatter.hoverPoints = function(pointData, xval, yval, hovermode) { - var cd = pointData.cd, - trace = cd[0].trace, - xa = pointData.xa, - ya = pointData.ya, - dx = function(di){ - // scatter points: d.mrc is the calculated marker radius - // adjust the distance so if you're inside the marker it - // always will show up regardless of point size, but - // prioritize smaller points - var rad = Math.max(3, di.mrc||0); - return Math.max(Math.abs(xa.c2p(di.x)-xa.c2p(xval))-rad, 1-3/rad); - }, - dy = function(di){ - var rad = Math.max(3, di.mrc||0); - return Math.max(Math.abs(ya.c2p(di.y)-ya.c2p(yval))-rad, 1-3/rad); - }, - dxy = function(di) { - var rad = Math.max(3, di.mrc||0), - dx = Math.abs(xa.c2p(di.x)-xa.c2p(xval)), - dy = Math.abs(ya.c2p(di.y)-ya.c2p(yval)); - return Math.max(Math.sqrt(dx*dx + dy*dy)-rad, 1-3/rad); - }, - distfn = Fx.getDistanceFunction(hovermode, dx, dy, dxy); - - Fx.getClosest(cd, distfn, pointData); - - // skip the rest (for this trace) if we didn't find a close point - if(pointData.index===false) return; - - // the closest data point - var di = cd[pointData.index], - xc = xa.c2p(di.x, true), - yc = ya.c2p(di.y, true), - rad = di.mrc||1; - - pointData.color = scatter.getTraceColor(trace, di); - - pointData.x0 = xc - rad; - pointData.x1 = xc + rad; - pointData.xLabelVal = di.x; - - pointData.y0 = yc - rad; - pointData.y1 = yc + rad; - pointData.yLabelVal = di.y; - - if(di.tx) pointData.text = di.tx; - else if(trace.text) pointData.text = trace.text; - - ErrorBars.hoverInfo(di, trace, pointData); - - return [pointData]; -}; diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 7f480d8717c..e93961aabd0 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -25,6 +25,7 @@ var Scatter = require('../scatter'); var ErrorBars = require('../../components/errorbars'); var makeBubbleSizeFn = require('../scatter/make_bubble_size_func'); +var getTraceColor = require('../scatter/get_trace_color'); var MARKER_SYMBOLS = require('../../constants/gl_markers.json'); var DASHES = require('../../constants/gl2d_dashes.json'); @@ -260,7 +261,7 @@ proto.update = function(options) { // not quite on-par with 'scatter', but close enough for now // does not handle the colorscale case - this.color = Scatter.getTraceColor(options, {}); + this.color = getTraceColor(options, {}); }; proto.updateFast = function(options) { From 71ad19ba2e661e625d33231bbd3a369965da2a68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Fri, 15 Jan 2016 09:57:42 -0500 Subject: [PATCH 05/10] require scatter subTypes directly, instead of require all of Scatter --- src/components/drawing/index.js | 3 ++- src/components/errorbars/index.js | 14 +++++++++----- src/components/legend/index.js | 10 ++++++---- src/traces/scattergeo/index.js | 11 ++--------- src/traces/scattergeo/plot.js | 9 ++++----- src/traces/scattergl/convert.js | 10 ++++------ src/traces/scattergl/index.js | 3 +-- 7 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/components/drawing/index.js b/src/components/drawing/index.js index 50e52f50bb3..6f2a025ae73 100644 --- a/src/components/drawing/index.js +++ b/src/components/drawing/index.js @@ -13,6 +13,7 @@ var Plotly = require('../../plotly'); var d3 = require('d3'); var isNumeric = require('fast-isnumeric'); +var subTypes = require('../../traces/scatter/subtypes'); var makeBubbleSizeFn = require('../../traces/scatter/make_bubble_size_func'); var drawing = module.exports = {}; @@ -184,7 +185,7 @@ drawing.pointStyle = function(s, trace) { // handle multi-trace graph edit case if(d.ms==='various' || marker.size==='various') r = 3; - else r = Plotly.Scatter.isBubble(trace) ? + else r = subTypes.isBubble(trace) ? sizeFn(d.ms) : (marker.size || 6) / 2; // store the calculated size so hover can use it diff --git a/src/components/errorbars/index.js b/src/components/errorbars/index.js index 4d1566701a1..2a4c9101c54 100644 --- a/src/components/errorbars/index.js +++ b/src/components/errorbars/index.js @@ -9,10 +9,14 @@ 'use strict'; -var Plotly = require('../../plotly'); var d3 = require('d3'); var isNumeric = require('fast-isnumeric'); +var Lib = require('../../lib'); +var Color = require('../color'); +var subTypes = require('../../traces/scatter/subtypes'); + + var errorBars = module.exports = {}; errorBars.attributes = require('./attributes'); @@ -70,13 +74,13 @@ errorBars.plot = function(gd, plotinfo, cd) { var trace = d[0].trace, xObj = trace.error_x, yObj = trace.error_y, - sparse = Plotly.Scatter.hasMarkers(trace) && + sparse = subTypes.hasMarkers(trace) && trace.marker.maxdisplayed>0; if(!yObj.visible && !xObj.visible) return; d3.select(this).selectAll('g') - .data(Plotly.Lib.identity) + .data(Lib.identity) .enter().append('g') .each(function(d){ coords = errorcoords(d, xa, ya); @@ -121,13 +125,13 @@ errorBars.style = function(gd){ eb.selectAll('g path.yerror') .style('stroke-width', yObj.thickness+'px') - .call(Plotly.Color.stroke, yObj.color); + .call(Color.stroke, yObj.color); if(xObj.copy_ystyle) xObj = yObj; eb.selectAll('g path.xerror') .style('stroke-width', xObj.thickness+'px') - .call(Plotly.Color.stroke, xObj.color); + .call(Color.stroke, xObj.color); }); }; diff --git a/src/components/legend/index.js b/src/components/legend/index.js index 2fb6e7c4b03..814ff8d9a78 100644 --- a/src/components/legend/index.js +++ b/src/components/legend/index.js @@ -1,3 +1,4 @@ + /** * Copyright 2012-2016, Plotly, Inc. * All rights reserved. @@ -12,6 +13,7 @@ var Plotly = require('../../plotly'); var d3 = require('d3'); +var subTypes = require('../../traces/scatter/subtypes'); var styleOne = require('../../traces/pie/style_one'); var legend = module.exports = {}; @@ -80,7 +82,7 @@ legend.supplyLayoutDefaults = function(layoutIn, layoutOut, fullData) { legend.lines = function(d){ var trace = d[0].trace, showFill = trace.visible && trace.fill && trace.fill!=='none', - showLine = Plotly.Scatter.hasLines(trace); + showLine = subTypes.hasLines(trace); var fill = d3.select(this).select('.legendfill').selectAll('path') .data(showFill ? [d] : []); @@ -100,9 +102,9 @@ legend.lines = function(d){ legend.points = function(d){ var d0 = d[0], trace = d0.trace, - showMarkers = Plotly.Scatter.hasMarkers(trace), - showText = Plotly.Scatter.hasText(trace), - showLines = Plotly.Scatter.hasLines(trace); + showMarkers = subTypes.hasMarkers(trace), + showText = subTypes.hasText(trace), + showLines = subTypes.hasLines(trace); var dMod, tMod; diff --git a/src/traces/scattergeo/index.js b/src/traces/scattergeo/index.js index c193af4816c..8d0dd121288 100644 --- a/src/traces/scattergeo/index.js +++ b/src/traces/scattergeo/index.js @@ -9,21 +9,14 @@ 'use strict'; -var Scatter = require('../scatter'); - var ScatterGeo = {}; ScatterGeo.attributes = require('./attributes'); ScatterGeo.supplyDefaults = require('./defaults'); -ScatterGeo.colorbar = Scatter.colorbar; +ScatterGeo.colorbar = require('../scatter/colorbar'); +ScatterGeo.calc = require('./calc'); ScatterGeo.plot = require('./plot').plot; -ScatterGeo.calc = function(gd, trace) { - - Scatter.calcMarkerColorscales(trace); - -}; - ScatterGeo.moduleType = 'trace'; ScatterGeo.name = 'scattergeo'; ScatterGeo.categories = ['geo', 'symbols', 'markerColorscale', 'showLegend']; diff --git a/src/traces/scattergeo/plot.js b/src/traces/scattergeo/plot.js index 4a3f2d987ba..fdaf4ad06d6 100644 --- a/src/traces/scattergeo/plot.js +++ b/src/traces/scattergeo/plot.js @@ -18,8 +18,7 @@ var arrayToCalcItem = require('../../lib/array_to_calc_item'); var Color = require('../../components/color'); var Drawing = require('../../components/drawing'); - -var Scatter = require('../scatter'); +var subTypes = require('../scatter/subtypes'); var attributes = require('./attributes'); @@ -132,7 +131,7 @@ plotScatterGeo.plot = function(geo, scattergeoData) { // TODO add hover - how? gScatterGeoTraces .each(function(trace) { - if(!Scatter.hasLines(trace) || trace.visible !== true) return; + if(!subTypes.hasLines(trace) || trace.visible !== true) return; d3.select(this) .append('path') .datum(makeLineGeoJSON(trace)) @@ -143,8 +142,8 @@ plotScatterGeo.plot = function(geo, scattergeoData) { .attr('class', 'points') .each(function(trace) { var s = d3.select(this), - showMarkers = Scatter.hasMarkers(trace), - showText = Scatter.hasText(trace); + showMarkers = subTypes.hasMarkers(trace), + showText = subTypes.hasText(trace); if((!showMarkers && !showText) || trace.visible !== true) { s.remove(); diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index e93961aabd0..13e77ac90e1 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -18,12 +18,10 @@ var createError = require('gl-error2d'); var isNumeric = require('fast-isnumeric'); var Lib = require('../../lib'); +var ErrorBars = require('../../components/errorbars'); var str2RGBArray = require('../../lib/str2rgbarray'); var formatColor = require('../../lib/gl_format_color'); - -var Scatter = require('../scatter'); - -var ErrorBars = require('../../components/errorbars'); +var subTypes = require('../scatter/subtypes'); var makeBubbleSizeFn = require('../scatter/make_bubble_size_func'); var getTraceColor = require('../scatter/get_trace_color'); @@ -241,10 +239,10 @@ proto.update = function(options) { this.hasMarkers = false; } else { - this.hasLines = Scatter.hasLines(options); + this.hasLines = subTypes.hasLines(options); this.hasErrorX = options.error_x.visible === true; this.hasErrorY = options.error_y.visible === true; - this.hasMarkers = Scatter.hasMarkers(options); + this.hasMarkers = subTypes.hasMarkers(options); } this.textLabels = options.text; diff --git a/src/traces/scattergl/index.js b/src/traces/scattergl/index.js index 53734f61ff4..7361781f589 100644 --- a/src/traces/scattergl/index.js +++ b/src/traces/scattergl/index.js @@ -8,14 +8,13 @@ 'use strict'; -var Scatter = require('../scatter'); var Scatter3D = require('../scatter3d'); var ScatterGl = {}; ScatterGl.attributes = require('./attributes'); ScatterGl.supplyDefaults = require('./defaults'); -ScatterGl.colorbar = Scatter.colorbar; +ScatterGl.colorbar = require('../scatter/colorbar'); // reuse the Scatter3D 'dummy' calc step so that legends know what to do ScatterGl.calc = Scatter3D.calc; From 6337313d55c90073b234d879e1a38d9f2c299d2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Fri, 15 Jan 2016 09:58:25 -0500 Subject: [PATCH 06/10] put scatter3d calc in separate, use it in scattergl --- src/traces/scatter3d/calc.js | 27 +++++++++++++++++++++++++++ src/traces/scatter3d/index.js | 15 +-------------- src/traces/scattergl/index.js | 4 +--- 3 files changed, 29 insertions(+), 17 deletions(-) create mode 100644 src/traces/scatter3d/calc.js diff --git a/src/traces/scatter3d/calc.js b/src/traces/scatter3d/calc.js new file mode 100644 index 00000000000..b50ae40b221 --- /dev/null +++ b/src/traces/scatter3d/calc.js @@ -0,0 +1,27 @@ +/** +* Copyright 2012-2016, 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 arraysToCalcdata = require('../scatter/arrays_to_calcdata'); +var calcMarkerColorscale = require('../scatter/marker_colorscale_calc'); + + +/** + * This is a kludge to put the array attributes into + * calcdata the way Scatter.plot does, so that legends and + * popovers know what to do with them. + */ +module.exports = function calc(gd, trace) { + var cd = [{x: false, y: false, trace: trace, t: {}}]; + + arraysToCalcdata(cd); + calcMarkerColorscale(trace); + + return cd; +}; diff --git a/src/traces/scatter3d/index.js b/src/traces/scatter3d/index.js index 5073ae12533..fd866886a83 100644 --- a/src/traces/scatter3d/index.js +++ b/src/traces/scatter3d/index.js @@ -8,8 +8,6 @@ 'use strict'; -var Scatter = require('../scatter'); - var Scatter3D = {}; Scatter3D.plot = require('./convert'); @@ -17,18 +15,7 @@ Scatter3D.attributes = require('./attributes'); Scatter3D.markerSymbols = require('../../constants/gl_markers.json'); Scatter3D.supplyDefaults = require('./defaults'); Scatter3D.colorbar = require('../scatter/colorbar'); - -Scatter3D.calc = function(gd, trace) { - // this is a kludge to put the array attributes into - // calcdata the way Scatter.plot does, so that legends and - // popovers know what to do with them. - var cd = [{x: false, y: false, trace: trace, t: {}}]; - Scatter.arraysToCalcdata(cd); - - Scatter.calcMarkerColorscales(trace); - - return cd; -}; +Scatter3D.calc = require('./calc'); Scatter3D.moduleType = 'trace'; Scatter3D.name = 'scatter3d'; diff --git a/src/traces/scattergl/index.js b/src/traces/scattergl/index.js index 7361781f589..72ba035c019 100644 --- a/src/traces/scattergl/index.js +++ b/src/traces/scattergl/index.js @@ -8,8 +8,6 @@ 'use strict'; -var Scatter3D = require('../scatter3d'); - var ScatterGl = {}; ScatterGl.attributes = require('./attributes'); @@ -17,7 +15,7 @@ ScatterGl.supplyDefaults = require('./defaults'); ScatterGl.colorbar = require('../scatter/colorbar'); // reuse the Scatter3D 'dummy' calc step so that legends know what to do -ScatterGl.calc = Scatter3D.calc; +ScatterGl.calc = require('../scatter3d/calc'); ScatterGl.plot = require('./convert'); ScatterGl.moduleType = 'trace'; From 6757bee694cfb5b039786cfa53f1514d0c029969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Fri, 15 Jan 2016 09:59:18 -0500 Subject: [PATCH 07/10] split up scatter methods into separate files --- src/traces/scatter/arrays_to_calcdata.js | 36 ++ src/traces/scatter/calc.js | 128 ++++ src/traces/scatter/clean_data.js | 39 ++ src/traces/scatter/index.js | 603 +------------------ src/traces/scatter/line_points.js | 167 +++++ src/traces/scatter/marker_colorscale_calc.js | 32 + src/traces/scatter/plot.js | 223 +++++++ src/traces/scatter/style.js | 37 ++ src/traces/scattergeo/calc.js | 21 + 9 files changed, 705 insertions(+), 581 deletions(-) create mode 100644 src/traces/scatter/arrays_to_calcdata.js create mode 100644 src/traces/scatter/calc.js create mode 100644 src/traces/scatter/clean_data.js create mode 100644 src/traces/scatter/line_points.js create mode 100644 src/traces/scatter/marker_colorscale_calc.js create mode 100644 src/traces/scatter/plot.js create mode 100644 src/traces/scatter/style.js create mode 100644 src/traces/scattergeo/calc.js diff --git a/src/traces/scatter/arrays_to_calcdata.js b/src/traces/scatter/arrays_to_calcdata.js new file mode 100644 index 00000000000..f2ddc40bff5 --- /dev/null +++ b/src/traces/scatter/arrays_to_calcdata.js @@ -0,0 +1,36 @@ +/** +* Copyright 2012-2016, 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'); + + +// arrayOk attributes, merge them into calcdata array +module.exports = function arraysToCalcdata(cd) { + var trace = cd[0].trace, + marker = trace.marker; + + Lib.mergeArray(trace.text, cd, 'tx'); + Lib.mergeArray(trace.textposition, cd, 'tp'); + if(trace.textfont) { + Lib.mergeArray(trace.textfont.size, cd, 'ts'); + Lib.mergeArray(trace.textfont.color, cd, 'tc'); + Lib.mergeArray(trace.textfont.family, cd, 'tf'); + } + + if(marker && marker.line) { + var markerLine = marker.line; + Lib.mergeArray(marker.opacity, cd, 'mo'); + Lib.mergeArray(marker.symbol, cd, 'mx'); + Lib.mergeArray(marker.color, cd, 'mc'); + Lib.mergeArray(markerLine.color, cd, 'mlc'); + Lib.mergeArray(markerLine.width, cd, 'mlw'); + } +}; diff --git a/src/traces/scatter/calc.js b/src/traces/scatter/calc.js new file mode 100644 index 00000000000..c7e6679f26c --- /dev/null +++ b/src/traces/scatter/calc.js @@ -0,0 +1,128 @@ +/** +* Copyright 2012-2016, 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 Axes = require('../../plots/cartesian/axes'); +var Lib = require('../../lib'); + +var subTypes = require('./subtypes'); +var calcMarkerColorscale = require('./marker_colorscale_calc'); + + +module.exports = function calc(gd, trace) { + var xa = Axes.getFromId(gd,trace.xaxis||'x'), + ya = Axes.getFromId(gd,trace.yaxis||'y'); + Lib.markTime('in Scatter.calc'); + var x = xa.makeCalcdata(trace,'x'); + Lib.markTime('finished convert x'); + var y = ya.makeCalcdata(trace,'y'); + Lib.markTime('finished convert y'); + var serieslen = Math.min(x.length,y.length), + marker, + s, + i; + + // cancel minimum tick spacings (only applies to bars and boxes) + xa._minDtick = 0; + ya._minDtick = 0; + + if(x.length>serieslen) x.splice(serieslen, x.length-serieslen); + if(y.length>serieslen) y.splice(serieslen, y.length-serieslen); + + // check whether bounds should be tight, padded, extended to zero... + // most cases both should be padded on both ends, so start with that. + var xOptions = {padded:true}, + yOptions = {padded:true}; + + if(subTypes.hasMarkers(trace)) { + + // Treat size like x or y arrays --- Run d2c + // this needs to go before ppad computation + marker = trace.marker; + s = marker.size; + + if (Array.isArray(s)) { + // I tried auto-type but category and dates dont make much sense. + var ax = {type: 'linear'}; + Axes.setConvert(ax); + s = ax.makeCalcdata(trace.marker, 'size'); + if(s.length>serieslen) s.splice(serieslen, s.length-serieslen); + } + + var sizeref = 1.6*(trace.marker.sizeref||1), + markerTrans; + if(trace.marker.sizemode==='area') { + markerTrans = function(v) { + return Math.max(Math.sqrt((v||0)/sizeref),3); + }; + } + else { + markerTrans = function(v) { + return Math.max((v||0)/sizeref,3); + }; + } + xOptions.ppad = yOptions.ppad = Array.isArray(s) ? + s.map(markerTrans) : markerTrans(s); + } + + calcMarkerColorscale(trace); + + // TODO: text size + + // include zero (tight) and extremes (padded) if fill to zero + // (unless the shape is closed, then it's just filling the shape regardless) + if((trace.fill==='tozerox' || (trace.fill==='tonextx' && gd.firstscatter)) && + (x[0]!==x[serieslen-1] || y[0]!==y[serieslen-1])) { + xOptions.tozero = true; + } + + // if no error bars, markers or text, or fill to y=0 remove x padding + else if(!trace.error_y.visible && ( + ['tonexty', 'tozeroy'].indexOf(trace.fill)!==-1 || + (!subTypes.hasMarkers(trace) && !subTypes.hasText(trace)) + )) { + xOptions.padded = false; + xOptions.ppad = 0; + } + + // now check for y - rather different logic, though still mostly padded both ends + // include zero (tight) and extremes (padded) if fill to zero + // (unless the shape is closed, then it's just filling the shape regardless) + if((trace.fill==='tozeroy' || (trace.fill==='tonexty' && gd.firstscatter)) && + (x[0]!==x[serieslen-1] || y[0]!==y[serieslen-1])) { + yOptions.tozero = true; + } + + // tight y: any x fill + else if(['tonextx', 'tozerox'].indexOf(trace.fill)!==-1) { + yOptions.padded = false; + } + + Lib.markTime('ready for Axes.expand'); + Axes.expand(xa, x, xOptions); + Lib.markTime('done expand x'); + Axes.expand(ya, y, yOptions); + Lib.markTime('done expand y'); + + // create the "calculated data" to plot + var cd = new Array(serieslen); + for(i = 0; i < serieslen; i++) { + cd[i] = (isNumeric(x[i]) && isNumeric(y[i])) ? + {x: x[i], y: y[i]} : {x: false, y: false}; + } + + // this has migrated up from arraysToCalcdata as we have a reference to 's' here + if (typeof s !== undefined) Lib.mergeArray(s, cd, 'ms'); + + gd.firstscatter = false; + return cd; +}; diff --git a/src/traces/scatter/clean_data.js b/src/traces/scatter/clean_data.js new file mode 100644 index 00000000000..5477da4dfc4 --- /dev/null +++ b/src/traces/scatter/clean_data.js @@ -0,0 +1,39 @@ +/** +* Copyright 2012-2016, 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 cleanData(fullData) { + var i, + tracei, + filli, + j, + tracej; + + // remove opacity for any trace that has a fill or is filled to + for(i = 0; i < fullData.length; i++) { + tracei = fullData[i]; + filli = tracei.fill; + if(filli==='none' || (tracei.type !== 'scatter')) continue; + tracei.opacity = undefined; + + if(filli === 'tonexty' || filli === 'tonextx') { + for(j = i - 1; j >= 0; j--) { + tracej = fullData[j]; + if((tracej.type === 'scatter') && + (tracej.xaxis === tracei.xaxis) && + (tracej.yaxis === tracei.yaxis)) { + tracej.opacity = undefined; + break; + } + } + } + } +}; diff --git a/src/traces/scatter/index.js b/src/traces/scatter/index.js index af4ea77cb23..6c3b8629c3d 100644 --- a/src/traces/scatter/index.js +++ b/src/traces/scatter/index.js @@ -9,36 +9,31 @@ 'use strict'; -var d3 = require('d3'); -var isNumeric = require('fast-isnumeric'); - -var Lib = require('../../lib'); - -var Fx = require('../../plots/cartesian/graph_interact'); -var Axes = require('../../plots/cartesian/axes'); - -var Color = require('../../components/color'); -var Colorscale = require('../../components/colorscale'); -var Drawing = require('../../components/drawing'); -var ErrorBars = require('../../components/errorbars'); +var Scatter = {}; var subtypes = require('./subtypes'); - -var scatter = module.exports = {}; - -scatter.hasLines = subtypes.hasLines; -scatter.hasMarkers = subtypes.hasMarkers; -scatter.hasText = subtypes.hasText; -scatter.isBubble = subtypes.isBubble; - -scatter.selectPoints = require('./select'); - -scatter.moduleType = 'trace'; -scatter.name = 'scatter'; -scatter.categories = ['cartesian', 'symbols', 'markerColorscale', 'errorBarsOK', 'showLegend']; -scatter.meta = { +Scatter.hasLines = subtypes.hasLines; +Scatter.hasMarkers = subtypes.hasMarkers; +Scatter.hasText = subtypes.hasText; +Scatter.isBubble = subtypes.isBubble; + +// traces with < this many points are by default shown +// with points and lines, > just get lines +Scatter.attributes = require('./attributes'); Scatter.supplyDefaults = require('./defaults'); +Scatter.cleanData = require('./clean_data'); +Scatter.calc = require('./calc'); +Scatter.arraysToCalcdata = require('./arrays_to_calcdata'); +Scatter.plot = require('./plot'); +Scatter.colorbar = require('./colorbar'); +Scatter.style = require('./style'); Scatter.hoverPoints = require('./hover'); +Scatter.selectPoints = require('./select'); + +Scatter.moduleType = 'trace'; +Scatter.name = 'scatter'; +Scatter.categories = ['cartesian', 'symbols', 'markerColorscale', 'errorBarsOK', 'showLegend']; +Scatter.meta = { description: [ 'The scatter trace type encompasses line charts, scatter charts, text charts, and bubble charts.', 'The data visualized as scatter point or lines is set in `x` and `y`.', @@ -48,558 +43,4 @@ Scatter.hoverPoints = require('./hover'); ].join(' ') }; -scatter.attributes = require('./attributes'); - -scatter.cleanData = function(fullData) { - var i, - tracei, - filli, - j, - tracej; - - // remove opacity for any trace that has a fill or is filled to - for(i = 0; i < fullData.length; i++) { - tracei = fullData[i]; - filli = tracei.fill; - if(filli==='none' || (tracei.type !== 'scatter')) continue; - tracei.opacity = undefined; - - if(filli === 'tonexty' || filli === 'tonextx') { - for(j = i - 1; j >= 0; j--) { - tracej = fullData[j]; - if((tracej.type === 'scatter') && - (tracej.xaxis === tracei.xaxis) && - (tracej.yaxis === tracei.yaxis)) { - tracej.opacity = undefined; - break; - } - } - } - } -}; - -scatter.colorbar = require('./colorbar'); - -scatter.calc = function(gd, trace) { - var xa = Axes.getFromId(gd,trace.xaxis||'x'), - ya = Axes.getFromId(gd,trace.yaxis||'y'); - Lib.markTime('in Scatter.calc'); - var x = xa.makeCalcdata(trace,'x'); - Lib.markTime('finished convert x'); - var y = ya.makeCalcdata(trace,'y'); - Lib.markTime('finished convert y'); - var serieslen = Math.min(x.length,y.length), - marker, - s, - i; - - // cancel minimum tick spacings (only applies to bars and boxes) - xa._minDtick = 0; - ya._minDtick = 0; - - if(x.length>serieslen) x.splice(serieslen, x.length-serieslen); - if(y.length>serieslen) y.splice(serieslen, y.length-serieslen); - - // check whether bounds should be tight, padded, extended to zero... - // most cases both should be padded on both ends, so start with that. - var xOptions = {padded:true}, - yOptions = {padded:true}; - - if(scatter.hasMarkers(trace)) { - - // Treat size like x or y arrays --- Run d2c - // this needs to go before ppad computation - marker = trace.marker; - s = marker.size; - - if (Array.isArray(s)) { - // I tried auto-type but category and dates dont make much sense. - var ax = {type: 'linear'}; - Axes.setConvert(ax); - s = ax.makeCalcdata(trace.marker, 'size'); - if(s.length>serieslen) s.splice(serieslen, s.length-serieslen); - } - - var sizeref = 1.6*(trace.marker.sizeref||1), - markerTrans; - if(trace.marker.sizemode==='area') { - markerTrans = function(v) { - return Math.max(Math.sqrt((v||0)/sizeref),3); - }; - } - else { - markerTrans = function(v) { - return Math.max((v||0)/sizeref,3); - }; - } - xOptions.ppad = yOptions.ppad = Array.isArray(s) ? - s.map(markerTrans) : markerTrans(s); - } - - scatter.calcMarkerColorscales(trace); - - // TODO: text size - - // include zero (tight) and extremes (padded) if fill to zero - // (unless the shape is closed, then it's just filling the shape regardless) - if((trace.fill==='tozerox' || (trace.fill==='tonextx' && gd.firstscatter)) && - (x[0]!==x[serieslen-1] || y[0]!==y[serieslen-1])) { - xOptions.tozero = true; - } - - // if no error bars, markers or text, or fill to y=0 remove x padding - else if(!trace.error_y.visible && ( - ['tonexty', 'tozeroy'].indexOf(trace.fill)!==-1 || - (!scatter.hasMarkers(trace) && !scatter.hasText(trace)) - )) { - xOptions.padded = false; - xOptions.ppad = 0; - } - - // now check for y - rather different logic, though still mostly padded both ends - // include zero (tight) and extremes (padded) if fill to zero - // (unless the shape is closed, then it's just filling the shape regardless) - if((trace.fill==='tozeroy' || (trace.fill==='tonexty' && gd.firstscatter)) && - (x[0]!==x[serieslen-1] || y[0]!==y[serieslen-1])) { - yOptions.tozero = true; - } - - // tight y: any x fill - else if(['tonextx', 'tozerox'].indexOf(trace.fill)!==-1) { - yOptions.padded = false; - } - - Lib.markTime('ready for Axes.expand'); - Axes.expand(xa, x, xOptions); - Lib.markTime('done expand x'); - Axes.expand(ya, y, yOptions); - Lib.markTime('done expand y'); - - // create the "calculated data" to plot - var cd = new Array(serieslen); - for(i = 0; i < serieslen; i++) { - cd[i] = (isNumeric(x[i]) && isNumeric(y[i])) ? - {x: x[i], y: y[i]} : {x: false, y: false}; - } - - // this has migrated up from arraysToCalcdata as we have a reference to 's' here - if (typeof s !== undefined) Lib.mergeArray(s, cd, 'ms'); - - gd.firstscatter = false; - return cd; -}; - -// common to 'scatter', 'scatter3d' and 'scattergeo' -scatter.calcMarkerColorscales = function(trace) { - if(!scatter.hasMarkers(trace)) return; - - var marker = trace.marker; - - // auto-z and autocolorscale if applicable - if(Colorscale.hasColorscale(trace, 'marker')) { - Colorscale.calc(trace, marker.color, 'marker', 'c'); - } - if(Colorscale.hasColorscale(trace, 'marker.line')) { - Colorscale.calc(trace, marker.line.color, 'marker.line', 'c'); - } -}; - -scatter.selectMarkers = function(gd, plotinfo, cdscatter) { - var xa = plotinfo.x(), - ya = plotinfo.y(), - xr = d3.extent(xa.range.map(xa.l2c)), - yr = d3.extent(ya.range.map(ya.l2c)); - - cdscatter.forEach(function(d,i) { - var trace = d[0].trace; - if(!scatter.hasMarkers(trace)) return; - // if marker.maxdisplayed is used, select a maximum of - // mnum markers to show, from the set that are in the viewport - var mnum = trace.marker.maxdisplayed; - - // TODO: remove some as we get away from the viewport? - if(mnum===0) return; - - var cd = d.filter(function(v) { - return v.x>=xr[0] && v.x<=xr[1] && v.y>=yr[0] && v.y<=yr[1]; - }), - inc = Math.ceil(cd.length/mnum), - tnum = 0; - cdscatter.forEach(function(cdj, j) { - var tracei = cdj[0].trace; - if(scatter.hasMarkers(tracei) && - tracei.marker.maxdisplayed>0 && j 1) { - tr.append('path').classed('js-line',true).attr('d', thispath); - } - } - if(tozero) { - if(pt0 && pt1) { - if(trace.fill.charAt(trace.fill.length-1)==='y') { - pt0[1]=pt1[1]=ya.c2p(0,true); - } - else pt0[0]=pt1[0]=xa.c2p(0,true); - - // fill to zero: full trace path, plus extension of - // the endpoints to the appropriate axis - tozero.attr('d',fullpath+'L'+pt1+'L'+pt0+'Z'); - } - } - else if(trace.fill.substr(0,6)==='tonext' && fullpath && prevpath) { - // fill to next: full trace path, plus the previous path reversed - tonext.attr('d',fullpath+prevpath+'Z'); - } - prevpath = revpath; - } - }); - - // remove paths that didn't get used - scattertraces.selectAll('path:not([d])').remove(); - - function visFilter(d){ - return d.filter(function(v){ return v.vis; }); - } - - scattertraces.append('g') - .attr('class','points') - .each(function(d){ - var trace = d[0].trace, - s = d3.select(this), - showMarkers = scatter.hasMarkers(trace), - showText = scatter.hasText(trace); - - if((!showMarkers && !showText) || trace.visible !== true) s.remove(); - else { - if(showMarkers) { - s.selectAll('path.point') - .data(trace.marker.maxdisplayed ? visFilter : Lib.identity) - .enter().append('path') - .classed('point', true) - .call(Drawing.translatePoints, xa, ya); - } - if(showText) { - s.selectAll('g') - .data(trace.marker.maxdisplayed ? visFilter : Lib.identity) - // each text needs to go in its own 'g' in case - // it gets converted to mathjax - .enter().append('g') - .append('text') - .call(Drawing.translatePoints, xa, ya); - } - } - }); -}; - -scatter.linePoints = function(d, opts) { - var xa = opts.xaxis, - ya = opts.yaxis, - connectGaps = opts.connectGaps, - baseTolerance = opts.baseTolerance, - linear = opts.linear, - segments = [], - badnum = Axes.BADNUM, - minTolerance = 0.2, // fraction of tolerance "so close we don't even consider it a new point" - pts = new Array(d.length), - pti = 0, - i, - - // pt variables are pixel coordinates [x,y] of one point - clusterStartPt, // these four are the outputs of clustering on a line - clusterEndPt, - clusterHighPt, - clusterLowPt, - thisPt, // "this" is the next point we're considering adding to the cluster - - clusterRefDist, - clusterHighFirst, // did we encounter the high point first, then a low point, or vice versa? - clusterUnitVector, // the first two points in the cluster determine its unit vector - // so the second is always in the "High" direction - thisVector, // the pixel delta from clusterStartPt - - // val variables are (signed) pixel distances along the cluster vector - clusterHighVal, - clusterLowVal, - thisVal, - - // deviation variables are (signed) pixel distances normal to the cluster vector - clusterMinDeviation, - clusterMaxDeviation, - thisDeviation; - - // turn one calcdata point into pixel coordinates - function getPt(index) { - var x = xa.c2p(d[index].x), - y = ya.c2p(d[index].y); - if(x === badnum || y === badnum) return false; - return [x, y]; - } - - // if we're off-screen, increase tolerance over baseTolerance - function getTolerance(pt) { - var xFrac = pt[0] / xa._length, - yFrac = pt[1] / ya._length; - return (1 + 10 * Math.max(0, -xFrac, xFrac - 1, -yFrac, yFrac - 1)) * baseTolerance; - } - - function ptDist(pt1, pt2) { - var dx = pt1[0] - pt2[0], - dy = pt1[1] - pt2[1]; - return Math.sqrt(dx * dx + dy * dy); - } - - // loop over ALL points in this trace - for(i = 0; i < d.length; i++) { - clusterStartPt = getPt(i); - if(!clusterStartPt) continue; - - pti = 0; - pts[pti++] = clusterStartPt; - - // loop over one segment of the trace - for(i++; i < d.length; i++) { - clusterHighPt = getPt(i); - if(!clusterHighPt) { - if(connectGaps) continue; - else break; - } - - // can't decimate if nonlinear line shape - // TODO: we *could* decimate [hv]{2,3} shapes if we restricted clusters to horz or vert again - // but spline would be verrry awkward to decimate - if(!linear) { - pts[pti++] = clusterHighPt; - continue; - } - - clusterRefDist = ptDist(clusterHighPt, clusterStartPt); - - if(clusterRefDist < getTolerance(clusterHighPt) * minTolerance) continue; - - clusterUnitVector = [ - (clusterHighPt[0] - clusterStartPt[0]) / clusterRefDist, - (clusterHighPt[1] - clusterStartPt[1]) / clusterRefDist - ]; - - clusterLowPt = clusterStartPt; - clusterHighVal = clusterRefDist; - clusterLowVal = clusterMinDeviation = clusterMaxDeviation = 0; - clusterHighFirst = false; - clusterEndPt = clusterHighPt; - - // loop over one cluster of points that collapse onto one line - for(i++; i < d.length; i++) { - thisPt = getPt(i); - if(!thisPt) { - if(connectGaps) continue; - else break; - } - thisVector = [ - thisPt[0] - clusterStartPt[0], - thisPt[1] - clusterStartPt[1] - ]; - // cross product (or dot with normal to the cluster vector) - thisDeviation = thisVector[0] * clusterUnitVector[1] - thisVector[1] * clusterUnitVector[0]; - clusterMinDeviation = Math.min(clusterMinDeviation, thisDeviation); - clusterMaxDeviation = Math.max(clusterMaxDeviation, thisDeviation); - - if(clusterMaxDeviation - clusterMinDeviation > getTolerance(thisPt)) break; - - clusterEndPt = thisPt; - thisVal = thisVector[0] * clusterUnitVector[0] + thisVector[1] * clusterUnitVector[1]; - - if(thisVal > clusterHighVal) { - clusterHighVal = thisVal; - clusterHighPt = thisPt; - clusterHighFirst = false; - } else if(thisVal < clusterLowVal) { - clusterLowVal = thisVal; - clusterLowPt = thisPt; - clusterHighFirst = true; - } - } - - // insert this cluster into pts - // we've already inserted the start pt, now check if we have high and low pts - if(clusterHighFirst) { - pts[pti++] = clusterHighPt; - if(clusterEndPt !== clusterLowPt) pts[pti++] = clusterLowPt; - } else { - if(clusterLowPt !== clusterStartPt) pts[pti++] = clusterLowPt; - if(clusterEndPt !== clusterHighPt) pts[pti++] = clusterHighPt; - } - // and finally insert the end pt - pts[pti++] = clusterEndPt; - - // have we reached the end of this segment? - if(i >= d.length || !thisPt) break; - - // otherwise we have an out-of-cluster point to insert as next clusterStartPt - pts[pti++] = thisPt; - clusterStartPt = thisPt; - } - - segments.push(pts.slice(0, pti)); - } - - return segments; -}; - -scatter.style = function(gd) { - var s = d3.select(gd).selectAll('g.trace.scatter'); - - s.style('opacity',function(d){ return d[0].trace.opacity; }); - - s.selectAll('g.points') - .each(function(d){ - d3.select(this).selectAll('path.point') - .call(Drawing.pointStyle,d.trace||d[0].trace); - d3.select(this).selectAll('text') - .call(Drawing.textPointStyle,d.trace||d[0].trace); - }); - - s.selectAll('g.trace path.js-line') - .call(Drawing.lineGroupStyle); - - s.selectAll('g.trace path.js-fill') - .call(Drawing.fillGroupStyle); -}; +module.exports = Scatter; diff --git a/src/traces/scatter/line_points.js b/src/traces/scatter/line_points.js new file mode 100644 index 00000000000..60d7e3c77ea --- /dev/null +++ b/src/traces/scatter/line_points.js @@ -0,0 +1,167 @@ +/** +* Copyright 2012-2016, 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'); + + +module.exports = function linePoints(d, opts) { + var xa = opts.xaxis, + ya = opts.yaxis, + connectGaps = opts.connectGaps, + baseTolerance = opts.baseTolerance, + linear = opts.linear, + segments = [], + badnum = Axes.BADNUM, + minTolerance = 0.2, // fraction of tolerance "so close we don't even consider it a new point" + pts = new Array(d.length), + pti = 0, + i, + + // pt variables are pixel coordinates [x,y] of one point + clusterStartPt, // these four are the outputs of clustering on a line + clusterEndPt, + clusterHighPt, + clusterLowPt, + thisPt, // "this" is the next point we're considering adding to the cluster + + clusterRefDist, + clusterHighFirst, // did we encounter the high point first, then a low point, or vice versa? + clusterUnitVector, // the first two points in the cluster determine its unit vector + // so the second is always in the "High" direction + thisVector, // the pixel delta from clusterStartPt + + // val variables are (signed) pixel distances along the cluster vector + clusterHighVal, + clusterLowVal, + thisVal, + + // deviation variables are (signed) pixel distances normal to the cluster vector + clusterMinDeviation, + clusterMaxDeviation, + thisDeviation; + + // turn one calcdata point into pixel coordinates + function getPt(index) { + var x = xa.c2p(d[index].x), + y = ya.c2p(d[index].y); + if(x === badnum || y === badnum) return false; + return [x, y]; + } + + // if we're off-screen, increase tolerance over baseTolerance + function getTolerance(pt) { + var xFrac = pt[0] / xa._length, + yFrac = pt[1] / ya._length; + return (1 + 10 * Math.max(0, -xFrac, xFrac - 1, -yFrac, yFrac - 1)) * baseTolerance; + } + + function ptDist(pt1, pt2) { + var dx = pt1[0] - pt2[0], + dy = pt1[1] - pt2[1]; + return Math.sqrt(dx * dx + dy * dy); + } + + // loop over ALL points in this trace + for(i = 0; i < d.length; i++) { + clusterStartPt = getPt(i); + if(!clusterStartPt) continue; + + pti = 0; + pts[pti++] = clusterStartPt; + + // loop over one segment of the trace + for(i++; i < d.length; i++) { + clusterHighPt = getPt(i); + if(!clusterHighPt) { + if(connectGaps) continue; + else break; + } + + // can't decimate if nonlinear line shape + // TODO: we *could* decimate [hv]{2,3} shapes if we restricted clusters to horz or vert again + // but spline would be verrry awkward to decimate + if(!linear) { + pts[pti++] = clusterHighPt; + continue; + } + + clusterRefDist = ptDist(clusterHighPt, clusterStartPt); + + if(clusterRefDist < getTolerance(clusterHighPt) * minTolerance) continue; + + clusterUnitVector = [ + (clusterHighPt[0] - clusterStartPt[0]) / clusterRefDist, + (clusterHighPt[1] - clusterStartPt[1]) / clusterRefDist + ]; + + clusterLowPt = clusterStartPt; + clusterHighVal = clusterRefDist; + clusterLowVal = clusterMinDeviation = clusterMaxDeviation = 0; + clusterHighFirst = false; + clusterEndPt = clusterHighPt; + + // loop over one cluster of points that collapse onto one line + for(i++; i < d.length; i++) { + thisPt = getPt(i); + if(!thisPt) { + if(connectGaps) continue; + else break; + } + thisVector = [ + thisPt[0] - clusterStartPt[0], + thisPt[1] - clusterStartPt[1] + ]; + // cross product (or dot with normal to the cluster vector) + thisDeviation = thisVector[0] * clusterUnitVector[1] - thisVector[1] * clusterUnitVector[0]; + clusterMinDeviation = Math.min(clusterMinDeviation, thisDeviation); + clusterMaxDeviation = Math.max(clusterMaxDeviation, thisDeviation); + + if(clusterMaxDeviation - clusterMinDeviation > getTolerance(thisPt)) break; + + clusterEndPt = thisPt; + thisVal = thisVector[0] * clusterUnitVector[0] + thisVector[1] * clusterUnitVector[1]; + + if(thisVal > clusterHighVal) { + clusterHighVal = thisVal; + clusterHighPt = thisPt; + clusterHighFirst = false; + } else if(thisVal < clusterLowVal) { + clusterLowVal = thisVal; + clusterLowPt = thisPt; + clusterHighFirst = true; + } + } + + // insert this cluster into pts + // we've already inserted the start pt, now check if we have high and low pts + if(clusterHighFirst) { + pts[pti++] = clusterHighPt; + if(clusterEndPt !== clusterLowPt) pts[pti++] = clusterLowPt; + } else { + if(clusterLowPt !== clusterStartPt) pts[pti++] = clusterLowPt; + if(clusterEndPt !== clusterHighPt) pts[pti++] = clusterHighPt; + } + // and finally insert the end pt + pts[pti++] = clusterEndPt; + + // have we reached the end of this segment? + if(i >= d.length || !thisPt) break; + + // otherwise we have an out-of-cluster point to insert as next clusterStartPt + pts[pti++] = thisPt; + clusterStartPt = thisPt; + } + + segments.push(pts.slice(0, pti)); + } + + return segments; +}; diff --git a/src/traces/scatter/marker_colorscale_calc.js b/src/traces/scatter/marker_colorscale_calc.js new file mode 100644 index 00000000000..d8db642d9fa --- /dev/null +++ b/src/traces/scatter/marker_colorscale_calc.js @@ -0,0 +1,32 @@ +/** +* Copyright 2012-2016, 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 hasColorscale = require('../../components/colorscale/has_colorscale'); +var calcColorscale = require('../../components/colorscale/calc'); + +var subTypes = require('./subtypes'); + + +// common to 'scatter', 'scatter3d' and 'scattergeo' +module.exports = function calcMarkerColorscale(trace) { + if(!subTypes.hasMarkers(trace)) return; + + var marker = trace.marker; + + // auto-z and autocolorscale if applicable + if(hasColorscale(trace, 'marker')) { + calcColorscale(trace, marker.color, 'marker', 'c'); + } + + if(hasColorscale(trace, 'marker.line')) { + calcColorscale(trace, marker.line.color, 'marker.line', 'c'); + } +}; diff --git a/src/traces/scatter/plot.js b/src/traces/scatter/plot.js new file mode 100644 index 00000000000..c4934faa9d1 --- /dev/null +++ b/src/traces/scatter/plot.js @@ -0,0 +1,223 @@ +/** +* Copyright 2012-2016, 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 Lib = require('../../lib'); +var Drawing = require('../../components/drawing'); + +var subTypes = require('./subtypes'); +var arraysToCalcdata = require('./arrays_to_calcdata'); +var linePoints = require('./line_points'); + + +module.exports = function plot(gd, plotinfo, cdscatter) { + selectMarkers(gd, plotinfo, cdscatter); + + var xa = plotinfo.x(), + ya = plotinfo.y(); + + // make the container for scatter plots + // (so error bars can find them along with bars) + var scattertraces = plotinfo.plot.select('.scatterlayer') + .selectAll('g.trace.scatter') + .data(cdscatter); + scattertraces.enter().append('g') + .attr('class','trace scatter') + .style('stroke-miterlimit',2); + + // BUILD LINES AND FILLS + var prevpath='', + tozero,tonext,nexttonext; + scattertraces.each(function(d){ + var trace = d[0].trace, + line = trace.line, + tr = d3.select(this); + if(trace.visible !== true) return; + + d[0].node3 = tr; // store node for tweaking by selectPoints + + arraysToCalcdata(d); + + if(!subTypes.hasLines(trace) && trace.fill==='none') return; + + var thispath, + // fullpath is all paths for this curve, joined together straight + // across gaps, for filling + fullpath = '', + // revpath is fullpath reversed, for fill-to-next + revpath = '', + // functions for converting a point array to a path + pathfn, revpathbase, revpathfn; + + // make the fill-to-zero path now, so it shows behind the line + // fill to next puts the fill associated with one trace + // grouped with the previous + if(trace.fill.substr(0,6)==='tozero' || + (trace.fill.substr(0,2)==='to' && !prevpath)) { + tozero = tr.append('path') + .classed('js-fill',true); + } + else tozero = null; + + // make the fill-to-next path now for the NEXT trace, so it shows + // behind both lines. + // nexttonext was created last time, but give it + // this curve's data for fill color + if(nexttonext) tonext = nexttonext.datum(d); + + // now make a new nexttonext for next time + nexttonext = tr.append('path').classed('js-fill',true); + + if(['hv','vh','hvh','vhv'].indexOf(line.shape)!==-1) { + pathfn = Drawing.steps(line.shape); + revpathbase = Drawing.steps( + line.shape.split('').reverse().join('') + ); + } + else if(line.shape==='spline') { + pathfn = revpathbase = function(pts) { + return Drawing.smoothopen(pts, line.smoothing); + }; + } + else { + pathfn = revpathbase = function(pts) { + return 'M' + pts.join('L'); + }; + } + + revpathfn = function(pts) { + // note: this is destructive (reverses pts in place) so can't use pts after this + return 'L'+revpathbase(pts.reverse()).substr(1); + }; + + var segments = linePoints(d, { + xaxis: xa, + yaxis: ya, + connectGaps: trace.connectgaps, + baseTolerance: Math.max(line.width || 1, 3) / 4, + linear: line.shape === 'linear' + }); + + if(segments.length) { + var pt0 = segments[0][0], + lastSegment = segments[segments.length - 1], + pt1 = lastSegment[lastSegment.length - 1]; + + for(var i = 0; i < segments.length; i++) { + var pts = segments[i]; + thispath = pathfn(pts); + fullpath += fullpath ? ('L'+thispath.substr(1)) : thispath; + revpath = revpathfn(pts) + revpath; + if(subTypes.hasLines(trace) && pts.length > 1) { + tr.append('path').classed('js-line',true).attr('d', thispath); + } + } + if(tozero) { + if(pt0 && pt1) { + if(trace.fill.charAt(trace.fill.length-1)==='y') { + pt0[1]=pt1[1]=ya.c2p(0,true); + } + else pt0[0]=pt1[0]=xa.c2p(0,true); + + // fill to zero: full trace path, plus extension of + // the endpoints to the appropriate axis + tozero.attr('d',fullpath+'L'+pt1+'L'+pt0+'Z'); + } + } + else if(trace.fill.substr(0,6)==='tonext' && fullpath && prevpath) { + // fill to next: full trace path, plus the previous path reversed + tonext.attr('d',fullpath+prevpath+'Z'); + } + prevpath = revpath; + } + }); + + // remove paths that didn't get used + scattertraces.selectAll('path:not([d])').remove(); + + function visFilter(d){ + return d.filter(function(v){ return v.vis; }); + } + + scattertraces.append('g') + .attr('class','points') + .each(function(d){ + var trace = d[0].trace, + s = d3.select(this), + showMarkers = subTypes.hasMarkers(trace), + showText = subTypes.hasText(trace); + + if((!showMarkers && !showText) || trace.visible !== true) s.remove(); + else { + if(showMarkers) { + s.selectAll('path.point') + .data(trace.marker.maxdisplayed ? visFilter : Lib.identity) + .enter().append('path') + .classed('point', true) + .call(Drawing.translatePoints, xa, ya); + } + if(showText) { + s.selectAll('g') + .data(trace.marker.maxdisplayed ? visFilter : Lib.identity) + // each text needs to go in its own 'g' in case + // it gets converted to mathjax + .enter().append('g') + .append('text') + .call(Drawing.translatePoints, xa, ya); + } + } + }); +}; + +function selectMarkers(gd, plotinfo, cdscatter) { + var xa = plotinfo.x(), + ya = plotinfo.y(), + xr = d3.extent(xa.range.map(xa.l2c)), + yr = d3.extent(ya.range.map(ya.l2c)); + + cdscatter.forEach(function(d,i) { + var trace = d[0].trace; + if(!subTypes.hasMarkers(trace)) return; + // if marker.maxdisplayed is used, select a maximum of + // mnum markers to show, from the set that are in the viewport + var mnum = trace.marker.maxdisplayed; + + // TODO: remove some as we get away from the viewport? + if(mnum===0) return; + + var cd = d.filter(function(v) { + return v.x>=xr[0] && v.x<=xr[1] && v.y>=yr[0] && v.y<=yr[1]; + }), + inc = Math.ceil(cd.length/mnum), + tnum = 0; + cdscatter.forEach(function(cdj, j) { + var tracei = cdj[0].trace; + if(subTypes.hasMarkers(tracei) && + tracei.marker.maxdisplayed>0 && j Date: Fri, 15 Jan 2016 09:59:34 -0500 Subject: [PATCH 08/10] require scatter components directly into jasmine test file --- test/jasmine/tests/scatter_test.js | 31 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/test/jasmine/tests/scatter_test.js b/test/jasmine/tests/scatter_test.js index 5b492ce170b..08f6d19ebf9 100644 --- a/test/jasmine/tests/scatter_test.js +++ b/test/jasmine/tests/scatter_test.js @@ -1,4 +1,7 @@ -var Plotly = require('@src/plotly'); +var Scatter = require('@src/traces/scatter'); +var makeBubbleSizeFn = require('@src/traces/scatter/make_bubble_size_func'); +var linePoints = require('@src/traces/scatter/line_points'); +var Lib = require('@src/lib'); describe('Test scatter', function() { 'use strict'; @@ -10,7 +13,7 @@ describe('Test scatter', function() { var defaultColor = '#444', layout = {}; - var supplyDefaults = Plotly.Scatter.supplyDefaults; + var supplyDefaults = Scatter.supplyDefaults; beforeEach(function() { traceOut = {}; @@ -66,7 +69,7 @@ describe('Test scatter', function() { size: [1, 4, 2, 10] } }, - isBubble = Plotly.Scatter.isBubble(trace); + isBubble = Scatter.isBubble(trace); expect(isBubble).toBe(true); }); @@ -77,7 +80,7 @@ describe('Test scatter', function() { size: 10 } }, - isBubble = Plotly.Scatter.isBubble(trace); + isBubble = Scatter.isBubble(trace); expect(isBubble).toBe(false); }); @@ -88,7 +91,7 @@ describe('Test scatter', function() { color: 'red' } }, - isBubble = Plotly.Scatter.isBubble(trace); + isBubble = Scatter.isBubble(trace); expect(isBubble).toBe(false); }); @@ -99,16 +102,15 @@ describe('Test scatter', function() { color: 'red' } }, - isBubble = Plotly.Scatter.isBubble(trace); + isBubble = Scatter.isBubble(trace); expect(isBubble).toBe(false); }); }); - describe('getBubbleSizeFn', function() { - var getBubbleSizeFn = Plotly.Scatter.getBubbleSizeFn, - markerSizes = [ + describe('makeBubbleSizeFn', function() { + var markerSizes = [ 0, '1', 2.21321321, 'not-a-number', 100, 1000.213213, 1e7, undefined, null, -100 ], @@ -118,7 +120,7 @@ describe('Test scatter', function() { it('should scale w.r.t. bubble diameter when sizemode=diameter', function() { trace.marker.sizemode = 'diameter'; - sizeFn = getBubbleSizeFn(trace); + sizeFn = makeBubbleSizeFn(trace); expected = [ 0, 0.5, 1.106606605, 0, 50, 500.1066065, 5000000, 0, 0, 0 @@ -128,7 +130,7 @@ describe('Test scatter', function() { it('should scale w.r.t. bubble area when sizemode=area', function() { trace.marker.sizemode = 'area'; - sizeFn = getBubbleSizeFn(trace); + sizeFn = makeBubbleSizeFn(trace); expected = [ 0, 0.7071067811865476, 1.051953708582274, 0, 7.0710678118654755, @@ -140,7 +142,7 @@ describe('Test scatter', function() { it('should adjust scaling according to sizeref', function() { trace.marker.sizemode = 'diameter'; trace.marker.sizeref = 0.1; - sizeFn = getBubbleSizeFn(trace); + sizeFn = makeBubbleSizeFn(trace); expected = [ 0, 5, 11.06606605, 0, 500, 5001.066065, 50000000, 0, 0, 0 @@ -152,7 +154,7 @@ describe('Test scatter', function() { trace.marker.sizemode = 'diameter'; trace.marker.sizeref = 10; trace.marker.sizemin = 5; - sizeFn = getBubbleSizeFn(trace); + sizeFn = makeBubbleSizeFn(trace); expected = [ 0, 5, 5, 0, 5, 50.01066065, 500000, 0, 0, 0 @@ -163,8 +165,7 @@ describe('Test scatter', function() { describe('linePoints', function() { // test axes are unit-scaled and 100 units long - var ax = {_length: 100, c2p: Plotly.Lib.identity}, - linePoints = Plotly.Scatter.linePoints, + var ax = {_length: 100, c2p: Lib.identity}, baseOpts = { xaxis: ax, yaxis: ax, From 0be5d332a1383a6242aaaa18b39cddf4858f1055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Fri, 15 Jan 2016 11:05:14 -0500 Subject: [PATCH 09/10] lint ('comma-spacing', 'key-spacing', some 'space-infix-ops') --- src/components/legend/index.js | 1 - src/traces/scatter/attributes.js | 2 +- src/traces/scatter/calc.js | 44 ++++++++------- src/traces/scatter/clean_data.js | 2 +- src/traces/scatter/colorbar.js | 2 +- src/traces/scatter/defaults.js | 2 +- src/traces/scatter/hover.js | 14 ++--- src/traces/scatter/make_bubble_size_func.js | 7 ++- src/traces/scatter/marker_defaults.js | 2 +- src/traces/scatter/plot.js | 61 +++++++++++---------- src/traces/scatter/style.js | 4 +- src/traces/scatter/xy_defaults.js | 4 +- 12 files changed, 75 insertions(+), 70 deletions(-) diff --git a/src/components/legend/index.js b/src/components/legend/index.js index 814ff8d9a78..464774b1d79 100644 --- a/src/components/legend/index.js +++ b/src/components/legend/index.js @@ -1,4 +1,3 @@ - /** * Copyright 2012-2016, Plotly, Inc. * All rights reserved. diff --git a/src/traces/scatter/attributes.js b/src/traces/scatter/attributes.js index aae4c38983a..0860f89c4c4 100644 --- a/src/traces/scatter/attributes.js +++ b/src/traces/scatter/attributes.js @@ -77,7 +77,7 @@ module.exports = { }, mode: { valType: 'flaglist', - flags: ['lines','markers','text'], + flags: ['lines', 'markers', 'text'], extras: ['none'], role: 'info', description: [ diff --git a/src/traces/scatter/calc.js b/src/traces/scatter/calc.js index c7e6679f26c..27a8e158561 100644 --- a/src/traces/scatter/calc.js +++ b/src/traces/scatter/calc.js @@ -19,14 +19,17 @@ var calcMarkerColorscale = require('./marker_colorscale_calc'); module.exports = function calc(gd, trace) { - var xa = Axes.getFromId(gd,trace.xaxis||'x'), - ya = Axes.getFromId(gd,trace.yaxis||'y'); + var xa = Axes.getFromId(gd, trace.xaxis || 'x'), + ya = Axes.getFromId(gd, trace.yaxis || 'y'); Lib.markTime('in Scatter.calc'); - var x = xa.makeCalcdata(trace,'x'); + + var x = xa.makeCalcdata(trace, 'x'); Lib.markTime('finished convert x'); - var y = ya.makeCalcdata(trace,'y'); + + var y = ya.makeCalcdata(trace, 'y'); Lib.markTime('finished convert y'); - var serieslen = Math.min(x.length,y.length), + + var serieslen = Math.min(x.length, y.length), marker, s, i; @@ -35,13 +38,13 @@ module.exports = function calc(gd, trace) { xa._minDtick = 0; ya._minDtick = 0; - if(x.length>serieslen) x.splice(serieslen, x.length-serieslen); - if(y.length>serieslen) y.splice(serieslen, y.length-serieslen); + if(x.length > serieslen) x.splice(serieslen, x.length - serieslen); + if(y.length > serieslen) y.splice(serieslen, y.length - serieslen); // check whether bounds should be tight, padded, extended to zero... // most cases both should be padded on both ends, so start with that. - var xOptions = {padded:true}, - yOptions = {padded:true}; + var xOptions = {padded: true}, + yOptions = {padded: true}; if(subTypes.hasMarkers(trace)) { @@ -55,19 +58,19 @@ module.exports = function calc(gd, trace) { var ax = {type: 'linear'}; Axes.setConvert(ax); s = ax.makeCalcdata(trace.marker, 'size'); - if(s.length>serieslen) s.splice(serieslen, s.length-serieslen); + if(s.length > serieslen) s.splice(serieslen, s.length - serieslen); } - var sizeref = 1.6*(trace.marker.sizeref||1), + var sizeref = 1.6 * (trace.marker.sizeref || 1), markerTrans; - if(trace.marker.sizemode==='area') { + if(trace.marker.sizemode === 'area') { markerTrans = function(v) { - return Math.max(Math.sqrt((v||0)/sizeref),3); + return Math.max(Math.sqrt((v || 0) / sizeref), 3); }; } else { markerTrans = function(v) { - return Math.max((v||0)/sizeref,3); + return Math.max((v || 0) / sizeref, 3); }; } xOptions.ppad = yOptions.ppad = Array.isArray(s) ? @@ -80,14 +83,15 @@ module.exports = function calc(gd, trace) { // include zero (tight) and extremes (padded) if fill to zero // (unless the shape is closed, then it's just filling the shape regardless) - if((trace.fill==='tozerox' || (trace.fill==='tonextx' && gd.firstscatter)) && - (x[0]!==x[serieslen-1] || y[0]!==y[serieslen-1])) { + if(((trace.fill === 'tozerox') || + ((trace.fill === 'tonextx') && gd.firstscatter)) && + ((x[0] !== x[serieslen - 1]) || (y[0] !== y[serieslen - 1]))) { xOptions.tozero = true; } // if no error bars, markers or text, or fill to y=0 remove x padding else if(!trace.error_y.visible && ( - ['tonexty', 'tozeroy'].indexOf(trace.fill)!==-1 || + ['tonexty', 'tozeroy'].indexOf(trace.fill) !== -1 || (!subTypes.hasMarkers(trace) && !subTypes.hasText(trace)) )) { xOptions.padded = false; @@ -97,13 +101,13 @@ module.exports = function calc(gd, trace) { // now check for y - rather different logic, though still mostly padded both ends // include zero (tight) and extremes (padded) if fill to zero // (unless the shape is closed, then it's just filling the shape regardless) - if((trace.fill==='tozeroy' || (trace.fill==='tonexty' && gd.firstscatter)) && - (x[0]!==x[serieslen-1] || y[0]!==y[serieslen-1])) { + if(((trace.fill === 'tozeroy') || ((trace.fill === 'tonexty') && gd.firstscatter)) && + ((x[0] !== x[serieslen - 1]) || (y[0] !== y[serieslen - 1]))) { yOptions.tozero = true; } // tight y: any x fill - else if(['tonextx', 'tozerox'].indexOf(trace.fill)!==-1) { + else if(['tonextx', 'tozerox'].indexOf(trace.fill) !== -1) { yOptions.padded = false; } diff --git a/src/traces/scatter/clean_data.js b/src/traces/scatter/clean_data.js index 5477da4dfc4..60ec2481e12 100644 --- a/src/traces/scatter/clean_data.js +++ b/src/traces/scatter/clean_data.js @@ -21,7 +21,7 @@ module.exports = function cleanData(fullData) { for(i = 0; i < fullData.length; i++) { tracei = fullData[i]; filli = tracei.fill; - if(filli==='none' || (tracei.type !== 'scatter')) continue; + if((filli === 'none') || (tracei.type !== 'scatter')) continue; tracei.opacity = undefined; if(filli === 'tonexty' || filli === 'tonextx') { diff --git a/src/traces/scatter/colorbar.js b/src/traces/scatter/colorbar.js index ff2bcdba0f8..03237f1f17d 100644 --- a/src/traces/scatter/colorbar.js +++ b/src/traces/scatter/colorbar.js @@ -28,7 +28,7 @@ module.exports = function colorbar(gd, cd) { // TODO unify Scatter.colorbar and Heatmap.colorbar // TODO make Plotly[module].colorbar support multiple colorbar per trace - if(marker===undefined || !marker.showscale){ + if((marker === undefined) || !marker.showscale){ Plotly.Plots.autoMargin(gd, cbId); return; } diff --git a/src/traces/scatter/defaults.js b/src/traces/scatter/defaults.js index 7b045a705cc..c18250b8208 100644 --- a/src/traces/scatter/defaults.js +++ b/src/traces/scatter/defaults.js @@ -68,5 +68,5 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout function lineShapeDefaults(traceIn, traceOut, coerce) { var shape = coerce('line.shape'); - if(shape==='spline') coerce('line.smoothing'); + if(shape === 'spline') coerce('line.smoothing'); } diff --git a/src/traces/scatter/hover.js b/src/traces/scatter/hover.js index 197d95c9fcc..8ade45b51b1 100644 --- a/src/traces/scatter/hover.js +++ b/src/traces/scatter/hover.js @@ -19,20 +19,20 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { trace = cd[0].trace, xa = pointData.xa, ya = pointData.ya, - dx = function(di){ + dx = function(di) { // scatter points: d.mrc is the calculated marker radius // adjust the distance so if you're inside the marker it // always will show up regardless of point size, but // prioritize smaller points - var rad = Math.max(3, di.mrc||0); + var rad = Math.max(3, di.mrc || 0); return Math.max(Math.abs(xa.c2p(di.x)-xa.c2p(xval))-rad, 1-3/rad); }, - dy = function(di){ - var rad = Math.max(3, di.mrc||0); + dy = function(di) { + var rad = Math.max(3, di.mrc || 0); return Math.max(Math.abs(ya.c2p(di.y)-ya.c2p(yval))-rad, 1-3/rad); }, dxy = function(di) { - var rad = Math.max(3, di.mrc||0), + var rad = Math.max(3, di.mrc || 0), dx = Math.abs(xa.c2p(di.x)-xa.c2p(xval)), dy = Math.abs(ya.c2p(di.y)-ya.c2p(yval)); return Math.max(Math.sqrt(dx*dx + dy*dy)-rad, 1-3/rad); @@ -42,13 +42,13 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { Fx.getClosest(cd, distfn, pointData); // skip the rest (for this trace) if we didn't find a close point - if(pointData.index===false) return; + if(pointData.index === false) return; // the closest data point var di = cd[pointData.index], xc = xa.c2p(di.x, true), yc = ya.c2p(di.y, true), - rad = di.mrc||1; + rad = di.mrc || 1; pointData.color = getTraceColor(trace, di); diff --git a/src/traces/scatter/make_bubble_size_func.js b/src/traces/scatter/make_bubble_size_func.js index c87639dba50..160c03ec667 100644 --- a/src/traces/scatter/make_bubble_size_func.js +++ b/src/traces/scatter/make_bubble_size_func.js @@ -23,7 +23,7 @@ module.exports = function makeBubbleSizeFn(trace) { // and by area or diameter. // Note this only applies to the array-value sizes - var baseFn = marker.sizemode==='area' ? + var baseFn = (marker.sizemode === 'area') ? function(v) { return Math.sqrt(v / sizeRef); } : function(v) { return v / sizeRef; }; @@ -33,7 +33,8 @@ module.exports = function makeBubbleSizeFn(trace) { var baseSize = baseFn(v / 2); // don't show non-numeric and negative sizes - return (isNumeric(baseSize) && baseSize>0) ? - Math.max(baseSize, sizeMin) : 0; + return (isNumeric(baseSize) && (baseSize > 0)) ? + Math.max(baseSize, sizeMin) : + 0; }; }; diff --git a/src/traces/scatter/marker_defaults.js b/src/traces/scatter/marker_defaults.js index ed6382fd94c..57b85dbeb1f 100644 --- a/src/traces/scatter/marker_defaults.js +++ b/src/traces/scatter/marker_defaults.js @@ -38,7 +38,7 @@ module.exports = function markerDefaults(traceIn, traceOut, defaultColor, layout // if there's a line with a different color than the marker, use // that line color as the default marker line color // mostly this is for transparent markers to behave nicely - if(lineColor && traceOut.marker.color!==lineColor) { + if(lineColor && (traceOut.marker.color !== lineColor)) { defaultMLC = lineColor; } else if(isBubble) defaultMLC = Color.background; diff --git a/src/traces/scatter/plot.js b/src/traces/scatter/plot.js index c4934faa9d1..09345bcea7d 100644 --- a/src/traces/scatter/plot.js +++ b/src/traces/scatter/plot.js @@ -31,12 +31,13 @@ module.exports = function plot(gd, plotinfo, cdscatter) { .selectAll('g.trace.scatter') .data(cdscatter); scattertraces.enter().append('g') - .attr('class','trace scatter') - .style('stroke-miterlimit',2); + .attr('class', 'trace scatter') + .style('stroke-miterlimit', 2); // BUILD LINES AND FILLS - var prevpath='', - tozero,tonext,nexttonext; + var prevpath = '', + tozero, tonext, nexttonext; + scattertraces.each(function(d){ var trace = d[0].trace, line = trace.line, @@ -47,7 +48,7 @@ module.exports = function plot(gd, plotinfo, cdscatter) { arraysToCalcdata(d); - if(!subTypes.hasLines(trace) && trace.fill==='none') return; + if(!subTypes.hasLines(trace) && trace.fill === 'none') return; var thispath, // fullpath is all paths for this curve, joined together straight @@ -61,10 +62,10 @@ module.exports = function plot(gd, plotinfo, cdscatter) { // make the fill-to-zero path now, so it shows behind the line // fill to next puts the fill associated with one trace // grouped with the previous - if(trace.fill.substr(0,6)==='tozero' || - (trace.fill.substr(0,2)==='to' && !prevpath)) { + if(trace.fill.substr(0, 6) === 'tozero' || + (trace.fill.substr(0, 2) === 'to' && !prevpath)) { tozero = tr.append('path') - .classed('js-fill',true); + .classed('js-fill', true); } else tozero = null; @@ -75,15 +76,15 @@ module.exports = function plot(gd, plotinfo, cdscatter) { if(nexttonext) tonext = nexttonext.datum(d); // now make a new nexttonext for next time - nexttonext = tr.append('path').classed('js-fill',true); + nexttonext = tr.append('path').classed('js-fill', true); - if(['hv','vh','hvh','vhv'].indexOf(line.shape)!==-1) { + if(['hv', 'vh', 'hvh', 'vhv'].indexOf(line.shape) !== -1) { pathfn = Drawing.steps(line.shape); revpathbase = Drawing.steps( line.shape.split('').reverse().join('') ); } - else if(line.shape==='spline') { + else if(line.shape === 'spline') { pathfn = revpathbase = function(pts) { return Drawing.smoothopen(pts, line.smoothing); }; @@ -96,7 +97,7 @@ module.exports = function plot(gd, plotinfo, cdscatter) { revpathfn = function(pts) { // note: this is destructive (reverses pts in place) so can't use pts after this - return 'L'+revpathbase(pts.reverse()).substr(1); + return 'L' + revpathbase(pts.reverse()).substr(1); }; var segments = linePoints(d, { @@ -115,27 +116,27 @@ module.exports = function plot(gd, plotinfo, cdscatter) { for(var i = 0; i < segments.length; i++) { var pts = segments[i]; thispath = pathfn(pts); - fullpath += fullpath ? ('L'+thispath.substr(1)) : thispath; + fullpath += fullpath ? ('L' + thispath.substr(1)) : thispath; revpath = revpathfn(pts) + revpath; if(subTypes.hasLines(trace) && pts.length > 1) { - tr.append('path').classed('js-line',true).attr('d', thispath); + tr.append('path').classed('js-line', true).attr('d', thispath); } } if(tozero) { if(pt0 && pt1) { - if(trace.fill.charAt(trace.fill.length-1)==='y') { - pt0[1]=pt1[1]=ya.c2p(0,true); + if(trace.fill.charAt(trace.fill.length - 1) === 'y') { + pt0[1] = pt1[1] = ya.c2p(0, true); } - else pt0[0]=pt1[0]=xa.c2p(0,true); + else pt0[0] = pt1[0] = xa.c2p(0, true); // fill to zero: full trace path, plus extension of // the endpoints to the appropriate axis - tozero.attr('d',fullpath+'L'+pt1+'L'+pt0+'Z'); + tozero.attr('d', fullpath + 'L' + pt1 + 'L' + pt0 + 'Z'); } } - else if(trace.fill.substr(0,6)==='tonext' && fullpath && prevpath) { + else if(trace.fill.substr(0, 6) === 'tonext' && fullpath && prevpath) { // fill to next: full trace path, plus the previous path reversed - tonext.attr('d',fullpath+prevpath+'Z'); + tonext.attr('d', fullpath + prevpath + 'Z'); } prevpath = revpath; } @@ -145,12 +146,12 @@ module.exports = function plot(gd, plotinfo, cdscatter) { scattertraces.selectAll('path:not([d])').remove(); function visFilter(d){ - return d.filter(function(v){ return v.vis; }); + return d.filter(function(v) { return v.vis; }); } scattertraces.append('g') - .attr('class','points') - .each(function(d){ + .attr('class', 'points') + .each(function(d) { var trace = d[0].trace, s = d3.select(this), showMarkers = subTypes.hasMarkers(trace), @@ -184,7 +185,7 @@ function selectMarkers(gd, plotinfo, cdscatter) { xr = d3.extent(xa.range.map(xa.l2c)), yr = d3.extent(ya.range.map(ya.l2c)); - cdscatter.forEach(function(d,i) { + cdscatter.forEach(function(d, i) { var trace = d[0].trace; if(!subTypes.hasMarkers(trace)) return; // if marker.maxdisplayed is used, select a maximum of @@ -192,12 +193,12 @@ function selectMarkers(gd, plotinfo, cdscatter) { var mnum = trace.marker.maxdisplayed; // TODO: remove some as we get away from the viewport? - if(mnum===0) return; + if(mnum === 0) return; var cd = d.filter(function(v) { return v.x>=xr[0] && v.x<=xr[1] && v.y>=yr[0] && v.y<=yr[1]; }), - inc = Math.ceil(cd.length/mnum), + inc = Math.ceil(cd.length / mnum), tnum = 0; cdscatter.forEach(function(cdj, j) { var tracei = cdj[0].trace; @@ -211,13 +212,13 @@ function selectMarkers(gd, plotinfo, cdscatter) { // display this formula offsets successive traces by 1/3 of the // increment, adding an extra small amount after each triplet so // it's not quite periodic - var i0 = Math.round(tnum*inc/3 + Math.floor(tnum/3)*inc/7.1); + var i0 = Math.round(tnum*inc/3 + Math.floor(tnum/3) * inc/7.1); // for error bars: save in cd which markers to show // so we don't have to repeat this - d.forEach(function(v){ delete v.vis; }); - cd.forEach(function(v,i) { - if(Math.round((i+i0)%inc)===0) v.vis = true; + d.forEach(function(v) { delete v.vis; }); + cd.forEach(function(v, i) { + if(Math.round((i + i0) % inc) === 0) v.vis = true; }); }); } diff --git a/src/traces/scatter/style.js b/src/traces/scatter/style.js index a0f9565f0ae..e788ed257f6 100644 --- a/src/traces/scatter/style.js +++ b/src/traces/scatter/style.js @@ -24,9 +24,9 @@ module.exports = function style(gd) { s.selectAll('g.points') .each(function(d){ d3.select(this).selectAll('path.point') - .call(Drawing.pointStyle,d.trace||d[0].trace); + .call(Drawing.pointStyle, d.trace || d[0].trace); d3.select(this).selectAll('text') - .call(Drawing.textPointStyle,d.trace||d[0].trace); + .call(Drawing.textPointStyle, d.trace || d[0].trace); }); s.selectAll('g.trace path.js-line') diff --git a/src/traces/scatter/xy_defaults.js b/src/traces/scatter/xy_defaults.js index c7665789ca0..d85a0aea2b1 100644 --- a/src/traces/scatter/xy_defaults.js +++ b/src/traces/scatter/xy_defaults.js @@ -23,8 +23,8 @@ module.exports = function handleXYDefaults(traceIn, traceOut, coerce) { // which could be a problem eg in streaming / editing if x and y // come in at different times // so we need to revisit calc before taking this out - if(len Date: Fri, 15 Jan 2016 11:17:05 -0500 Subject: [PATCH 10/10] rm Scatter from internal Plotly object --- src/plotly.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/plotly.js b/src/plotly.js index b7834839b4a..cc0f90b584e 100644 --- a/src/plotly.js +++ b/src/plotly.js @@ -84,11 +84,8 @@ exports.register = function register(_modules) { } }; - -exports.register(require('./traces/scatter')); - // Scatter is the only trace included by default -exports.Scatter = Plots.getModule('scatter'); +exports.register(require('./traces/scatter')); // plot api require('./plot_api/plot_api');