Upstream version 5.34.98.0
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / src / ui / camera.js
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 'use strict';
6
7 base.require('ui');
8 base.require('base.settings');
9
10 base.exportTo('ui', function() {
11
12   var constants = {
13     DEFAULT_SCALE: 0.5,
14     DEFAULT_EYE_DISTANCE: 10000,
15     MINIMUM_DISTANCE: 1000,
16     MAXIMUM_DISTANCE: 100000,
17     FOV: 15,
18     RESCALE_TIMEOUT_MS: 200,
19     MAXIMUM_TILT: 80,
20     SETTINGS_NAMESPACE: 'ui_camera'
21   };
22
23
24   var Camera = ui.define('camera');
25
26   Camera.prototype = {
27     __proto__: HTMLUnknownElement.prototype,
28
29     decorate: function(eventSource) {
30       this.eventSource_ = eventSource;
31
32       this.eventSource_.addEventListener('beginpan',
33           this.onPanBegin_.bind(this));
34       this.eventSource_.addEventListener('updatepan',
35           this.onPanUpdate_.bind(this));
36       this.eventSource_.addEventListener('endpan',
37           this.onPanEnd_.bind(this));
38
39       this.eventSource_.addEventListener('beginzoom',
40           this.onZoomBegin_.bind(this));
41       this.eventSource_.addEventListener('updatezoom',
42           this.onZoomUpdate_.bind(this));
43       this.eventSource_.addEventListener('endzoom',
44           this.onZoomEnd_.bind(this));
45
46       this.eventSource_.addEventListener('beginrotate',
47           this.onRotateBegin_.bind(this));
48       this.eventSource_.addEventListener('updaterotate',
49           this.onRotateUpdate_.bind(this));
50       this.eventSource_.addEventListener('endrotate',
51           this.onRotateEnd_.bind(this));
52
53       this.eye_ = [0, 0, constants.DEFAULT_EYE_DISTANCE];
54       this.gazeTarget_ = [0, 0, 0];
55       this.rotation_ = [0, 0];
56
57       this.pixelRatio_ = window.devicePixelRatio || 1;
58     },
59
60
61     get modelViewMatrix() {
62       var mvMatrix = mat4.create();
63
64       mat4.lookAt(mvMatrix, this.eye_, this.gazeTarget_, [0, 1, 0]);
65       return mvMatrix;
66     },
67
68     get projectionMatrix() {
69       var rect =
70           base.windowRectForElement(this.canvas_).scaleSize(this.pixelRatio_);
71
72       var aspectRatio = rect.width / rect.height;
73       var matrix = mat4.create();
74       mat4.perspective(
75           matrix, base.deg2rad(constants.FOV), aspectRatio, 1, 100000);
76
77       return matrix;
78     },
79
80     set canvas(c) {
81       this.canvas_ = c;
82     },
83
84     set deviceRect(rect) {
85       this.deviceRect_ = rect;
86     },
87
88     get stackingDistanceDampening() {
89       var gazeVector = [
90         this.gazeTarget_[0] - this.eye_[0],
91         this.gazeTarget_[1] - this.eye_[1],
92         this.gazeTarget_[2] - this.eye_[2]];
93       vec3.normalize(gazeVector, gazeVector);
94       return 1 + gazeVector[2];
95     },
96
97     loadCameraFromSettings: function(settings) {
98       this.eye_ = settings.get(
99           'eye', this.eye_, constants.SETTINGS_NAMESPACE);
100       this.gazeTarget_ = settings.get(
101           'gaze_target', this.gazeTarget_, constants.SETTINGS_NAMESPACE);
102       this.rotation_ = settings.get(
103           'rotation', this.rotation_, constants.SETTINGS_NAMESPACE);
104
105       this.dispatchRenderEvent_();
106     },
107
108     saveCameraToSettings: function(settings) {
109       settings.set(
110           'eye', this.eye_, constants.SETTINGS_NAMESPACE);
111       settings.set(
112           'gaze_target', this.gazeTarget_, constants.SETTINGS_NAMESPACE);
113       settings.set(
114           'rotation', this.rotation_, constants.SETTINGS_NAMESPACE);
115     },
116
117     resetCamera: function() {
118       this.eye_ = [0, 0, constants.DEFAULT_EYE_DISTANCE];
119       this.gazeTarget_ = [0, 0, 0];
120       this.rotation_ = [0, 0];
121
122       var settings = base.SessionSettings();
123       var keys = settings.keys(constants.SETTINGS_NAMESPACE);
124       if (keys.length !== 0) {
125         this.loadCameraFromSettings(settings);
126         return;
127       }
128
129       if (this.deviceRect_) {
130         var rect =
131             base.windowRectForElement(this.canvas_).scaleSize(this.pixelRatio_);
132
133         this.eye_[0] = this.deviceRect_.width / 2;
134         this.eye_[1] = this.deviceRect_.height / 2;
135
136         this.gazeTarget_[0] = this.deviceRect_.width / 2;
137         this.gazeTarget_[1] = this.deviceRect_.height / 2;
138       }
139
140       this.saveCameraToSettings(settings);
141       this.dispatchRenderEvent_();
142     },
143
144     updatePanByDelta: function(delta) {
145       var rect =
146           base.windowRectForElement(this.canvas_).scaleSize(this.pixelRatio_);
147
148       // Get the eye vector, since we'll be adjusting gazeTarget.
149       var eyeVector = [
150         this.eye_[0] - this.gazeTarget_[0],
151         this.eye_[1] - this.gazeTarget_[1],
152         this.eye_[2] - this.gazeTarget_[2]];
153       var length = vec3.length(eyeVector);
154       vec3.normalize(eyeVector, eyeVector);
155
156       var halfFov = constants.FOV / 2;
157       var multiplier =
158           2.0 * length * Math.tan(base.deg2rad(halfFov)) / rect.height;
159
160       // Get the up and right vectors.
161       var up = [0, 1, 0];
162       var rotMatrix = mat4.create();
163       mat4.rotate(
164           rotMatrix, rotMatrix, base.deg2rad(this.rotation_[1]), [0, 1, 0]);
165       mat4.rotate(
166           rotMatrix, rotMatrix, base.deg2rad(this.rotation_[0]), [1, 0, 0]);
167       vec3.transformMat4(up, up, rotMatrix);
168
169       var right = [0, 0, 0];
170       vec3.cross(right, eyeVector, up);
171       vec3.normalize(right, right);
172
173       // Update the gaze target.
174       for (var i = 0; i < 3; ++i) {
175         this.gazeTarget_[i] +=
176             delta[0] * multiplier * right[i] - delta[1] * multiplier * up[i];
177
178         this.eye_[i] = this.gazeTarget_[i] + length * eyeVector[i];
179       }
180
181       // If we have some z offset, we need to reposition gazeTarget
182       // to be on the plane z = 0 with normal [0, 0, 1].
183       if (Math.abs(this.gazeTarget_[2]) > 1e-6) {
184         var gazeVector = [-eyeVector[0], -eyeVector[1], -eyeVector[2]];
185         var newLength = base.clamp(
186             -this.eye_[2] / gazeVector[2],
187             constants.MINIMUM_DISTANCE,
188             constants.MAXIMUM_DISTANCE);
189
190         for (var i = 0; i < 3; ++i)
191           this.gazeTarget_[i] = this.eye_[i] + newLength * gazeVector[i];
192       }
193
194       this.saveCameraToSettings(base.SessionSettings());
195       this.dispatchRenderEvent_();
196     },
197
198     updateZoomByDelta: function(delta) {
199       var deltaY = delta[1];
200       deltaY = base.clamp(deltaY, -50, 50);
201       var scale = 1.0 - deltaY / 100.0;
202
203       var eyeVector = [0, 0, 0];
204       vec3.subtract(eyeVector, this.eye_, this.gazeTarget_);
205
206       var length = vec3.length(eyeVector);
207
208       // Clamp the length to allowed values by changing the scale.
209       if (length * scale < constants.MINIMUM_DISTANCE)
210         scale = constants.MINIMUM_DISTANCE / length;
211       else if (length * scale > constants.MAXIMUM_DISTANCE)
212         scale = constants.MAXIMUM_DISTANCE / length;
213
214       vec3.scale(eyeVector, eyeVector, scale);
215       vec3.add(this.eye_, this.gazeTarget_, eyeVector);
216
217       this.saveCameraToSettings(base.SessionSettings());
218       this.dispatchRenderEvent_();
219     },
220
221     updateRotateByDelta: function(delta) {
222       delta[0] *= 0.5;
223       delta[1] *= 0.5;
224
225       if (Math.abs(this.rotation_[0] + delta[1]) > constants.MAXIMUM_TILT)
226         return;
227       if (Math.abs(this.rotation_[1] - delta[0]) > constants.MAXIMUM_TILT)
228         return;
229
230       var eyeVector = [0, 0, 0, 0];
231       vec3.subtract(eyeVector, this.eye_, this.gazeTarget_);
232
233       // Undo the current rotation.
234       var rotMatrix = mat4.create();
235       mat4.rotate(
236           rotMatrix, rotMatrix, -base.deg2rad(this.rotation_[0]), [1, 0, 0]);
237       mat4.rotate(
238           rotMatrix, rotMatrix, -base.deg2rad(this.rotation_[1]), [0, 1, 0]);
239       vec4.transformMat4(eyeVector, eyeVector, rotMatrix);
240
241       // Update rotation values.
242       this.rotation_[0] += delta[1];
243       this.rotation_[1] -= delta[0];
244
245       // Redo the new rotation.
246       mat4.identity(rotMatrix);
247       mat4.rotate(
248           rotMatrix, rotMatrix, base.deg2rad(this.rotation_[1]), [0, 1, 0]);
249       mat4.rotate(
250           rotMatrix, rotMatrix, base.deg2rad(this.rotation_[0]), [1, 0, 0]);
251       vec4.transformMat4(eyeVector, eyeVector, rotMatrix);
252
253       vec3.add(this.eye_, this.gazeTarget_, eyeVector);
254
255       this.saveCameraToSettings(base.SessionSettings());
256       this.dispatchRenderEvent_();
257     },
258
259
260     // Event callbacks.
261     onPanBegin_: function(e) {
262       this.panning_ = true;
263       this.lastMousePosition_ = this.getMousePosition_(e);
264     },
265
266     onPanUpdate_: function(e) {
267       if (!this.panning_)
268         return;
269
270       var delta = this.getMouseDelta_(e, this.lastMousePosition_);
271       this.lastMousePosition_ = this.getMousePosition_(e);
272       this.updatePanByDelta(delta);
273     },
274
275     onPanEnd_: function(e) {
276       this.panning_ = false;
277     },
278
279     onZoomBegin_: function(e) {
280       this.zooming_ = true;
281
282       var p = this.getMousePosition_(e);
283
284       this.lastMousePosition_ = p;
285       this.zoomPoint_ = p;
286     },
287
288     onZoomUpdate_: function(e) {
289       if (!this.zooming_)
290         return;
291
292       var delta = this.getMouseDelta_(e, this.lastMousePosition_);
293       this.lastMousePosition_ = this.getMousePosition_(e);
294       this.updateZoomByDelta(delta);
295     },
296
297     onZoomEnd_: function(e) {
298       this.zooming_ = false;
299       this.zoomPoint_ = undefined;
300     },
301
302     onRotateBegin_: function(e) {
303       this.rotating_ = true;
304       this.lastMousePosition_ = this.getMousePosition_(e);
305     },
306
307     onRotateUpdate_: function(e) {
308       if (!this.rotating_)
309         return;
310
311       var delta = this.getMouseDelta_(e, this.lastMousePosition_);
312       this.lastMousePosition_ = this.getMousePosition_(e);
313       this.updateRotateByDelta(delta);
314     },
315
316     onRotateEnd_: function(e) {
317       this.rotating_ = false;
318     },
319
320
321     // Misc helper functions.
322     getMousePosition_: function(e) {
323       var rect = base.windowRectForElement(this.canvas_);
324       return [(e.clientX - rect.x) * this.pixelRatio_,
325               (e.clientY - rect.y) * this.pixelRatio_];
326     },
327
328     getMouseDelta_: function(e, p) {
329       var newP = this.getMousePosition_(e);
330       return [newP[0] - p[0], newP[1] - p[1]];
331     },
332
333     dispatchRenderEvent_: function() {
334       base.dispatchSimpleEvent(this, 'renderrequired', false, false);
335     }
336   };
337
338   return {
339     Camera: Camera
340   };
341 });