@gov.nasa.jpl.honeycomb/telemetry-animator
v0.0.6
Published
Classes for iterating, animating, and scrubbing over time series data.
Downloads
3
Readme
Telemetry Animator
Animators that take a set of data frames and tracks playback.
Frames are expected in the following form and sorted by time. Time is not required to be in a specific format.
[
{
time: <number>,
state: <object>
},
...
]!> The KeyframeAnimator and LiveAnimator classes use the object-cache package to cache keyframes so it is assumed that all array data is consistently typed.
Use
Using an Animator
import { TelemetryAnimator } from '@gov.nasa.jpl.honeycomb/telemetry-animator';
// ...
const animator = new TelemetryAnimator(frames);
animator.onChange = state => {
console.log('New State: ', state);
}
let lastTime = window.performance.now();
const loop = () => {
const delta = window.performance.now() - lastTime;
lastTime = window.performance.now();
animator.step(delta);
requestAnimationFrame(loop);
}Adding Custom Telemetry Rollup
import { CustomTelemetryAnimator } from '@gov.nasa.jpl.honeycomb/telemetry-animator';
const mergeMap = {
data: ( from, to ) => {
if ( ! Array.isArray( to ) ) {
to = [];
}
to.push( from );
}
};
const frames = [
{ time: 1, state: { data: 1 } },
{ time: 2, state: { data: 2 } },
];
const animator = new CustomTelemetryAnimator( frames, { mergeMap } );
animator.setTime( 3 );
console.log( animator.state );
// { data: [ 1, 2 ] }
Data from a Websocket
import { TelemetryAnimator } from '@gov.nasa.jpl.honeycomb/telemetry-animator';
const animator = new TelemetryAnimator();
// Disable seekability so we always display the latest
// websocket data and we won't keep old data around.
animator.seekable = false;
ws.on('data', e => {
animator.addFrames([{
time: window.performance.now(),
state: e.state,
}]);
});
animator.addEventListener('change', () => {
animator.setTime(window.performance.now());
});Seekable data from a Websocket
TODO
Preprocessing Data From File Paths in State
import { TelemetryAnimator, LookAheadAnimatorMixin } from '@gov.nasa.jpl.honeycomb/telemetry-animator';
const LookAheadAnimator = LookAheadAnimatorMixin(TelemetryAnimator);
const animator = new LookAheadAnimator();
const cache = {};
animator.preloadData = function( state ) {
if ( 'filePath' in state ) {
const path = state.filePath;
if ( ! ( path in cache ) ) {
const controller = new AbortController();
const info = {};
info.controller = controller;
info.references = 0;
info.promise = fetch( path, { signal: controller.signal } )
.then( res => res.buffer() )
.then( buffer => ( info.buffer = buffer ) );
cache[path] = info;
}
info.references ++;
return info.promise;
} else {
return null;
}
};
animator.unloadData = function( state ) {
if ( 'filePath' in state ) {
const path = state.filePath;
const info = cache[ path ];
info.references --;
if ( info.references === 0 ) {
info.controller.abort();
delete cache[ path ];
}
}
}
animator.preprocessState = function( state ) {
if ( 'filePath' in state ) {
const path = state.filePath;
const info = cache[ path ];
if ( info.buffer ) {
state.loadedData = buffer;
return true;
}
}
return false;
}Buffering Large Files from HTTP
import { TelemetryAnimator, BuferedAnimatorMixin } from '@gov.nasa.jpl.honeycomb/telemetry-animator';
const BufferedAnimator = BuferedAnimatorMixin(TelemetryAnimator);
const animator = new BufferedAnimator();
animator.getFrames = function( time, duration ) {
return fetch( `./data/server/?time=${ time }&duration=${ duration }` )
.then( res => res.json() )
.then( json => json.frames );
};Buffering Large Files from Disk
import { TelemetryAnimator, BuferedAnimatorMixin } from '@gov.nasa.jpl.honeycomb/telemetry-animator';
const BufferedAnimator = BuferedAnimatorMixin(TelemetryAnimator);
const animator = new BufferedAnimator();
animator.getFrames = async function( time, duration ) {
let frames;
// ... read frame data at times from disk into frames variables in an async manner ...
return frames;
};Preprocessing a file to use Keyframes
import { TelemetryAnimator, KeyframeAnimatorMixin, BufferedAnimatorMixin } from '@gov.nasa.jpl.honeycomb/telemetry-animator';
const AnimatorClass = KeyframeAnimatorMixin(BuferedAnimatorMixin(TelemetryAnimator));
const animator = new AnimatorClass();
// ... set up buffered animator functions ...
await animator.generateKeyframes();Live Data With File Cache
TODO
API
Functions
BufferedAnimatorMixin
BufferedAnimatorMixin( base : class ) : voidMixin function that produces a new class that inherits from base and mixes in BufferedAnimator. Base class is expected to derive from TelemetryAnimator.
LookAheadAnimatorMixin
LookAheadAnimatorMixin( base : class ) : voidMixin function that produces a new class that inherits from base and mixes in LookAheadAnimator. Base class is expected to derive from TelemetryAnimator.
KeyframeAnimatorMixin
KeyframeAnimatorMixin( base : class ) : voidMixin function that produces a new class that inherits from base and mixes in KeyframeAnimator. Base class is expected to derive from TelemetryAnimator.
Frame
time
time : NumberThe time in milliseconds that this frame is associated with.
state
state : ObjectAn object that represents a set of data for this time.
TelemetryAnimator
Class that manages and can step through a set of frames over time.
extends EventDispatcher
Events
added-frames
Fired when new frames are added to the animator.
change
Fired when the rolled up state has changed.
reset
Fired when the animator has been reset.
dispose
Fired when the animator has been disposed.
ready
ready : BooleanReturns whether or not frames are available to be animated.
startTime
startTime : NumberThe beginning time for the frames.
endTime
endTime : NumberThe end time for the frames.
seekable
seekable : Boolean = trueWhether or not this animator can be rewound. Set this to false if using a websocket and frames are being streamed in using .addFrames. If false old data behind the playhead will be discarded.
!> Note that if the playhead is not kept at the latest frame using .setTime then frames could be accumulated using addFrames and overrun available memory.
nonSeekableBuffer
nonSeekableBuffer : Number = 0The duration of frames to retain when seekable === false.
liveData
liveData : Boolean = falseA field indicating that the data provided by the animator is coming in live, such as through a websocket. This doesn't change the behavior of the class and is just an informative flag.
continuous
continuous : Boolean = falseIf true indicates that data can be interpolated and if the time is set to before the start time of the frames the initial frame data is assumed to be set before frame 0.
interpolate
interpolate : Boolean = trueIf true then the values in returned state are interpolated when the
current time lands in between frames.
traverseArrays
traverseArrays : Boolean = falseBy default arrays in states are copied when being merging states rather than merging objects within the array. Setting this field to true enables copying and deep merging objects within the array.
stale
stale : Boolean = falseIf true indicates that the current data is out of date and waiting
on something to be up to date. This flag can be set by derivative
animators that dynamically fetch data from the server or preprocess
data and may not be ready immediately especially when jumping ahead.
frames
frames : Array<Frame> = nullThe array of frames the animator will iterate over.
time
time : Number = 0The current time the animator has iterated up to and that state is set to.
state
state : ObjectThe last state stepped to. If interpolation = true then this will included interpolation.
constructor
constructor( frames : Array<Frames> = null ) : voidsetTime
setTime( time : Number ) : PromiseSet the time to track the animator up to. Frames will be iterated over to the given time and merged using the mergeState function. If the given time is between frames and interpolate is true, then the interpolateState function will be used to interpolate the data.
The resultant state will be set on state and the current time on time.
findFrameAtTime
findFrameAtTime( time : Number, nextFrame : Boolean = false ) : FramePerforms a binary search to find the frame at the given time. Returns the
frame (which should not be modified) that occurs on or right before the
given time. If nextTime is true then the frame after the given time
is returned.
getNextSignificantTime
getNextSignificantTime( ) : NumberReturns the time of the next frame after the current time that the animator is at.
getPrevSignificantTime
getPrevSignificantTime( ) : NumberReturns the time of the previous frame before the current time that the animator is at.
reset
reset( ) : PromiseResets the animation to time 0. This is called every time the time is reversed in order to re-roll up all frames to the new time. Returns a promise when the animator has finished resetting in case setting the time requires asynchronous loading.
forEachFrame
forEachFrame( cb : ( frame : Frame ) => void, options : Object = null ) : PromiseIterates over and calls cb for every frame in the data set. By default
every frame is iterated over and merged. If the callback returns "true"
then the iteration ends early. The set of options available options are:
{
// If true then an unmerged frame state will be provided
raw = false : boolean,
// The time bounds over which to iterate. Defaults to all frames
startTime : number,
endTime : number
}Returns a promise that resolves when all frames have been iterated over in the case that frames need to be loaded and processed asynchronously.
seekBack
seekBack( cb : ( frame : Frame ) => void, fromTime : Number | null = null ) : voidLook back through all available frames that are currently loaded. Stops when "true" is returned from the callback or all data is iterated over. Raw, unmerged frame state data is passed into the callback
optimize
optimize( ) : voidMerges any frames that describe the same point in time to minimize the amount of frames stored and iterated over.
sort
sort( ) : voidSorts the frames by the time field.
addFrames
addFrames( newFrames : Array<Frame> ) : voidAppends the given frames to the list of loaded frames. It is assumed that these frames are already sorted and come after the set of already loaded frames.
dispose
dispose( ) : voidMarks the animator as disposed so any async tasks can cancel and not resolve.
interpolateState
interpolateState( currState : Object, nextState : Object, ratio : Number, target : Object ) : voidThe function that is called to interpolate a state object. The
interpolated values are expected to be placed on the target object.
currState and nextState are to not be modified. The target object (or
a replacement for it) is expected to be returned.
Intended to be overriden by derivative classes. By default only shallow fields are interpolated.
mergeState
mergeState( from : Object, to : Object ) : voidThe function that is called to merge states when iterating over frames.
The fields from from are expected to be copied or moved onto to.
Intended to be overriden by derivative classes. By default only shallow
fields are moved onto to.
JoinedTelemetryAnimator
Animator made up of multiple animators and produces a state composed of all frame data in the child canimators.
extends TelemetryAnimator, EventDispatcher
ready
ready : Booleanseekable
seekable : BooleanliveData
liveData : Booleaninterpolate
interpolate : Booleanstale
stale : BooleanendTime
endTime : NumberstartTime
startTime : NumbergeneratingKeyframes
generatingKeyframes : BooleangeneratedKeyframesUpTo
generatedKeyframesUpTo : Numberanimators
animators : Object<String, TelemetryAniamtor>state
state : Objecttime
time : Numberconstructor
constructor( animators : Object<String, TelemetryAniamtor> = {} ) : voidaddAnimator
addAnimator( animator : TelemetryAnimator, id : String ) : voidAdd an animator with the given name.
removeAnimator
removeAnimator( name : String ) : voidRemoves the animator with the given name.
NestedTelemetryAnimator
Telemetry Animator that can handle interpolating nested objects
extends TelemetryAnimator
args
args : ...nullBufferedAnimator
Mixin class used for dynamically reading frames from disk or fetching them
in chunks via a custom getFrames implementation. Use if the full set of
frame data cannot be loaded at once.
chunkSize
chunkSize : Number = 1The size of a "chunk" of frames or the amount of frames to load at once relative to the datas time scale
buffer
buffer : Number = 5The buffer amount relative to the datas time scale before and after the current time to load.
constructor
constructor( options : Object = {} ) : voidFrames array is not taken because it is read as needed.
CustomTelemetryAnimator
Provides means for creating an animator with custom merge and interpolate functions for different sub objects.
extends NestedTelemetryAnimator
constructor
constructor( frames : Array<Frame>, options : Object = {} ) : voidOptions object can take a value for interpolateMap and #CustomTelemetryAnimator#mergeMap.
LiveAnimator
A wrapper animator that helps facilitate live data playback and caching of frames for scrubbing.
extends TelemetryAnimator, EventDispatcher
endTime
endTime : BooleanstartTime
startTime : Booleanstale
stale : Booleanstate
state : Objectinterpolate
interpolate : BooleantraverseArrays
traverseArrays : Booleanconnected
connected : BooleanconnectionHost
connectionHost : StringconnectionChangeTime
connectionChangeTime : Date | nullconstructor
constructor(
streamingAnimator : TelemetryAnimator,
bufferedKeyframeAnimator : BufferedKeyframeAnimator
) : voidTakes an animator that is filled with the latest streaming data and another that spools and reads data to and from disk for scrolling back in time.
LookAheadAnimator
Mixin class used for premptively loading data for upcoming (and previous) frames within some threshold. Used when frame data contains references to external data that must be loaded ahead of time. When frames leave the provided window any loaded data is unloaded and pending loads are cancelled.
lookAhead
lookAhead : Number = 1000The amount of time in milliseconds to look ahead to prep loaded data.
lookBack
lookBack : Number = 100The amount of time in milliseconds to look back to prep loaded data.
KeyframeAnimator
Mixin class used for generating keyframes up front using an ObjectCache and reading those keyframes as the time is scrubbed to avoid having to iterate over all prior frames. Used for cases when all telemetry data must be progressively read from disk on demand because it's too much to read into memory.
keyframeStride
keyframeStride : Number = 5The time relative to the datas time scale between generated keyframes.
generatingKeyframes
generatingKeyframes : BooleanWhether or not keyframes are actively being generated.
generatedKeyframesUpTo
generatedKeyframesUpTo : NumberThe time in milliseconds that keyframes have been generated up to.
