Skip to content

Commit

Permalink
Fast Animation (#724)
Browse files Browse the repository at this point in the history
* Fast animation by splitting tubes into static and dynamic portions such that only the dynamic portions require update each frame.

animate.js - now tracks the previous frame.  Should allow rewind in the future.
animations-controller.js - now treats static and dynamic tube segments separately
draw.js - Added method for creation of TubeBufferGeometry object for static tube segments.
		added method for updating draw range of TubeBufferGeometry
sceneplotview3d.js - now adds both static and dynamic tubes to the scene.
trajectory.js - New function to grab an interpolated tube segment / last two points of a trajectory for the current frame
view.js - split tubes into static and dynamic

* Updated tests for split of tubes field

* Fix for potential divide by zero

* Updated test for trajectory to check dynamic tube section equivalence

* Updated comment for jsdoc

* Moved variable declaration into for loop.  Added utility method to view to retrieve all tubes

* Added test for interpolating trajectories with duplicate points

* Removed drawTrajectoryLine

* Used preferred Material property setting through constructor.  Removed the Blah.

* Updated material constructor parameters for consistency.  Added dispose functions for each tube type.  Moving forward, project should decide if it wants to dispose all meshes the same, or each type of mesh differently, difference is whether or not materials and geometries should have potential to be reused.

* animations-controller now disposes of tube objects when removing them from the scene.  Draw no longer sets matrixAutoUpdate property on tube materials -- matrixAutoUpdate is a property of meshes.  It's unclear what was intended by setting it previously

* Ran fixjsstyle

* Manually fixed lines over 80 characters
  • Loading branch information
dhakim87 authored and ElDeveloper committed Aug 16, 2019
1 parent 027aa16 commit c63e8be
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 29 deletions.
8 changes: 8 additions & 0 deletions emperor/support_files/js/animate.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ function(_, trajectory) {
* @default -1
*/
this.currentFrame = -1;

/**
* @type {Integer}
* The previous frame served by the director
*/
this.previousFrame = -1;

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

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

view.tubes.forEach(function(tube) {
if (tube.parent !== null) {
view.staticTubes.forEach(function(tube) {
if (tube !== null && tube.parent !== null) {
tube.parent.remove(tube);
disposeTrajectoryLineStatic(tube);
}
});
view.dynamicTubes.forEach(function(tube) {
if (tube !== null && tube.parent !== null) {
tube.parent.remove(tube);
disposeTrajectoryLineDynamic(tube);
}
});

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

view.needsUpdate = true;

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

view.tubes.forEach(function(tube) {
if (tube === undefined) {
for (var i = 0; i < this.director.trajectories.length; i++) {
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);
disposeTrajectoryLineDynamic(tube);
}
});

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

var tube = drawTrajectoryLine(trajectory, scope.director.currentFrame,
color, radius);
//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
103 changes: 95 additions & 8 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 @@ -275,32 +279,111 @@ define(['underscore', 'three', 'jquery'], function(_, THREE, $) {
return arrow;
}

function drawTrajectoryLine(trajectory, currentFrame, color, radius) {
/**
* Returns a new trajectory line dynamic mesh
*/
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.representativeCoordinatesAtIndex(currentFrame);
_trajectory = trajectory.representativeInterpolatedCoordinatesAtIndex(
currentFrame);
if (_trajectory === null || _trajectory.length == 0)
return null;

material = new THREE.MeshPhongMaterial({color: color});
material.matrixAutoUpdate = true;
material.transparent = false;
material = new THREE.MeshPhongMaterial({
color: color,
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) * 3, radius,
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);
}

/**
* Disposes a trajectory line dynamic mesh
*/
function disposeTrajectoryLineDynamic(mesh) {
mesh.geometry.dispose();
mesh.material.dispose();
}

/**
* Returns a new trajectory line static mesh
*/
function drawTrajectoryLineStatic(trajectory, color, radius) {
var _trajectory = trajectory.coordinates;

var material = new THREE.MeshPhongMaterial({
color: color,
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);
}

/**
* Disposes a trajectory line static mesh
*/
function disposeTrajectoryLineStatic(mesh) {
mesh.geometry.dispose();
mesh.material.dispose();
}

function updateStaticTrajectoryDrawRange(trajectory, currentFrame, threeMesh)
{
//Reverse engineering the number of points in a THREE tube is not fun, and
//may be implementation/version dependent.
//Number of points drawn per tube segment =
// 2 (triangles) * 3 (points per triangle) * NUM_TUBE_CROSS_SECTION_POINTS
//Number of tube segments per pair of consecutive points =
// NUM_TUBE_SEGMENTS

var multiplier = 2 * 3 * NUM_TUBE_CROSS_SECTION_POINTS * NUM_TUBE_SEGMENTS;
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 @@ -414,6 +497,10 @@ define(['underscore', 'three', 'jquery'], function(_, THREE, $) {

return {'formatSVGLegend': formatSVGLegend, 'makeLine': makeLine,
'makeLabel': makeLabel, 'makeArrow': makeArrow,
'drawTrajectoryLine': drawTrajectoryLine,
'drawTrajectoryLineStatic': drawTrajectoryLineStatic,
'disposeTrajectoryLineStatic': disposeTrajectoryLineStatic,
'drawTrajectoryLineDynamic': drawTrajectoryLineDynamic,
'disposeTrajectoryLineDynamic': disposeTrajectoryLineDynamic,
'updateStaticTrajectoryDrawRange': updateStaticTrajectoryDrawRange,
'makeLineCollection': makeLineCollection};
});
5 changes: 3 additions & 2 deletions emperor/support_files/js/sceneplotview3d.js
Original file line number Diff line number Diff line change
Expand Up @@ -677,8 +677,9 @@ define([
});

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

Expand Down
29 changes: 29 additions & 0 deletions emperor/support_files/js/trajectory.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,35 @@ 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 &&
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
20 changes: 18 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 Expand Up @@ -120,6 +128,14 @@ DecompositionView.prototype.getGeometryFactor = function() {
this.decomp.dimensionRanges.min[0]) * 0.012;
};

/**
* Retrieve a shallow copy of concatenated static and dynamic tube arrays
* @type {THREE.Mesh[]}
*/
DecompositionView.prototype.getTubes = function() {
return this.staticTubes.concat(this.dynamicTubes);
};

/**
*
* Helper method to initialize the base THREE.js objects.
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
Loading

0 comments on commit c63e8be

Please sign in to comment.