Skip to content

Commit a1f876a

Browse files
committed
introduce group scattermode attributes
1 parent 3516391 commit a1f876a

15 files changed

+237
-9
lines changed

src/plots/plots.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ var BADNUM = require('../constants/numerical').BADNUM;
1414

1515
var axisIDs = require('./cartesian/axis_ids');
1616
var clearOutline = require('../components/shapes/handle_outline').clearOutline;
17+
var scatterAttrs = require('../traces/scatter/layout_attributes');
1718

1819
var animationAttrs = require('./animation_attributes');
1920
var frameAttrs = require('./frame_attributes');
@@ -1566,6 +1567,8 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) {
15661567
'fx',
15671568
'supplyLayoutGlobalDefaults'
15681569
)(layoutIn, layoutOut, coerce);
1570+
1571+
Lib.coerce(layoutIn, layoutOut, scatterAttrs, 'scattermode');
15691572
};
15701573

15711574
function getComputedSize(attr) {

src/traces/bar/cross_trace_calc.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,14 @@ function setBarCenterAndWidth(pa, sieve) {
441441

442442
// store the actual bar width and position, for use by hover
443443
var width = calcBar.w = barwidthIsArray ? barwidth[j] : barwidth;
444-
calcBar[pLetter] = calcBar.p + (poffsetIsArray ? poffset[j] : poffset) + width / 2;
444+
445+
if(calcBar.p === undefined) {
446+
calcBar.p = calcBar[pLetter];
447+
calcBar['orig_' + pLetter] = calcBar[pLetter];
448+
}
449+
450+
var delta = (poffsetIsArray ? poffset[j] : poffset) + width / 2;
451+
calcBar[pLetter] = calcBar.p + delta;
445452
}
446453
}
447454
}
@@ -498,13 +505,17 @@ function setBaseAndTop(sa, sieve) {
498505
for(var i = 0; i < calcTraces.length; i++) {
499506
var calcTrace = calcTraces[i];
500507
var fullTrace = calcTrace[0].trace;
508+
var isScatter = fullTrace.type === 'scatter';
509+
var isVertical = fullTrace.orientation === 'v';
501510
var pts = [];
502511
var tozero = false;
503512

504513
for(var j = 0; j < calcTrace.length; j++) {
505514
var bar = calcTrace[j];
506-
var base = bar.b;
507-
var top = base + bar.s;
515+
var base = isScatter ? 0 : bar.b;
516+
var top = isScatter ? (
517+
isVertical ? bar.y : bar.x
518+
) : base + bar.s;
508519

509520
bar[sLetter] = top;
510521
pts.push(top);

src/traces/bar/sieve.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
module.exports = Sieve;
44

55
var distinctVals = require('../../lib').distinctVals;
6-
var BADNUM = require('../../constants/numerical').BADNUM;
76

87
/**
98
* Helper class to sieve data from traces into bins
@@ -27,12 +26,18 @@ function Sieve(traces, opts) {
2726
// for single-bin histograms - see histogram/calc
2827
var width1 = Infinity;
2928

29+
var axLetter = opts.posAxis._id.charAt(0);
30+
3031
var positions = [];
3132
for(var i = 0; i < traces.length; i++) {
3233
var trace = traces[i];
3334
for(var j = 0; j < trace.length; j++) {
3435
var bar = trace[j];
35-
if(bar.p !== BADNUM) positions.push(bar.p);
36+
var pos = bar.p;
37+
if(pos === undefined) {
38+
pos = bar[axLetter];
39+
}
40+
if(pos !== undefined) positions.push(pos);
3641
}
3742
if(trace[0] && trace[0].width1) {
3843
width1 = Math.min(trace[0].width1, width1);

src/traces/scatter/attributes.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,9 @@ module.exports = {
146146
values: ['v', 'h'],
147147
editType: 'calc',
148148
description: [
149-
'Only relevant when `stackgroup` is used, and only the first',
149+
'Only relevant in the following cases:',
150+
'1. when `scattermode` is set to *group*.',
151+
'2. when `stackgroup` is used, and only the first',
150152
'`orientation` found in the `stackgroup` will be used - including',
151153
'if `visible` is *legendonly* but not if it is `false`. Sets the',
152154
'stacking direction. With *v* (*h*), the y (x) values of subsequent',

src/traces/scatter/cross_trace_calc.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,54 @@
11
'use strict';
22

33
var calc = require('./calc');
4+
var setGroupPositions = require('../bar/cross_trace_calc').setGroupPositions;
5+
6+
function groupCrossTraceCalc(gd, plotinfo) {
7+
var xa = plotinfo.xaxis;
8+
var ya = plotinfo.yaxis;
9+
10+
var fullLayout = gd._fullLayout;
11+
var fullTraces = gd._fullData;
12+
var calcTraces = gd.calcdata;
13+
var calcTracesHorz = [];
14+
var calcTracesVert = [];
15+
16+
for(var i = 0; i < fullTraces.length; i++) {
17+
var fullTrace = fullTraces[i];
18+
if(
19+
fullTrace.visible === true &&
20+
fullTrace.type === 'scatter' &&
21+
fullTrace.xaxis === xa._id &&
22+
fullTrace.yaxis === ya._id
23+
) {
24+
if(fullTrace.orientation === 'h') {
25+
calcTracesHorz.push(calcTraces[i]);
26+
} else if(fullTrace.orientation === 'v') { // check for v since certain scatter traces may not have an orientation
27+
calcTracesVert.push(calcTraces[i]);
28+
}
29+
}
30+
}
31+
32+
var opts = {
33+
mode: fullLayout.scattermode,
34+
gap: fullLayout.scattergap,
35+
groupgap: fullLayout.scattergroupgap
36+
};
37+
38+
setGroupPositions(gd, xa, ya, calcTracesVert, opts);
39+
setGroupPositions(gd, ya, xa, calcTracesHorz, opts);
40+
}
441

542
/*
643
* Scatter stacking & normalization calculations
744
* runs per subplot, and can handle multiple stacking groups
845
*/
946

1047
module.exports = function crossTraceCalc(gd, plotinfo) {
48+
if(gd._fullLayout.scattermode === 'group') {
49+
groupCrossTraceCalc(gd, plotinfo);
50+
}
51+
1152
var xa = plotinfo.xaxis;
1253
var ya = plotinfo.yaxis;
1354
var subplot = xa._id + ya._id;

src/traces/scatter/defaults.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
3131
coerce('yhoverformat');
3232

3333
var stackGroupOpts = handleStackDefaults(traceIn, traceOut, layout, coerce);
34+
if(
35+
layout.scattermode === 'group' &&
36+
traceOut.orientation === undefined
37+
) {
38+
coerce('orientation', 'v');
39+
}
3440

3541
var defaultMode = !stackGroupOpts && (len < constants.PTS_LINESONLY) ?
3642
'lines+markers' : 'lines';

src/traces/scatter/format_labels.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@ module.exports = function formatLabels(cdi, trace, fullLayout) {
99
var xa = Axes.getFromTrace(mockGd, trace, 'x');
1010
var ya = Axes.getFromTrace(mockGd, trace, 'y');
1111

12-
labels.xLabel = Axes.tickText(xa, xa.c2l(cdi.x), true).text;
13-
labels.yLabel = Axes.tickText(ya, ya.c2l(cdi.y), true).text;
12+
var x = cdi.orig_x;
13+
if(x === undefined) x = cdi.x;
14+
15+
var y = cdi.orig_y;
16+
if(y === undefined) y = cdi.y;
17+
18+
labels.xLabel = Axes.tickText(xa, xa.c2l(x), true).text;
19+
labels.yLabel = Axes.tickText(ya, ya.c2l(y), true).text;
1420

1521
return labels;
1622
};

src/traces/scatter/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ module.exports = {
99
isBubble: subtypes.isBubble,
1010

1111
attributes: require('./attributes'),
12+
layoutAttributes: require('./layout_attributes'),
1213
supplyDefaults: require('./defaults'),
1314
crossTraceDefaults: require('./cross_trace_defaults'),
15+
supplyLayoutDefaults: require('./layout_defaults'),
1416
calc: require('./calc').calc,
1517
crossTraceCalc: require('./cross_trace_calc'),
1618
arraysToCalcdata: require('./arrays_to_calcdata'),
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
'use strict';
2+
3+
4+
module.exports = {
5+
scattermode: {
6+
valType: 'enumerated',
7+
values: ['group', 'overlay'],
8+
dflt: 'overlay',
9+
editType: 'calc',
10+
description: [
11+
'Determines how scatter points at the same location coordinate',
12+
'are displayed on the graph.',
13+
'With *group*, the scatter points are plotted next to one another',
14+
'centered around the shared location.',
15+
'With *overlay*, the scatter points are plotted over one another,',
16+
'you might need to reduce *opacity* to see multiple scatter points.'
17+
].join(' ')
18+
},
19+
scattergap: {
20+
valType: 'number',
21+
min: 0,
22+
max: 1,
23+
editType: 'calc',
24+
description: [
25+
'Sets the gap (in plot fraction) between scatter points of',
26+
'adjacent location coordinates.',
27+
'Defaults to `bargap`.'
28+
].join(' ')
29+
},
30+
scattergroupgap: {
31+
valType: 'number',
32+
min: 0,
33+
max: 1,
34+
dflt: 0,
35+
editType: 'calc',
36+
description: [
37+
'Sets the gap (in plot fraction) between scatter points of',
38+
'the same location coordinate.'
39+
].join(' ')
40+
}
41+
};

src/traces/scatter/layout_defaults.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use strict';
2+
3+
var Lib = require('../../lib');
4+
5+
var layoutAttributes = require('./layout_attributes');
6+
7+
module.exports = function(layoutIn, layoutOut) {
8+
function coerce(attr, dflt) {
9+
return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
10+
}
11+
12+
var groupBarmode = layoutOut.barmode === 'group';
13+
14+
if(layoutOut.scattermode === 'group') {
15+
coerce('scattergap', groupBarmode ? layoutOut.bargap : 0.2);
16+
coerce('scattergroupgap');
17+
}
18+
};
Loading
21.2 KB
Loading
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"data": [
3+
{
4+
"mode": "markers+lines+text",
5+
"texttemplate": "x:%{x}",
6+
"textposition": "bottom center",
7+
"textfont": { "size": 16 },
8+
"y": [
9+
1,
10+
3,
11+
2
12+
]
13+
},
14+
{
15+
"mode": "markers+lines+text",
16+
"texttemplate": "x:%{x}",
17+
"textposition": "bottom center",
18+
"textfont": { "size": 16 },
19+
"y": [
20+
2,
21+
1,
22+
3
23+
]
24+
}
25+
],
26+
"layout": {
27+
"width": 600,
28+
"height": 400,
29+
"scattermode": "group"
30+
}
31+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"data": [
3+
{
4+
"x": [
5+
"giraffes",
6+
"orangutans",
7+
"monkeys"
8+
],
9+
"y": [
10+
20,
11+
14,
12+
23
13+
],
14+
"name": "SF Zoo"
15+
},
16+
{
17+
"x": [
18+
"giraffes",
19+
"orangutans",
20+
"monkeys"
21+
],
22+
"y": [
23+
12,
24+
18,
25+
29
26+
],
27+
"name": "LA Zoo"
28+
}
29+
],
30+
"layout": {
31+
"width": 600,
32+
"height": 400,
33+
"scattermode": "group"
34+
}
35+
}

test/plot-schema.json

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44902,7 +44902,7 @@
4490244902
"valType": "number"
4490344903
},
4490444904
"orientation": {
44905-
"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`.",
44905+
"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`.",
4490644906
"editType": "calc",
4490744907
"valType": "enumerated",
4490844908
"values": [
@@ -45307,6 +45307,33 @@
4530745307
"scatter-like",
4530845308
"zoomScale"
4530945309
],
45310+
"layoutAttributes": {
45311+
"scattergap": {
45312+
"description": "Sets the gap (in plot fraction) between scatter points of adjacent location coordinates. Defaults to `bargap`.",
45313+
"editType": "calc",
45314+
"max": 1,
45315+
"min": 0,
45316+
"valType": "number"
45317+
},
45318+
"scattergroupgap": {
45319+
"description": "Sets the gap (in plot fraction) between scatter points of the same location coordinate.",
45320+
"dflt": 0,
45321+
"editType": "calc",
45322+
"max": 1,
45323+
"min": 0,
45324+
"valType": "number"
45325+
},
45326+
"scattermode": {
45327+
"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.",
45328+
"dflt": "overlay",
45329+
"editType": "calc",
45330+
"valType": "enumerated",
45331+
"values": [
45332+
"group",
45333+
"overlay"
45334+
]
45335+
}
45336+
},
4531045337
"meta": {
4531145338
"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."
4531245339
},

0 commit comments

Comments
 (0)