Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce group scatter attributes #6381

Merged
merged 6 commits into from
Dec 21, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/plots/plots.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -1566,6 +1567,8 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) {
'fx',
'supplyLayoutGlobalDefaults'
)(layoutIn, layoutOut, coerce);

Lib.coerce(layoutIn, layoutOut, scatterAttrs, 'scattermode');
};

function getComputedSize(attr) {
Expand Down
17 changes: 14 additions & 3 deletions src/traces/bar/cross_trace_calc.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 7 additions & 2 deletions src/traces/bar/sieve.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion src/traces/scatter/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,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',
Expand Down
41 changes: 41 additions & 0 deletions src/traces/scatter/cross_trace_calc.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,54 @@
'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,
groupgap: fullLayout.scattergroupgap
};

setGroupPositions(gd, xa, ya, calcTracesVert, opts);
setGroupPositions(gd, ya, xa, calcTracesHorz, opts);
}

/*
* Scatter stacking & normalization calculations
* runs per subplot, and can handle multiple stacking groups
*/

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;
Expand Down
6 changes: 6 additions & 0 deletions src/traces/scatter/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
10 changes: 8 additions & 2 deletions src/traces/scatter/format_labels.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
2 changes: 2 additions & 0 deletions src/traces/scatter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down
41 changes: 41 additions & 0 deletions src/traces/scatter/layout_attributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'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(' ')
},
scattergroupgap: {
archmoj marked this conversation as resolved.
Show resolved Hide resolved
valType: 'number',
min: 0,
max: 1,
dflt: 0,
editType: 'calc',
description: [
'Sets the gap (in plot fraction) between scatter points of',
'the same location coordinate.'
].join(' ')
}
};
18 changes: 18 additions & 0 deletions src/traces/scatter/layout_defaults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'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);
coerce('scattergroupgap');
}
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/image/baselines/zz-grouped_scatter.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions test/image/mocks/zz-grouped_scatter-linear.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
35 changes: 35 additions & 0 deletions test/image/mocks/zz-grouped_scatter.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
29 changes: 28 additions & 1 deletion test/plot-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -44902,7 +44902,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": [
Expand Down Expand Up @@ -45307,6 +45307,33 @@
"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"
},
"scattergroupgap": {
"description": "Sets the gap (in plot fraction) between scatter points of the same location coordinate.",
"dflt": 0,
"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."
},
Expand Down