Skip to content

parcoords #1256

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

Merged
merged 7 commits into from
Feb 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
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: 2 additions & 1 deletion lib/index-gl2d.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ Plotly.register([
require('./scattergl'),
require('./pointcloud'),
require('./heatmapgl'),
require('./contourgl')
require('./contourgl'),
require('./parcoords')
]);

module.exports = Plotly;
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Plotly.register([
require('./scattergl'),
require('./pointcloud'),
require('./heatmapgl'),
require('./parcoords'),

require('./scattermapbox'),

Expand Down
11 changes: 11 additions & 0 deletions lib/parcoords.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Copyright 2012-2017, 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 = require('../src/traces/parcoords');
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@
"version": "npm run build && git add -A dist src build",
"postversion": "node -e \"console.log('Version bumped and committed. If ok, run: git push && git push --tags')\""
},
"browserify": {
"transform": ["glslify"]
},
"dependencies": {
"3d-view": "^2.0.0",
"alpha-shape": "^1.0.0",
Expand Down Expand Up @@ -79,10 +82,11 @@
"mapbox-gl": "^0.22.0",
"mouse-change": "^1.4.0",
"mouse-wheel": "^1.0.2",
"ndarray": "^1.0.16",
"ndarray": "^1.0.18",
"ndarray-fill": "^1.0.1",
"ndarray-homography": "^1.0.0",
"ndarray-ops": "^1.2.2",
"regl": "^1.3.0",
"right-now": "^1.0.0",
"robust-orientation": "^1.1.3",
"sane-topojson": "^2.0.0",
Expand All @@ -103,6 +107,7 @@
"fs-extra": "^2.0.0",
"fuse.js": "^2.6.1",
"glob": "^7.0.0",
"glslify": "^4.0.0",
"gzip-size": "^3.0.0",
"image-size": "^0.5.1",
"jasmine-core": "^2.4.1",
Expand Down
158 changes: 158 additions & 0 deletions src/traces/parcoords/attributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/**
* Copyright 2012-2017, 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 colorAttributes = require('../../components/colorscale/color_attributes');
var colorbarAttrs = require('../../components/colorbar/attributes');
var colorscales = require('../../components/colorscale/scales');
var axesAttrs = require('../../plots/cartesian/layout_attributes');

var extendDeep = require('../../lib/extend').extendDeep;
var extendFlat = require('../../lib/extend').extendFlat;

module.exports = {

domain: {
x: {
valType: 'info_array',
role: 'info',
items: [
{valType: 'number', min: 0, max: 1},
{valType: 'number', min: 0, max: 1}
],
dflt: [0, 1],
description: [
'Sets the horizontal domain of this `parcoords` trace',
'(in plot fraction).'
].join(' ')
},
y: {
valType: 'info_array',
role: 'info',
items: [
{valType: 'number', min: 0, max: 1},
{valType: 'number', min: 0, max: 1}
],
dflt: [0, 1],
description: [
'Sets the vertical domain of this `parcoords` trace',
'(in plot fraction).'
].join(' ')
}
},

dimensions: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we settle the dimensions vs dims debate?

I got to like dimensions better (as e.g. we don't abbreviate annotations), but we should make sure @cldougl @chriddyp @alexcjohnson agree.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 I definitely prefer dimensions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, feedback was unanimous, no code change then.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great. dimensions

_isLinkedToArray: 'dimension',
label: {
valType: 'string',
role: 'info',
description: 'The shown name of the dimension.'
},
tickvals: axesAttrs.tickvals,
ticktext: axesAttrs.ticktext,
tickformat: {
valType: 'string',
dflt: '3s',
role: 'style',
description: [
'Sets the tick label formatting rule using d3 formatting mini-language',
'which is similar to those of Python. See',
'https://github.com/d3/d3-format/blob/master/README.md#locale_format'
].join(' ')
},
visible: {
valType: 'boolean',
dflt: true,
role: 'info',
description: 'Shows the dimension when set to `true` (the default). Hides the dimension for `false`.'
},
range: {
valType: 'info_array',
role: 'info',
items: [
{valType: 'number'},
{valType: 'number'}
],
description: [
'The domain range that represents the full, shown axis extent. Defaults to the `values` extent.',
'Must be an array of `[fromValue, toValue]` with finite numbers as elements.'
].join(' ')
},
constraintrange: {
valType: 'info_array',
role: 'info',
items: [
{valType: 'number'},
{valType: 'number'}
],
description: [
'The domain range to which the filter on the dimension is constrained. Must be an array',
'of `[fromValue, toValue]` with finite numbers as elements.'
].join(' ')
},
values: {
valType: 'data_array',
role: 'info',
dflt: [],
description: [
'Dimension values. `values[n]` represents the value of the `n`th point in the dataset,',
'therefore the `values` vector for all dimensions must be the same (longer vectors',
'will be truncated). Each value must be a finite number.'
].join(' ')
},
description: 'The dimensions (variables) of the parallel coordinates chart. 2..60 dimensions are supported.'
},

line: extendFlat({},

// the default autocolorscale isn't quite usable for parcoords due to context ambiguity around 0 (grey, off-white)
// autocolorscale therefore defaults to false too, to avoid being overridden by the blue-white-red autocolor palette
extendDeep(
{},
colorAttributes('line'),
{
colorscale: extendDeep(
{},
colorAttributes('line').colorscale,
{dflt: colorscales.Viridis}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very strong choice!

We should probably make Viridis the default colorscale across the board in v2.

),
autocolorscale: extendDeep(
{},
colorAttributes('line').autocolorscale,
{
dflt: false,
description: [
'Has an effect only if line.color` is set to a numerical array.',
'Determines whether the colorscale is a default palette (`autocolorscale: true`)',
'or the palette determined by `line.colorscale`.',
'In case `colorscale` is unspecified or `autocolorscale` is true, the default ',
'palette will be chosen according to whether numbers in the `color` array are',
'all positive, all negative or mixed.',
'The default value is false, so that `parcoords` colorscale can default to `Viridis`.'
].join(' ')
}
)

}
),

{
showscale: {
valType: 'boolean',
role: 'info',
dflt: false,
description: [
'Has an effect only if `line.color` is set to a numerical array.',
'Determines whether or not a colorbar is displayed.'
].join(' ')
},
colorbar: colorbarAttrs
}
)
};
75 changes: 75 additions & 0 deletions src/traces/parcoords/base_plot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* Copyright 2012-2017, 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 Plots = require('../../plots/plots');
var parcoordsPlot = require('./plot');
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');
var c = require('./constants');

exports.name = 'parcoords';

exports.attr = 'type';

exports.plot = function(gd) {
var calcData = Plots.getSubplotCalcData(gd.calcdata, 'parcoords', 'parcoords');
if(calcData.length) parcoordsPlot(gd, calcData);
};

exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
var hadParcoords = (oldFullLayout._has && oldFullLayout._has('parcoords'));
var hasParcoords = (newFullLayout._has && newFullLayout._has('parcoords'));

if(hadParcoords && !hasParcoords) {
oldFullLayout._paperdiv.selectAll('.parcoords-line-layers').remove();
oldFullLayout._paperdiv.selectAll('.parcoords-line-layers').remove();
oldFullLayout._paperdiv.selectAll('.parcoords').remove();
oldFullLayout._paperdiv.selectAll('.parcoords').remove();
oldFullLayout._glimages.selectAll('*').remove();
}
};

exports.toSVG = function(gd) {

var imageRoot = gd._fullLayout._glimages;
var root = d3.selectAll('.svg-container');
var canvases = root.filter(function(d, i) {return i === root.size() - 1;})
.selectAll('.parcoords-lines.context, .parcoords-lines.focus');

function canvasToImage(d) {
var canvas = this;
var imageData = canvas.toDataURL('image/png');
var image = imageRoot.append('svg:image');
var size = gd._fullLayout._size;
var domain = gd._fullData[d.model.key].domain;

image.attr({
xmlns: xmlnsNamespaces.svg,
'xlink:href': imageData,
x: size.l + size.w * domain.x[0] - c.overdrag,
y: size.t + size.h * (1 - domain.y[1]),
width: (domain.x[1] - domain.x[0]) * size.w + 2 * c.overdrag,
height: (domain.y[1] - domain.y[0]) * size.h,
preserveAspectRatio: 'none'
});
}

imageRoot.selectAll('*').remove();
canvases.each(canvasToImage);

// Chrome / Safari bug workaround - browser apparently loses connection to the defined pattern
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks very much for the comment 🎉

// Without the workaround, these browsers 'lose' the filter brush styling (color etc.) after a snapshot
// on a subsequent interaction.
// Firefox works fine without this workaround
window.setTimeout(function() {
d3.selectAll('#filterBarPattern')
.attr('id', 'filterBarPattern');
}, 60);
};
29 changes: 29 additions & 0 deletions src/traces/parcoords/calc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Copyright 2012-2017, 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 Lib = require('../../lib');


module.exports = function calc(gd, trace) {
var cs = !!trace.line.colorscale && Lib.isArray(trace.line.color);
var color = cs ? trace.line.color : Array.apply(0, Array(trace.dimensions.reduce(function(p, n) {return Math.max(p, n.values.length);}, 0))).map(function() {return 0.5;});
var cscale = cs ? trace.line.colorscale : [[0, trace.line.color], [1, trace.line.color]];

trace.line.color = color;
trace.line.colorscale = cscale;

if(hasColorscale(trace, 'line')) {
calcColorscale(trace, trace.line.color, 'line', 'c');
}

return [{}];
};
52 changes: 52 additions & 0 deletions src/traces/parcoords/colorbar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Copyright 2012-2017, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/


'use strict';

var isNumeric = require('fast-isnumeric');

var Lib = require('../../lib');
var Plots = require('../../plots/plots');
var Colorscale = require('../../components/colorscale');
var drawColorbar = require('../../components/colorbar/draw');


module.exports = function colorbar(gd, cd) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: generalise this and make some reusable routine for all trace types that support colorbars.

var trace = cd[0].trace,
line = trace.line,
cbId = 'cb' + trace.uid;

gd._fullLayout._infolayer.selectAll('.' + cbId).remove();

if((line === undefined) || !line.showscale) {
Plots.autoMargin(gd, cbId);
return;
}

var vals = line.color,
cmin = line.cmin,
cmax = line.cmax;

if(!isNumeric(cmin)) cmin = Lib.aggNums(Math.min, null, vals);
if(!isNumeric(cmax)) cmax = Lib.aggNums(Math.max, null, vals);

var cb = cd[0].t.cb = drawColorbar(gd, cbId);
var sclFunc = Colorscale.makeColorScaleFunc(
Colorscale.extractScale(
line.colorscale,
cmin,
cmax
),
{ noNumericCheck: true }
);

cb.fillcolor(sclFunc)
.filllevels({start: cmin, end: cmax, size: (cmax - cmin) / 254})
.options(line.colorbar)();
};
Loading