diff --git a/draftlogs/6381_add.md b/draftlogs/6381_add.md
new file mode 100644
index 00000000000..562a5f008f5
--- /dev/null
+++ b/draftlogs/6381_add.md
@@ -0,0 +1,2 @@
+ - Introduce group attributes for `scatter` trace i.e. `alignmentgroup`, `offsetgroup`, `scattermode` and `scattergap` [[#6381](https://github.com/plotly/plotly.js/pull/6381)],
+ this feature was anonymously sponsored: thank you to our sponsor!
diff --git a/src/plots/plots.js b/src/plots/plots.js
index 29b375d62fa..3790ef8e9bc 100644
--- a/src/plots/plots.js
+++ b/src/plots/plots.js
@@ -14,6 +14,7 @@ var BADNUM = require('../constants/numerical').BADNUM;
var axisIDs = require('./cartesian/axis_ids');
var clearOutline = require('../components/shapes/handle_outline').clearOutline;
+var scatterAttrs = require('../traces/scatter/layout_attributes');
var animationAttrs = require('./animation_attributes');
var frameAttrs = require('./frame_attributes');
@@ -1566,6 +1567,8 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) {
'fx',
'supplyLayoutGlobalDefaults'
)(layoutIn, layoutOut, coerce);
+
+ Lib.coerce(layoutIn, layoutOut, scatterAttrs, 'scattermode');
};
function getComputedSize(attr) {
diff --git a/src/traces/bar/attributes.js b/src/traces/bar/attributes.js
index de48a83c666..12d909f4c95 100644
--- a/src/traces/bar/attributes.js
+++ b/src/traces/bar/attributes.js
@@ -196,27 +196,8 @@ module.exports = {
marker: marker,
- offsetgroup: {
- valType: 'string',
- dflt: '',
- editType: 'calc',
- description: [
- 'Set several traces linked to the same position axis',
- 'or matching axes to the same',
- 'offsetgroup where bars of the same position coordinate will line up.'
- ].join(' ')
- },
- alignmentgroup: {
- valType: 'string',
- dflt: '',
- editType: 'calc',
- description: [
- 'Set several traces linked to the same position axis',
- 'or matching axes to the same',
- 'alignmentgroup. This controls whether bars compute their positional',
- 'range dependently or independently.'
- ].join(' ')
- },
+ offsetgroup: scatterAttrs.offsetgroup,
+ alignmentgroup: scatterAttrs.alignmentgroup,
selected: {
marker: {
diff --git a/src/traces/bar/cross_trace_calc.js b/src/traces/bar/cross_trace_calc.js
index f1cad80924d..a63b4cc248b 100644
--- a/src/traces/bar/cross_trace_calc.js
+++ b/src/traces/bar/cross_trace_calc.js
@@ -441,7 +441,14 @@ function setBarCenterAndWidth(pa, sieve) {
// store the actual bar width and position, for use by hover
var width = calcBar.w = barwidthIsArray ? barwidth[j] : barwidth;
- calcBar[pLetter] = calcBar.p + (poffsetIsArray ? poffset[j] : poffset) + width / 2;
+
+ if(calcBar.p === undefined) {
+ calcBar.p = calcBar[pLetter];
+ calcBar['orig_' + pLetter] = calcBar[pLetter];
+ }
+
+ var delta = (poffsetIsArray ? poffset[j] : poffset) + width / 2;
+ calcBar[pLetter] = calcBar.p + delta;
}
}
}
@@ -498,13 +505,17 @@ function setBaseAndTop(sa, sieve) {
for(var i = 0; i < calcTraces.length; i++) {
var calcTrace = calcTraces[i];
var fullTrace = calcTrace[0].trace;
+ var isScatter = fullTrace.type === 'scatter';
+ var isVertical = fullTrace.orientation === 'v';
var pts = [];
var tozero = false;
for(var j = 0; j < calcTrace.length; j++) {
var bar = calcTrace[j];
- var base = bar.b;
- var top = base + bar.s;
+ var base = isScatter ? 0 : bar.b;
+ var top = isScatter ? (
+ isVertical ? bar.y : bar.x
+ ) : base + bar.s;
bar[sLetter] = top;
pts.push(top);
diff --git a/src/traces/bar/defaults.js b/src/traces/bar/defaults.js
index 419cb45c095..3ed0e57ddea 100644
--- a/src/traces/bar/defaults.js
+++ b/src/traces/bar/defaults.js
@@ -7,7 +7,7 @@ var Registry = require('../../registry');
var handleXYDefaults = require('../scatter/xy_defaults');
var handlePeriodDefaults = require('../scatter/period_defaults');
var handleStyleDefaults = require('./style_defaults');
-var handleGroupingDefaults = require('./grouping_defaults');
+var handleGroupingDefaults = require('../scatter/grouping_defaults');
var attributes = require('./attributes');
var coerceFont = Lib.coerceFont;
diff --git a/src/traces/bar/sieve.js b/src/traces/bar/sieve.js
index ad7eec482f8..54dc5a74b25 100644
--- a/src/traces/bar/sieve.js
+++ b/src/traces/bar/sieve.js
@@ -3,7 +3,6 @@
module.exports = Sieve;
var distinctVals = require('../../lib').distinctVals;
-var BADNUM = require('../../constants/numerical').BADNUM;
/**
* Helper class to sieve data from traces into bins
@@ -27,12 +26,18 @@ function Sieve(traces, opts) {
// for single-bin histograms - see histogram/calc
var width1 = Infinity;
+ var axLetter = opts.posAxis._id.charAt(0);
+
var positions = [];
for(var i = 0; i < traces.length; i++) {
var trace = traces[i];
for(var j = 0; j < trace.length; j++) {
var bar = trace[j];
- if(bar.p !== BADNUM) positions.push(bar.p);
+ var pos = bar.p;
+ if(pos === undefined) {
+ pos = bar[axLetter];
+ }
+ if(pos !== undefined) positions.push(pos);
}
if(trace[0] && trace[0].width1) {
width1 = Math.min(trace[0].width1, width1);
diff --git a/src/traces/box/defaults.js b/src/traces/box/defaults.js
index 5ecb56e0e02..f2f087b714a 100644
--- a/src/traces/box/defaults.js
+++ b/src/traces/box/defaults.js
@@ -4,7 +4,7 @@ var Lib = require('../../lib');
var Registry = require('../../registry');
var Color = require('../../components/color');
var handlePeriodDefaults = require('../scatter/period_defaults');
-var handleGroupingDefaults = require('../bar/grouping_defaults');
+var handleGroupingDefaults = require('../scatter/grouping_defaults');
var autoType = require('../../plots/cartesian/axis_autotype');
var attributes = require('./attributes');
diff --git a/src/traces/funnel/defaults.js b/src/traces/funnel/defaults.js
index eaba2fb103b..f729bc21639 100644
--- a/src/traces/funnel/defaults.js
+++ b/src/traces/funnel/defaults.js
@@ -2,7 +2,7 @@
var Lib = require('../../lib');
-var handleGroupingDefaults = require('../bar/grouping_defaults');
+var handleGroupingDefaults = require('../scatter/grouping_defaults');
var handleText = require('../bar/defaults').handleText;
var handleXYDefaults = require('../scatter/xy_defaults');
var handlePeriodDefaults = require('../scatter/period_defaults');
diff --git a/src/traces/histogram/cross_trace_defaults.js b/src/traces/histogram/cross_trace_defaults.js
index 15eeb3c8eb7..5e09a9e34c6 100644
--- a/src/traces/histogram/cross_trace_defaults.js
+++ b/src/traces/histogram/cross_trace_defaults.js
@@ -4,7 +4,7 @@ var Lib = require('../../lib');
var axisIds = require('../../plots/cartesian/axis_ids');
var traceIs = require('../../registry').traceIs;
-var handleGroupingDefaults = require('../bar/grouping_defaults');
+var handleGroupingDefaults = require('../scatter/grouping_defaults');
var nestedProperty = Lib.nestedProperty;
var getAxisGroup = require('../../plots/cartesian/constraints').getAxisGroup;
diff --git a/src/traces/scatter/attributes.js b/src/traces/scatter/attributes.js
index cce2de0cd7d..f6beafa16a4 100644
--- a/src/traces/scatter/attributes.js
+++ b/src/traces/scatter/attributes.js
@@ -123,6 +123,29 @@ module.exports = {
xhoverformat: axisHoverFormat('x'),
yhoverformat: axisHoverFormat('y'),
+ offsetgroup: {
+ valType: 'string',
+ dflt: '',
+ editType: 'calc',
+ description: [
+ 'Set several traces linked to the same position axis',
+ 'or matching axes to the same',
+ 'offsetgroup where bars of the same position coordinate will line up.'
+ ].join(' ')
+ },
+
+ alignmentgroup: {
+ valType: 'string',
+ dflt: '',
+ editType: 'calc',
+ description: [
+ 'Set several traces linked to the same position axis',
+ 'or matching axes to the same',
+ 'alignmentgroup. This controls whether bars compute their positional',
+ 'range dependently or independently.'
+ ].join(' ')
+ },
+
stackgroup: {
valType: 'string',
dflt: '',
@@ -146,7 +169,9 @@ module.exports = {
values: ['v', 'h'],
editType: 'calc',
description: [
- 'Only relevant when `stackgroup` is used, and only the first',
+ 'Only relevant in the following cases:',
+ '1. when `scattermode` is set to *group*.',
+ '2. when `stackgroup` is used, and only the first',
'`orientation` found in the `stackgroup` will be used - including',
'if `visible` is *legendonly* but not if it is `false`. Sets the',
'stacking direction. With *v* (*h*), the y (x) values of subsequent',
diff --git a/src/traces/scatter/cross_trace_calc.js b/src/traces/scatter/cross_trace_calc.js
index 69006d8d45a..0456de332c7 100644
--- a/src/traces/scatter/cross_trace_calc.js
+++ b/src/traces/scatter/cross_trace_calc.js
@@ -1,6 +1,42 @@
'use strict';
var calc = require('./calc');
+var setGroupPositions = require('../bar/cross_trace_calc').setGroupPositions;
+
+function groupCrossTraceCalc(gd, plotinfo) {
+ var xa = plotinfo.xaxis;
+ var ya = plotinfo.yaxis;
+
+ var fullLayout = gd._fullLayout;
+ var fullTraces = gd._fullData;
+ var calcTraces = gd.calcdata;
+ var calcTracesHorz = [];
+ var calcTracesVert = [];
+
+ for(var i = 0; i < fullTraces.length; i++) {
+ var fullTrace = fullTraces[i];
+ if(
+ fullTrace.visible === true &&
+ fullTrace.type === 'scatter' &&
+ fullTrace.xaxis === xa._id &&
+ fullTrace.yaxis === ya._id
+ ) {
+ if(fullTrace.orientation === 'h') {
+ calcTracesHorz.push(calcTraces[i]);
+ } else if(fullTrace.orientation === 'v') { // check for v since certain scatter traces may not have an orientation
+ calcTracesVert.push(calcTraces[i]);
+ }
+ }
+ }
+
+ var opts = {
+ mode: fullLayout.scattermode,
+ gap: fullLayout.scattergap
+ };
+
+ setGroupPositions(gd, xa, ya, calcTracesVert, opts);
+ setGroupPositions(gd, ya, xa, calcTracesHorz, opts);
+}
/*
* Scatter stacking & normalization calculations
@@ -8,6 +44,10 @@ var calc = require('./calc');
*/
module.exports = function crossTraceCalc(gd, plotinfo) {
+ if(gd._fullLayout.scattermode === 'group') {
+ groupCrossTraceCalc(gd, plotinfo);
+ }
+
var xa = plotinfo.xaxis;
var ya = plotinfo.yaxis;
var subplot = xa._id + ya._id;
diff --git a/src/traces/scatter/cross_trace_defaults.js b/src/traces/scatter/cross_trace_defaults.js
index 61b4f734b57..6505c6573d7 100644
--- a/src/traces/scatter/cross_trace_defaults.js
+++ b/src/traces/scatter/cross_trace_defaults.js
@@ -1,9 +1,29 @@
'use strict';
+var Lib = require('../../lib');
+var handleGroupingDefaults = require('./grouping_defaults');
+var attributes = require('./attributes');
// remove opacity for any trace that has a fill or is filled to
-module.exports = function crossTraceDefaults(fullData) {
- for(var i = 0; i < fullData.length; i++) {
+module.exports = function crossTraceDefaults(fullData, fullLayout) {
+ var traceIn, traceOut, i;
+
+ function coerce(attr) {
+ return Lib.coerce(traceOut._input, traceOut, attributes, attr);
+ }
+
+ if(fullLayout.scattermode === 'group') {
+ for(i = 0; i < fullData.length; i++) {
+ traceOut = fullData[i];
+
+ if(traceOut.type === 'scatter') {
+ traceIn = traceOut._input;
+ handleGroupingDefaults(traceIn, traceOut, fullLayout, coerce);
+ }
+ }
+ }
+
+ for(i = 0; i < fullData.length; i++) {
var tracei = fullData[i];
if(tracei.type !== 'scatter') continue;
diff --git a/src/traces/scatter/defaults.js b/src/traces/scatter/defaults.js
index 33ac037c920..9c980ca69de 100644
--- a/src/traces/scatter/defaults.js
+++ b/src/traces/scatter/defaults.js
@@ -31,6 +31,12 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
coerce('yhoverformat');
var stackGroupOpts = handleStackDefaults(traceIn, traceOut, layout, coerce);
+ if(
+ layout.scattermode === 'group' &&
+ traceOut.orientation === undefined
+ ) {
+ coerce('orientation', 'v');
+ }
var defaultMode = !stackGroupOpts && (len < constants.PTS_LINESONLY) ?
'lines+markers' : 'lines';
diff --git a/src/traces/scatter/format_labels.js b/src/traces/scatter/format_labels.js
index 5908a425466..079afd0956f 100644
--- a/src/traces/scatter/format_labels.js
+++ b/src/traces/scatter/format_labels.js
@@ -9,8 +9,14 @@ module.exports = function formatLabels(cdi, trace, fullLayout) {
var xa = Axes.getFromTrace(mockGd, trace, 'x');
var ya = Axes.getFromTrace(mockGd, trace, 'y');
- labels.xLabel = Axes.tickText(xa, xa.c2l(cdi.x), true).text;
- labels.yLabel = Axes.tickText(ya, ya.c2l(cdi.y), true).text;
+ var x = cdi.orig_x;
+ if(x === undefined) x = cdi.x;
+
+ var y = cdi.orig_y;
+ if(y === undefined) y = cdi.y;
+
+ labels.xLabel = Axes.tickText(xa, xa.c2l(x), true).text;
+ labels.yLabel = Axes.tickText(ya, ya.c2l(y), true).text;
return labels;
};
diff --git a/src/traces/bar/grouping_defaults.js b/src/traces/scatter/grouping_defaults.js
similarity index 100%
rename from src/traces/bar/grouping_defaults.js
rename to src/traces/scatter/grouping_defaults.js
diff --git a/src/traces/scatter/index.js b/src/traces/scatter/index.js
index ca331864668..2de0e3ed8cd 100644
--- a/src/traces/scatter/index.js
+++ b/src/traces/scatter/index.js
@@ -9,8 +9,10 @@ module.exports = {
isBubble: subtypes.isBubble,
attributes: require('./attributes'),
+ layoutAttributes: require('./layout_attributes'),
supplyDefaults: require('./defaults'),
crossTraceDefaults: require('./cross_trace_defaults'),
+ supplyLayoutDefaults: require('./layout_defaults'),
calc: require('./calc').calc,
crossTraceCalc: require('./cross_trace_calc'),
arraysToCalcdata: require('./arrays_to_calcdata'),
diff --git a/src/traces/scatter/layout_attributes.js b/src/traces/scatter/layout_attributes.js
new file mode 100644
index 00000000000..86c2d474f47
--- /dev/null
+++ b/src/traces/scatter/layout_attributes.js
@@ -0,0 +1,30 @@
+'use strict';
+
+
+module.exports = {
+ scattermode: {
+ valType: 'enumerated',
+ values: ['group', 'overlay'],
+ dflt: 'overlay',
+ editType: 'calc',
+ description: [
+ 'Determines how scatter points at the same location coordinate',
+ 'are displayed on the graph.',
+ 'With *group*, the scatter points are plotted next to one another',
+ 'centered around the shared location.',
+ 'With *overlay*, the scatter points are plotted over one another,',
+ 'you might need to reduce *opacity* to see multiple scatter points.'
+ ].join(' ')
+ },
+ scattergap: {
+ valType: 'number',
+ min: 0,
+ max: 1,
+ editType: 'calc',
+ description: [
+ 'Sets the gap (in plot fraction) between scatter points of',
+ 'adjacent location coordinates.',
+ 'Defaults to `bargap`.'
+ ].join(' ')
+ }
+};
diff --git a/src/traces/scatter/layout_defaults.js b/src/traces/scatter/layout_defaults.js
new file mode 100644
index 00000000000..37deee850db
--- /dev/null
+++ b/src/traces/scatter/layout_defaults.js
@@ -0,0 +1,17 @@
+'use strict';
+
+var Lib = require('../../lib');
+
+var layoutAttributes = require('./layout_attributes');
+
+module.exports = function(layoutIn, layoutOut) {
+ function coerce(attr, dflt) {
+ return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
+ }
+
+ var groupBarmode = layoutOut.barmode === 'group';
+
+ if(layoutOut.scattermode === 'group') {
+ coerce('scattergap', groupBarmode ? layoutOut.bargap : 0.2);
+ }
+};
diff --git a/src/traces/waterfall/defaults.js b/src/traces/waterfall/defaults.js
index 299c894e39d..e5b07290ab6 100644
--- a/src/traces/waterfall/defaults.js
+++ b/src/traces/waterfall/defaults.js
@@ -2,7 +2,7 @@
var Lib = require('../../lib');
-var handleGroupingDefaults = require('../bar/grouping_defaults');
+var handleGroupingDefaults = require('../scatter/grouping_defaults');
var handleText = require('../bar/defaults').handleText;
var handleXYDefaults = require('../scatter/xy_defaults');
var handlePeriodDefaults = require('../scatter/period_defaults');
diff --git a/test/image/baselines/zz-grouped_scatter-linear.png b/test/image/baselines/zz-grouped_scatter-linear.png
new file mode 100644
index 00000000000..55e0666acaa
Binary files /dev/null and b/test/image/baselines/zz-grouped_scatter-linear.png differ
diff --git a/test/image/baselines/zz-grouped_scatter.png b/test/image/baselines/zz-grouped_scatter.png
new file mode 100644
index 00000000000..7d19f1a20d9
Binary files /dev/null and b/test/image/baselines/zz-grouped_scatter.png differ
diff --git a/test/image/baselines/zz-scatter-grouping-vs-defaults.png b/test/image/baselines/zz-scatter-grouping-vs-defaults.png
new file mode 100644
index 00000000000..42e135f6824
Binary files /dev/null and b/test/image/baselines/zz-scatter-grouping-vs-defaults.png differ
diff --git a/test/image/mocks/zz-grouped_scatter-linear.json b/test/image/mocks/zz-grouped_scatter-linear.json
new file mode 100644
index 00000000000..e0d0ce0da1b
--- /dev/null
+++ b/test/image/mocks/zz-grouped_scatter-linear.json
@@ -0,0 +1,31 @@
+{
+ "data": [
+ {
+ "mode": "markers+lines+text",
+ "texttemplate": "x:%{x}",
+ "textposition": "bottom center",
+ "textfont": { "size": 16 },
+ "y": [
+ 1,
+ 3,
+ 2
+ ]
+ },
+ {
+ "mode": "markers+lines+text",
+ "texttemplate": "x:%{x}",
+ "textposition": "bottom center",
+ "textfont": { "size": 16 },
+ "y": [
+ 2,
+ 1,
+ 3
+ ]
+ }
+ ],
+ "layout": {
+ "width": 600,
+ "height": 400,
+ "scattermode": "group"
+ }
+}
diff --git a/test/image/mocks/zz-grouped_scatter.json b/test/image/mocks/zz-grouped_scatter.json
new file mode 100644
index 00000000000..2ad6f2efd01
--- /dev/null
+++ b/test/image/mocks/zz-grouped_scatter.json
@@ -0,0 +1,35 @@
+{
+ "data": [
+ {
+ "x": [
+ "giraffes",
+ "orangutans",
+ "monkeys"
+ ],
+ "y": [
+ 20,
+ 14,
+ 23
+ ],
+ "name": "SF Zoo"
+ },
+ {
+ "x": [
+ "giraffes",
+ "orangutans",
+ "monkeys"
+ ],
+ "y": [
+ 12,
+ 18,
+ 29
+ ],
+ "name": "LA Zoo"
+ }
+ ],
+ "layout": {
+ "width": 600,
+ "height": 400,
+ "scattermode": "group"
+ }
+}
diff --git a/test/image/mocks/zz-scatter-grouping-vs-defaults.json b/test/image/mocks/zz-scatter-grouping-vs-defaults.json
new file mode 100644
index 00000000000..6512763fdde
--- /dev/null
+++ b/test/image/mocks/zz-scatter-grouping-vs-defaults.json
@@ -0,0 +1,99 @@
+{
+ "data": [
+ {
+ "type": "bar",
+ "y": [ 1, 2, 1 ],
+ "yaxis": "y2"
+ },
+ {
+ "type": "bar",
+ "y": [ 2, 1, 2 ]
+ },
+ {
+ "type": "bar",
+ "y": [ 1, 3, 0 ]
+ },
+ {
+ "type": "bar",
+ "y": [ 1, 2, 1 ],
+ "alignmentgroup": "top",
+ "hovertext": "alignmentgroup: top",
+ "xaxis": "x2",
+ "yaxis": "y2"
+ },
+ {
+ "type": "bar",
+ "y": [ 2, 1, 2 ],
+ "hovertext": "alignmentgroup: top
offsetgroup: 1",
+ "alignmentgroup": "bottom",
+ "offsetgroup": "1",
+ "xaxis": "x2"
+ },
+ {
+ "type": "bar",
+ "y": [ 1, 3, 0 ],
+ "hovertext": "alignmentgroup: top
offsetgroup: 2",
+ "alignmentgroup": "bottom",
+ "offsetgroup": "2",
+ "xaxis": "x2"
+ },
+ {
+ "type": "scatter",
+ "y": [ 1, 2, 1 ],
+ "yaxis": "y2"
+ },
+ {
+ "type": "scatter",
+ "y": [ 2, 1, 2 ]
+ },
+ {
+ "type": "scatter",
+ "y": [ 1, 3, 0 ]
+ },
+ {
+ "type": "scatter",
+ "y": [ 1, 2, 1 ],
+ "alignmentgroup": "top",
+ "hovertext": "alignmentgroup: top",
+ "xaxis": "x2",
+ "yaxis": "y2"
+ },
+ {
+ "type": "scatter",
+ "y": [ 2, 1, 2 ],
+ "hovertext": "alignmentgroup: top
offsetgroup: 1",
+ "alignmentgroup": "bottom",
+ "offsetgroup": "1",
+ "xaxis": "x2"
+ },
+ {
+ "type": "scatter",
+ "y": [ 1, 3, 0 ],
+ "hovertext": "alignmentgroup: top
offsetgroup: 2",
+ "alignmentgroup": "bottom",
+ "offsetgroup": "2",
+ "xaxis": "x2"
+ }
+ ],
+ "layout": {
+ "scattermode": "group",
+ "showlegend": false,
+ "grid": {
+ "rows": 2,
+ "columns": 2,
+ "roworder": "bottom to top"
+ },
+ "colorway": [ "blue", "orange", "green" ],
+ "margin": { "t": 20 },
+ "xaxis": {
+ "title": {
+ "text": "no alignmentgroup
no offsetgroup"
+ }
+ },
+ "xaxis2": {
+ "title": {
+ "text": "with alignmentgroup
with offsetgroup"
+ }
+ }
+ }
+}
diff --git a/test/plot-schema.json b/test/plot-schema.json
index eed6f90148e..2f6ea1bc6a4 100644
--- a/test/plot-schema.json
+++ b/test/plot-schema.json
@@ -42942,6 +42942,12 @@
"scatter": {
"animatable": true,
"attributes": {
+ "alignmentgroup": {
+ "description": "Set several traces linked to the same position axis or matching axes to the same alignmentgroup. This controls whether bars compute their positional range dependently or independently.",
+ "dflt": "",
+ "editType": "calc",
+ "valType": "string"
+ },
"cliponaxis": {
"description": "Determines whether or not markers and text nodes are clipped about the subplot axes. To show markers and text nodes above axis lines and tick labels, make sure to set `xaxis.layer` and `yaxis.layer` to *below traces*.",
"dflt": true,
@@ -44900,6 +44906,12 @@
"editType": "style",
"valType": "string"
},
+ "offsetgroup": {
+ "description": "Set several traces linked to the same position axis or matching axes to the same offsetgroup where bars of the same position coordinate will line up.",
+ "dflt": "",
+ "editType": "calc",
+ "valType": "string"
+ },
"opacity": {
"description": "Sets the opacity of the trace.",
"dflt": 1,
@@ -44909,7 +44921,7 @@
"valType": "number"
},
"orientation": {
- "description": "Only relevant when `stackgroup` is used, and only the first `orientation` found in the `stackgroup` will be used - including if `visible` is *legendonly* but not if it is `false`. Sets the stacking direction. With *v* (*h*), the y (x) values of subsequent traces are added. Also affects the default value of `fill`.",
+ "description": "Only relevant in the following cases: 1. when `scattermode` is set to *group*. 2. when `stackgroup` is used, and only the first `orientation` found in the `stackgroup` will be used - including if `visible` is *legendonly* but not if it is `false`. Sets the stacking direction. With *v* (*h*), the y (x) values of subsequent traces are added. Also affects the default value of `fill`.",
"editType": "calc",
"valType": "enumerated",
"values": [
@@ -45314,6 +45326,25 @@
"scatter-like",
"zoomScale"
],
+ "layoutAttributes": {
+ "scattergap": {
+ "description": "Sets the gap (in plot fraction) between scatter points of adjacent location coordinates. Defaults to `bargap`.",
+ "editType": "calc",
+ "max": 1,
+ "min": 0,
+ "valType": "number"
+ },
+ "scattermode": {
+ "description": "Determines how scatter points at the same location coordinate are displayed on the graph. With *group*, the scatter points are plotted next to one another centered around the shared location. With *overlay*, the scatter points are plotted over one another, you might need to reduce *opacity* to see multiple scatter points.",
+ "dflt": "overlay",
+ "editType": "calc",
+ "valType": "enumerated",
+ "values": [
+ "group",
+ "overlay"
+ ]
+ }
+ },
"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`. Text (appearing either on the chart or on hover only) is via `text`. Bubble charts are achieved by setting `marker.size` and/or `marker.color` to numerical arrays."
},