Skip to content

[WIP] Frame + Animate API #717

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

Closed
wants to merge 94 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
f3fee88
Reuse SVG DOM elements for scatter traces
rreusser Jun 22, 2016
f50c295
Implement plotly .animate() and keyframe API
rreusser Jun 30, 2016
3054cb5
Reuse SVG DOM elements for scatter traces
rreusser Jun 22, 2016
fb04b65
Implement plotly .animate() and keyframe API
rreusser Jun 30, 2016
28be237
Merge branch 'merge-animate-api-take-3' into animate-api-take-3
rreusser Jul 5, 2016
3e18a98
Clean up frame API tests and corner cases
rreusser Jul 6, 2016
01b9287
Implement Lib.expandObjectPaths
rreusser Jul 6, 2016
42467ec
Frame merging logic
rreusser Jul 6, 2016
0542213
Clean up object identity sloppiness in frame computation
rreusser Jul 7, 2016
cdfec13
Remove dependence of frame logic on gd
rreusser Jul 7, 2016
c7be054
Really simple .animate function
rreusser Jul 7, 2016
e4363a2
Add .animate tests
rreusser Jul 7, 2016
2fd9c26
Start adding back transition behavior
rreusser Jul 7, 2016
b4a553b
Fix animation tests; lint
rreusser Jul 8, 2016
aec44c6
Animation cleanup
rreusser Jul 8, 2016
e031c27
Avoid transitioning axes if no change
rreusser Jul 8, 2016
bd949e6
Transfer animation code over to new PR
rreusser Jul 11, 2016
9132eb6
Fix bad json
rreusser Jul 11, 2016
a4e8282
Fix scatter line issue
rreusser Jul 12, 2016
86ca5d6
Expand the trace update to all interdependent traces #717
rreusser Jul 14, 2016
dd995a9
Fix lint issues
rreusser Jul 14, 2016
cef8bd5
Reorder path fill drawing
rreusser Jul 14, 2016
2fb95a7
Add config to disable line simplification
rreusser Jul 14, 2016
bf2aeec
Error bar styling tweaks
rreusser Jul 18, 2016
bf079e9
Cut losses; make error bars basically work
rreusser Jul 18, 2016
bb8893f
Merge remote-tracking branch 'upstream/master' into animate-api-take-3
rreusser Jul 21, 2016
8717877
Fix for issue 702.
nielsenb-jf Jul 12, 2016
d5a8834
Move injectStyles and helpers to lib/plotcss_utils
nielsenb-jf Jul 21, 2016
34b0ca3
Merge remote-tracking branch 'upstream/master' into animate-api-take-3
rreusser Jul 22, 2016
f218e09
Handle case where cssRules are undefined.
nielsenb-jf Jul 22, 2016
8f4e11e
clear promise queue on restyle and relayout (not after Plotly.plot)
etpinard Jul 25, 2016
30a3fbe
fix mapbox access token tests
etpinard Jul 25, 2016
c7a68e7
Merge remote-tracking branch 'upstream/master' into animate-api-take-3
rreusser Jul 25, 2016
e762bf4
Merge remote-tracking branch 'upstream/master' into animate-api-take-3
rreusser Jul 26, 2016
79e6e4b
Add 'in' to filter transform
rreusser Jul 26, 2016
feb7636
Clean up scatter trace lines
rreusser Jul 26, 2016
ff37557
Fix lint errors
rreusser Jul 26, 2016
1086023
Fix frame API tests and set queueLength as needed
rreusser Jul 26, 2016
977afdd
Add missing scattergeo attribute
rreusser Jul 27, 2016
246f815
Add missing scatterternay attribute
rreusser Jul 27, 2016
db8a1c5
Remove animation mock
rreusser Jul 27, 2016
bc7fe74
Catch degenerate trace-fill-linking case
rreusser Jul 27, 2016
48eec0c
Merge pull request #776 from plotly/mapbox-access-token-catches
etpinard Jul 28, 2016
85c2e20
Merge pull request #764 from nielsenb-jf/fix_child_window
etpinard Jul 28, 2016
6376166
lint: flatten range selector suite
etpinard Jul 28, 2016
10a655d
rangeselector: skip non-object buttons items
etpinard Jul 28, 2016
5904af2
Update README.md
etpinard Jul 28, 2016
c09cbb3
isPlainObject: by pass prototype check in nw.js environments
etpinard Jul 28, 2016
e1f6818
enfore isPlainObject in src files
etpinard Jul 28, 2016
9e2f251
HTML encode attributes in <tspan>s and <a>s
scjody Jul 29, 2016
4e2761c
Add tests of <tspan> generation
scjody Jul 29, 2016
763485c
Add test that relative links work
scjody Jul 29, 2016
8fc47ef
Merge pull request #791 from plotly/html-encode-attributes
scjody Jul 29, 2016
c9a6549
Merge pull request #792 from plotly/enforce-is-plain-object
etpinard Jul 29, 2016
2102d02
Merge branch 'master' into rangeselector-buttons
etpinard Jul 29, 2016
a992b59
Merge pull request #793 from plotly/rangeselector-buttons
etpinard Jul 29, 2016
0aab643
Reuse SVG DOM elements for scatter traces
rreusser Jul 29, 2016
d7e4e1a
Implement plotly .animate() and keyframe API
rreusser Jul 29, 2016
b39f221
Reuse SVG DOM elements for scatter traces
rreusser Jul 29, 2016
1767a73
Implement plotly .animate() and keyframe API
rreusser Jul 29, 2016
c2e1710
Clean up frame API tests and corner cases
rreusser Jul 6, 2016
9ea6850
Implement Lib.expandObjectPaths
rreusser Jul 6, 2016
bcb3ce2
Frame merging logic
rreusser Jul 6, 2016
78b37e2
Clean up object identity sloppiness in frame computation
rreusser Jul 7, 2016
57fe960
Remove dependence of frame logic on gd
rreusser Jul 7, 2016
f483788
Really simple .animate function
rreusser Jul 7, 2016
d2d7099
Add .animate tests
rreusser Jul 7, 2016
ea7c037
Start adding back transition behavior
rreusser Jul 7, 2016
9773720
Fix animation tests; lint
rreusser Jul 8, 2016
17529ab
Animation cleanup
rreusser Jul 8, 2016
9955625
Avoid transitioning axes if no change
rreusser Jul 8, 2016
93eeaa8
Transfer animation code over to new PR
rreusser Jul 11, 2016
9643811
Fix bad json
rreusser Jul 11, 2016
7e2e2d8
Fix scatter line issue
rreusser Jul 12, 2016
2cecc66
Expand the trace update to all interdependent traces #717
rreusser Jul 14, 2016
fd5a3bc
Fix lint issues
rreusser Jul 14, 2016
13053a3
Reorder path fill drawing
rreusser Jul 14, 2016
15b47f6
Add config to disable line simplification
rreusser Jul 14, 2016
e887faa
Error bar styling tweaks
rreusser Jul 18, 2016
f2ffa2a
Cut losses; make error bars basically work
rreusser Jul 18, 2016
2b0c537
Add 'in' to filter transform
rreusser Jul 26, 2016
fb87b42
Clean up scatter trace lines
rreusser Jul 29, 2016
8cf4395
Fix lint errors
rreusser Jul 26, 2016
99ee7d9
Fix frame API tests and set queueLength as needed
rreusser Jul 26, 2016
bcd2fbf
Add missing scattergeo attribute
rreusser Jul 27, 2016
7319c83
Add missing scatterternay attribute
rreusser Jul 27, 2016
248736e
Remove animation mock
rreusser Jul 27, 2016
6afc339
Catch degenerate trace-fill-linking case
rreusser Jul 27, 2016
6e426e7
Clean up scatter trace line enter/exit
rreusser Aug 1, 2016
0180730
Add dummy line attrs to scattergl/scatter3d
rreusser Aug 1, 2016
1166ac7
Merge branch 'animate-api-take-3' of github.com:plotly/plotly.js into…
rreusser Aug 1, 2016
3ee4bf5
Restore missing function
rreusser Aug 1, 2016
085203d
Fix lint issues
rreusser Aug 1, 2016
266f21a
Fix lint error for the last time
rreusser Aug 1, 2016
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
6 changes: 3 additions & 3 deletions src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -546,10 +546,10 @@ lib.objectFromPath = function(path, value) {
* Iterate through an object in-place, converting dotted properties to objects.
*
* @example
* lib.expandObjectPaths('nested.test[2].path', 'value');
* // returns { nested: { test: [null, null, { path: 'value' }]}
*
* lib.expandObjectPaths({'nested.test.path': 'value'});
* // returns { nested: { test: {path: 'value'}}}
*/

// Store this to avoid recompiling regex on every prop since this may happen many
// many times for animations.
// TODO: Premature optimization? Remove?
Expand Down
31 changes: 0 additions & 31 deletions src/lib/merge_frames.js

This file was deleted.

83 changes: 83 additions & 0 deletions src/plots/plots.js
Original file line number Diff line number Diff line change
Expand Up @@ -1181,3 +1181,86 @@ plots.modifyFrames = function(gd, operations) {

return Promise.resolve();
};

/*
* Compute a keyframe. Merge a keyframe into its base frame(s) and
* expand properties.
*
* @param {object} frame
* The keyframe to be computed
*
* Returns: a third object with the merged content
*/
plots.computeFrame = function(gd, frameName) {
var i, traceIndices, traceIndex, expandedObj, destIndex;
var _hash = gd._frameData._frameHash;

var framePtr = _hash[frameName];

// Return false if the name is invalid:
if(!framePtr) {
return false;
}

var frameStack = [framePtr];
var frameNameStack = [framePtr.name];

// Follow frame pointers:
while((framePtr = gd._frameData._frameHash[framePtr.baseFrame])) {
// Avoid infinite loops:
if(frameNameStack.indexOf(framePtr.name) !== -1) break;

frameStack.push(framePtr);
frameNameStack.push(framePtr.name);
}

// A new object for the merged result:
var result = {};

// Merge, starting with the last and ending with the desired frame:
while((framePtr = frameStack.pop())) {
if(framePtr.layout) {
expandedObj = Lib.expandObjectPaths(framePtr.layout);
result.layout = Lib.extendDeepNoArrays(result.layout || {}, expandedObj);
}

if(framePtr.data) {
if(!result.data) {
result.data = [];
}
traceIndices = framePtr.traceIndices;

if(!traceIndices) {
// If not defined, assume serial order starting at zero
traceIndices = [];
for(i = 0; i < framePtr.data.length; i++) {
traceIndices[i] = i;
}
}

if(!result.traceIndices) {
result.traceIndices = [];
}

for(i = 0; i < framePtr.data.length; i++) {
// Loop through this frames data, find out where it should go,
// and merge it!
traceIndex = traceIndices[i];
if(traceIndex === undefined || traceIndex === null) {
continue;
}

destIndex = result.traceIndices.indexOf(traceIndex);
if(destIndex === -1) {
destIndex = result.data.length;
result.traceIndices[destIndex] = traceIndex;
}

expandedObj = Lib.expandObjectPaths(framePtr.data[i]);
result.data[destIndex] = Lib.extendDeepNoArrays(result.data[destIndex] || {}, expandedObj);
}
}
}

return result;
};
185 changes: 185 additions & 0 deletions test/jasmine/tests/compute_frame_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
var Plotly = require('@lib/index');

var createGraphDiv = require('../assets/create_graph_div');
var destroyGraphDiv = require('../assets/destroy_graph_div');
var computeFrame = require('@src/plots/plots').computeFrame;

describe('Test mergeFrames', function() {
'use strict';

var gd, mock;

beforeEach(function(done) {
mock = [{x: [1, 2, 3], y: [2, 1, 3]}, {x: [1, 2, 3], y: [6, 4, 5]}];
gd = createGraphDiv();
Plotly.plot(gd, mock).then(done);
});

afterEach(destroyGraphDiv);

describe('computing a single frame', function() {
var frame1;

beforeEach(function(done) {
frame1 = {
name: 'frame1',
data: [{'marker.size': 8, marker: {color: 'red'}}]
};

Plotly.addFrames(gd, [frame1]).then(done);
});

it('returns false if the frame does not exist', function() {
expect(computeFrame(gd, 'frame8')).toBe(false);
});

it('returns a new object', function() {
expect(computeFrame(gd, 'frame1')).not.toBe(frame1);
});

it('computes a single frame', function() {
var computed = computeFrame(gd, 'frame1');
var expected = {data: [{marker: {size: 8, color: 'red'}}], traceIndices: [0]};
expect(computed).toEqual(expected);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note to self: add tests to verify the merged frames are not modified.

});
});

describe('circularly defined frames', function() {
Copy link
Contributor

Choose a reason for hiding this comment

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

🎉 great.

var frames, results;

beforeEach(function(done) {
frames = [
{name: 'frame0', baseFrame: 'frame1', data: [{'marker.size': 0}]},
{name: 'frame1', baseFrame: 'frame2', data: [{'marker.size': 1}]},
{name: 'frame2', baseFrame: 'frame0', data: [{'marker.size': 2}]}
];

results = [
{traceIndices: [0], data: [{marker: {size: 0}}]},
{traceIndices: [0], data: [{marker: {size: 1}}]},
{traceIndices: [0], data: [{marker: {size: 2}}]}
];

Plotly.addFrames(gd, frames).then(done);
});

function doTest(i) {
it('avoid infinite recursion (starting point = ' + i + ')', function() {
var result = computeFrame(gd, 'frame' + i);
expect(result).toEqual(results[i]);
});
}

for(var ii = 0; ii < 3; ii++) {
doTest(ii);
}
});

describe('computing trace data', function() {
var frames;

beforeEach(function(done) {
frames = [{
name: 'frame0',
data: [{'marker.size': 0}],
traceIndices: [2]
}, {
name: 'frame1',
data: [{'marker.size': 1}],
traceIndices: [8]
}, {
name: 'frame2',
data: [{'marker.size': 2}],
traceIndices: [2]
}, {
name: 'frame3',
data: [{'marker.size': 3}, {'marker.size': 4}],
traceIndices: [2, 8]
}, {
name: 'frame4',
data: [
{'marker.size': 5},
{'marker.size': 6},
{'marker.size': 7}
]
}];

Plotly.addFrames(gd, frames).then(done);
});

it('merges orthogonal traces', function() {
frames[0].baseFrame = frames[1].name;
var result = computeFrame(gd, 'frame0');
expect(result).toEqual({
traceIndices: [8, 2],
data: [
{marker: {size: 1}},
{marker: {size: 0}}
]
});
});

it('merges overlapping traces', function() {
frames[0].baseFrame = frames[2].name;
var result = computeFrame(gd, 'frame0');
expect(result).toEqual({
traceIndices: [2],
data: [{marker: {size: 0}}]
});
});

it('merges partially overlapping traces', function() {
frames[0].baseFrame = frames[1].name;
frames[1].baseFrame = frames[2].name;
frames[2].baseFrame = frames[3].name;
var result = computeFrame(gd, 'frame0');
expect(result).toEqual({
traceIndices: [2, 8],
data: [
{marker: {size: 0}},
{marker: {size: 1}}
]
});
});

it('assumes serial order without traceIndices specified', function() {
frames[4].baseFrame = frames[3].name;
var result = computeFrame(gd, 'frame4');
expect(result).toEqual({
traceIndices: [2, 8, 0, 1],
data: [
{marker: {size: 7}},
{marker: {size: 4}},
{marker: {size: 5}},
{marker: {size: 6}}
]
});
});
});

describe('computing trace layout', function() {
var frames;

beforeEach(function(done) {
frames = [{
name: 'frame0',
layout: {'margin.l': 40}
}, {
name: 'frame1',
layout: {'margin.l': 80}
}];

Plotly.addFrames(gd, frames).then(done);
});

it('merges layouts', function() {
frames[0].baseFrame = frames[1].name;
var result = computeFrame(gd, 'frame0');

expect(result).toEqual({
layout: {margin: {l: 40}}
});
});

});
});
48 changes: 0 additions & 48 deletions test/jasmine/tests/merge_frame_test.js

This file was deleted.