1 /***************************************
  2  * Copyright 2011, 2012 GlobWeb contributors.
  3  *
  4  * This file is part of GlobWeb.
  5  *
  6  * GlobWeb is free software: you can redistribute it and/or modify
  7  * it under the terms of the GNU Lesser General Public License as published by
  8  * the Free Software Foundation, version 3 of the License, or
  9  * (at your option) any later version.
 10  *
 11  * GlobWeb is distributed in the hope that it will be useful,
 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 14  * GNU Lesser General Public License for more details.
 15  *
 16  * You should have received a copy of the GNU General Public License
 17  * along with GlobWeb. If not, see <http://www.gnu.org/licenses/>.
 18  ***************************************/
 19 
 20  define(['./Utils','./Animation','./CoordinateSystem','./Numeric'], function(Utils,Animation,CoordinateSystem,Numeric) {
 21 
 22 /**************************************************************************************************************/
 23 
 24 /** @export
 25 	@constructor
 26   PathAnimation is an animation defined with a path.
 27  */
 28 var PathAnimation = function(coords,speed,valueSetter)
 29 {
 30     // Call ancestor constructor
 31     Animation.prototype.constructor.call(this);
 32 
 33     this.speed = speed * CoordinateSystem.heightScale / 1000;
 34 	this.nodes = [];
 35 	for ( var i = 0; i < coords.length; i++ )
 36 	{
 37 		var node = { position: CoordinateSystem.fromGeoTo3D(coords[i]),
 38 					velocity: null,
 39 					distance: 0.0 };
 40 		this.nodes.push( node );
 41 		if ( i > 0 )
 42 		{
 43 			var dx = this.nodes[i].position[0] - this.nodes[i-1].position[0];
 44 			var dy = this.nodes[i].position[1] - this.nodes[i-1].position[1];
 45 			var dz = this.nodes[i].position[2] - this.nodes[i-1].position[2];
 46 			this.nodes[i-1].distance = Math.sqrt( dx*dx + dy*dy + dz*dz );
 47 		}
 48 	}
 49 	
 50 	for ( var i = 1; i <  coords.length - 1; i++ )
 51 	{
 52 		var vec1 = vec3.subtract( this.nodes[i+1].position, this.nodes[i].position, vec3.create() );
 53 		var vec2 = vec3.subtract( this.nodes[i-1].position, this.nodes[i].position, vec3.create() );
 54 		vec3.normalize(vec1);
 55 		vec3.normalize(vec2);
 56 		this.nodes[i].velocity = vec3.subtract( vec1, vec2, vec3.create() );
 57 		vec3.normalize(this.nodes[i].velocity);
 58 	}
 59 	
 60 	// Start velocity
 61 	var temp = vec3.subtract( this.nodes[1].position, this.nodes[0].position, vec3.create() );
 62 	vec3.scale( temp, ( 3 / this.nodes[0].distance ) );
 63 	this.nodes[0].velocity = vec3.subtract( temp, this.nodes[1].velocity, vec3.create() );
 64 	vec3.scale( this.nodes[0].velocity, 0.5 );
 65 	
 66 	// End velocity
 67 	var i = coords.length - 1;
 68 	var temp = vec3.subtract( this.nodes[i].position, this.nodes[i-1].position, vec3.create() );
 69 	vec3.scale( temp, ( 3 / this.nodes[i-1].distance ) );
 70 	this.nodes[i].velocity = vec3.subtract( temp, this.nodes[i-1].velocity, vec3.create() );
 71 	vec3.scale( this.nodes[i].velocity, 0.5 );
 72 
 73 	this.index = 0;
 74 	this.currentDistance = 0;
 75 	this.previousTime = -1;
 76 	this.currentDirection = [];
 77 	this.centerOffset = -0.2;
 78 	this.altitudeOffset = 1000;
 79 	
 80 	var that = this;
 81 	if ( valueSetter )
 82 	{
 83 		this.valueSetter = valueSetter;
 84 	}
 85 	else
 86 	{
 87 		this.valueSetter = function(value,direction)
 88 		{				
 89 			var up = vec3.normalize( value, vec3.create() );
 90 			
 91 			var geoEye = CoordinateSystem.from3DToGeo( value );
 92 			geoEye[2] = that.globe.getElevation( geoEye[0], geoEye[1] ) + that.altitudeOffset;
 93 			var eye =  CoordinateSystem.fromGeoTo3D( geoEye );
 94 			
 95 			//var eye = vec3.add( vec3.scale(up, (that.altitudeOffset+elevation) * CoordinateSystem.heightScale, vec3.create()), value );
 96 			var dirn = vec3.normalize( direction, vec3.create() );
 97 			var center = vec3.add( eye, dirn, vec3.create() );
 98 			vec3.add( center, vec3.scale(up, that.centerOffset, vec3.create()) );
 99 			mat4.lookAt( eye, center, up, that.globe.renderContext.viewMatrix );
100 		};
101 	}
102 }
103 
104 /**************************************************************************************************************/
105 
106 Utils.inherits(Animation,PathAnimation);
107 
108 /**************************************************************************************************************/
109 
110 /**
111 	Set the speed
112  */
113 PathAnimation.prototype.setSpeed = function(val)
114 {
115     this.speed = parseFloat(val) * CoordinateSystem.heightScale / 1000;
116 }
117 
118 /**************************************************************************************************************/
119 
120 /**
121 	Set the altitude offset
122  */
123 PathAnimation.prototype.setAltitudeOffset = function(val)
124 {
125 	this.altitudeOffset = parseFloat(val);
126 }
127 
128 /**************************************************************************************************************/
129 
130 /**
131 	Set the direction angle
132  */
133 PathAnimation.prototype.setDirectionAngle = function(vertical)
134 {
135 	this.centerOffset = Math.tan( parseFloat(vertical) * Math.PI / 180.0 );
136 }
137 
138 /**************************************************************************************************************/
139 
140 /** @export
141 	Start the animation
142  */
143 PathAnimation.prototype.start = function()
144 {
145 	var previousStartTime = -1;
146 	if ( this.pauseTime != -1 )
147 	{
148 		previousStartTime = this.startTime;
149 	}
150 
151     Animation.prototype.start.call(this);
152 	
153 	if ( previousStartTime != -1 )
154 	{
155 		this.previousTime += this.startTime - previousStartTime;
156 	}
157 	else
158 	{
159 		this.previousTime = -1;
160 	}
161 }
162 
163 /**************************************************************************************************************/
164 
165 /*
166 	Animation update method
167 */
168 PathAnimation.prototype.update = function(now)
169 {
170 	if ( this.previousTime == -1 )
171 	{
172 		this.index = 0;
173 		this.currentDistance = 0;
174 	}
175 	else
176 	{
177 		this.currentDistance += (now - this.previousTime) * this.speed;
178 	}
179 	this.previousTime = now;
180 
181 	while ( this.currentDistance >= this.nodes[this.index].distance && this.index < this.nodes.length - 1 )
182 	{
183 		this.currentDistance -= this.nodes[this.index].distance;
184 		this.index = this.index + 1;
185 	}
186 	
187 	if ( this.index < this.nodes.length - 1 )
188 	{
189 		var t = this.currentDistance / this.nodes[this.index].distance;
190 		var startPos = this.nodes[this.index].position;
191 		var endPos = this.nodes[this.index+1].position;
192 		var startVel = vec3.scale( this.nodes[this.index].velocity, this.nodes[this.index].distance, vec3.create() );
193 		var endVel = vec3.scale( this.nodes[this.index+1].velocity, this.nodes[this.index].distance, vec3.create() );
194 		var position = Numeric.cubicInterpolation( t, startPos, startVel, endPos, endVel );
195 		var direction = Numeric.cubicInterpolationDerivative( t, startPos, startVel, endPos, endVel );
196 		this.valueSetter( position, direction );
197 	}
198 	else if ( this.index == this.nodes.length - 1 )
199 	{
200 		this.valueSetter( this.nodes[this.index].position, this.nodes[this.index].velocity );
201 	}
202 	else
203 	{
204 		this.stop();
205 	}
206 }
207 
208 /**************************************************************************************************************/
209 
210 return PathAnimation;
211 
212 });
213