Skip to content
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

Fast Animation #724

Merged
merged 14 commits into from
Aug 16, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
10 changes: 10 additions & 0 deletions emperor/support_files/js/animate.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,15 @@ function(_, trajectory) {
* @default -1
*/
this.currentFrame = -1;

/*
dhakim87 marked this conversation as resolved.
Show resolved Hide resolved
* @type {Integer}
* The previous frame served by the director
* (Note, more or less always equals currentFrame - 1 in current implementation,
* but built more generically to allow rewind and frame skipping in the future)
*/
this.previousFrame = -1;

/**
* @type {Array}
* Array where each element in the trajectory is a trajectory with the
Expand Down Expand Up @@ -276,6 +285,7 @@ function(_, trajectory) {
*/
AnimationDirector.prototype.updateFrame = function() {
if (this.animationCycleFinished() === false) {
this.previousFrame = this.currentFrame;
this.currentFrame = this.currentFrame + 1;
}
};
Expand Down
42 changes: 32 additions & 10 deletions emperor/support_files/js/animations-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ define([
draw, Color, ColorViewController) {
var EmperorViewController = ViewControllers.EmperorViewController;
var drawTrajectoryLine = draw.drawTrajectoryLine;
var drawTrajectoryLineStatic = draw.drawTrajectoryLineStatic;
var drawTrajectoryLineDynamic = draw.drawTrajectoryLineDynamic;
var updateStaticTrajectoryDrawRange = draw.updateStaticTrajectoryDrawRange;
var ColorEditor = Color.ColorEditor, ColorFormatter = Color.ColorFormatter;

/**
Expand Down Expand Up @@ -440,13 +443,16 @@ define([
this.playing = false;
this.director = null;

view.tubes.forEach(function(tube) {
if (tube.parent !== null) {
var allTubes = view.staticTubes.concat(view.dynamicTubes);
dhakim87 marked this conversation as resolved.
Show resolved Hide resolved
allTubes.forEach(function(tube) {
if (tube !== null && tube.parent !== null) {
tube.parent.remove(tube);
}
});

view.tubes = [];
view.staticTubes = [];
view.dynamicTubes = [];

view.needsUpdate = true;

this._updateButtons();
Expand Down Expand Up @@ -538,19 +544,35 @@ define([
var radius = view.getGeometryFactor();
radius *= 0.45 * this.getRadius();

view.tubes.forEach(function(tube) {
if (tube === undefined) {
var i = 0;
for (i = 0; i < this.director.trajectories.length; i++) {
dhakim87 marked this conversation as resolved.
Show resolved Hide resolved
var trajectory = this.director.trajectories[i];

//Ensure static tubes are constructed
if (view.staticTubes[i] === null || view.staticTubes[i] === undefined)
{
var color = this._colors[trajectory.metadataCategoryName] || 'red';
view.staticTubes[i] = drawTrajectoryLineStatic(trajectory, color, radius);
}

//Ensure static tube draw ranges are set to visible segment
updateStaticTrajectoryDrawRange(trajectory, this.director.currentFrame, view.staticTubes[i]);
}

//Remove any old dynamic tubes from the scene
view.dynamicTubes.forEach(function(tube) {
if (tube === undefined || tube === null) {
return;
}
if (tube.parent !== null) {
tube.parent.remove(tube);
}
});

view.tubes = this.director.trajectories.map(function(trajectory) {
color = scope._colors[trajectory.metadataCategoryName] || 'red';

var tube = drawTrajectoryLine(trajectory, scope.director.currentFrame,
//Construct new dynamic tubes containing necessary interpolated segment for the current frame
view.dynamicTubes = this.director.trajectories.map(function(trajectory) {
var color = scope._colors[trajectory.metadataCategoryName] || 'red';
var tube = drawTrajectoryLineDynamic(trajectory, scope.director.currentFrame,
color, radius);
return tube;
});
Expand Down
80 changes: 78 additions & 2 deletions emperor/support_files/js/draw.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
/** @module draw */
define(['underscore', 'three', 'jquery'], function(_, THREE, $) {

var NUM_TUBE_SEGMENTS = 3;
var NUM_TUBE_CROSS_SECTION_POINTS = 10;

// useful for some calculations
var ZERO = new THREE.Vector3();

Expand Down Expand Up @@ -295,11 +299,80 @@ define(['underscore', 'three', 'jquery'], function(_, THREE, $) {
// the line will contain the two vertices and the described material
// we increase the number of points to have a smoother transition on
// edges i. e. where the trajectory changes the direction it is going
lineGeometry = new THREE.TubeGeometry(path, (points.length - 1) * 3, radius,
dhakim87 marked this conversation as resolved.
Show resolved Hide resolved
10, false);
lineGeometry = new THREE.TubeGeometry(path, (points.length - 1) * NUM_TUBE_SEGMENTS, radius,
NUM_TUBE_CROSS_SECTION_POINTS, false);

return new THREE.Mesh(lineGeometry, material);
}

function drawTrajectoryLineDynamic(trajectory, currentFrame, color, radius) {
// based on the example described in:
// https://github.com/mrdoob/three.js/wiki/Drawing-lines
var material, points = [], lineGeometry, limit = 0, path;

_trajectory = trajectory.representativeInterpolatedCoordinatesAtIndex(currentFrame);
if (_trajectory === null || _trajectory.length == 0)
return null;

material = new THREE.MeshPhongMaterial({color: color});
material.matrixAutoUpdate = true;
dhakim87 marked this conversation as resolved.
Show resolved Hide resolved
material.transparent = false;

for (var index = 0; index < _trajectory.length; index++) {
points.push(new THREE.Vector3(_trajectory[index].x,
_trajectory[index].y, _trajectory[index].z));
}

path = new THREE.EmperorTrajectory(points);

// the line will contain the two vertices and the described material
// we increase the number of points to have a smoother transition on
// edges i. e. where the trajectory changes the direction it is going
lineGeometry = new THREE.TubeGeometry(path, (points.length - 1) * NUM_TUBE_SEGMENTS, radius,
NUM_TUBE_CROSS_SECTION_POINTS, false);

return new THREE.Mesh(lineGeometry, material);
}

function drawTrajectoryLineStatic(trajectory, color, radius) {
var _trajectory = trajectory.coordinates;

var material = new THREE.MeshPhongMaterial({color: color});
material.matrixAutoUpdate = true;
material.transparent = false;

var allPoints = [];
for (var index = 0; index < _trajectory.length; index++) {
allPoints.push(new THREE.Vector3(_trajectory[index].x,
_trajectory[index].y, _trajectory[index].z));
}

var path = new THREE.EmperorTrajectory(allPoints);

//Tubes are straight segments, but adding vertices along them might change lighting effects
//under certain models and lighting conditions.
var tubeBufferGeom = new THREE.TubeBufferGeometry(path, (allPoints.length - 1) * NUM_TUBE_SEGMENTS, radius, NUM_TUBE_CROSS_SECTION_POINTS, false)

return new THREE.Mesh(tubeBufferGeom, material);
}

function updateStaticTrajectoryDrawRange(trajectory, currentFrame, threeMesh)
{
//Blah, reverse engineering the number of points in a THREE tube is not fun, and may be implementation/version dependent.
dhakim87 marked this conversation as resolved.
Show resolved Hide resolved
//Number of points drawn per tube segment = 2 (triangles) * 3 (points per triangle) * NUM_TUBE_CROSS_SECTION_POINTS (number of vertices in a cross section of tube)
//Number of tube segments per pair of consecutive points = NUM_TUBE_SEGMENTS

var multiplier = 2 * 3 * NUM_TUBE_CROSS_SECTION_POINTS * NUM_TUBE_SEGMENTS
Copy link
Member

Choose a reason for hiding this comment

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

I think the "parameters" property might have this information stored somewhere. Let's have a look at it on Monday.

if (currentFrame < trajectory._intervalValues.length)
{
var intervalValue = trajectory._intervalValues[currentFrame];
threeMesh.geometry.setDrawRange(0, intervalValue * multiplier);
}
else
{
threeMesh.geometry.setDrawRange(0, (trajectory.coordinates.length - 1) * multiplier);
}
}


/**
Expand Down Expand Up @@ -415,5 +488,8 @@ define(['underscore', 'three', 'jquery'], function(_, THREE, $) {
return {'formatSVGLegend': formatSVGLegend, 'makeLine': makeLine,
'makeLabel': makeLabel, 'makeArrow': makeArrow,
'drawTrajectoryLine': drawTrajectoryLine,
'drawTrajectoryLineStatic': drawTrajectoryLineStatic,
'drawTrajectoryLineDynamic': drawTrajectoryLineDynamic,
'updateStaticTrajectoryDrawRange': updateStaticTrajectoryDrawRange,
'makeLineCollection': makeLineCollection};
});
6 changes: 5 additions & 1 deletion emperor/support_files/js/sceneplotview3d.js
Original file line number Diff line number Diff line change
Expand Up @@ -677,9 +677,13 @@ define([
});

_.each(this.decViews, function(view) {
view.tubes.forEach(function(tube) {
view.staticTubes.forEach(function(tube) {
scope.scene.add(tube);
});
view.dynamicTubes.forEach(function(tube) {
if (tube !== null)
scope.scene.add(tube);
});
});

// check if the visible dimensions have changed
Expand Down
29 changes: 28 additions & 1 deletion emperor/support_files/js/trajectory.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,34 @@ define([

return output;
};


/**
*
* Grab only the interpolated portion of representativeCoordinatesAtIndex.
*
* @param {integer} idx Value for which to determine the required number of
* points.
*
* @return {Array[]} Array containing the representative float x, y, z
* coordinates needed to draw the interpolated portion of a trajectory at the given index.
*/
TrajectoryOfSamples.prototype.representativeInterpolatedCoordinatesAtIndex = function(idx) {
if (idx === 0)
return null;
if (this.interpolatedCoordinates.length -1 <= idx)
return null;

lastStaticPoint = this.coordinates[this._intervalValues[idx]];
interpPoint = this.interpolatedCoordinates[idx];
if (lastStaticPoint.x === interpPoint.x
dhakim87 marked this conversation as resolved.
Show resolved Hide resolved
&& lastStaticPoint.y === interpPoint.y
&& lastStaticPoint.z === interpPoint.z) {
return null; //Shouldn't pass on a zero length segment
}

return [lastStaticPoint, interpPoint];
}

/**
*
* Function to interpolate a certain number of steps between two three
Expand Down
12 changes: 10 additions & 2 deletions emperor/support_files/js/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,18 @@ function DecompositionView(decomp, asPointCloud) {
*/
this.backgroundColor = '#000000';
/**
* Tube objects on screen (used for animations)
* Static tubes objects covering an entire trajectory.
* Can use setDrawRange on the underlying geometry to display
* just part of the trajectory.
* @type {THREE.Mesh[]}
*/
this.tubes = [];
this.staticTubes = [];
/**
* Dynamic tubes covering the final tube segment of a trajectory
* Must be rebuilt each frame by the animations controller
* @type {THREE.Mesh[]}
*/
this.dynamicTubes = [];
/**
* Array of THREE.Mesh objects on screen (represent samples).
* @type {THREE.Mesh[]}
Expand Down
12 changes: 8 additions & 4 deletions tests/javascript_tests/test_decomposition_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ requirejs([
equal(dv.getVisibleCount(), 2, 'visibleCount set correctly');
deepEqual(dv.visibleDimensions, [0, 1, 2],
'visibleDimensions set correctly');
deepEqual(dv.tubes, [], 'tubes set correctly');
deepEqual(dv.staticTubes, [], 'tubes set correctly');
deepEqual(dv.dynamicTubes, [], 'tubes set correctly');

equal(dv.axesColor, '#FFFFFF');
equal(dv.backgroundColor, '#000000');
Expand Down Expand Up @@ -138,7 +139,8 @@ requirejs([
equal(dv.getVisibleCount(), 2, 'visibleCount set correctly');
deepEqual(dv.visibleDimensions, [0, 1],
'visibleDimensions set correctly');
deepEqual(dv.tubes, [], 'tubes set correctly');
deepEqual(dv.staticTubes, [], 'tubes set correctly');
deepEqual(dv.dynamicTubes, [], 'tubes set correctly');

equal(dv.axesColor, '#FFFFFF');
equal(dv.backgroundColor, '#000000');
Expand All @@ -160,7 +162,8 @@ requirejs([
equal(view.count, 2);
equal(view.getVisibleCount(), 2);
deepEqual(view.visibleDimensions, [0, 1, 2]);
deepEqual(view.tubes, []);
deepEqual(view.staticTubes, [], 'tubes set correctly');
deepEqual(view.dynamicTubes, [], 'tubes set correctly');
equal(view.axesColor, '#FFFFFF');
equal(view.backgroundColor, '#000000');
deepEqual(view.axesOrientation, [1, 1, 1]);
Expand Down Expand Up @@ -199,7 +202,8 @@ requirejs([
equal(view.count, 2);
equal(view.getVisibleCount(), 2);
deepEqual(view.visibleDimensions, [0, 1, 2]);
deepEqual(view.tubes, []);
deepEqual(view.staticTubes, [], 'tubes set correctly');
deepEqual(view.dynamicTubes, [], 'tubes set correctly');
equal(view.axesColor, '#FFFFFF');
equal(view.backgroundColor, '#000000');
deepEqual(view.axesOrientation, [1, 1, 1]);
Expand Down
13 changes: 11 additions & 2 deletions tests/javascript_tests/test_trajectory.js
Original file line number Diff line number Diff line change
Expand Up @@ -361,15 +361,24 @@ requirejs(['underscore', 'trajectory'], function(_, trajectory) {
{'x': 6.75, 'y': 6.75, 'z': 6.75},
{'x': 8, 'y': 8, 'z': 8}];

deepEqual(trajectory.representativeCoordinatesAtIndex(3),
var fullCoordinates3 = trajectory.representativeCoordinatesAtIndex(3);
deepEqual(fullCoordinates3,
[{'x': 0, 'y': 0, 'z': 0},
{'x': 0.75, 'y': 0.75, 'z': 0.75}],
'Coordinates are retrieved correctly at index 3');
deepEqual(trajectory.representativeCoordinatesAtIndex(11),

var fullCoordinates11 = trajectory.representativeCoordinatesAtIndex(11);
deepEqual(fullCoordinates11,
[{'x': 0, 'y': 0, 'z': 0}, {'x': 1, 'y': 1, 'z': 1},
{'x': -9, 'y': -9, 'z': -9}, {'x': 3, 'y': 3, 'z': 3},
{'x': 4.25, 'y': 4.25, 'z': 4.25}],
'Coordinates are retrieved correctly at index 11');

var dynamicCoordinates3 = trajectory.representativeInterpolatedCoordinatesAtIndex(3);
var dynamicCoordinates11 = trajectory.representativeInterpolatedCoordinatesAtIndex(11);

deepEqual(fullCoordinates3.slice(-2), dynamicCoordinates3, "Dynamic coordinates match on frame 3");
deepEqual(fullCoordinates11.slice(-2), dynamicCoordinates11, "Dynamic coordinates match on frame 11");

});

Expand Down