Skip to content

Commit

Permalink
Merge pull request #3966 from plotly/refactor-parcoords-bug-fixes-lab…
Browse files Browse the repository at this point in the history
…elangle-side

Refactor parcoords - bug fixes - reuse axes ticktext - support pseudo html labels - features for label angle & position
  • Loading branch information
archmoj authored Jun 27, 2019
2 parents a6393bc + 2228339 commit 3d383b7
Show file tree
Hide file tree
Showing 37 changed files with 4,457 additions and 626 deletions.
72 changes: 46 additions & 26 deletions src/traces/parcoords/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,68 +17,88 @@ var extendFlat = require('../../lib/extend').extendFlat;
var templatedArray = require('../../plot_api/plot_template').templatedArray;

module.exports = {
domain: domainAttrs({name: 'parcoords', trace: true, editType: 'calc'}),
domain: domainAttrs({name: 'parcoords', trace: true, editType: 'plot'}),

labelangle: {
valType: 'angle',
dflt: 0,
role: 'info',
editType: 'plot',
description: [
'Sets the angle of the labels with respect to the horizontal.',
'For example, a `tickangle` of -90 draws the labels vertically.',
'Tilted labels with *labelangle* may be positioned better',
'inside margins when `labelposition` is set to *bottom*.'
].join(' ')
},

labelside: {
valType: 'enumerated',
role: 'info',
values: ['top', 'bottom'],
dflt: 'top',
editType: 'plot',
description: [
'Specifies the location of the `label`.',
'*top* positions labels above, next to the title',
'*bottom* positions labels below the graph',
'Tilted labels with *labelangle* may be positioned better',
'inside margins when `labelposition` is set to *bottom*.'
].join(' ')
},

labelfont: fontAttrs({
editType: 'calc',
editType: 'plot',
description: 'Sets the font for the `dimension` labels.'
}),
tickfont: fontAttrs({
editType: 'calc',
editType: 'plot',
description: 'Sets the font for the `dimension` tick values.'
}),
rangefont: fontAttrs({
editType: 'calc',
editType: 'plot',
description: 'Sets the font for the `dimension` range values.'
}),

dimensions: templatedArray('dimension', {
label: {
valType: 'string',
role: 'info',
editType: 'calc',
editType: 'plot',
description: 'The shown name of the dimension.'
},
// TODO: better way to determine ordinal vs continuous axes,
// so users can use tickvals/ticktext with a continuous axis.
tickvals: extendFlat({}, axesAttrs.tickvals, {
editType: 'calc',
editType: 'plot',
description: [
'Sets the values at which ticks on this axis appear.'
].join(' ')
}),
ticktext: extendFlat({}, axesAttrs.ticktext, {
editType: 'calc',
editType: 'plot',
description: [
'Sets the text displayed at the ticks position via `tickvals`.'
].join(' ')
}),
tickformat: {
valType: 'string',
dflt: '3s',
role: 'style',
editType: 'calc',
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(' ')
},
tickformat: extendFlat({}, axesAttrs.tickformat, {
editType: 'plot'
}),
visible: {
valType: 'boolean',
dflt: true,
role: 'info',
editType: 'calc',
editType: 'plot',
description: 'Shows the dimension when set to `true` (the default). Hides the dimension for `false`.'
},
range: {
valType: 'info_array',
role: 'info',
items: [
{valType: 'number', editType: 'calc'},
{valType: 'number', editType: 'calc'}
{valType: 'number', editType: 'plot'},
{valType: 'number', editType: 'plot'}
],
editType: 'calc',
editType: 'plot',
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.'
Expand All @@ -90,10 +110,10 @@ module.exports = {
freeLength: true,
dimensions: '1-2',
items: [
{valType: 'number', editType: 'calc'},
{valType: 'number', editType: 'calc'}
{valType: 'number', editType: 'plot'},
{valType: 'number', editType: 'plot'}
],
editType: 'calc',
editType: 'plot',
description: [
'The domain range to which the filter on the dimension is constrained. Must be an array',
'of `[fromValue, toValue]` with `fromValue <= toValue`, or if `multiselect` is not',
Expand All @@ -104,7 +124,7 @@ module.exports = {
valType: 'boolean',
dflt: true,
role: 'info',
editType: 'calc',
editType: 'plot',
description: 'Do we allow multiple selection ranges or just a single range?'
},
values: {
Expand Down
10 changes: 9 additions & 1 deletion src/traces/parcoords/axisbrush.js
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ function getBrushExtent(brush) {

function brushClear(brush) {
brush.filterSpecified = false;
brush.svgBrush.extent = [[0, 1]];
brush.svgBrush.extent = [[-Infinity, Infinity]];
}

function axisBrushMoved(callback) {
Expand Down Expand Up @@ -458,6 +458,14 @@ function makeFilter() {
filter = a
.map(function(d) { return d.slice().sort(sortAsc); })
.sort(startAsc);

// handle unselected case
if(filter.length === 1 &&
filter[0][0] === -Infinity &&
filter[0][1] === Infinity) {
filter = [[0, -1]];
}

consolidated = dedupeRealRanges(filter);
bounds = filter.reduce(function(p, n) {
return [Math.min(p[0], n[0]), Math.max(p[1], n[1])];
Expand Down
10 changes: 4 additions & 6 deletions src/traces/parcoords/base_plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,16 @@ var getModuleCalcData = require('../../plots/get_data').getModuleCalcData;
var parcoordsPlot = require('./plot');
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');

var PARCOORDS = 'parcoords';

exports.name = PARCOORDS;
exports.name = 'parcoords';

exports.plot = function(gd) {
var calcData = getModuleCalcData(gd.calcdata, PARCOORDS)[0];
var calcData = getModuleCalcData(gd.calcdata, 'parcoords')[0];
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));
var hadParcoords = (oldFullLayout._has && oldFullLayout._has('parcoords'));
var hasParcoords = (newFullLayout._has && newFullLayout._has('parcoords'));

if(hadParcoords && !hasParcoords) {
oldFullLayout._paperdiv.selectAll('.parcoords').remove();
Expand Down
13 changes: 2 additions & 11 deletions src/traces/parcoords/calc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,15 @@

'use strict';

var isArrayOrTypedArray = require('../../lib').isArrayOrTypedArray;
var Colorscale = require('../../components/colorscale');
var Lib = require('../../lib');
var wrap = require('../../lib/gup').wrap;

module.exports = function calc(gd, trace) {
for(var i = 0; i < trace.dimensions.length; i++) {
trace.dimensions[i].values = convertTypedArray(trace.dimensions[i].values);
}
trace.line.color = convertTypedArray(trace.line.color);

var lineColor;
var cscale;

if(Colorscale.hasColorscale(trace, 'line') && Array.isArray(trace.line.color)) {
if(Colorscale.hasColorscale(trace, 'line') && isArrayOrTypedArray(trace.line.color)) {
lineColor = trace.line.color;
cscale = Colorscale.extractOpts(trace.line).colorscale;

Expand All @@ -45,7 +40,3 @@ function constHalf(len) {
}
return out;
}

function convertTypedArray(a) {
return Lib.isTypedArray(a) ? Array.prototype.slice.call(a) : a;
}
1 change: 1 addition & 0 deletions src/traces/parcoords/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ module.exports = {
layers: ['contextLineLayer', 'focusLineLayer', 'pickLineLayer'],
axisTitleOffset: 28,
axisExtentOffset: 10,
deselectedLineColor: '#777',
bar: {
width: 4, // Visible width of the filter bar
captureWidth: 10, // Mouse-sensitive width for interaction (Fitts law)
Expand Down
19 changes: 17 additions & 2 deletions src/traces/parcoords/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var hasColorscale = require('../../components/colorscale/helpers').hasColorscale
var colorscaleDefaults = require('../../components/colorscale/defaults');
var handleDomainDefaults = require('../../plots/domain').defaults;
var handleArrayContainerDefaults = require('../../plots/array_container_defaults');
var Axes = require('../../plots/cartesian/axes');

var attributes = require('./attributes');
var axisBrush = require('./axisbrush');
Expand All @@ -37,7 +38,7 @@ function handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce) {
return Infinity;
}

function dimensionDefaults(dimensionIn, dimensionOut) {
function dimensionDefaults(dimensionIn, dimensionOut, parentOut, opts) {
function coerce(attr, dflt) {
return Lib.coerce(dimensionIn, dimensionOut, attributes.dimensions, attr, dflt);
}
Expand All @@ -53,7 +54,17 @@ function dimensionDefaults(dimensionIn, dimensionOut) {
coerce('tickvals');
coerce('ticktext');
coerce('tickformat');
coerce('range');
var range = coerce('range');

dimensionOut._ax = {
_id: 'y',
type: 'linear',
showexponent: 'all',
exponentformat: 'B',
range: range
};

Axes.setConvert(dimensionOut._ax, opts.layout);

coerce('multiselect');
var constraintRange = coerce('constraintrange');
Expand All @@ -76,6 +87,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout

var dimensions = handleArrayContainerDefaults(traceIn, traceOut, {
name: 'dimensions',
layout: layout,
handleItemDefaults: dimensionDefaults
});

Expand All @@ -100,4 +112,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
Lib.coerceFont(coerce, 'labelfont', fontDflt);
Lib.coerceFont(coerce, 'tickfont', fontDflt);
Lib.coerceFont(coerce, 'rangefont', fontDflt);

coerce('labelangle');
coerce('labelside');
};
23 changes: 23 additions & 0 deletions src/traces/parcoords/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Copyright 2012-2019, 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 isTypedArray = require('../../lib').isTypedArray;

exports.convertTypedArray = function(a) {
return isTypedArray(a) ? Array.prototype.slice.call(a) : a;
};

exports.isOrdinal = function(dimension) {
return !!dimension.tickvals;
};

exports.isVisible = function(dimension) {
return dimension.visible || !('visible' in dimension);
};
Loading

0 comments on commit 3d383b7

Please sign in to comment.