Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 2k
Improved animation merging for layout and traces#1041
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
0e86edda0da51db612ac38d35b71c93c18f33e23db511de3dadee9b2e085a53File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1299,7 +1299,7 @@ plots.modifyFrames = function(gd, operations) { | ||
| */ | ||
| plots.computeFrame = function(gd, frameName) { | ||
| var frameLookup = gd._transitionData._frameHash; | ||
| var i, traceIndices, traceIndex, expandedObj, destIndex, copy; | ||
| var i, traceIndices, traceIndex, destIndex; | ||
| var framePtr = frameLookup[frameName]; | ||
| @@ -1326,9 +1326,7 @@ plots.computeFrame = function(gd, frameName) { | ||
| // Merge, starting with the last and ending with the desired frame: | ||
| while((framePtr = frameStack.pop())) { | ||
| if(framePtr.layout) { | ||
| copy = Lib.extendDeepNoArrays({}, framePtr.layout); | ||
| expandedObj = Lib.expandObjectPaths(copy); | ||
| result.layout = Lib.extendDeepNoArrays(result.layout || {}, expandedObj); | ||
| result.layout = plots.extendLayout(result.layout, framePtr.layout); | ||
| } | ||
| if(framePtr.data) { | ||
| @@ -1363,16 +1361,99 @@ plots.computeFrame = function(gd, frameName) { | ||
| result.traces[destIndex] = traceIndex; | ||
| } | ||
| copy = Lib.extendDeepNoArrays({}, framePtr.data[i]); | ||
| expandedObj = Lib.expandObjectPaths(copy); | ||
| result.data[destIndex] = Lib.extendDeepNoArrays(result.data[destIndex] || {}, expandedObj); | ||
| result.data[destIndex] = plots.extendTrace(result.data[destIndex], framePtr.data[i]); | ||
| } | ||
| } | ||
| } | ||
| return result; | ||
| }; | ||
| /** | ||
| * Extend an object, treating container arrays very differently by extracting | ||
| * their contents and merging them separately. | ||
| * | ||
| * This exists so that we can extendDeepNoArrays and avoid stepping into data | ||
| * arrays without knowledge of the plot schema, but so that we may also manually | ||
| * recurse into known container arrays, such as transforms. | ||
| * | ||
| * See extendTrace and extendLayout below for usage. | ||
| */ | ||
| plots.extendObjectWithContainers = function(dest, src, containerPaths) { | ||
| var containerProp, containerVal, i, j, srcProp, destProp, srcContainer, destContainer; | ||
| var copy = Lib.extendDeepNoArrays({}, src || {}); | ||
| var expandedObj = Lib.expandObjectPaths(copy); | ||
| var containerObj = {}; | ||
| // Step through and extract any container properties. Otherwise extendDeepNoArrays | ||
| // will clobber any existing properties with an empty array and then supplyDefaults | ||
| // will reset everything to defaults. | ||
| if(containerPaths && containerPaths.length) { | ||
| for(i = 0; i < containerPaths.length; i++) { | ||
| containerProp = Lib.nestedProperty(expandedObj, containerPaths[i]); | ||
| containerVal = containerProp.get(); | ||
| containerProp.set(null); | ||
| Lib.nestedProperty(containerObj, containerPaths[i]).set(containerVal); | ||
| } | ||
| } | ||
| dest = Lib.extendDeepNoArrays(dest || {}, expandedObj); | ||
| if(containerPaths && containerPaths.length) { | ||
| for(i = 0; i < containerPaths.length; i++) { | ||
| srcProp = Lib.nestedProperty(containerObj, containerPaths[i]); | ||
| srcContainer = srcProp.get(); | ||
| if(!srcContainer) continue; | ||
| destProp = Lib.nestedProperty(dest, containerPaths[i]); | ||
| destContainer = destProp.get(); | ||
| if(!Array.isArray(destContainer)) { | ||
| destContainer = []; | ||
| destProp.set(destContainer); | ||
| } | ||
| for(j = 0; j < srcContainer.length; j++) { | ||
| destContainer[j] = plots.extendObjectWithContainers(destContainer[j], srcContainer[j]); | ||
| } | ||
| } | ||
| } | ||
| return dest; | ||
| }; | ||
| /* | ||
| * Extend a trace definition. This method: | ||
| * | ||
| * 1. directly transfers any array references | ||
| * 2. manually recurses into container arrays like transforms | ||
| * | ||
| * The result is the original object reference with the new contents merged in. | ||
| */ | ||
| plots.extendTrace = function(destTrace, srcTrace) { | ||
| return plots.extendObjectWithContainers(destTrace, srcTrace, ['transforms']); | ||
| ||
| }; | ||
| /* | ||
| * Extend a layout definition. This method: | ||
| * | ||
| * 1. directly transfers any array references (not critically important for | ||
| * layout since there aren't really data arrays) | ||
| * 2. manually recurses into container arrays like annotations | ||
| * | ||
| * The result is the original object reference with the new contents merged in. | ||
| */ | ||
| plots.extendLayout = function(destLayout, srcLayout) { | ||
| return plots.extendObjectWithContainers(destLayout, srcLayout, [ | ||
| 'annotations', | ||
| 'shapes', | ||
| 'images', | ||
| 'sliders', | ||
| 'updatemenus' | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 | ||
| ]); | ||
| }; | ||
| /** | ||
| * Transition to a set of new data and layout properties | ||
| * | ||
| @@ -1411,16 +1492,7 @@ plots.transition = function(gd, data, layout, traces, frameOpts, transitionOpts) | ||
| transitionedTraces.push(traceIdx); | ||
| // This is a multi-step process. First clone w/o arrays so that | ||
| // we're not modifying the original: | ||
| var update = Lib.extendDeepNoArrays({}, data[i]); | ||
| // Then expand object paths since we don't obey object-overwrite | ||
| // semantics here: | ||
| update = Lib.expandObjectPaths(update); | ||
| // Finally apply the update (without copying arrays, of course): | ||
| Lib.extendDeepNoArrays(gd.data[traceIndices[i]], update); | ||
| gd.data[traceIndices[i]] = plots.extendTrace(gd.data[traceIndices[i]], data[i]); | ||
| } | ||
| // Follow the same procedure. Clone it so we don't mangle the input, then | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -537,6 +537,90 @@ describe('Test lib.js:', function() { | ||
| expect(input).toBe(expanded); | ||
| expect(origArray).toBe(newArray); | ||
| }); | ||
| it('expands bracketed array notation', function() { | ||
| var input = {'marker[1]': {color: 'red'}}; | ||
| var expected = {marker: [undefined, {color: 'red'}]}; | ||
| expect(Lib.expandObjectPaths(input)).toEqual(expected); | ||
| }); | ||
| it('expands nested arrays', function() { | ||
| var input = {'marker[1].range[1]': 5}; | ||
| var expected = {marker: [undefined, {range: [undefined, 5]}]}; | ||
| var computed = Lib.expandObjectPaths(input); | ||
| expect(computed).toEqual(expected); | ||
| }); | ||
| it('expands bracketed array with more nested attributes', function() { | ||
| var input = {'marker[1]': {'color.alpha': 2}}; | ||
| var expected = {marker: [undefined, {color: {alpha: 2}}]}; | ||
| var computed = Lib.expandObjectPaths(input); | ||
| expect(computed).toEqual(expected); | ||
| }); | ||
| it('expands bracketed array notation without further nesting', function() { | ||
| var input = {'marker[1]': 8}; | ||
| var expected = {marker: [undefined, 8]}; | ||
| var computed = Lib.expandObjectPaths(input); | ||
| expect(computed).toEqual(expected); | ||
| }); | ||
| it('expands bracketed array notation with further nesting', function() { | ||
| var input = {'marker[1].size': 8}; | ||
| var expected = {marker: [undefined, {size: 8}]}; | ||
| var computed = Lib.expandObjectPaths(input); | ||
| expect(computed).toEqual(expected); | ||
| }); | ||
| it('expands bracketed array notation with further nesting', function() { | ||
| var input = {'marker[1].size.magnitude': 8}; | ||
| var expected = {marker: [undefined, {size: {magnitude: 8}}]}; | ||
| var computed = Lib.expandObjectPaths(input); | ||
| expect(computed).toEqual(expected); | ||
| }); | ||
| it('combines changes with single array nesting', function() { | ||
| var input = {'marker[1].foo': 5, 'marker[0].foo': 4}; | ||
| var expected = {marker: [{foo: 4}, {foo: 5}]}; | ||
| var computed = Lib.expandObjectPaths(input); | ||
| expect(computed).toEqual(expected); | ||
| }); | ||
| // TODO: This test is unimplemented since it's a currently-unused corner case. | ||
| // Getting the test to pass requires some extension (pun?) to extendDeepNoArrays | ||
| // that's intelligent enough to only selectively merge *some* arrays, in particular | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. those arrays are declared as Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does varinput={'marker[1].range': [4,5]};Lib.expandObjectPaths(input);// => {marker: [undefined, {range: [4, 5]}]}??
| ||
| // not data arrays but yes on arrays that were previously expanded. This is a bit | ||
| // tricky to get to work just right and currently doesn't have any known use since | ||
| // container arrays are not multiply nested. | ||
| // | ||
| // Additional notes on what works or what doesn't work. This case does *not* work | ||
| // because the two nested arrays that would result from the expansion need to be | ||
| // deep merged. | ||
| // | ||
| // Lib.expandObjectPaths({'marker.range[0]': 5, 'marker.range[1]': 2}) | ||
| // | ||
| // // => {marker: {range: [null, 2]}} | ||
| // | ||
| // This case *does* work becuase the array merging does not require a deep extend: | ||
| // | ||
| // Lib.expandObjectPaths({'range[0]': 5, 'range[1]': 2} | ||
| // | ||
| // // => {range: [5, 2]} | ||
| // | ||
| // Finally note that this case works fine becuase there's no merge necessary: | ||
| // | ||
| // Lib.expandObjectPaths({'marker.range[1]': 2}) | ||
| // | ||
| // // => {marker: {range: [null, 2]}} | ||
| // | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. very nice 👍 | ||
| /* | ||
| it('combines changes', function() { | ||
| var input = {'marker[1].range[1]': 5, 'marker[1].range[0]': 4}; | ||
| var expected = {marker: [undefined, {range: [4, 5]}]}; | ||
| var computed = Lib.expandObjectPaths(input); | ||
| expect(computed).toEqual(expected); | ||
| }); | ||
| */ | ||
| }); | ||
| describe('coerce', function() { | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for reusing this method 🎉