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 });