Skip to content

Commit 987f1aa

Browse files
authored
Merge pull request #978 from plotly/transforms-primetime
Filter and groupby transforms in main bundle
2 parents dd94b9f + f5c64d2 commit 987f1aa

16 files changed

+1099
-776
lines changed

lib/groupby.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Copyright 2012-2016, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
module.exports = require('../src/transforms/groupby');

lib/index.js

+13-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

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

13+
// traces
1314
Plotly.register([
1415
require('./bar'),
1516
require('./box'),
@@ -30,9 +31,19 @@ Plotly.register([
3031
require('./scattermapbox')
3132
]);
3233

33-
// add transforms
34+
// transforms
35+
//
36+
// Please note that all *transform* methods are executed before
37+
// all *calcTransform* methods - which could possibly lead to
38+
// unexpected results when applying multiple transforms of different types
39+
// to a given trace.
40+
//
41+
// For more info, see:
42+
// https://github.com/plotly/plotly.js/pull/978#pullrequestreview-2403353
43+
//
3444
Plotly.register([
35-
require('./filter')
45+
require('./filter'),
46+
require('./groupby')
3647
]);
3748

3849
module.exports = Plotly;

src/lib/coerce.js

+50
Original file line numberDiff line numberDiff line change
@@ -409,3 +409,53 @@ exports.crawl = function(attrs, callback, specifiedLevel) {
409409
if(isPlainObject(attr)) exports.crawl(attr, callback, level + 1);
410410
});
411411
};
412+
413+
/**
414+
* Find all data array attributes in a given trace object - including
415+
* `arrayOk` attributes.
416+
*
417+
* @param {object} trace
418+
* full trace object that contains a reference to `_module.attributes`
419+
*
420+
* @return {array} arrayAttributes
421+
* list of array attributes for the given trace
422+
*/
423+
exports.findArrayAttributes = function(trace) {
424+
var arrayAttributes = [],
425+
stack = [];
426+
427+
/**
428+
* A closure that gathers attribute paths into its enclosed arraySplitAttributes
429+
* Attribute paths are collected iff their leaf node is a splittable attribute
430+
*
431+
* @callback callback
432+
* @param {object} attr an attribute
433+
* @param {String} attrName name string
434+
* @param {object[]} attrs all the attributes
435+
* @param {Number} level the recursion level, 0 at the root
436+
*
437+
* @closureVariable {String[][]} arrayAttributes the set of gathered attributes
438+
* Example of filled closure variable (expected to be initialized to []):
439+
* [["marker","size"],["marker","line","width"],["marker","line","color"]]
440+
*/
441+
function callback(attr, attrName, attrs, level) {
442+
stack = stack.slice(0, level).concat([attrName]);
443+
444+
var splittableAttr = attr.valType === 'data_array' || attr.arrayOk === true;
445+
if(!splittableAttr) return;
446+
447+
var astr = toAttrString(stack);
448+
var val = nestedProperty(trace, astr).get();
449+
if(!Array.isArray(val)) return;
450+
451+
arrayAttributes.push(astr);
452+
}
453+
454+
function toAttrString(stack) {
455+
return stack.join('.');
456+
}
457+
458+
exports.crawl(trace._module.attributes, callback);
459+
460+
return arrayAttributes;
461+
};

src/lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ lib.coerceFont = coerceModule.coerceFont;
2525
lib.validate = coerceModule.validate;
2626
lib.isValObject = coerceModule.isValObject;
2727
lib.crawl = coerceModule.crawl;
28+
lib.findArrayAttributes = coerceModule.findArrayAttributes;
2829
lib.IS_SUBPLOT_OBJ = coerceModule.IS_SUBPLOT_OBJ;
2930
lib.IS_LINKED_TO_ARRAY = coerceModule.IS_LINKED_TO_ARRAY;
3031
lib.DEPRECATED = coerceModule.DEPRECATED;

src/plot_api/register.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,28 @@ function registerTransformModule(newModule) {
6161

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

64-
if(typeof newModule.transform !== 'function') {
65-
throw new Error(prefix + ' is missing a *transform* function.');
64+
var hasTransform = typeof newModule.transform === 'function',
65+
hasCalcTransform = typeof newModule.calcTransform === 'function';
66+
67+
68+
if(!hasTransform && !hasCalcTransform) {
69+
throw new Error(prefix + ' is missing a *transform* or *calcTransform* method.');
70+
}
71+
72+
if(hasTransform && hasCalcTransform) {
73+
Lib.log([
74+
prefix + ' has both a *transform* and *calcTransform* methods.',
75+
'Please note that all *transform* methods are executed',
76+
'before all *calcTransform* methods.'
77+
].join(' '));
6678
}
79+
6780
if(!Lib.isPlainObject(newModule.attributes)) {
6881
Lib.log(prefix + ' registered without an *attributes* object.');
6982
}
83+
7084
if(typeof newModule.supplyDefaults !== 'function') {
71-
Lib.log(prefix + ' registered without a *supplyDefaults* function.');
85+
Lib.log(prefix + ' registered without a *supplyDefaults* method.');
7286
}
7387

7488
Registry.transformsRegistry[newModule.name] = newModule;

src/plots/plots.js

+29-43
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ var Plotly = require('../plotly');
1616
var Registry = require('../registry');
1717
var Lib = require('../lib');
1818
var Color = require('../components/color');
19+
1920
var plots = module.exports = {};
21+
2022
var animationAttrs = require('./animation_attributes');
2123
var frameAttrs = require('./frame_attributes');
2224

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

789-
var attributeSets = dataOut.map(function(trace) {
790-
791-
var arraySplitAttributes = [];
792-
793-
var stack = [];
794-
795-
/**
796-
* A closure that gathers attribute paths into its enclosed arraySplitAttributes
797-
* Attribute paths are collected iff their leaf node is a splittable attribute
798-
* @callback callback
799-
* @param {object} attr an attribute
800-
* @param {String} attrName name string
801-
* @param {object[]} attrs all the attributes
802-
* @param {Number} level the recursion level, 0 at the root
803-
* @closureVariable {String[][]} arraySplitAttributes the set of gathered attributes
804-
* Example of filled closure variable (expected to be initialized to []):
805-
* [["marker","size"],["marker","line","width"],["marker","line","color"]]
806-
*/
807-
function callback(attr, attrName, attrs, level) {
808-
809-
stack = stack.slice(0, level).concat([attrName]);
810-
811-
var splittableAttr = attr.valType === 'data_array' || attr.arrayOk === true;
812-
if(splittableAttr) {
813-
arraySplitAttributes.push(stack.slice());
814-
}
815-
}
816-
817-
Lib.crawl(trace._module.attributes, callback);
818-
819-
return arraySplitAttributes.map(function(path) {
820-
return path.join('.');
821-
});
822-
});
823-
824791
for(var i = 0; i < container.length; i++) {
825792
var transform = container[i],
826-
type = transform.type,
827-
_module = transformsRegistry[type];
793+
_module = transformsRegistry[transform.type];
828794

829-
if(_module) {
795+
if(_module && _module.transform) {
830796
dataOut = _module.transform(dataOut, {
831797
transform: transform,
832798
fullTrace: fullTrace,
833799
fullData: fullData,
834-
attributeSets: attributeSets,
835800
layout: layout,
836801
transformIndex: i
837802
});
@@ -1617,7 +1582,7 @@ plots.doCalcdata = function(gd, traces) {
16171582
var axList = Plotly.Axes.list(gd),
16181583
fullData = gd._fullData,
16191584
fullLayout = gd._fullLayout,
1620-
i;
1585+
i, j;
16211586

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

16571622
var trace = fullData[i],
1658-
_module = trace._module,
16591623
cd = [];
16601624

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

1668-
if(_module && trace.visible === true) {
1669-
if(_module.calc) cd = _module.calc(gd, trace);
1632+
var _module;
1633+
if(trace.visible === true) {
1634+
1635+
// call calcTransform method if any
1636+
if(trace.transforms) {
1637+
1638+
// we need one round of trace module calc before
1639+
// the calc transform to 'fill in' the categories list
1640+
// used for example in the data-to-coordinate method
1641+
_module = trace._module;
1642+
if(_module && _module.calc) _module.calc(gd, trace);
1643+
1644+
for(j = 0; j < trace.transforms.length; j++) {
1645+
var transform = trace.transforms[j];
1646+
1647+
_module = transformsRegistry[transform.type];
1648+
if(_module && _module.calcTransform) {
1649+
_module.calcTransform(gd, trace, transform);
1650+
}
1651+
}
1652+
}
1653+
1654+
_module = trace._module;
1655+
if(_module && _module.calc) cd = _module.calc(gd, trace);
16701656
}
16711657

16721658
// make sure there is a first point

0 commit comments

Comments
 (0)