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', './CoordinateSystem', './BaseNavigation', './SegmentedAnimation', './Numeric', './glMatrix'], function(Utils,CoordinateSystem,BaseNavigation,SegmentedAnimation,Numeric) {
 21 
 22 /**************************************************************************************************************/
 23 
 24 /** @export
 25 	@constructor
 26 	AstroNavigator constructor
 27 	@param globe Globe
 28 	@param options Configuration properties for the AstroNavigation :
 29 		<ul>
 30 			<li>minFov : The minimum field of view in degrees</li>
 31 			<li>maxFov : The maximum field of view in degrees</li>
 32 		</ul>
 33  */
 34 var AstroNavigation = function(globe, options)
 35 {
 36 	// Default values for fov (in degrees)
 37 	this['minFov'] = 0.25;
 38 	this['maxFov'] = 100;
 39 	
 40 	BaseNavigation.prototype.constructor.call( this, globe, options );
 41 
 42 	// Initialize the navigator
 43 	this.center3d = [1.0, 0.0, 0.0];
 44 	this.up = [0., 0., 1.]
 45 	
 46 	// Update the view matrix now
 47 	this.computeViewMatrix();
 48 }
 49 
 50 /**************************************************************************************************************/
 51 
 52 Utils.inherits( BaseNavigation, AstroNavigation );
 53 
 54 /**************************************************************************************************************/
 55 
 56 /** @export
 57 	Zoom to a 3d position
 58 	@param {Float[]} geoPos Array of two floats corresponding to final Longitude and Latitude(in this order) to zoom
 59 	@param {Int} fov Final zooming fov in degrees
 60 	@param {Int} duration Duration of animation in milliseconds
 61  */
 62 AstroNavigation.prototype.zoomTo = function(geoPos, fov, duration)
 63 {
 64 	var navigator = this;
 65 	
 66 	// default values
 67 	var destFov = fov || 15.0;
 68 	duration = duration || 5000;
 69 	
 70 	// Create a single animation to animate center3d and fov
 71 	var geoStart = [];
 72 	var middleFov = 25.0;	// arbitrary middle fov value which determines if the animation needs two segments
 73 	
 74 	CoordinateSystem.from3DToGeo(this.center3d, geoStart);
 75 	var startValue = [geoStart[0], geoStart[1], this.globe.renderContext.fov];
 76 	var endValue = [geoPos[0], geoPos[1], destFov];
 77 	
 78 	// Compute the shortest path if needed
 79 	if (Math.abs(geoPos[0] - geoStart[0]) > 180. )
 80 	{
 81 		if (geoStart[0] < geoPos[0])
 82 			startValue[0] += 360;
 83 		else
 84 			endValue[0] +=360;
 85 	}
 86 	var animation = new SegmentedAnimation(
 87 		duration,
 88 		// Value setter
 89 		function(value) {
 90 			var position3d = CoordinateSystem.fromGeoTo3D( [ value[0], value[1] ] );
 91 			navigator.center3d[0] = position3d[0];
 92 			navigator.center3d[1] = position3d[1];
 93 			navigator.center3d[2] = position3d[2];
 94 			this.globe.renderContext.fov = value[2];
 95 			navigator.computeViewMatrix();
 96 		});
 97 	
 98 	// TODO : removed two steps animation ? Not very good with astro
 99 	if (false) //middleFov > this.globe.renderContext.fov)
100 	{
101 		// Two steps animation, 'rising' & 'falling'
102 		
103 		// Compute the middle value
104 		var midValue = [startValue[0]*0.5 + endValue[0]*0.5,
105 			startValue[1]*0.5 + endValue[1]*0.5,
106 			middleFov];
107 
108 		// Add two segments
109 		animation.addSegment(
110 			0.0, startValue,
111 			0.5, midValue,
112 			function(t, a, b) {
113 				var pt = Numeric.easeInQuad(t);
114 				var dt = Numeric.easeOutQuad(t);
115 				return [Numeric.lerp(pt, a[0], b[0]), // geoPos.long
116 					Numeric.lerp(pt, a[1], b[1]), // geoPos.lat
117 					Numeric.lerp(dt, a[2], b[2])]; // fov
118 			});
119 
120 		animation.addSegment(
121 			0.5, midValue,
122 			1.0, endValue,
123 			function(t, a, b) {
124 				var pt = Numeric.easeOutQuad(t);
125 				var dt = Numeric.easeInQuad(t);
126 				return [Numeric.lerp(pt, a[0], b[0]), // geoPos.long
127 					Numeric.lerp(pt, a[1], b[1]), // geoPos.lat
128 					Numeric.lerp(dt, a[2], b[2])]; // fov
129 		});
130 	}
131 	else
132 	{
133 		// One step animation, 'falling' only
134 		
135 		// Add only one segment
136 		animation.addSegment(
137 			0.0, startValue,
138 			1.0, endValue,
139 			function(t, a, b) {
140 				var pt = Numeric.easeOutQuad(t);
141 				var dt = Numeric.easeInQuad(t);
142 				return [Numeric.lerp(pt, a[0], b[0]),  // geoPos.long
143 					Numeric.lerp(pt, a[1], b[1]),  // geoPos.lat
144 					Numeric.lerp(dt, a[2], b[2])];  // fov
145 		});
146 	}
147 
148 	animation.onstop = function() {
149 		navigator.globe.publish("endNavigation");
150 	}
151 	
152 	this.globe.addAnimation(animation);
153 	animation.start();
154 	this.zoomToAnimation = animation;
155 	
156 	this.globe.publish("startNavigation");
157 }
158 
159 /**************************************************************************************************************/
160 
161 /** @export
162 	Move to a 3d position
163 	@param {Float[]} geoPos Array of two floats corresponding to final Longitude and Latitude(in this order) to zoom
164 	@param {Int} duration Duration of animation in milliseconds
165  */
166 AstroNavigation.prototype.moveTo = function(geoPos, duration )
167 {
168 	var navigator = this;
169 	
170 	duration = duration || 5000;
171 	
172 	// Create a single animation to animate center3d
173 	var geoStart = [];
174 	CoordinateSystem.from3DToGeo(this.center3d, geoStart);
175 	
176 	var startValue = [geoStart[0], geoStart[1]];
177 	var endValue = [geoPos[0], geoPos[1]];
178 	
179 	// Compute the shortest path if needed
180 	if (Math.abs(geoPos[0] - geoStart[0]) > 180. )
181 	{
182 		if (geoStart[0] < geoPos[0])
183 			startValue[0] += 360;
184 		else
185 			endValue[0] +=360;
186 	}
187 	
188 	var animation = new SegmentedAnimation(
189 		duration,
190 		// Value setter
191 		function(value) {
192 			var position3d = CoordinateSystem.fromGeoTo3D( [ value[0], value[1] ] );
193 			navigator.center3d[0] = position3d[0];
194 			navigator.center3d[1] = position3d[1];
195 			navigator.center3d[2] = position3d[2];
196 			navigator.computeViewMatrix();
197 		}
198 	);
199 	
200 	animation.addSegment(
201 		0.0, startValue,
202 		1.0, endValue,
203 		function(t, a, b) {
204 			var pt = Numeric.easeOutQuad(t);
205 			return [Numeric.lerp(pt, a[0], b[0]),  // geoPos.long
206 				Numeric.lerp(pt, a[1], b[1])];  // geoPos.lat
207 		}
208 	);
209 
210 	animation.onstop = function() {
211 		navigator.globe.publish("endNavigation");
212 	}
213 	
214 	this.globe.addAnimation(animation);
215 	animation.start();
216 	
217 	this.globe.publish("startNavigation");
218 }
219 
220 /**************************************************************************************************************/
221 
222 /**
223 	Compute the view matrix
224  */
225 AstroNavigation.prototype.computeViewMatrix = function()
226 {
227 	var eye = [];
228 	vec3.normalize(this.center3d);
229 	
230 	var vm = this.globe.renderContext.viewMatrix;
231 
232 	mat4.lookAt([0., 0., 0.], this.center3d, this.up, vm);
233 	// mat4.inverse( vm );
234 	// mat4.rotate(vm, this.heading * Math.PI/180., [1., 0., 0.])
235 	// mat4.inverse( vm );
236 
237 	this.up = [ vm[1], vm[5], vm[9] ];
238 
239 }
240 
241 /**************************************************************************************************************/
242 
243 /**
244 	Event handler for mouse wheel
245 	@param delta Delta zoom
246  */
247 AstroNavigation.prototype.zoom = function(delta)
248 {
249 	this.globe.publish("startNavigation");
250 	// Arbitrary value for smooth zooming
251 	delta = 1 + delta * 0.1;
252 	
253 	// Check differences between firefox and the rest of the world 
254 	this.globe.renderContext.fov *= delta;
255 	
256 	if ( this.globe.renderContext.fov > this['maxFov'] )
257 	{
258 		this.globe.renderContext.fov = this['maxFov'];
259 	}
260 	if ( this.globe.renderContext.fov < this['minFov'] )
261 	{
262 		this.globe.renderContext.fov = this['minFov'];
263 	}
264 	
265 	this.computeViewMatrix();
266 	
267 	this.globe.publish("endNavigation");
268 }
269 
270 /**************************************************************************************************************/
271 
272 /**
273 	Pan the navigator by computing the difference between 3D centers
274 	@param dx Window delta x
275 	@param dy Window delta y
276  */
277 AstroNavigation.prototype.pan = function(dx, dy)
278 {
279 	var x = this.globe.renderContext.canvas.width / 2.;
280 	var y = this.globe.renderContext.canvas.height / 2.;
281 	this.center3d = this.globe.renderContext.get3DFromPixel(x - dx, y - dy);
282 		
283 	this.computeViewMatrix();
284 }
285 
286 /**************************************************************************************************************/
287 
288 /**
289 	Rotate the navigator
290 	@param dx Window delta x
291 	@param dy Window delta y
292  */
293 AstroNavigation.prototype.rotate = function(dx,dy)
294 {
295 	// constant tiny angle 
296 	var angle = dx * 0.1 * Math.PI/180.;
297 	
298 	var rot = quat4.fromAngleAxis(angle,this.center3d);
299 	quat4.multiplyVec3( rot, this.up );
300 
301 	this.computeViewMatrix();
302 }
303 
304 /**************************************************************************************************************/
305 
306 return AstroNavigation;
307 
308 });