Skip to content

Animate API #802

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 45 commits into from
Sep 6, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
57892ed
Animate API
rreusser Aug 1, 2016
ae7c54c
Partial draw: Correct traces selected based on fill
rreusser Aug 4, 2016
4e23c21
Handle the case where no module found
rreusser Aug 4, 2016
b98be71
supplyDataDefaults instead of supplyDefaults for transitions
rreusser Aug 4, 2016
00bfd52
Dragbox compares axes by value instead of reference
rreusser Aug 4, 2016
f46e5fb
Attempt simultaneous axis and data transitions
rreusser Aug 9, 2016
981c1a8
Fall back to disallowing simultaneous data + axis transitions
rreusser Aug 11, 2016
80078e4
lib function to interleave trace updates into a restyle
rreusser Aug 12, 2016
a36fb6e
Add supplyTransitionDefaults to coerce input
rreusser Aug 12, 2016
b249f7b
Remove unexposed cascade option
rreusser Aug 19, 2016
d06699d
Group transitions to avoid race conditions
rreusser Aug 23, 2016
4ea79b2
Overhaul transition completion behavior
rreusser Aug 23, 2016
639faa2
Trigger transition completion even if nothing else happened
rreusser Aug 23, 2016
36207ee
Fixes from first review pass
rreusser Aug 24, 2016
4834703
Fix broken tests
rreusser Aug 24, 2016
6f0b42b
Mirror changes from #878 and small typo fix
rreusser Aug 25, 2016
a311922
Fix errorbar typo
rreusser Aug 25, 2016
d5c82c6
Add a tests for scatter simplify: false
rreusser Aug 25, 2016
0f848ab
Fix the scatter select id field
rreusser Aug 25, 2016
a6be166
Disable mouse interaction during finite-duration transition
rreusser Aug 25, 2016
dd0b1f8
Expand the animate API
rreusser Aug 25, 2016
bd6e8c0
Rework transition and animate to resolve at the *end*
rreusser Aug 28, 2016
1b497e3
Regroup animate tests to prevent race conditions
rreusser Aug 28, 2016
9079632
Add a couple more purge tests
rreusser Aug 28, 2016
14516cf
Tweak point enter/exit transitions
rreusser Aug 28, 2016
9c83914
Write Plotly.transition out of the picture yayyy
rreusser Aug 28, 2016
ee95be1
Fix frame attributes
rreusser Aug 29, 2016
dfd2037
Update attributes to improve interop with animations
rreusser Aug 29, 2016
82fefa9
Move point symbol style to single point style func
rreusser Aug 30, 2016
db6b942
Test moving transition and doCalcdata to plots.js
rreusser Sep 2, 2016
0d3da5e
Change namespace of transition
rreusser Sep 2, 2016
0221c79
Refactor animationOpts API
rreusser Sep 2, 2016
f3a292d
Apply non-range props from layout to transition
rreusser Sep 2, 2016
f709572
Use Lib.isPlainObject instead of typeof
rreusser Sep 2, 2016
68b9a0c
Stop testing the things that don't pass the tests 😑
rreusser Sep 6, 2016
29c14d7
Switch to window.[request|cancel]AnimationFrame for consistency
rreusser Sep 6, 2016
84c93fe
Remove .transition from core
rreusser Sep 6, 2016
75ec8a5
Add immediate interrupt
rreusser Sep 6, 2016
c8e46f2
Fix one of the animate tests
rreusser Sep 6, 2016
1d43355
Tweak tests
rreusser Sep 6, 2016
1fe652d
Fix race condition in .animate
rreusser Sep 6, 2016
2bc29b8
Limit transition duration to <= frame duration
rreusser Sep 6, 2016
0f23eef
jsDoc for animate, addFrames, and deleteFrames
rreusser Sep 6, 2016
c85c3a2
Verify gd is a plot div for animate, addFrames, deleteFrames
rreusser Sep 6, 2016
4e88184
Throw animate API errors whne gd is not a plot
rreusser Sep 6, 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
Prev Previous commit
Next Next commit
Attempt simultaneous axis and data transitions
  • Loading branch information
rreusser committed Sep 6, 2016
commit f46e5fbb4b030d59b75e3f19a46dd2115770d183
2 changes: 1 addition & 1 deletion src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,7 @@ lib.numSeparate = function(value, separators, separatethousands) {
* expand properties.
*
* @param {object} frameLookup
* An object containing frames keyed by name (i.e. gd._frameData._frameHash)
* An object containing frames keyed by name (i.e. gd._transitionData._frameHash)
* @param {string} frame
* The name of the keyframe to be computed
*
Expand Down
95 changes: 58 additions & 37 deletions src/plot_api/plot_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

'use strict';


var d3 = require('d3');
var m4FromQuat = require('gl-mat4/fromQuat');
var isNumeric = require('fast-isnumeric');
Expand Down Expand Up @@ -2568,6 +2569,7 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
var transitionedTraces = [];

function prepareTransitions() {
var plotinfo, i;
for(i = 0; i < traceIndices.length; i++) {
var traceIdx = traceIndices[i];
var trace = gd._fullData[traceIdx];
Expand All @@ -2591,30 +2593,46 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
Lib.extendDeepNoArrays(gd.data[traceIndices[i]], update);
}

Plots.supplyDataDefaults(gd.data, gd._fullData, gd._fullLayout);
// Supply defaults after applying the incoming properties. Note that any attempt
// to simplify this step and reduce the amount of work resulted in the reconstruction
// of essentially the whole supplyDefaults step, so that it seems sensible to just use
// supplyDefaults even though it's heavier than would otherwise be desired for
// transitions:
Plots.supplyDefaults(gd);
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the comment here.


// TODO: Add logic that computes transitionedTraces to avoid unnecessary work while
// still handling things like box plots that are interrelated.
// doCalcdata(gd, transitionedTraces);
//Plotly.Axes.saveRangeInitial(gd, true);
Copy link
Contributor

Choose a reason for hiding this comment

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

to (re)save the initial axis range or not?


// This step fies the .xaxis and .yaxis references that otherwise
// aren't updated by the supplyDefaults step:
var subplots = Plotly.Axes.getSubplots(gd);
for(i = 0; i < subplots.length; i++) {
plotinfo = gd._fullLayout._plots[subplots[i]];
plotinfo.xaxis = plotinfo.x();
plotinfo.yaxis = plotinfo.y();
}

doCalcdata(gd);

ErrorBars.calc(gd);
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh. This thing shouldn't exist. Oh well, that will be for another clean up PR. Nice find.

}

// While transitions are occuring, occurring, we get a double-transform
// issue if we transform the drawn layer *and* use the new axis range to
// draw the data. This causes setConvert to use the pre-interaction values
// of the axis range:
var axList = Plotly.Axes.list(gd);
for(i = 0; i < axList.length; i++) {
axList[i].setScale(true);
function executeCallbacks(list) {
var p = Promise.resolve();
if (!list) return p;
while(list.length) {
p = p.then((list.shift()));
}
return p;
}

var restyleList = [];
var completionTimeout = null;
var resolveTransitionCallback = null;
function flushCallbacks(list) {
if (!list) return;
while(list.length) {
list.shift();
}
}

var restyleList = [];
function executeTransitions() {
var hasTraceTransition = false;
var j;
Expand All @@ -2637,30 +2655,33 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
}
}

gd._transitionData._completionTimeout = setTimeout(completeTransition, transitionConfig.duration);

if(!hasAxisTransition && !hasTraceTransition) {
return false;
}
}

return new Promise(function(resolve) {
resolveTransitionCallback = resolve;
completionTimeout = setTimeout(resolve, transitionConfig.duration);
});
function completeTransition() {
flushCallbacks(gd._transitionData._interruptCallbacks);

gd.emit('plotly_endtransition', []);

return executeCallbacks(gd._transitionData._cleanupCallbacks);
}

function interruptPreviousTransitions() {
clearTimeout(completionTimeout);

if(resolveTransitionCallback) {
resolveTransitionCallback();
}
if (gd._transitionData._completionTimeout) {
// Prevent the previous completion from occurring:
clearTimeout(gd._transitionData._completionTimeout);
gd._transitionData._completionTimeout = null;

while(gd._frameData._layoutInterrupts.length) {
(gd._frameData._layoutInterrupts.pop())();
// Interrupt an event to indicate that a transition was running:
gd.emit('plotly_interrupttransition', []);
}

while(gd._frameData._styleInterrupts.length) {
(gd._frameData._styleInterrupts.pop())();
}
flushCallbacks(gd._transitionData._cleanupCallbacks);
return executeCallbacks(gd._transitionData._interruptCallbacks);
}

for(i = 0; i < traceIndices.length; i++) {
Expand All @@ -2677,23 +2698,23 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
thisUpdate[ai] = [data[i][ai]];
}

restyleList.push((function(md, data, traces) {
/*restyleList.push((function(md, data, traces) {
return function() {
return Plotly.restyle(gd, data, traces);
};
}(module, thisUpdate, [traceIdx])));
}(module, thisUpdate, [traceIdx])));*/
}
}

var seq = [Plots.previousPromises, interruptPreviousTransitions, prepareTransitions, executeTransitions];
seq = seq.concat(restyleList);
//seq = seq.concat(restyleList);
Copy link
Contributor

Choose a reason for hiding this comment

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

?

Copy link
Contributor

Choose a reason for hiding this comment

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

is the restyle list made obsolete by the Plotly.redraw on the complete-transition callback?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup. Commented-out code removed.


var plotDone = Lib.syncOrAsync(seq, gd);

if(!plotDone || !plotDone.then) plotDone = Promise.resolve();

return plotDone.then(function() {
gd.emit('plotly_beginanimate', []);
gd.emit('plotly_begintransition', []);
return gd;
});
};
Expand All @@ -2709,7 +2730,7 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
Plotly.animate = function(gd, frameName, transitionConfig) {
Copy link
Contributor

Choose a reason for hiding this comment

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

@rreusser Would you be opposed to making Plotly.animate optionally take array of frame names?

So that,

Plotly.plot(gd, mock.data, mock.layout);
Plotly.addFrames(gd, mock.frames);
Plotly.animate(gd, mock.frames.map((f) => f.name));

Or maybe even make Plotly.animate take look into objects for name properties so that

Plotly.plot(gd, mock.data, mock.layout);
Plotly.addFrames(gd, mock.frames);
Plotly.animate(gd, mock.frames);

would work. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What would an array of frames do?

Copy link
Contributor

@etpinard etpinard Aug 25, 2016

Choose a reason for hiding this comment

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

From slack conversation on 2016/08/24, we should try make Plotly.animate a little more polymorphic:

Plotly.animate(gd, groupNameOrFrameOrFrameList, transitionOpts);

// where `groupNameOrFrameOrFrameList`:
// if string -> group (as specified in each frame)
// if array -> frame list
// if object -> frame (would call `Plotly.addFrames` internally)

gd = getGraphDiv(gd);

if(!gd._frameData._frameHash[frameName]) {
if(!gd._transitionData._frameHash[frameName]) {
Lib.warn('animateToFrame failure: keyframe does not exist', frameName);
return Promise.reject();
}
Expand Down Expand Up @@ -2739,8 +2760,8 @@ Plotly.addFrames = function(gd, frameList, indices) {
gd = getGraphDiv(gd);

var i, frame, j, idx;
var _frames = gd._frameData._frames;
var _hash = gd._frameData._frameHash;
var _frames = gd._transitionData._frames;
var _hash = gd._transitionData._frameHash;


if(!Array.isArray(frameList)) {
Expand Down Expand Up @@ -2780,7 +2801,7 @@ Plotly.addFrames = function(gd, frameList, indices) {
if(!frame.name) {
// Repeatedly assign a default name, incrementing the counter each time until
// we get a name that's not in the hashed lookup table:
while(_hash[(frame.name = 'frame ' + gd._frameData._counter++)]);
while(_hash[(frame.name = 'frame ' + gd._transitionData._counter++)]);
}

if(_hash[frame.name]) {
Expand Down Expand Up @@ -2820,7 +2841,7 @@ Plotly.deleteFrames = function(gd, frameList) {
gd = getGraphDiv(gd);

var i, idx;
var _frames = gd._frameData._frames;
var _frames = gd._transitionData._frames;
var ops = [];
var revops = [];

Expand Down
Loading