Skip to content

Commit

Permalink
Merge pull request #978 from plotly/transforms-primetime
Browse files Browse the repository at this point in the history
Filter and groupby transforms in main bundle
  • Loading branch information
etpinard committed Oct 6, 2016
2 parents dd94b9f + f5c64d2 commit 987f1aa
Show file tree
Hide file tree
Showing 16 changed files with 1,099 additions and 776 deletions.
11 changes: 11 additions & 0 deletions lib/groupby.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Copyright 2012-2016, 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/transforms/groupby');
15 changes: 13 additions & 2 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

var Plotly = require('./core');

// traces
Plotly.register([
require('./bar'),
require('./box'),
Expand All @@ -30,9 +31,19 @@ Plotly.register([
require('./scattermapbox')
]);

// add transforms
// transforms
//
// Please note that all *transform* methods are executed before
// all *calcTransform* methods - which could possibly lead to
// unexpected results when applying multiple transforms of different types
// to a given trace.
//
// For more info, see:
// https://github.com/plotly/plotly.js/pull/978#pullrequestreview-2403353
//
Plotly.register([
require('./filter')
require('./filter'),
require('./groupby')
]);

module.exports = Plotly;
50 changes: 50 additions & 0 deletions src/lib/coerce.js
Original file line number Diff line number Diff line change
Expand Up @@ -409,3 +409,53 @@ exports.crawl = function(attrs, callback, specifiedLevel) {
if(isPlainObject(attr)) exports.crawl(attr, callback, level + 1);
});
};

/**
* Find all data array attributes in a given trace object - including
* `arrayOk` attributes.
*
* @param {object} trace
* full trace object that contains a reference to `_module.attributes`
*
* @return {array} arrayAttributes
* list of array attributes for the given trace
*/
exports.findArrayAttributes = function(trace) {
var arrayAttributes = [],
stack = [];

/**
* A closure that gathers attribute paths into its enclosed arraySplitAttributes
* Attribute paths are collected iff their leaf node is a splittable attribute
*
* @callback callback
* @param {object} attr an attribute
* @param {String} attrName name string
* @param {object[]} attrs all the attributes
* @param {Number} level the recursion level, 0 at the root
*
* @closureVariable {String[][]} arrayAttributes the set of gathered attributes
* Example of filled closure variable (expected to be initialized to []):
* [["marker","size"],["marker","line","width"],["marker","line","color"]]
*/
function callback(attr, attrName, attrs, level) {
stack = stack.slice(0, level).concat([attrName]);

var splittableAttr = attr.valType === 'data_array' || attr.arrayOk === true;
if(!splittableAttr) return;

var astr = toAttrString(stack);
var val = nestedProperty(trace, astr).get();
if(!Array.isArray(val)) return;

arrayAttributes.push(astr);
}

function toAttrString(stack) {
return stack.join('.');
}

exports.crawl(trace._module.attributes, callback);

return arrayAttributes;
};
1 change: 1 addition & 0 deletions src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ lib.coerceFont = coerceModule.coerceFont;
lib.validate = coerceModule.validate;
lib.isValObject = coerceModule.isValObject;
lib.crawl = coerceModule.crawl;
lib.findArrayAttributes = coerceModule.findArrayAttributes;
lib.IS_SUBPLOT_OBJ = coerceModule.IS_SUBPLOT_OBJ;
lib.IS_LINKED_TO_ARRAY = coerceModule.IS_LINKED_TO_ARRAY;
lib.DEPRECATED = coerceModule.DEPRECATED;
Expand Down
20 changes: 17 additions & 3 deletions src/plot_api/register.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,28 @@ function registerTransformModule(newModule) {

var prefix = 'Transform module ' + newModule.name;

if(typeof newModule.transform !== 'function') {
throw new Error(prefix + ' is missing a *transform* function.');
var hasTransform = typeof newModule.transform === 'function',
hasCalcTransform = typeof newModule.calcTransform === 'function';


if(!hasTransform && !hasCalcTransform) {
throw new Error(prefix + ' is missing a *transform* or *calcTransform* method.');
}

if(hasTransform && hasCalcTransform) {
Lib.log([
prefix + ' has both a *transform* and *calcTransform* methods.',
'Please note that all *transform* methods are executed',
'before all *calcTransform* methods.'
].join(' '));
}

if(!Lib.isPlainObject(newModule.attributes)) {
Lib.log(prefix + ' registered without an *attributes* object.');
}

if(typeof newModule.supplyDefaults !== 'function') {
Lib.log(prefix + ' registered without a *supplyDefaults* function.');
Lib.log(prefix + ' registered without a *supplyDefaults* method.');
}

Registry.transformsRegistry[newModule.name] = newModule;
Expand Down
72 changes: 29 additions & 43 deletions src/plots/plots.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ var Plotly = require('../plotly');
var Registry = require('../registry');
var Lib = require('../lib');
var Color = require('../components/color');

var plots = module.exports = {};

var animationAttrs = require('./animation_attributes');
var frameAttrs = require('./frame_attributes');

Expand Down Expand Up @@ -786,52 +788,15 @@ function applyTransforms(fullTrace, fullData, layout) {
var container = fullTrace.transforms,
dataOut = [fullTrace];

var attributeSets = dataOut.map(function(trace) {

var arraySplitAttributes = [];

var stack = [];

/**
* A closure that gathers attribute paths into its enclosed arraySplitAttributes
* Attribute paths are collected iff their leaf node is a splittable attribute
* @callback callback
* @param {object} attr an attribute
* @param {String} attrName name string
* @param {object[]} attrs all the attributes
* @param {Number} level the recursion level, 0 at the root
* @closureVariable {String[][]} arraySplitAttributes the set of gathered attributes
* Example of filled closure variable (expected to be initialized to []):
* [["marker","size"],["marker","line","width"],["marker","line","color"]]
*/
function callback(attr, attrName, attrs, level) {

stack = stack.slice(0, level).concat([attrName]);

var splittableAttr = attr.valType === 'data_array' || attr.arrayOk === true;
if(splittableAttr) {
arraySplitAttributes.push(stack.slice());
}
}

Lib.crawl(trace._module.attributes, callback);

return arraySplitAttributes.map(function(path) {
return path.join('.');
});
});

for(var i = 0; i < container.length; i++) {
var transform = container[i],
type = transform.type,
_module = transformsRegistry[type];
_module = transformsRegistry[transform.type];

if(_module) {
if(_module && _module.transform) {
dataOut = _module.transform(dataOut, {
transform: transform,
fullTrace: fullTrace,
fullData: fullData,
attributeSets: attributeSets,
layout: layout,
transformIndex: i
});
Expand Down Expand Up @@ -1617,7 +1582,7 @@ plots.doCalcdata = function(gd, traces) {
var axList = Plotly.Axes.list(gd),
fullData = gd._fullData,
fullLayout = gd._fullLayout,
i;
i, j;

// XXX: Is this correct? Needs a closer look so that *some* traces can be recomputed without
// *all* needing doCalcdata:
Expand Down Expand Up @@ -1655,7 +1620,6 @@ plots.doCalcdata = function(gd, traces) {
}

var trace = fullData[i],
_module = trace._module,
cd = [];

// If traces were specified and this trace was not included, then transfer it over from
Expand All @@ -1665,8 +1629,30 @@ plots.doCalcdata = function(gd, traces) {
continue;
}

if(_module && trace.visible === true) {
if(_module.calc) cd = _module.calc(gd, trace);
var _module;
if(trace.visible === true) {

// call calcTransform method if any
if(trace.transforms) {

// we need one round of trace module calc before
// the calc transform to 'fill in' the categories list
// used for example in the data-to-coordinate method
_module = trace._module;
if(_module && _module.calc) _module.calc(gd, trace);

for(j = 0; j < trace.transforms.length; j++) {
var transform = trace.transforms[j];

_module = transformsRegistry[transform.type];
if(_module && _module.calcTransform) {
_module.calcTransform(gd, trace, transform);
}
}
}

_module = trace._module;
if(_module && _module.calc) cd = _module.calc(gd, trace);
}

// make sure there is a first point
Expand Down
Loading

0 comments on commit 987f1aa

Please sign in to comment.