UNPKG

220 kBJavaScriptView Raw
1/*
2Copyright (c) 2017-present NAVER Corp.
3name: @egjs/view360
4license: MIT
5author: NAVER Corp.
6repository: https://github.com/naver/egjs-view360
7version: 3.6.1
8*/
9(function (global, factory) {
10 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@egjs/component'), require('promise-polyfill'), require('@egjs/agent'), require('@egjs/axes'), require('gl-matrix'), require('@egjs/imready')) :
11 typeof define === 'function' && define.amd ? define(['@egjs/component', 'promise-polyfill', '@egjs/agent', '@egjs/axes', 'gl-matrix', '@egjs/imready'], factory) :
12 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, (global.eg = global.eg || {}, global.eg.view360 = factory(global.eg.Component, global.Promise, global.eg.agent, global.eg.Axes, global.glMatrix, global.eg.ImReady)));
13}(this, (function (Component, Promise$1, agent$1, Axes, glMatrix, ImReady) { 'use strict';
14
15 var VERSION = "3.6.1";
16
17 /*! *****************************************************************************
18 Copyright (c) Microsoft Corporation.
19
20 Permission to use, copy, modify, and/or distribute this software for any
21 purpose with or without fee is hereby granted.
22
23 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
24 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
25 AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
26 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
27 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
28 OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
29 PERFORMANCE OF THIS SOFTWARE.
30 ***************************************************************************** */
31
32 /* global Reflect, Promise */
33 var extendStatics = function (d, b) {
34 extendStatics = Object.setPrototypeOf || {
35 __proto__: []
36 } instanceof Array && function (d, b) {
37 d.__proto__ = b;
38 } || function (d, b) {
39 for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
40 };
41
42 return extendStatics(d, b);
43 };
44
45 function __extends(d, b) {
46 extendStatics(d, b);
47
48 function __() {
49 this.constructor = d;
50 }
51
52 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
53 }
54 var __assign = function () {
55 __assign = Object.assign || function __assign(t) {
56 for (var s, i = 1, n = arguments.length; i < n; i++) {
57 s = arguments[i];
58
59 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
60 }
61
62 return t;
63 };
64
65 return __assign.apply(this, arguments);
66 };
67 function __awaiter(thisArg, _arguments, P, generator) {
68 function adopt(value) {
69 return value instanceof P ? value : new P(function (resolve) {
70 resolve(value);
71 });
72 }
73
74 return new (P || (P = Promise))(function (resolve, reject) {
75 function fulfilled(value) {
76 try {
77 step(generator.next(value));
78 } catch (e) {
79 reject(e);
80 }
81 }
82
83 function rejected(value) {
84 try {
85 step(generator["throw"](value));
86 } catch (e) {
87 reject(e);
88 }
89 }
90
91 function step(result) {
92 result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
93 }
94
95 step((generator = generator.apply(thisArg, _arguments || [])).next());
96 });
97 }
98 function __generator(thisArg, body) {
99 var _ = {
100 label: 0,
101 sent: function () {
102 if (t[0] & 1) throw t[1];
103 return t[1];
104 },
105 trys: [],
106 ops: []
107 },
108 f,
109 y,
110 t,
111 g;
112 return g = {
113 next: verb(0),
114 "throw": verb(1),
115 "return": verb(2)
116 }, typeof Symbol === "function" && (g[Symbol.iterator] = function () {
117 return this;
118 }), g;
119
120 function verb(n) {
121 return function (v) {
122 return step([n, v]);
123 };
124 }
125
126 function step(op) {
127 if (f) throw new TypeError("Generator is already executing.");
128
129 while (_) try {
130 if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
131 if (y = 0, t) op = [op[0] & 2, t.value];
132
133 switch (op[0]) {
134 case 0:
135 case 1:
136 t = op;
137 break;
138
139 case 4:
140 _.label++;
141 return {
142 value: op[1],
143 done: false
144 };
145
146 case 5:
147 _.label++;
148 y = op[1];
149 op = [0];
150 continue;
151
152 case 7:
153 op = _.ops.pop();
154
155 _.trys.pop();
156
157 continue;
158
159 default:
160 if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
161 _ = 0;
162 continue;
163 }
164
165 if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
166 _.label = op[1];
167 break;
168 }
169
170 if (op[0] === 6 && _.label < t[1]) {
171 _.label = t[1];
172 t = op;
173 break;
174 }
175
176 if (t && _.label < t[2]) {
177 _.label = t[2];
178
179 _.ops.push(op);
180
181 break;
182 }
183
184 if (t[2]) _.ops.pop();
185
186 _.trys.pop();
187
188 continue;
189 }
190
191 op = body.call(thisArg, _);
192 } catch (e) {
193 op = [6, e];
194 y = 0;
195 } finally {
196 f = t = 0;
197 }
198
199 if (op[0] & 5) throw op[1];
200 return {
201 value: op[0] ? op[1] : void 0,
202 done: true
203 };
204 }
205 }
206 function __values(o) {
207 var s = typeof Symbol === "function" && Symbol.iterator,
208 m = s && o[s],
209 i = 0;
210 if (m) return m.call(o);
211 if (o && typeof o.length === "number") return {
212 next: function () {
213 if (o && i >= o.length) o = void 0;
214 return {
215 value: o && o[i++],
216 done: !o
217 };
218 }
219 };
220 throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
221 }
222 function __read(o, n) {
223 var m = typeof Symbol === "function" && o[Symbol.iterator];
224 if (!m) return o;
225 var i = m.call(o),
226 r,
227 ar = [],
228 e;
229
230 try {
231 while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
232 } catch (error) {
233 e = {
234 error: error
235 };
236 } finally {
237 try {
238 if (r && !r.done && (m = i["return"])) m.call(i);
239 } finally {
240 if (e) throw e.error;
241 }
242 }
243
244 return ar;
245 }
246 function __spread() {
247 for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
248
249 return ar;
250 }
251
252 var merge = function (target) {
253 var srcs = [];
254
255 for (var _i = 1; _i < arguments.length; _i++) {
256 srcs[_i - 1] = arguments[_i];
257 }
258
259 srcs.forEach(function (source) {
260 Object.keys(source).forEach(function (key) {
261 var value = source[key];
262
263 if (Array.isArray(target[key]) && Array.isArray(value)) {
264 target[key] = __spread(target[key], value);
265 } else {
266 target[key] = value;
267 }
268 });
269 });
270 return target;
271 };
272 var toImageElement = function (image) {
273 var images = image instanceof Array ? image : [image];
274 var parsedImages = images.map(function (img) {
275 var imgEl = img;
276
277 if (typeof img === "string") {
278 imgEl = new Image();
279 imgEl.crossOrigin = "anonymous";
280 imgEl.src = img;
281 }
282
283 return imgEl;
284 });
285 return parsedImages.length === 1 ? parsedImages[0] : parsedImages;
286 };
287 var toVideoElement = function (videoCandidate) {
288 if (videoCandidate instanceof HTMLVideoElement) {
289 return videoCandidate;
290 } else {
291 // url
292 var video_1 = document.createElement("video");
293 video_1.setAttribute("crossorigin", "anonymous");
294 video_1.setAttribute("webkit-playsinline", "");
295 video_1.setAttribute("playsinline", "");
296
297 if (videoCandidate instanceof Array) {
298 videoCandidate.forEach(function (v) {
299 return appendSourceElement(video_1, v);
300 });
301 } else {
302 appendSourceElement(video_1, videoCandidate);
303 }
304
305 var sourceCount = video_1.querySelectorAll("source").length;
306
307 if (sourceCount > 0) {
308 if (video_1.readyState < 1) {
309 video_1.load();
310 }
311 }
312
313 return video_1;
314 }
315 };
316 /**
317 *
318 * @param {Object | String} videoUrl Object or String containing Video Source URL<ko>비디오 URL 정보를 담고 있는 문자열이나 객체 {type, src}</ko>
319 */
320
321 var appendSourceElement = function (video, videoUrl) {
322 var videoSrc;
323 var videoType;
324
325 if (typeof videoUrl === "object") {
326 videoSrc = videoUrl.src;
327 videoType = videoUrl.type;
328 } else if (typeof videoUrl === "string") {
329 videoSrc = videoUrl;
330 }
331
332 if (!videoSrc) {
333 return false;
334 }
335
336 var sourceElement = document.createElement("source");
337 sourceElement.src = videoSrc;
338
339 if (videoType) {
340 sourceElement.type = videoType;
341 }
342
343 video.appendChild(sourceElement);
344 };
345
346 /* eslint-disable @typescript-eslint/no-implied-eval */
347 /* eslint-disable no-new-func, no-nested-ternary */
348
349 var win = typeof window !== "undefined" && window.Math === Math ? window : typeof self !== "undefined" && self.Math === Math ? self : Function("return this")();
350 /* eslint-enable no-new-func, no-nested-ternary */
351
352 var doc = win.document;
353 var nav = win.navigator;
354 var agent = agent$1();
355 var osName = agent.os.name;
356 var browserName = agent.browser.name;
357 var IS_IOS = osName === "ios";
358 var IS_SAFARI_ON_DESKTOP = osName === "mac" && browserName === "safari";
359
360 /* eslint-disable @typescript-eslint/naming-convention */
361 win.Float32Array = typeof win.Float32Array !== "undefined" ? win.Float32Array : win.Array;
362 var Float32Array$1 = win.Float32Array;
363 var getComputedStyle = win.getComputedStyle;
364 var userAgent = win.navigator && win.navigator.userAgent;
365 var SUPPORT_TOUCH = ("ontouchstart" in win);
366 var SUPPORT_DEVICEMOTION = ("ondevicemotion" in win);
367 var DeviceMotionEvent = win.DeviceMotionEvent;
368 var devicePixelRatio = win.devicePixelRatio;
369
370 var TRANSFORM = function () {
371 var _a;
372
373 var docStyle = (_a = doc === null || doc === void 0 ? void 0 : doc.documentElement.style) !== null && _a !== void 0 ? _a : {};
374 var target = ["transform", "webkitTransform", "msTransform", "mozTransform"];
375
376 for (var i = 0, len = target.length; i < len; i++) {
377 if (target[i] in docStyle) {
378 return target[i];
379 }
380 }
381
382 return "";
383 }(); // check for will-change support
384
385
386 var SUPPORT_WILLCHANGE = win.CSS && win.CSS.supports && win.CSS.supports("will-change", "transform");
387 var WEBXR_SUPPORTED = false;
388
389 var checkXRSupport = function () {
390 var navigator = window.navigator;
391
392 if (!navigator.xr) {
393 return;
394 }
395
396 if (navigator.xr.isSessionSupported) {
397 navigator.xr.isSessionSupported("immersive-vr").then(function (res) {
398 WEBXR_SUPPORTED = res;
399 }).catch(function () {
400 return void 0;
401 });
402 } else if (navigator.xr.supportsSession) {
403 navigator.xr.supportsSession("immersive-vr").then(function (res) {
404 WEBXR_SUPPORTED = res;
405 }).catch(function () {
406 return void 0;
407 });
408 }
409 };
410
411 /**
412 * Original Code
413 * https://github.com/toji/gl-matrix/blob/v2.3.2/src/gl-matrix.js
414 * Math Util
415 * modified by egjs
416 */
417
418 var quatToVec3 = function (quaternion) {
419 var baseV = glMatrix.vec3.fromValues(0, 0, 1);
420 glMatrix.vec3.transformQuat(baseV, baseV, quaternion);
421 return baseV;
422 };
423
424 var toDegree = function (a) {
425 return a * 180 / Math.PI;
426 };
427
428 var util = {};
429
430 util.isPowerOfTwo = function (n) {
431 return n && (n & n - 1) === 0;
432 };
433
434 util.extractPitchFromQuat = function (quaternion) {
435 var baseV = quatToVec3(quaternion);
436 return -1 * Math.atan2(baseV[1], Math.sqrt(Math.pow(baseV[0], 2) + Math.pow(baseV[2], 2)));
437 };
438
439 util.hypot = Math.hypot || function (x, y) {
440 return Math.sqrt(x * x + y * y);
441 }; // implement reference
442 // the general equation of a plane : http://www.gisdeveloper.co.kr/entry/평면의-공식
443 // calculating angle between two vectors : http://darkpgmr.tistory.com/121
444
445
446 var ROTATE_CONSTANT = {
447 PITCH_DELTA: 1,
448 YAW_DELTA_BY_ROLL: 2,
449 YAW_DELTA_BY_YAW: 3
450 };
451 ROTATE_CONSTANT[ROTATE_CONSTANT.PITCH_DELTA] = {
452 targetAxis: [0, 1, 0],
453 meshPoint: [0, 0, 1]
454 };
455 ROTATE_CONSTANT[ROTATE_CONSTANT.YAW_DELTA_BY_ROLL] = {
456 targetAxis: [0, 1, 0],
457 meshPoint: [1, 0, 0]
458 };
459 ROTATE_CONSTANT[ROTATE_CONSTANT.YAW_DELTA_BY_YAW] = {
460 targetAxis: [1, 0, 0],
461 meshPoint: [0, 0, 1]
462 };
463
464 var getRotationDelta = function (prevQ, curQ, rotateKind) {
465 var targetAxis = glMatrix.vec3.fromValues(ROTATE_CONSTANT[rotateKind].targetAxis[0], ROTATE_CONSTANT[rotateKind].targetAxis[1], ROTATE_CONSTANT[rotateKind].targetAxis[2]);
466 var meshPoint = ROTATE_CONSTANT[rotateKind].meshPoint;
467 var prevQuaternion = glMatrix.quat.clone(prevQ);
468 var curQuaternion = glMatrix.quat.clone(curQ);
469 glMatrix.quat.normalize(prevQuaternion, prevQuaternion);
470 glMatrix.quat.normalize(curQuaternion, curQuaternion);
471 var prevPoint = glMatrix.vec3.fromValues(0, 0, 1);
472 var curPoint = glMatrix.vec3.fromValues(0, 0, 1);
473 glMatrix.vec3.transformQuat(prevPoint, prevPoint, prevQuaternion);
474 glMatrix.vec3.transformQuat(curPoint, curPoint, curQuaternion);
475 glMatrix.vec3.transformQuat(targetAxis, targetAxis, curQuaternion);
476 var rotateDistance = glMatrix.vec3.dot(targetAxis, glMatrix.vec3.cross(glMatrix.vec3.create(), prevPoint, curPoint));
477 var rotateDirection = rotateDistance > 0 ? 1 : -1; // when counter clock wise, use vec3.fromValues(0,1,0)
478 // when clock wise, use vec3.fromValues(0,-1,0)
479 // const meshPoint1 = vec3.fromValues(0, 0, 0);
480
481 var meshPoint2 = glMatrix.vec3.fromValues(meshPoint[0], meshPoint[1], meshPoint[2]);
482 var meshPoint3;
483
484 if (rotateKind !== ROTATE_CONSTANT.YAW_DELTA_BY_YAW) {
485 meshPoint3 = glMatrix.vec3.fromValues(0, rotateDirection, 0);
486 } else {
487 meshPoint3 = glMatrix.vec3.fromValues(rotateDirection, 0, 0);
488 }
489
490 glMatrix.vec3.transformQuat(meshPoint2, meshPoint2, curQuaternion);
491 glMatrix.vec3.transformQuat(meshPoint3, meshPoint3, curQuaternion);
492 var vecU = meshPoint2;
493 var vecV = meshPoint3;
494 var vecN = glMatrix.vec3.create();
495 glMatrix.vec3.cross(vecN, vecU, vecV);
496 glMatrix.vec3.normalize(vecN, vecN);
497 var coefficientA = vecN[0];
498 var coefficientB = vecN[1];
499 var coefficientC = vecN[2]; // const coefficientD = -1 * vec3.dot(vecN, meshPoint1);
500 // a point on the plane
501
502 curPoint = glMatrix.vec3.fromValues(meshPoint[0], meshPoint[1], meshPoint[2]);
503 glMatrix.vec3.transformQuat(curPoint, curPoint, curQuaternion); // a point should project on the plane
504
505 prevPoint = glMatrix.vec3.fromValues(meshPoint[0], meshPoint[1], meshPoint[2]);
506 glMatrix.vec3.transformQuat(prevPoint, prevPoint, prevQuaternion); // distance between prevPoint and the plane
507
508 var distance = Math.abs(prevPoint[0] * coefficientA + prevPoint[1] * coefficientB + prevPoint[2] * coefficientC);
509 var projectedPrevPoint = glMatrix.vec3.create();
510 glMatrix.vec3.subtract(projectedPrevPoint, prevPoint, glMatrix.vec3.scale(glMatrix.vec3.create(), vecN, distance));
511 var trigonometricRatio = (projectedPrevPoint[0] * curPoint[0] + projectedPrevPoint[1] * curPoint[1] + projectedPrevPoint[2] * curPoint[2]) / (glMatrix.vec3.length(projectedPrevPoint) * glMatrix.vec3.length(curPoint)); // defensive block
512
513 if (trigonometricRatio > 1) {
514 trigonometricRatio = 1;
515 }
516
517 var theta = Math.acos(trigonometricRatio);
518 var crossVec = glMatrix.vec3.cross(glMatrix.vec3.create(), curPoint, projectedPrevPoint);
519 distance = coefficientA * crossVec[0] + coefficientB * crossVec[1] + coefficientC * crossVec[2];
520 var thetaDirection;
521
522 if (rotateKind !== ROTATE_CONSTANT.YAW_DELTA_BY_YAW) {
523 thetaDirection = distance > 0 ? 1 : -1;
524 } else {
525 thetaDirection = distance < 0 ? 1 : -1;
526 }
527
528 var deltaRadian = theta * thetaDirection * rotateDirection;
529 return toDegree(deltaRadian);
530 };
531
532 var angleBetweenVec2 = function (v1, v2) {
533 var det = v1[0] * v2[1] - v2[0] * v1[1];
534 var theta = -Math.atan2(det, glMatrix.vec2.dot(v1, v2));
535 return theta;
536 };
537
538 util.yawOffsetBetween = function (viewDir, targetDir) {
539 var viewDirXZ = glMatrix.vec2.fromValues(viewDir[0], viewDir[2]);
540 var targetDirXZ = glMatrix.vec2.fromValues(targetDir[0], targetDir[2]);
541 glMatrix.vec2.normalize(viewDirXZ, viewDirXZ);
542 glMatrix.vec2.normalize(targetDirXZ, targetDirXZ);
543 var theta = -angleBetweenVec2(viewDirXZ, targetDirXZ);
544 return theta;
545 };
546
547 util.sign = function (x) {
548 return Math.sign ? Math.sign(x) : Number(x > 0) - Number(x < 0) || +x;
549 };
550
551 util.toDegree = toDegree;
552 util.getRotationDelta = getRotationDelta;
553 util.angleBetweenVec2 = angleBetweenVec2;
554
555 var toAxis = function (source, offset) {
556 return offset.reduce(function (acc, v, i) {
557 if (source[i]) {
558 acc[source[i]] = v;
559 }
560
561 return acc;
562 }, {});
563 };
564
565 /**
566 * Returns a number value indiciating the version of Chrome being used,
567 * or otherwise `null` if not on Chrome.
568 *
569 * Ref: https://github.com/immersive-web/cardboard-vr-display/pull/19
570 */
571
572 /**
573 * In Chrome m65, `devicemotion` events are broken but subsequently fixed
574 * in 65.0.3325.148. Since many browsers use Chromium, ensure that
575 * we scope this detection by branch and build numbers to provide
576 * a proper fallback.
577 * https://github.com/immersive-web/webvr-polyfill/issues/307
578 */
579
580 var version = -1; // It should not be null because it will be compared with number
581
582 var branch = null;
583 var build = null;
584 var match = /Chrome\/([0-9]+)\.(?:[0-9]*)\.([0-9]*)\.([0-9]*)/i.exec(userAgent);
585
586 if (match) {
587 version = parseInt(match[1], 10);
588 branch = match[2];
589 build = match[3];
590 }
591
592 var CHROME_VERSION = version;
593 var IS_CHROME_WITHOUT_DEVICE_MOTION = version === 65 && branch === "3325" && parseInt(build, 10) < 148;
594 var IS_ANDROID = /Android/i.test(userAgent);
595 var CONTROL_MODE_VR = 1;
596 var CONTROL_MODE_YAWPITCH = 2;
597 var TOUCH_DIRECTION_NONE = 1;
598 var TOUCH_DIRECTION_YAW = 2;
599 var TOUCH_DIRECTION_PITCH = 4;
600 var TOUCH_DIRECTION_ALL = TOUCH_DIRECTION_YAW | TOUCH_DIRECTION_PITCH;
601 /* Const for MovableCoord */
602
603 var MC_DECELERATION = 0.0014;
604 var MC_MAXIMUM_DURATION = 1000;
605 var MC_BIND_SCALE = [0.20, 0.20];
606 var MAX_FIELD_OF_VIEW = 110;
607 var PAN_SCALE = 320; // const DELTA_THRESHOLD = 0.015;
608
609 var YAW_RANGE_HALF = 180;
610 var PITCH_RANGE_HALF = 90;
611 var CIRCULAR_PITCH_RANGE_HALF = 180;
612 var GYRO_MODE = {
613 NONE: "none",
614 YAWPITCH: "yawPitch",
615 VR: "VR"
616 };
617
618 /* eslint-disable */
619 var MathUtil = win.MathUtil || {};
620 MathUtil.degToRad = Math.PI / 180;
621 MathUtil.radToDeg = 180 / Math.PI; // Some minimal math functionality borrowed from THREE.Math and stripped down
622 // for the purposes of this library.
623
624 MathUtil.Vector2 = function (x, y) {
625 this.x = x || 0;
626 this.y = y || 0;
627 };
628
629 MathUtil.Vector2.prototype = {
630 constructor: MathUtil.Vector2,
631 set: function (x, y) {
632 this.x = x;
633 this.y = y;
634 return this;
635 },
636 copy: function (v) {
637 this.x = v.x;
638 this.y = v.y;
639 return this;
640 },
641 subVectors: function (a, b) {
642 this.x = a.x - b.x;
643 this.y = a.y - b.y;
644 return this;
645 }
646 };
647
648 MathUtil.Vector3 = function (x, y, z) {
649 this.x = x || 0;
650 this.y = y || 0;
651 this.z = z || 0;
652 };
653
654 MathUtil.Vector3.prototype = {
655 constructor: MathUtil.Vector3,
656 set: function (x, y, z) {
657 this.x = x;
658 this.y = y;
659 this.z = z;
660 return this;
661 },
662 copy: function (v) {
663 this.x = v.x;
664 this.y = v.y;
665 this.z = v.z;
666 return this;
667 },
668 length: function () {
669 return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
670 },
671 normalize: function () {
672 var scalar = this.length();
673
674 if (scalar !== 0) {
675 var invScalar = 1 / scalar;
676 this.multiplyScalar(invScalar);
677 } else {
678 this.x = 0;
679 this.y = 0;
680 this.z = 0;
681 }
682
683 return this;
684 },
685 multiplyScalar: function (scalar) {
686 this.x *= scalar;
687 this.y *= scalar;
688 this.z *= scalar;
689 },
690 applyQuaternion: function (q) {
691 var x = this.x;
692 var y = this.y;
693 var z = this.z;
694 var qx = q.x;
695 var qy = q.y;
696 var qz = q.z;
697 var qw = q.w; // calculate quat * vector
698
699 var ix = qw * x + qy * z - qz * y;
700 var iy = qw * y + qz * x - qx * z;
701 var iz = qw * z + qx * y - qy * x;
702 var iw = -qx * x - qy * y - qz * z; // calculate result * inverse quat
703
704 this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy;
705 this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz;
706 this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx;
707 return this;
708 },
709 dot: function (v) {
710 return this.x * v.x + this.y * v.y + this.z * v.z;
711 },
712 crossVectors: function (a, b) {
713 var ax = a.x;
714 var ay = a.y;
715 var az = a.z;
716 var bx = b.x;
717 var by = b.y;
718 var bz = b.z;
719 this.x = ay * bz - az * by;
720 this.y = az * bx - ax * bz;
721 this.z = ax * by - ay * bx;
722 return this;
723 }
724 };
725
726 MathUtil.Quaternion = function (x, y, z, w) {
727 this.x = x || 0;
728 this.y = y || 0;
729 this.z = z || 0;
730 this.w = w !== undefined ? w : 1;
731 };
732
733 MathUtil.Quaternion.prototype = {
734 constructor: MathUtil.Quaternion,
735 set: function (x, y, z, w) {
736 this.x = x;
737 this.y = y;
738 this.z = z;
739 this.w = w;
740 return this;
741 },
742 copy: function (quaternion) {
743 this.x = quaternion.x;
744 this.y = quaternion.y;
745 this.z = quaternion.z;
746 this.w = quaternion.w;
747 return this;
748 },
749 setFromEulerXYZ: function (x, y, z) {
750 var c1 = Math.cos(x / 2);
751 var c2 = Math.cos(y / 2);
752 var c3 = Math.cos(z / 2);
753 var s1 = Math.sin(x / 2);
754 var s2 = Math.sin(y / 2);
755 var s3 = Math.sin(z / 2);
756 this.x = s1 * c2 * c3 + c1 * s2 * s3;
757 this.y = c1 * s2 * c3 - s1 * c2 * s3;
758 this.z = c1 * c2 * s3 + s1 * s2 * c3;
759 this.w = c1 * c2 * c3 - s1 * s2 * s3;
760 return this;
761 },
762 setFromEulerYXZ: function (x, y, z) {
763 var c1 = Math.cos(x / 2);
764 var c2 = Math.cos(y / 2);
765 var c3 = Math.cos(z / 2);
766 var s1 = Math.sin(x / 2);
767 var s2 = Math.sin(y / 2);
768 var s3 = Math.sin(z / 2);
769 this.x = s1 * c2 * c3 + c1 * s2 * s3;
770 this.y = c1 * s2 * c3 - s1 * c2 * s3;
771 this.z = c1 * c2 * s3 - s1 * s2 * c3;
772 this.w = c1 * c2 * c3 + s1 * s2 * s3;
773 return this;
774 },
775 setFromAxisAngle: function (axis, angle) {
776 // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
777 // assumes axis is normalized
778 var halfAngle = angle / 2;
779 var s = Math.sin(halfAngle);
780 this.x = axis.x * s;
781 this.y = axis.y * s;
782 this.z = axis.z * s;
783 this.w = Math.cos(halfAngle);
784 return this;
785 },
786 multiply: function (q) {
787 return this.multiplyQuaternions(this, q);
788 },
789 multiplyQuaternions: function (a, b) {
790 // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
791 var qax = a.x;
792 var qay = a.y;
793 var qaz = a.z;
794 var qaw = a.w;
795 var qbx = b.x;
796 var qby = b.y;
797 var qbz = b.z;
798 var qbw = b.w;
799 this.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
800 this.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
801 this.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
802 this.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
803 return this;
804 },
805 inverse: function () {
806 this.x *= -1;
807 this.y *= -1;
808 this.z *= -1;
809 this.normalize();
810 return this;
811 },
812 normalize: function () {
813 var l = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
814
815 if (l === 0) {
816 this.x = 0;
817 this.y = 0;
818 this.z = 0;
819 this.w = 1;
820 } else {
821 l = 1 / l;
822 this.x = this.x * l;
823 this.y = this.y * l;
824 this.z = this.z * l;
825 this.w = this.w * l;
826 }
827
828 return this;
829 },
830 slerp: function (qb, t) {
831 if (t === 0) return this;
832 if (t === 1) return this.copy(qb);
833 var x = this.x;
834 var y = this.y;
835 var z = this.z;
836 var w = this.w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
837
838 var cosHalfTheta = w * qb.w + x * qb.x + y * qb.y + z * qb.z;
839
840 if (cosHalfTheta < 0) {
841 this.w = -qb.w;
842 this.x = -qb.x;
843 this.y = -qb.y;
844 this.z = -qb.z;
845 cosHalfTheta = -cosHalfTheta;
846 } else {
847 this.copy(qb);
848 }
849
850 if (cosHalfTheta >= 1.0) {
851 this.w = w;
852 this.x = x;
853 this.y = y;
854 this.z = z;
855 return this;
856 }
857
858 var halfTheta = Math.acos(cosHalfTheta);
859 var sinHalfTheta = Math.sqrt(1.0 - cosHalfTheta * cosHalfTheta);
860
861 if (Math.abs(sinHalfTheta) < 0.001) {
862 this.w = 0.5 * (w + this.w);
863 this.x = 0.5 * (x + this.x);
864 this.y = 0.5 * (y + this.y);
865 this.z = 0.5 * (z + this.z);
866 return this;
867 }
868
869 var ratioA = Math.sin((1 - t) * halfTheta) / sinHalfTheta;
870 var ratioB = Math.sin(t * halfTheta) / sinHalfTheta;
871 this.w = w * ratioA + this.w * ratioB;
872 this.x = x * ratioA + this.x * ratioB;
873 this.y = y * ratioA + this.y * ratioB;
874 this.z = z * ratioA + this.z * ratioB;
875 return this;
876 },
877 setFromUnitVectors: function () {
878 // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
879 // assumes direction vectors vFrom and vTo are normalized
880 var v1;
881 var r;
882 var EPS = 0.000001;
883 return function (vFrom, vTo) {
884 if (v1 === undefined) v1 = new MathUtil.Vector3();
885 r = vFrom.dot(vTo) + 1;
886
887 if (r < EPS) {
888 r = 0;
889
890 if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) {
891 v1.set(-vFrom.y, vFrom.x, 0);
892 } else {
893 v1.set(0, -vFrom.z, vFrom.y);
894 }
895 } else {
896 v1.crossVectors(vFrom, vTo);
897 }
898
899 this.x = v1.x;
900 this.y = v1.y;
901 this.z = v1.z;
902 this.w = r;
903 this.normalize();
904 return this;
905 };
906 }()
907 };
908
909 /* eslint-disable */
910
911 /*
912 * Copyright 2015 Google Inc. All Rights Reserved.
913 * Licensed under the Apache License, Version 2.0 (the "License");
914 * you may not use this file except in compliance with the License.
915 * You may obtain a copy of the License at
916 *
917 * http://www.apache.org/licenses/LICENSE-2.0
918 *
919 * Unless required by applicable law or agreed to in writing, software
920 * distributed under the License is distributed on an "AS IS" BASIS,
921 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
922 * See the License for the specific language governing permissions and
923 * limitations under the License.
924 */
925 var _a; // tslint:disable: only-arrow-functions
926 var userAgent$1 = (_a = nav === null || nav === void 0 ? void 0 : nav.userAgent) !== null && _a !== void 0 ? _a : "";
927 var Util = win.Util || {};
928 Util.MIN_TIMESTEP = 0.001;
929 Util.MAX_TIMESTEP = 1;
930
931 Util.base64 = function (mimeType, base64) {
932 return "data:" + mimeType + ";base64," + base64;
933 };
934
935 Util.clamp = function (value, min, max) {
936 return Math.min(Math.max(min, value), max);
937 };
938
939 Util.lerp = function (a, b, t) {
940 return a + (b - a) * t;
941 };
942
943 Util.isIOS = function () {
944 var isIOS = /iPad|iPhone|iPod/.test(nav === null || nav === void 0 ? void 0 : nav.platform);
945 return function () {
946 return isIOS;
947 };
948 }();
949
950 Util.isWebViewAndroid = function () {
951 var isWebViewAndroid = userAgent$1.indexOf("Version") !== -1 && userAgent$1.indexOf("Android") !== -1 && userAgent$1.indexOf("Chrome") !== -1;
952 return function () {
953 return isWebViewAndroid;
954 };
955 }();
956
957 Util.isSafari = function () {
958 var isSafari = /^((?!chrome|android).)*safari/i.test(userAgent$1);
959 return function () {
960 return isSafari;
961 };
962 }();
963
964 Util.isFirefoxAndroid = function () {
965 var isFirefoxAndroid = userAgent$1.indexOf("Firefox") !== -1 && userAgent$1.indexOf("Android") !== -1;
966 return function () {
967 return isFirefoxAndroid;
968 };
969 }();
970
971 Util.isR7 = function () {
972 var isR7 = userAgent$1.indexOf("R7 Build") !== -1;
973 return function () {
974 return isR7;
975 };
976 }();
977
978 Util.isLandscapeMode = function () {
979 var rtn = win.orientation === 90 || win.orientation === -90;
980 return Util.isR7() ? !rtn : rtn;
981 }; // Helper method to validate the time steps of sensor timestamps.
982
983
984 Util.isTimestampDeltaValid = function (timestampDeltaS) {
985 if (isNaN(timestampDeltaS)) {
986 return false;
987 }
988
989 if (timestampDeltaS <= Util.MIN_TIMESTEP) {
990 return false;
991 }
992
993 if (timestampDeltaS > Util.MAX_TIMESTEP) {
994 return false;
995 }
996
997 return true;
998 };
999
1000 Util.getScreenWidth = function () {
1001 return Math.max(win.screen.width, win.screen.height) * win.devicePixelRatio;
1002 };
1003
1004 Util.getScreenHeight = function () {
1005 return Math.min(win.screen.width, win.screen.height) * win.devicePixelRatio;
1006 };
1007
1008 Util.requestFullscreen = function (element) {
1009 if (Util.isWebViewAndroid()) {
1010 return false;
1011 }
1012
1013 if (element.requestFullscreen) {
1014 element.requestFullscreen();
1015 } else if (element.webkitRequestFullscreen) {
1016 element.webkitRequestFullscreen();
1017 } else if (element.mozRequestFullScreen) {
1018 element.mozRequestFullScreen();
1019 } else if (element.msRequestFullscreen) {
1020 element.msRequestFullscreen();
1021 } else {
1022 return false;
1023 }
1024
1025 return true;
1026 };
1027
1028 Util.exitFullscreen = function () {
1029 if (doc.exitFullscreen) {
1030 doc.exitFullscreen();
1031 } else if (doc.webkitExitFullscreen) {
1032 doc.webkitExitFullscreen();
1033 } else if (doc.mozCancelFullScreen) {
1034 doc.mozCancelFullScreen();
1035 } else if (doc.msExitFullscreen) {
1036 doc.msExitFullscreen();
1037 } else {
1038 return false;
1039 }
1040
1041 return true;
1042 };
1043
1044 Util.getFullscreenElement = function () {
1045 return doc.fullscreenElement || doc.webkitFullscreenElement || doc.mozFullScreenElement || doc.msFullscreenElement;
1046 };
1047
1048 Util.linkProgram = function (gl, vertexSource, fragmentSource, attribLocationMap) {
1049 // No error checking for brevity.
1050 var vertexShader = gl.createShader(gl.VERTEX_SHADER);
1051 gl.shaderSource(vertexShader, vertexSource);
1052 gl.compileShader(vertexShader);
1053 var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
1054 gl.shaderSource(fragmentShader, fragmentSource);
1055 gl.compileShader(fragmentShader);
1056 var program = gl.createProgram();
1057 gl.attachShader(program, vertexShader);
1058 gl.attachShader(program, fragmentShader);
1059
1060 for (var attribName in attribLocationMap) gl.bindAttribLocation(program, attribLocationMap[attribName], attribName);
1061
1062 gl.linkProgram(program);
1063 gl.deleteShader(vertexShader);
1064 gl.deleteShader(fragmentShader);
1065 return program;
1066 };
1067
1068 Util.getProgramUniforms = function (gl, program) {
1069 var uniforms = {};
1070 var uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
1071 var uniformName = "";
1072
1073 for (var i = 0; i < uniformCount; i++) {
1074 var uniformInfo = gl.getActiveUniform(program, i);
1075 uniformName = uniformInfo.name.replace("[0]", "");
1076 uniforms[uniformName] = gl.getUniformLocation(program, uniformName);
1077 }
1078
1079 return uniforms;
1080 };
1081
1082 Util.orthoMatrix = function (out, left, right, bottom, top, near, far) {
1083 var lr = 1 / (left - right);
1084 var bt = 1 / (bottom - top);
1085 var nf = 1 / (near - far);
1086 out[0] = -2 * lr;
1087 out[1] = 0;
1088 out[2] = 0;
1089 out[3] = 0;
1090 out[4] = 0;
1091 out[5] = -2 * bt;
1092 out[6] = 0;
1093 out[7] = 0;
1094 out[8] = 0;
1095 out[9] = 0;
1096 out[10] = 2 * nf;
1097 out[11] = 0;
1098 out[12] = (left + right) * lr;
1099 out[13] = (top + bottom) * bt;
1100 out[14] = (far + near) * nf;
1101 out[15] = 1;
1102 return out;
1103 };
1104
1105 Util.copyArray = function (source, dest) {
1106 for (var i = 0, n = source.length; i < n; i++) {
1107 dest[i] = source[i];
1108 }
1109 };
1110
1111 Util.isMobile = function () {
1112 var check = false;
1113
1114 (function (a) {
1115 if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true;
1116 })(userAgent$1 || (nav === null || nav === void 0 ? void 0 : nav.vendor) || win.opera);
1117
1118 return check;
1119 };
1120
1121 Util.extend = function (dest, src) {
1122 for (var key in src) {
1123 if (src.hasOwnProperty(key)) {
1124 dest[key] = src[key];
1125 }
1126 }
1127
1128 return dest;
1129 };
1130
1131 Util.safariCssSizeWorkaround = function (canvas) {
1132 // TODO(smus): Remove this workaround when Safari for iOS is fixed.
1133 // iOS only workaround (for https://bugs.webkit.org/show_bug.cgi?id=152556).
1134 //
1135 // "To the last I grapple with thee;
1136 // from hell's heart I stab at thee;
1137 // for hate's sake I spit my last breath at thee."
1138 // -- Moby Dick, by Herman Melville
1139 if (Util.isIOS()) {
1140 var width_1 = canvas.style.width;
1141 var height_1 = canvas.style.height;
1142 canvas.style.width = parseInt(width_1) + 1 + "px";
1143 canvas.style.height = parseInt(height_1) + "px";
1144 setTimeout(function () {
1145 canvas.style.width = width_1;
1146 canvas.style.height = height_1;
1147 }, 100);
1148 } // Debug only.
1149
1150
1151 win.Util = Util;
1152 win.canvas = canvas;
1153 };
1154
1155 Util.isDebug = function () {
1156 return Util.getQueryParameter("debug");
1157 };
1158
1159 Util.getQueryParameter = function (name) {
1160 name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
1161 var regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
1162 var results = regex.exec(location.search);
1163 return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
1164 };
1165
1166 Util.frameDataFromPose = function () {
1167 var piOver180 = Math.PI / 180.0;
1168 var rad45 = Math.PI * 0.25; // Borrowed from glMatrix.
1169
1170 function mat4_perspectiveFromFieldOfView(out, fov, near, far) {
1171 var upTan = Math.tan(fov ? fov.upDegrees * piOver180 : rad45);
1172 var downTan = Math.tan(fov ? fov.downDegrees * piOver180 : rad45);
1173 var leftTan = Math.tan(fov ? fov.leftDegrees * piOver180 : rad45);
1174 var rightTan = Math.tan(fov ? fov.rightDegrees * piOver180 : rad45);
1175 var xScale = 2.0 / (leftTan + rightTan);
1176 var yScale = 2.0 / (upTan + downTan);
1177 out[0] = xScale;
1178 out[1] = 0.0;
1179 out[2] = 0.0;
1180 out[3] = 0.0;
1181 out[4] = 0.0;
1182 out[5] = yScale;
1183 out[6] = 0.0;
1184 out[7] = 0.0;
1185 out[8] = -((leftTan - rightTan) * xScale * 0.5);
1186 out[9] = (upTan - downTan) * yScale * 0.5;
1187 out[10] = far / (near - far);
1188 out[11] = -1.0;
1189 out[12] = 0.0;
1190 out[13] = 0.0;
1191 out[14] = far * near / (near - far);
1192 out[15] = 0.0;
1193 return out;
1194 }
1195
1196 function mat4_fromRotationTranslation(out, q, v) {
1197 // Quaternion math
1198 var x = q[0];
1199 var y = q[1];
1200 var z = q[2];
1201 var w = q[3];
1202 var x2 = x + x;
1203 var y2 = y + y;
1204 var z2 = z + z;
1205 var xx = x * x2;
1206 var xy = x * y2;
1207 var xz = x * z2;
1208 var yy = y * y2;
1209 var yz = y * z2;
1210 var zz = z * z2;
1211 var wx = w * x2;
1212 var wy = w * y2;
1213 var wz = w * z2;
1214 out[0] = 1 - (yy + zz);
1215 out[1] = xy + wz;
1216 out[2] = xz - wy;
1217 out[3] = 0;
1218 out[4] = xy - wz;
1219 out[5] = 1 - (xx + zz);
1220 out[6] = yz + wx;
1221 out[7] = 0;
1222 out[8] = xz + wy;
1223 out[9] = yz - wx;
1224 out[10] = 1 - (xx + yy);
1225 out[11] = 0;
1226 out[12] = v[0];
1227 out[13] = v[1];
1228 out[14] = v[2];
1229 out[15] = 1;
1230 return out;
1231 }
1232
1233 function mat4_translate(out, a, v) {
1234 var x = v[0];
1235 var y = v[1];
1236 var z = v[2];
1237 var a00;
1238 var a01;
1239 var a02;
1240 var a03;
1241 var a10;
1242 var a11;
1243 var a12;
1244 var a13;
1245 var a20;
1246 var a21;
1247 var a22;
1248 var a23;
1249
1250 if (a === out) {
1251 out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
1252 out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
1253 out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
1254 out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
1255 } else {
1256 a00 = a[0];
1257 a01 = a[1];
1258 a02 = a[2];
1259 a03 = a[3];
1260 a10 = a[4];
1261 a11 = a[5];
1262 a12 = a[6];
1263 a13 = a[7];
1264 a20 = a[8];
1265 a21 = a[9];
1266 a22 = a[10];
1267 a23 = a[11];
1268 out[0] = a00;
1269 out[1] = a01;
1270 out[2] = a02;
1271 out[3] = a03;
1272 out[4] = a10;
1273 out[5] = a11;
1274 out[6] = a12;
1275 out[7] = a13;
1276 out[8] = a20;
1277 out[9] = a21;
1278 out[10] = a22;
1279 out[11] = a23;
1280 out[12] = a00 * x + a10 * y + a20 * z + a[12];
1281 out[13] = a01 * x + a11 * y + a21 * z + a[13];
1282 out[14] = a02 * x + a12 * y + a22 * z + a[14];
1283 out[15] = a03 * x + a13 * y + a23 * z + a[15];
1284 }
1285
1286 return out;
1287 }
1288
1289 function mat4_invert(out, a) {
1290 var a00 = a[0];
1291 var a01 = a[1];
1292 var a02 = a[2];
1293 var a03 = a[3];
1294 var a10 = a[4];
1295 var a11 = a[5];
1296 var a12 = a[6];
1297 var a13 = a[7];
1298 var a20 = a[8];
1299 var a21 = a[9];
1300 var a22 = a[10];
1301 var a23 = a[11];
1302 var a30 = a[12];
1303 var a31 = a[13];
1304 var a32 = a[14];
1305 var a33 = a[15];
1306 var b00 = a00 * a11 - a01 * a10;
1307 var b01 = a00 * a12 - a02 * a10;
1308 var b02 = a00 * a13 - a03 * a10;
1309 var b03 = a01 * a12 - a02 * a11;
1310 var b04 = a01 * a13 - a03 * a11;
1311 var b05 = a02 * a13 - a03 * a12;
1312 var b06 = a20 * a31 - a21 * a30;
1313 var b07 = a20 * a32 - a22 * a30;
1314 var b08 = a20 * a33 - a23 * a30;
1315 var b09 = a21 * a32 - a22 * a31;
1316 var b10 = a21 * a33 - a23 * a31;
1317 var b11 = a22 * a33 - a23 * a32; // Calculate the determinant
1318
1319 var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
1320
1321 if (!det) {
1322 return null;
1323 }
1324
1325 det = 1.0 / det;
1326 out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
1327 out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
1328 out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
1329 out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
1330 out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
1331 out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
1332 out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
1333 out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
1334 out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
1335 out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
1336 out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
1337 out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
1338 out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
1339 out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
1340 out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
1341 out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
1342 return out;
1343 }
1344
1345 var defaultOrientation = new Float32Array([0, 0, 0, 1]);
1346 var defaultPosition = new Float32Array([0, 0, 0]);
1347
1348 function updateEyeMatrices(projection, view, pose, parameters, vrDisplay) {
1349 mat4_perspectiveFromFieldOfView(projection, parameters ? parameters.fieldOfView : null, vrDisplay.depthNear, vrDisplay.depthFar);
1350 var orientation = pose.orientation || defaultOrientation;
1351 var position = pose.position || defaultPosition;
1352 mat4_fromRotationTranslation(view, orientation, position);
1353 if (parameters) mat4_translate(view, view, parameters.offset);
1354 mat4_invert(view, view);
1355 }
1356
1357 return function (frameData, pose, vrDisplay) {
1358 if (!frameData || !pose) return false;
1359 frameData.pose = pose;
1360 frameData.timestamp = pose.timestamp;
1361 updateEyeMatrices(frameData.leftProjectionMatrix, frameData.leftViewMatrix, pose, vrDisplay.getEyeParameters("left"), vrDisplay);
1362 updateEyeMatrices(frameData.rightProjectionMatrix, frameData.rightViewMatrix, pose, vrDisplay.getEyeParameters("right"), vrDisplay);
1363 return true;
1364 };
1365 }();
1366
1367 Util.isInsideCrossDomainIFrame = function () {
1368 var isFramed = win.self !== win.top;
1369 var refDomain = Util.getDomainFromUrl(doc.referrer);
1370 var thisDomain = Util.getDomainFromUrl(win.location.href);
1371 return isFramed && refDomain !== thisDomain;
1372 }; // From http://stackoverflow.com/a/23945027.
1373
1374
1375 Util.getDomainFromUrl = function (url) {
1376 var domain; // Find & remove protocol (http, ftp, etc.) and get domain.
1377
1378 if (url.indexOf("://") > -1) {
1379 domain = url.split("/")[2];
1380 } else {
1381 domain = url.split("/")[0];
1382 } // find & remove port number
1383
1384
1385 domain = domain.split(":")[0];
1386 return domain;
1387 };
1388
1389 /* eslint-disable */
1390 /**
1391 * Given an orientation and the gyroscope data, predicts the future orientation
1392 * of the head. This makes rendering appear faster.
1393 *
1394 * Also see: http://msl.cs.uiuc.edu/~lavalle/papers/LavYerKatAnt14.pdf
1395 * @param {Number} predictionTimeS time from head movement to the appearance of
1396 * the corresponding image.
1397 */
1398
1399 var PosePredictor =
1400 /*#__PURE__*/
1401 function () {
1402 function PosePredictor(predictionTimeS) {
1403 this.predictionTimeS = predictionTimeS; // The quaternion corresponding to the previous state.
1404
1405 this.previousQ = new MathUtil.Quaternion(); // Previous time a prediction occurred.
1406
1407 this.previousTimestampS = null; // The delta quaternion that adjusts the current pose.
1408
1409 this.deltaQ = new MathUtil.Quaternion(); // The output quaternion.
1410
1411 this.outQ = new MathUtil.Quaternion();
1412 }
1413
1414 var __proto = PosePredictor.prototype;
1415
1416 __proto.getPrediction = function (currentQ, gyro, timestampS) {
1417 if (!this.previousTimestampS) {
1418 this.previousQ.copy(currentQ);
1419 this.previousTimestampS = timestampS;
1420 return currentQ;
1421 } // Calculate axis and angle based on gyroscope rotation rate data.
1422
1423
1424 var axis = new MathUtil.Vector3();
1425 axis.copy(gyro);
1426 axis.normalize();
1427 var angularSpeed = gyro.length(); // If we're rotating slowly, don't do prediction.
1428
1429 if (angularSpeed < MathUtil.degToRad * 20) {
1430 if (Util.isDebug()) {
1431 console.log("Moving slowly, at %s deg/s: no prediction", (MathUtil.radToDeg * angularSpeed).toFixed(1));
1432 }
1433
1434 this.outQ.copy(currentQ);
1435 this.previousQ.copy(currentQ);
1436 return this.outQ;
1437 } // Get the predicted angle based on the time delta and latency.
1438
1439
1440 var deltaT = timestampS - this.previousTimestampS;
1441 var predictAngle = angularSpeed * this.predictionTimeS;
1442 this.deltaQ.setFromAxisAngle(axis, predictAngle);
1443 this.outQ.copy(this.previousQ);
1444 this.outQ.multiply(this.deltaQ);
1445 this.previousQ.copy(currentQ);
1446 this.previousTimestampS = timestampS;
1447 return this.outQ;
1448 };
1449
1450 return PosePredictor;
1451 }();
1452
1453 var STILLNESS_THRESHOLD = 200; // millisecond
1454
1455 var DeviceMotion =
1456 /*#__PURE__*/
1457 function (_super) {
1458 __extends(DeviceMotion, _super);
1459
1460 function DeviceMotion() {
1461 var _this = _super.call(this) || this;
1462
1463 _this._onDeviceMotion = _this._onDeviceMotion.bind(_this);
1464 _this._onDeviceOrientation = _this._onDeviceOrientation.bind(_this);
1465 _this._onChromeWithoutDeviceMotion = _this._onChromeWithoutDeviceMotion.bind(_this);
1466 _this.isWithoutDeviceMotion = IS_CHROME_WITHOUT_DEVICE_MOTION;
1467 _this.isAndroid = IS_ANDROID;
1468 _this.stillGyroVec = glMatrix.vec3.create();
1469 _this.rawGyroVec = glMatrix.vec3.create();
1470 _this.adjustedGyroVec = glMatrix.vec3.create();
1471 _this._timer = -1;
1472 _this.lastDevicemotionTimestamp = 0;
1473 _this._isEnabled = false;
1474
1475 _this.enable();
1476
1477 return _this;
1478 }
1479
1480 var __proto = DeviceMotion.prototype;
1481
1482 __proto.enable = function () {
1483 if (this.isAndroid) {
1484 win.addEventListener("deviceorientation", this._onDeviceOrientation);
1485 }
1486
1487 if (this.isWithoutDeviceMotion) {
1488 win.addEventListener("deviceorientation", this._onChromeWithoutDeviceMotion);
1489 } else {
1490 win.addEventListener("devicemotion", this._onDeviceMotion);
1491 }
1492
1493 this._isEnabled = true;
1494 };
1495
1496 __proto.disable = function () {
1497 win.removeEventListener("deviceorientation", this._onDeviceOrientation);
1498 win.removeEventListener("deviceorientation", this._onChromeWithoutDeviceMotion);
1499 win.removeEventListener("devicemotion", this._onDeviceMotion);
1500 this._isEnabled = false;
1501 };
1502
1503 __proto._onChromeWithoutDeviceMotion = function (e) {
1504 var alpha = e.alpha,
1505 beta = e.beta,
1506 gamma = e.gamma; // There is deviceorientation event trigged with empty values
1507 // on Headless Chrome.
1508
1509 if (alpha === null) {
1510 return;
1511 } // convert to radian
1512
1513
1514 alpha = (alpha || 0) * Math.PI / 180;
1515 beta = (beta || 0) * Math.PI / 180;
1516 gamma = (gamma || 0) * Math.PI / 180;
1517 this.trigger(new Component.ComponentEvent("devicemotion", {
1518 inputEvent: {
1519 deviceorientation: {
1520 alpha: alpha,
1521 beta: beta,
1522 gamma: -gamma
1523 }
1524 }
1525 }));
1526 };
1527
1528 __proto._onDeviceOrientation = function () {
1529 var _this = this;
1530
1531 if (this._timer) {
1532 clearTimeout(this._timer);
1533 }
1534
1535 this._timer = win.setTimeout(function () {
1536 if (new Date().getTime() - _this.lastDevicemotionTimestamp < STILLNESS_THRESHOLD) {
1537 glMatrix.vec3.copy(_this.stillGyroVec, _this.rawGyroVec);
1538 }
1539 }, STILLNESS_THRESHOLD);
1540 };
1541
1542 __proto._onDeviceMotion = function (e) {
1543 // desktop chrome triggers devicemotion event with empthy sensor values.
1544 // Those events should ignored.
1545 var isGyroSensorAvailable = !(e.rotationRate.alpha == null);
1546 var isGravitySensorAvailable = !(e.accelerationIncludingGravity.x == null);
1547
1548 if (e.interval === 0 || !(isGyroSensorAvailable && isGravitySensorAvailable)) {
1549 return;
1550 }
1551
1552 var devicemotionEvent = __assign({}, e);
1553
1554 devicemotionEvent.interval = e.interval;
1555 devicemotionEvent.timeStamp = e.timeStamp;
1556 devicemotionEvent.type = e.type;
1557 devicemotionEvent.rotationRate = {
1558 alpha: e.rotationRate.alpha,
1559 beta: e.rotationRate.beta,
1560 gamma: e.rotationRate.gamma
1561 };
1562 devicemotionEvent.accelerationIncludingGravity = {
1563 x: e.accelerationIncludingGravity.x,
1564 y: e.accelerationIncludingGravity.y,
1565 z: e.accelerationIncludingGravity.z
1566 };
1567 devicemotionEvent.acceleration = {
1568 x: e.acceleration.x,
1569 y: e.acceleration.y,
1570 z: e.acceleration.z
1571 };
1572
1573 if (this.isAndroid) {
1574 glMatrix.vec3.set(this.rawGyroVec, e.rotationRate.alpha || 0, e.rotationRate.beta || 0, e.rotationRate.gamma || 0);
1575 glMatrix.vec3.subtract(this.adjustedGyroVec, this.rawGyroVec, this.stillGyroVec);
1576 this.lastDevicemotionTimestamp = new Date().getTime();
1577 devicemotionEvent.adjustedRotationRate = {
1578 alpha: this.adjustedGyroVec[0],
1579 beta: this.adjustedGyroVec[1],
1580 gamma: this.adjustedGyroVec[2]
1581 };
1582 }
1583
1584 this.trigger(new Component.ComponentEvent("devicemotion", {
1585 inputEvent: devicemotionEvent
1586 }));
1587 };
1588
1589 return DeviceMotion;
1590 }(Component);
1591
1592 var SensorSample =
1593 /*#__PURE__*/
1594 function () {
1595 function SensorSample(sample, timestampS) {
1596 this.set(sample, timestampS);
1597 }
1598
1599 var __proto = SensorSample.prototype;
1600
1601 __proto.set = function (sample, timestampS) {
1602 this.sample = sample;
1603 this.timestampS = timestampS;
1604 };
1605
1606 __proto.copy = function (sensorSample) {
1607 this.set(sensorSample.sample, sensorSample.timestampS);
1608 };
1609
1610 return SensorSample;
1611 }();
1612
1613 /* eslint-disable */
1614 /**
1615 * An implementation of a simple complementary filter, which fuses gyroscope and
1616 * accelerometer data from the 'devicemotion' event.
1617 *
1618 * Accelerometer data is very noisy, but stable over the long term.
1619 * Gyroscope data is smooth, but tends to drift over the long term.
1620 *
1621 * This fusion is relatively simple:
1622 * 1. Get orientation estimates from accelerometer by applying a low-pass filter
1623 * on that data.
1624 * 2. Get orientation estimates from gyroscope by integrating over time.
1625 * 3. Combine the two estimates, weighing (1) in the long term, but (2) for the
1626 * short term.
1627 */
1628
1629 var ComplementaryFilter =
1630 /*#__PURE__*/
1631 function () {
1632 function ComplementaryFilter(kFilter) {
1633 this.addGyroMeasurement = function (vector, timestampS) {
1634 this.currentGyroMeasurement.set(vector, timestampS);
1635 var deltaT = timestampS - this.previousGyroMeasurement.timestampS;
1636
1637 if (Util.isTimestampDeltaValid(deltaT)) {
1638 this.run_();
1639 }
1640
1641 this.previousGyroMeasurement.copy(this.currentGyroMeasurement);
1642 };
1643
1644 this.kFilter = kFilter; // Raw sensor measurements.
1645
1646 this.currentAccelMeasurement = new SensorSample();
1647 this.currentGyroMeasurement = new SensorSample();
1648 this.previousGyroMeasurement = new SensorSample(); // Set default look direction to be in the correct direction.
1649
1650 if (Util.isIOS()) {
1651 this.filterQ = new MathUtil.Quaternion(-1, 0, 0, 1);
1652 } else {
1653 this.filterQ = new MathUtil.Quaternion(1, 0, 0, 1);
1654 }
1655
1656 this.previousFilterQ = new MathUtil.Quaternion();
1657 this.previousFilterQ.copy(this.filterQ); // Orientation based on the accelerometer.
1658
1659 this.accelQ = new MathUtil.Quaternion(); // Whether or not the orientation has been initialized.
1660
1661 this.isOrientationInitialized = false; // Running estimate of gravity based on the current orientation.
1662
1663 this.estimatedGravity = new MathUtil.Vector3(); // Measured gravity based on accelerometer.
1664
1665 this.measuredGravity = new MathUtil.Vector3(); // Debug only quaternion of gyro-based orientation.
1666
1667 this.gyroIntegralQ = new MathUtil.Quaternion();
1668 }
1669
1670 var __proto = ComplementaryFilter.prototype;
1671
1672 __proto.addAccelMeasurement = function (vector, timestampS) {
1673 this.currentAccelMeasurement.set(vector, timestampS);
1674 };
1675
1676 __proto.getOrientation = function () {
1677 return this.filterQ;
1678 };
1679
1680 __proto.run_ = function () {
1681 if (!this.isOrientationInitialized) {
1682 this.accelQ = this.accelToQuaternion_(this.currentAccelMeasurement.sample);
1683 this.previousFilterQ.copy(this.accelQ);
1684 this.isOrientationInitialized = true;
1685 return;
1686 }
1687
1688 var deltaT = this.currentGyroMeasurement.timestampS - this.previousGyroMeasurement.timestampS; // Convert gyro rotation vector to a quaternion delta.
1689
1690 var gyroDeltaQ = this.gyroToQuaternionDelta_(this.currentGyroMeasurement.sample, deltaT);
1691 this.gyroIntegralQ.multiply(gyroDeltaQ); // filter_1 = K * (filter_0 + gyro * dT) + (1 - K) * accel.
1692
1693 this.filterQ.copy(this.previousFilterQ);
1694 this.filterQ.multiply(gyroDeltaQ); // Calculate the delta between the current estimated gravity and the real
1695 // gravity vector from accelerometer.
1696
1697 var invFilterQ = new MathUtil.Quaternion();
1698 invFilterQ.copy(this.filterQ);
1699 invFilterQ.inverse();
1700 this.estimatedGravity.set(0, 0, -1);
1701 this.estimatedGravity.applyQuaternion(invFilterQ);
1702 this.estimatedGravity.normalize();
1703 this.measuredGravity.copy(this.currentAccelMeasurement.sample);
1704 this.measuredGravity.normalize(); // Compare estimated gravity with measured gravity, get the delta quaternion
1705 // between the two.
1706
1707 var deltaQ = new MathUtil.Quaternion();
1708 deltaQ.setFromUnitVectors(this.estimatedGravity, this.measuredGravity);
1709 deltaQ.inverse();
1710
1711 if (Util.isDebug()) {
1712 console.log("Delta: %d deg, G_est: (%s, %s, %s), G_meas: (%s, %s, %s)", MathUtil.radToDeg * Util.getQuaternionAngle(deltaQ), this.estimatedGravity.x.toFixed(1), this.estimatedGravity.y.toFixed(1), this.estimatedGravity.z.toFixed(1), this.measuredGravity.x.toFixed(1), this.measuredGravity.y.toFixed(1), this.measuredGravity.z.toFixed(1));
1713 } // Calculate the SLERP target: current orientation plus the measured-estimated
1714 // quaternion delta.
1715
1716
1717 var targetQ = new MathUtil.Quaternion();
1718 targetQ.copy(this.filterQ);
1719 targetQ.multiply(deltaQ); // SLERP factor: 0 is pure gyro, 1 is pure accel.
1720
1721 this.filterQ.slerp(targetQ, 1 - this.kFilter);
1722 this.previousFilterQ.copy(this.filterQ);
1723 };
1724
1725 __proto.accelToQuaternion_ = function (accel) {
1726 var normAccel = new MathUtil.Vector3();
1727 normAccel.copy(accel);
1728 normAccel.normalize();
1729 var quat = new MathUtil.Quaternion();
1730 quat.setFromUnitVectors(new MathUtil.Vector3(0, 0, -1), normAccel);
1731 quat.inverse();
1732 return quat;
1733 };
1734
1735 __proto.gyroToQuaternionDelta_ = function (gyro, dt) {
1736 // Extract axis and angle from the gyroscope data.
1737 var quat = new MathUtil.Quaternion();
1738 var axis = new MathUtil.Vector3();
1739 axis.copy(gyro);
1740 axis.normalize();
1741 quat.setFromAxisAngle(axis, gyro.length() * dt);
1742 return quat;
1743 };
1744
1745 return ComplementaryFilter;
1746 }();
1747
1748 ComplementaryFilter.prototype.run_ = function () {
1749 if (!this.isOrientationInitialized) {
1750 this.accelQ = this.accelToQuaternion_(this.currentAccelMeasurement.sample);
1751 this.previousFilterQ.copy(this.accelQ);
1752 this.isOrientationInitialized = true;
1753 return;
1754 }
1755
1756 var deltaT = this.currentGyroMeasurement.timestampS - this.previousGyroMeasurement.timestampS; // Convert gyro rotation vector to a quaternion delta.
1757
1758 var gyroDeltaQ = this.gyroToQuaternionDelta_(this.currentGyroMeasurement.sample, deltaT);
1759 this.gyroIntegralQ.multiply(gyroDeltaQ); // filter_1 = K * (filter_0 + gyro * dT) + (1 - K) * accel.
1760
1761 this.filterQ.copy(this.previousFilterQ);
1762 this.filterQ.multiply(gyroDeltaQ); // Calculate the delta between the current estimated gravity and the real
1763 // gravity vector from accelerometer.
1764
1765 var invFilterQ = new MathUtil.Quaternion();
1766 invFilterQ.copy(this.filterQ);
1767 invFilterQ.inverse();
1768 this.estimatedGravity.set(0, 0, -1);
1769 this.estimatedGravity.applyQuaternion(invFilterQ);
1770 this.estimatedGravity.normalize();
1771 this.measuredGravity.copy(this.currentAccelMeasurement.sample);
1772 this.measuredGravity.normalize(); // Compare estimated gravity with measured gravity, get the delta quaternion
1773 // between the two.
1774
1775 var deltaQ = new MathUtil.Quaternion();
1776 deltaQ.setFromUnitVectors(this.estimatedGravity, this.measuredGravity);
1777 deltaQ.inverse(); // Calculate the SLERP target: current orientation plus the measured-estimated
1778 // quaternion delta.
1779
1780 var targetQ = new MathUtil.Quaternion();
1781 targetQ.copy(this.filterQ);
1782 targetQ.multiply(deltaQ); // SLERP factor: 0 is pure gyro, 1 is pure accel.
1783
1784 this.filterQ.slerp(targetQ, 1 - this.kFilter);
1785 this.previousFilterQ.copy(this.filterQ);
1786
1787 if (!this.isFilterQuaternionInitialized) {
1788 this.isFilterQuaternionInitialized = true;
1789 }
1790 };
1791
1792 ComplementaryFilter.prototype.getOrientation = function () {
1793 if (this.isFilterQuaternionInitialized) {
1794 return this.filterQ;
1795 } else {
1796 return null;
1797 }
1798 };
1799
1800 var K_FILTER = 0.98;
1801 var PREDICTION_TIME_S = 0.040;
1802
1803 var FusionPoseSensor =
1804 /*#__PURE__*/
1805 function (_super) {
1806 __extends(FusionPoseSensor, _super);
1807
1808 function FusionPoseSensor() {
1809 var _this = _super.call(this) || this;
1810
1811 _this.deviceMotion = new DeviceMotion();
1812 _this.accelerometer = new MathUtil.Vector3();
1813 _this.gyroscope = new MathUtil.Vector3();
1814 _this._onDeviceMotionChange = _this._onDeviceMotionChange.bind(_this);
1815 _this._onScreenOrientationChange = _this._onScreenOrientationChange.bind(_this);
1816 _this.filter = new ComplementaryFilter(K_FILTER);
1817 _this.posePredictor = new PosePredictor(PREDICTION_TIME_S);
1818 _this.filterToWorldQ = new MathUtil.Quaternion();
1819 _this.isFirefoxAndroid = Util.isFirefoxAndroid(); // This includes iPhone & iPad(both desktop and mobile mode) ref #326
1820
1821 _this.isIOS = IS_IOS || IS_SAFARI_ON_DESKTOP; // Ref https://github.com/immersive-web/cardboard-vr-display/issues/18
1822
1823 _this.isChromeUsingDegrees = CHROME_VERSION >= 66;
1824 _this._isEnabled = false; // Set the filter to world transform, depending on OS.
1825
1826 if (_this.isIOS) {
1827 _this.filterToWorldQ.setFromAxisAngle(new MathUtil.Vector3(1, 0, 0), Math.PI / 2);
1828 } else {
1829 _this.filterToWorldQ.setFromAxisAngle(new MathUtil.Vector3(1, 0, 0), -Math.PI / 2);
1830 }
1831
1832 _this.inverseWorldToScreenQ = new MathUtil.Quaternion();
1833 _this.worldToScreenQ = new MathUtil.Quaternion();
1834 _this.originalPoseAdjustQ = new MathUtil.Quaternion();
1835
1836 _this.originalPoseAdjustQ.setFromAxisAngle(new MathUtil.Vector3(0, 0, 1), -win.orientation * Math.PI / 180);
1837
1838 _this._setScreenTransform(); // Adjust this filter for being in landscape mode.
1839
1840
1841 if (Util.isLandscapeMode()) {
1842 _this.filterToWorldQ.multiply(_this.inverseWorldToScreenQ);
1843 } // Keep track of a reset transform for resetSensor.
1844
1845
1846 _this.resetQ = new MathUtil.Quaternion();
1847
1848 _this.deviceMotion.on("devicemotion", _this._onDeviceMotionChange);
1849
1850 _this.enable();
1851
1852 return _this;
1853 }
1854
1855 var __proto = FusionPoseSensor.prototype;
1856
1857 __proto.enable = function () {
1858 if (this.isEnabled()) {
1859 return;
1860 }
1861
1862 this.deviceMotion.enable();
1863 this._isEnabled = true;
1864 win.addEventListener("orientationchange", this._onScreenOrientationChange);
1865 };
1866
1867 __proto.disable = function () {
1868 if (!this.isEnabled()) {
1869 return;
1870 }
1871
1872 this.deviceMotion.disable();
1873 this._isEnabled = false;
1874 win.removeEventListener("orientationchange", this._onScreenOrientationChange);
1875 };
1876
1877 __proto.isEnabled = function () {
1878 return this._isEnabled;
1879 };
1880
1881 __proto.destroy = function () {
1882 this.disable();
1883 this.deviceMotion = null;
1884 };
1885
1886 __proto.getOrientation = function () {
1887 var _this = this;
1888
1889 var orientation; // Hack around using deviceorientation instead of devicemotion
1890
1891 if (this.deviceMotion.isWithoutDeviceMotion && this._deviceOrientationQ) {
1892 this.deviceOrientationFixQ = this.deviceOrientationFixQ || function () {
1893 var y = new MathUtil.Quaternion().setFromAxisAngle(new MathUtil.Vector3(0, 1, 0), -_this._alpha);
1894 return y;
1895 }();
1896
1897 orientation = this._deviceOrientationQ;
1898 var out = new MathUtil.Quaternion();
1899 out.copy(orientation);
1900 out.multiply(this.filterToWorldQ);
1901 out.multiply(this.resetQ);
1902 out.multiply(this.worldToScreenQ);
1903 out.multiplyQuaternions(this.deviceOrientationFixQ, out); // return quaternion as glmatrix quaternion object
1904
1905 var outQuat = glMatrix.quat.fromValues(out.x, out.y, out.z, out.w);
1906 return glMatrix.quat.normalize(outQuat, outQuat);
1907 } else {
1908 // Convert from filter space to the the same system used by the
1909 // deviceorientation event.
1910 orientation = this.filter.getOrientation();
1911
1912 if (!orientation) {
1913 return null;
1914 }
1915
1916 var out = this._convertFusionToPredicted(orientation); // return quaternion as glmatrix quaternion object
1917
1918
1919 var outQuat = glMatrix.quat.fromValues(out.x, out.y, out.z, out.w);
1920 return glMatrix.quat.normalize(outQuat, outQuat);
1921 }
1922 };
1923
1924 __proto._triggerChange = function () {
1925 var orientation = this.getOrientation(); // if orientation is not prepared. don't trigger change event
1926
1927 if (!orientation) {
1928 return;
1929 }
1930
1931 if (!this._prevOrientation) {
1932 this._prevOrientation = orientation;
1933 return;
1934 }
1935
1936 if (glMatrix.quat.equals(this._prevOrientation, orientation)) {
1937 return;
1938 }
1939
1940 this.trigger(new Component.ComponentEvent("change", {
1941 quaternion: orientation
1942 }));
1943 };
1944
1945 __proto._convertFusionToPredicted = function (orientation) {
1946 // Predict orientation.
1947 this.predictedQ = this.posePredictor.getPrediction(orientation, this.gyroscope, this.previousTimestampS); // Convert to THREE coordinate system: -Z forward, Y up, X right.
1948
1949 var out = new MathUtil.Quaternion();
1950 out.copy(this.filterToWorldQ);
1951 out.multiply(this.resetQ);
1952 out.multiply(this.predictedQ);
1953 out.multiply(this.worldToScreenQ);
1954 return out;
1955 };
1956
1957 __proto._onDeviceMotionChange = function (_a) {
1958 var inputEvent = _a.inputEvent;
1959 var deviceorientation = inputEvent.deviceorientation;
1960 var deviceMotion = inputEvent;
1961 var accGravity = deviceMotion.accelerationIncludingGravity;
1962 var rotRate = deviceMotion.adjustedRotationRate || deviceMotion.rotationRate;
1963 var timestampS = deviceMotion.timeStamp / 1000;
1964
1965 if (deviceorientation) {
1966 if (!this._alpha) {
1967 this._alpha = deviceorientation.alpha;
1968 }
1969
1970 this._deviceOrientationQ = this._deviceOrientationQ || new MathUtil.Quaternion();
1971
1972 this._deviceOrientationQ.setFromEulerYXZ(deviceorientation.beta, deviceorientation.alpha, deviceorientation.gamma);
1973
1974 this._triggerChange();
1975 } else {
1976 // Firefox Android timeStamp returns one thousandth of a millisecond.
1977 if (this.isFirefoxAndroid) {
1978 timestampS /= 1000;
1979 }
1980
1981 this.accelerometer.set(-accGravity.x, -accGravity.y, -accGravity.z);
1982 this.gyroscope.set(rotRate.alpha, rotRate.beta, rotRate.gamma); // Browsers on iOS, Firefox/Android, and Chrome m66/Android `rotationRate`
1983 // is reported in degrees, so we first convert to radians.
1984
1985 if (this.isIOS || this.isFirefoxAndroid || this.isChromeUsingDegrees) {
1986 this.gyroscope.multiplyScalar(Math.PI / 180);
1987 }
1988
1989 this.filter.addAccelMeasurement(this.accelerometer, timestampS);
1990 this.filter.addGyroMeasurement(this.gyroscope, timestampS);
1991
1992 this._triggerChange();
1993
1994 this.previousTimestampS = timestampS;
1995 }
1996 };
1997
1998 __proto._onScreenOrientationChange = function () {
1999 this._setScreenTransform();
2000 };
2001
2002 __proto._setScreenTransform = function () {
2003 this.worldToScreenQ.set(0, 0, 0, 1);
2004 var orientation = win.orientation;
2005
2006 switch (orientation) {
2007 case 0:
2008 break;
2009
2010 case 90:
2011 case -90:
2012 case 180:
2013 this.worldToScreenQ.setFromAxisAngle(new MathUtil.Vector3(0, 0, 1), orientation / -180 * Math.PI);
2014 break;
2015 }
2016
2017 this.inverseWorldToScreenQ.copy(this.worldToScreenQ);
2018 this.inverseWorldToScreenQ.inverse();
2019 };
2020
2021 return FusionPoseSensor;
2022 }(Component);
2023
2024 var getDeltaYaw = function (prvQ, curQ) {
2025 var yawDeltaByYaw = util.getRotationDelta(prvQ, curQ, ROTATE_CONSTANT.YAW_DELTA_BY_YAW);
2026 var yawDeltaByRoll = util.getRotationDelta(prvQ, curQ, ROTATE_CONSTANT.YAW_DELTA_BY_ROLL) * Math.sin(util.extractPitchFromQuat(curQ));
2027 return yawDeltaByRoll + yawDeltaByYaw;
2028 };
2029
2030 var getDeltaPitch = function (prvQ, curQ) {
2031 var pitchDelta = util.getRotationDelta(prvQ, curQ, ROTATE_CONSTANT.PITCH_DELTA);
2032 return pitchDelta;
2033 }; // eslint-disable-next-line @typescript-eslint/ban-types
2034
2035
2036 var TiltMotionInput =
2037 /*#__PURE__*/
2038 function (_super) {
2039 __extends(TiltMotionInput, _super);
2040
2041 function TiltMotionInput(el, options) {
2042 if (options === void 0) {
2043 options = {};
2044 }
2045
2046 var _this = _super.call(this) || this;
2047
2048 _this.element = el;
2049 _this._prevQuaternion = null;
2050 _this._quaternion = null;
2051 _this.fusionPoseSensor = null;
2052 _this.options = __assign({
2053 scale: 1,
2054 threshold: 0
2055 }, options);
2056 _this._onPoseChange = _this._onPoseChange.bind(_this);
2057 return _this;
2058 }
2059
2060 var __proto = TiltMotionInput.prototype;
2061
2062 __proto.mapAxes = function (axes) {
2063 this.axes = axes;
2064 };
2065
2066 __proto.connect = function (observer) {
2067 if (this.observer) {
2068 return this;
2069 }
2070
2071 this.observer = observer;
2072 this.fusionPoseSensor = new FusionPoseSensor();
2073 this.fusionPoseSensor.enable();
2074
2075 this._attachEvent();
2076
2077 return this;
2078 };
2079
2080 __proto.disconnect = function () {
2081 if (!this.observer) {
2082 return this;
2083 }
2084
2085 this._dettachEvent();
2086
2087 this.fusionPoseSensor.disable();
2088 this.fusionPoseSensor.destroy();
2089 this.fusionPoseSensor = null;
2090 this.observer = null;
2091 return this;
2092 };
2093
2094 __proto.destroy = function () {
2095 this.disconnect();
2096 this.element = null;
2097 this.options = null;
2098 this.axes = null;
2099 this._prevQuaternion = null;
2100 this._quaternion = null;
2101 };
2102
2103 __proto._onPoseChange = function (event) {
2104 if (!this._prevQuaternion) {
2105 this._prevQuaternion = glMatrix.quat.clone(event.quaternion);
2106 this._quaternion = glMatrix.quat.clone(event.quaternion);
2107 return;
2108 }
2109
2110 glMatrix.quat.copy(this._prevQuaternion, this._quaternion);
2111 glMatrix.quat.copy(this._quaternion, event.quaternion);
2112 this.observer.change(this, event, toAxis(this.axes, [getDeltaYaw(this._prevQuaternion, this._quaternion), getDeltaPitch(this._prevQuaternion, this._quaternion)]));
2113 };
2114
2115 __proto._attachEvent = function () {
2116 this.fusionPoseSensor.on("change", this._onPoseChange);
2117 };
2118
2119 __proto._dettachEvent = function () {
2120 this.fusionPoseSensor.off("change", this._onPoseChange);
2121 };
2122
2123 return TiltMotionInput;
2124 }(Component);
2125
2126 var screenRotationAngleInst = null;
2127 var refCount = 0;
2128
2129 var ScreenRotationAngle =
2130 /*#__PURE__*/
2131 function () {
2132 function ScreenRotationAngle() {
2133 refCount++;
2134
2135 if (screenRotationAngleInst) {
2136 return screenRotationAngleInst;
2137 }
2138 /* eslint-disable */
2139
2140
2141 screenRotationAngleInst = this;
2142 /* eslint-enable */
2143
2144 this._onDeviceOrientation = this._onDeviceOrientation.bind(this);
2145 this._onOrientationChange = this._onOrientationChange.bind(this);
2146 this._spinR = 0;
2147 this._screenOrientationAngle = 0;
2148 win.addEventListener("deviceorientation", this._onDeviceOrientation);
2149 win.addEventListener("orientationchange", this._onOrientationChange);
2150 }
2151
2152 var __proto = ScreenRotationAngle.prototype;
2153
2154 __proto.getRadian = function () {
2155 // Join with screen orientation
2156 // this._testVal = this._spinR + ", " + this._screenOrientationAngle + ", " + window.orientation;
2157 return this._spinR + glMatrix.glMatrix.toRadian(this._screenOrientationAngle);
2158 };
2159
2160 __proto.unref = function () {
2161 if (--refCount > 0) {
2162 return;
2163 }
2164
2165 win.removeEventListener("deviceorientation", this._onDeviceOrientation);
2166 win.removeEventListener("orientationchange", this._onOrientationChange);
2167 this._spinR = 0;
2168 this._screenOrientationAngle = 0;
2169 /* eslint-disable */
2170
2171 screenRotationAngleInst = null;
2172 /* eslint-enable */
2173
2174 refCount = 0;
2175 };
2176
2177 __proto._onDeviceOrientation = function (e) {
2178 if (e.beta === null || e.gamma === null) {
2179 // (Chrome) deviceorientation is fired with invalid information {alpha=null, beta=null, ...} despite of not dispatching it. We skip it.
2180 return;
2181 } // Radian
2182
2183
2184 var betaR = glMatrix.glMatrix.toRadian(e.beta);
2185 var gammaR = glMatrix.glMatrix.toRadian(e.gamma);
2186 /* spinR range = [-180, 180], left side: 0 ~ -180(deg), right side: 0 ~ 180(deg) */
2187
2188 this._spinR = Math.atan2(Math.cos(betaR) * Math.sin(gammaR), Math.sin(betaR));
2189 };
2190
2191 __proto._onOrientationChange = function () {
2192 if (win.screen && win.screen.orientation && win.screen.orientation.angle !== undefined) {
2193 this._screenOrientationAngle = screen.orientation.angle;
2194 } else if (win.orientation !== undefined) {
2195 /* iOS */
2196 this._screenOrientationAngle = win.orientation >= 0 ? win.orientation : 360 + win.orientation;
2197 }
2198 };
2199
2200 return ScreenRotationAngle;
2201 }();
2202
2203 /**
2204 * RotationPanInput is extension of PanInput to compensate coordinates by screen rotation angle.
2205 *
2206 * The reason for using this function is that in VR mode,
2207 * the roll angle is adjusted in the direction opposite to the screen rotation angle.
2208 *
2209 * Therefore, the angle that the user touches and moves does not match the angle at which the actual object should move.
2210 * @extends PanInput
2211 */
2212 // @ts-ignore
2213
2214 var RotationPanInput =
2215 /*#__PURE__*/
2216 function (_super) {
2217 __extends(RotationPanInput, _super);
2218 /**
2219 * Constructor
2220 * @private
2221 * @param {HTMLElement} el target element
2222 * @param {Object} [options] The option object
2223 * @param {Boolean} [options.useRotation] Whether to use rotation(or VR)
2224 */
2225
2226
2227 function RotationPanInput(el, options) {
2228 if (options === void 0) {
2229 options = {};
2230 }
2231
2232 var _this = _super.call(this, el, options) || this;
2233
2234 _this._useRotation = false;
2235 _this._screenRotationAngle = null;
2236
2237 _this.setUseRotation(!!(options && options.useRotation));
2238
2239 _this._userDirection = Axes.DIRECTION_ALL;
2240 return _this;
2241 }
2242
2243 var __proto = RotationPanInput.prototype;
2244
2245 __proto.setUseRotation = function (useRotation) {
2246 this._useRotation = useRotation;
2247
2248 if (this._screenRotationAngle) {
2249 this._screenRotationAngle.unref();
2250
2251 this._screenRotationAngle = null;
2252 }
2253
2254 if (this._useRotation) {
2255 this._screenRotationAngle = new ScreenRotationAngle();
2256 }
2257 };
2258
2259 __proto.connect = function (observer) {
2260 // User intetened direction
2261 this._userDirection = this._direction; // In VR Mode, Use ALL direction if direction is not none
2262 // Because horizontal and vertical is changed dynamically by screen rotation.
2263 // this._direction is used to initialize hammerjs
2264
2265 if (this._useRotation && this._direction & Axes.DIRECTION_ALL) {
2266 this._direction = Axes.DIRECTION_HORIZONTAL;
2267 }
2268
2269 return _super.prototype.connect.call(this, observer);
2270 };
2271
2272 __proto.destroy = function () {
2273 if (this._useRotation && this._screenRotationAngle) {
2274 this._screenRotationAngle.unref();
2275 }
2276
2277 _super.prototype.destroy.call(this);
2278 }; // eslint-disable-next-line @typescript-eslint/naming-convention
2279
2280
2281 __proto.getOffset = function (properties, useDirection) {
2282 if (this._useRotation === false) {
2283 // @ts-ignore
2284 return _super.prototype.getOffset.call(this, properties, useDirection);
2285 } // @ts-ignore
2286
2287
2288 var offset = _super.prototype.getOffset.call(this, properties, [true, true]);
2289
2290 var newOffset = [0, 0];
2291
2292 var theta = this._screenRotationAngle.getRadian();
2293
2294 var cosTheta = Math.cos(theta);
2295 var sinTheta = Math.sin(theta); // RotateZ
2296
2297 newOffset[0] = offset[0] * cosTheta - offset[1] * sinTheta;
2298 newOffset[1] = offset[1] * cosTheta + offset[0] * sinTheta; // Use only user allowed direction.
2299
2300 if (!(this._userDirection & Axes.DIRECTION_HORIZONTAL)) {
2301 newOffset[0] = 0;
2302 } else if (!(this._userDirection & Axes.DIRECTION_VERTICAL)) {
2303 newOffset[1] = 0;
2304 }
2305
2306 return newOffset;
2307 };
2308
2309 return RotationPanInput;
2310 }(Axes.PanInput);
2311 /**
2312 * Override getDirectionByAngle to return DIRECTION_ALL
2313 * Ref: https://github.com/naver/egjs-axes/issues/99
2314 *
2315 * But we obey axes's rule. If axes's rule is problem, let's apply following code.
2316 */
2317 // PanInput.getDirectionByAngle = function (angle, thresholdAngle) {
2318 // return DIRECTION_ALL;
2319 // };
2320
2321 var Y_AXIS_VECTOR = glMatrix.vec3.fromValues(0, 1, 0);
2322
2323 var DeviceQuaternion =
2324 /*#__PURE__*/
2325 function (_super) {
2326 __extends(DeviceQuaternion, _super);
2327
2328 function DeviceQuaternion() {
2329 var _this = _super.call(this) || this;
2330
2331 _this._fusionPoseSensor = new FusionPoseSensor();
2332 _this._quaternion = glMatrix.quat.create();
2333
2334 _this._fusionPoseSensor.enable();
2335
2336 _this._fusionPoseSensor.on("change", function (e) {
2337 _this._quaternion = e.quaternion;
2338
2339 _this.trigger(new Component.ComponentEvent("change", {
2340 isTrusted: true
2341 }));
2342 });
2343
2344 return _this;
2345 }
2346
2347 var __proto = DeviceQuaternion.prototype;
2348
2349 __proto.getCombinedQuaternion = function (yaw) {
2350 var yawQ = glMatrix.quat.setAxisAngle(glMatrix.quat.create(), Y_AXIS_VECTOR, glMatrix.glMatrix.toRadian(-yaw));
2351 var conj = glMatrix.quat.conjugate(glMatrix.quat.create(), this._quaternion); // Multiply pitch quaternion -> device quaternion -> yaw quaternion
2352
2353 var outQ = glMatrix.quat.multiply(glMatrix.quat.create(), conj, yawQ);
2354 return outQ;
2355 };
2356
2357 __proto.destroy = function () {
2358 // detach all event handler
2359 this.off();
2360
2361 if (this._fusionPoseSensor) {
2362 this._fusionPoseSensor.off();
2363
2364 this._fusionPoseSensor.destroy();
2365
2366 this._fusionPoseSensor = null;
2367 }
2368 };
2369
2370 return DeviceQuaternion;
2371 }(Component);
2372
2373 var DEFAULT_YAW_RANGE = [-YAW_RANGE_HALF, YAW_RANGE_HALF];
2374 var DEFAULT_PITCH_RANGE = [-PITCH_RANGE_HALF, PITCH_RANGE_HALF];
2375 var CIRCULAR_PITCH_RANGE = [-CIRCULAR_PITCH_RANGE_HALF, CIRCULAR_PITCH_RANGE_HALF];
2376 /**
2377 * A module used to provide coordinate based on yaw/pitch orientation. This module receives user touch action, keyboard, mouse and device orientation(if it exists) as input, then combines them and converts it to yaw/pitch coordinates.
2378 * @alias eg.YawPitchControl
2379 * @extends eg.Component
2380 *
2381 * @support {"ie": "10+", "ch" : "latest", "ff" : "latest", "sf" : "latest", "edge" : "latest", "ios" : "7+", "an" : "2.3+ (except 3.x)"}
2382 */
2383
2384 var YawPitchControl =
2385 /*#__PURE__*/
2386 function (_super) {
2387 __extends(YawPitchControl, _super);
2388 /**
2389 * @param {object} options The option object of the eg.YawPitch module
2390 * @param {HTMLElement|null}[options.element=null] element A base element for the eg.YawPitch module
2391 * @param {number} [options.yaw=0] initial yaw (degree)
2392 * @param {number} [options.pitch=0] initial pitch (degree)
2393 * @param {number} [options.fov=65] initial field of view (degree)
2394 * @param {boolean} [optiosn.showPolePoint=true] Indicates whether pole is shown
2395 * @param {boolean} [options.useZoom=true] Indicates whether zoom is available
2396 * @param {boolean} [options.useKeyboard=true] Indicates whether keyboard is enabled
2397 * @param {string} [config.gyroMode=yawPitch] Enables control through device motion.
2398 * @param {number} [options.touchDirection=TOUCH_DIRECTION_ALL] Direction of the touch movement (TOUCH_DIRECTION_ALL: all, TOUCH_DIRECTION_YAW: horizontal, TOUCH_DIRECTION_PITCH: vertical, TOUCH_DIRECTION_NONE: no move)
2399 * @param {number[]} [options.yawRange=[-180, 180] Range of visible yaw
2400 * @param {number[]} [options.pitchRange=[-90, 90] Range of visible pitch
2401 * @param {number[]} [options.fovRange=[30, 110] Range of FOV
2402 * @param {number} [options.aspectRatio=1] Aspect Ratio
2403 */
2404
2405
2406 function YawPitchControl(options) {
2407 var _this = _super.call(this) || this;
2408
2409 _this.options = {};
2410
2411 var opt = __assign({
2412 element: null,
2413 yaw: 0,
2414 pitch: 0,
2415 fov: 65,
2416 showPolePoint: false,
2417 useZoom: true,
2418 useKeyboard: true,
2419 gyroMode: GYRO_MODE.YAWPITCH,
2420 touchDirection: TOUCH_DIRECTION_ALL,
2421 yawRange: DEFAULT_YAW_RANGE,
2422 pitchRange: DEFAULT_PITCH_RANGE,
2423 fovRange: [30, 110],
2424 aspectRatio: 1
2425 /* TODO: Need Mandatory? */
2426
2427 }, options);
2428
2429 _this._element = opt.element;
2430 _this._initialFov = opt.fov;
2431 _this._enabled = false;
2432 _this._isAnimating = false;
2433 _this._deviceQuaternion = null;
2434
2435 _this._initAxes(opt);
2436
2437 _this.option(opt);
2438
2439 return _this;
2440 }
2441 /**
2442 * Update Pan Scale
2443 *
2444 * Scale(Sensitivity) values of panning is related with fov and height.
2445 * If at least one of them is changed, this function need to be called.
2446 * @param {*} param
2447 */
2448
2449
2450 var __proto = YawPitchControl.prototype;
2451
2452 __proto.updatePanScale = function (param) {
2453 if (param === void 0) {
2454 param = {};
2455 }
2456
2457 var fov = this._axes.get().fov;
2458
2459 var areaHeight = param.height || parseInt(window.getComputedStyle(this._element).height, 10);
2460 var scale = MC_BIND_SCALE[0] * fov / this._initialFov * PAN_SCALE / areaHeight;
2461 this._axesPanInput.options.scale = [scale, scale];
2462 this._axes.options.deceleration = MC_DECELERATION * fov / MAX_FIELD_OF_VIEW;
2463 return this;
2464 };
2465 /*
2466 * Override component's option method
2467 * to call method for updating values which is affected by option change.
2468 *
2469 * @param {*} args
2470 */
2471
2472
2473 __proto.option = function (key, newValue) {
2474 // Getter
2475 if (!key) {
2476 return this._getOptions();
2477 } else if (key && typeof key === "string" && typeof newValue === "undefined") {
2478 return this._getOptions(key);
2479 } // Setter
2480
2481
2482 var newOptions = {};
2483 var changedKeyList = []; // TODO: if value is not changed, then do not push on changedKeyList.
2484
2485 if (typeof key === "string") {
2486 changedKeyList.push(key);
2487 newOptions[key] = newValue;
2488 } else {
2489 var options = key; // Retrieving object here
2490
2491 changedKeyList = Object.keys(options);
2492 newOptions = __assign({}, options);
2493 }
2494
2495 this._setOptions(this._getValidatedOptions(newOptions));
2496
2497 this._applyOptions(changedKeyList);
2498
2499 return this;
2500 };
2501 /**
2502 * Enable YawPitch functionality
2503 * @method eg.YawPitch#enable
2504 */
2505
2506
2507 __proto.enable = function () {
2508 if (this._enabled) {
2509 return this;
2510 }
2511
2512 this._enabled = true; // touchDirection is decided by parameter is valid string (Ref. Axes.connect)
2513
2514 this._applyOptions(Object.keys(this.options)); // TODO: Is this code is needed? Check later.
2515
2516
2517 this.updatePanScale();
2518 return this;
2519 };
2520 /**
2521 * Disable YawPitch functionality
2522 * @method eg.YawPitch#disable
2523 */
2524
2525
2526 __proto.disable = function (persistOrientation) {
2527 if (persistOrientation === void 0) {
2528 persistOrientation = false;
2529 }
2530
2531 if (!this._enabled) {
2532 return this;
2533 } // TODO: Check peristOrientation is needed!
2534
2535
2536 if (!persistOrientation) {
2537 this._resetOrientation();
2538 }
2539
2540 this._axes.disconnect();
2541
2542 this._enabled = false;
2543 return this;
2544 };
2545 /**
2546 * Set one or more of yaw, pitch, fov
2547 * @param {Object} coordinate yaw, pitch, fov
2548 * @param {Number} duration Animation duration. if it is above 0 then it's animated.
2549 */
2550
2551
2552 __proto.lookAt = function (_a, duration) {
2553 var yaw = _a.yaw,
2554 pitch = _a.pitch,
2555 fov = _a.fov;
2556
2557 var pos = this._axes.get();
2558
2559 var y = yaw === undefined ? 0 : yaw - pos.yaw;
2560 var p = pitch === undefined ? 0 : pitch - pos.pitch;
2561 var f = fov === undefined ? 0 : fov - pos.fov; // Allow duration of animation to have more than MC_MAXIMUM_DURATION.
2562
2563 this._axes.options.maximumDuration = Infinity;
2564
2565 this._axes.setBy({
2566 yaw: y,
2567 pitch: p,
2568 fov: f
2569 }, duration);
2570 };
2571
2572 __proto.getYawPitch = function () {
2573 var yawPitch = this._axes.get();
2574
2575 return {
2576 yaw: yawPitch.yaw,
2577 pitch: yawPitch.pitch
2578 };
2579 };
2580
2581 __proto.getFov = function () {
2582 return this._axes.get().fov;
2583 };
2584
2585 __proto.getQuaternion = function () {
2586 var pos = this._axes.get();
2587
2588 return this._deviceQuaternion.getCombinedQuaternion(pos.yaw);
2589 };
2590
2591 __proto.shouldRenderWithQuaternion = function () {
2592 return this.options.gyroMode === GYRO_MODE.VR;
2593 };
2594 /**
2595 * Destroys objects
2596 */
2597
2598
2599 __proto.destroy = function () {
2600 /* eslint-disable @typescript-eslint/no-unused-expressions */
2601 this._axes && this._axes.destroy();
2602 this._axesPanInput && this._axesPanInput.destroy();
2603 this._axesWheelInput && this._axesWheelInput.destroy();
2604 this._axesTiltMotionInput && this._axesTiltMotionInput.destroy();
2605 this._axesPinchInput && this._axesPinchInput.destroy();
2606 this._axesMoveKeyInput && this._axesMoveKeyInput.destroy();
2607 this._deviceQuaternion && this._deviceQuaternion.destroy();
2608 /* eslint-enable @typescript-eslint/no-unused-expressions */
2609 };
2610
2611 __proto._initAxes = function (opt) {
2612 var _this = this;
2613
2614 var yRange = this._updateYawRange(opt.yawRange, opt.fov, opt.aspectRatio);
2615
2616 var pRange = this._updatePitchRange(opt.pitchRange, opt.fov, opt.showPolePoint);
2617
2618 var useRotation = opt.gyroMode === GYRO_MODE.VR;
2619 this._axesPanInput = new RotationPanInput(this._element, {
2620 useRotation: useRotation
2621 });
2622 this._axesWheelInput = new Axes.WheelInput(this._element, {
2623 scale: -4
2624 });
2625 this._axesTiltMotionInput = null;
2626 this._axesPinchInput = SUPPORT_TOUCH ? new Axes.PinchInput(this._element, {
2627 scale: -1
2628 }) : null;
2629 this._axesMoveKeyInput = new Axes.MoveKeyInput(this._element, {
2630 scale: [-6, 6]
2631 });
2632 this._axes = new Axes({
2633 yaw: {
2634 range: yRange,
2635 circular: this._isCircular(yRange),
2636 bounce: [0, 0]
2637 },
2638 pitch: {
2639 range: pRange,
2640 circular: this._isCircular(pRange),
2641 bounce: [0, 0]
2642 },
2643 fov: {
2644 range: opt.fovRange,
2645 circular: [false, false],
2646 bounce: [0, 0]
2647 }
2648 }, {
2649 deceleration: MC_DECELERATION,
2650 maximumDuration: MC_MAXIMUM_DURATION
2651 }, {
2652 yaw: opt.yaw,
2653 pitch: opt.pitch,
2654 fov: opt.fov
2655 }).on({
2656 // TODO: change event type after Axes event type inference update
2657 hold: function (evt) {
2658 // Restore maximumDuration not to be spin too mush.
2659 _this._axes.options.maximumDuration = MC_MAXIMUM_DURATION;
2660
2661 _this.trigger(new Component.ComponentEvent("hold", {
2662 isTrusted: evt.isTrusted
2663 }));
2664 },
2665 change: function (evt) {
2666 if (evt.delta.fov !== 0) {
2667 _this._updateControlScale(evt);
2668
2669 _this.updatePanScale();
2670 }
2671
2672 _this._triggerChange(evt);
2673 },
2674 release: function (evt) {
2675 _this._triggerChange(evt);
2676 },
2677 animationEnd: function (evt) {
2678 _this.trigger(new Component.ComponentEvent("animationEnd", {
2679 isTrusted: evt.isTrusted
2680 }));
2681 }
2682 });
2683 };
2684
2685 __proto._getValidatedOptions = function (newOptions) {
2686 if (newOptions.yawRange) {
2687 newOptions.yawRange = this._getValidYawRange(newOptions.yawRange, newOptions.fov, newOptions.aspectRatio);
2688 }
2689
2690 if (newOptions.pitchRange) {
2691 newOptions.pitchRange = this._getValidPitchRange(newOptions.pitchRange, newOptions.fov);
2692 }
2693
2694 return newOptions;
2695 };
2696
2697 __proto._getOptions = function (key) {
2698 var value;
2699
2700 if (typeof key === "string") {
2701 value = this.options[key];
2702 } else if (arguments.length === 0) {
2703 value = this.options;
2704 }
2705
2706 return value;
2707 };
2708
2709 __proto._setOptions = function (options) {
2710 for (var key in options) {
2711 this.options[key] = options[key];
2712 }
2713 };
2714
2715 __proto._applyOptions = function (keys) {
2716 var options = this.options;
2717 var axes = this._axes;
2718 var isVR = options.gyroMode === GYRO_MODE.VR;
2719 var isYawPitch = options.gyroMode === GYRO_MODE.YAWPITCH; // If it's VR mode, restrict user interaction to yaw direction only
2720
2721 var touchDirection = isVR ? TOUCH_DIRECTION_YAW & options.touchDirection : options.touchDirection; // If one of below is changed, call updateControlScale()
2722
2723 if (keys.some(function (key) {
2724 return key === "showPolePoint" || key === "fov" || key === "aspectRatio" || key === "yawRange" || key === "pitchRange";
2725 })) {
2726 // If fov is changed, update pan scale
2727 if (keys.indexOf("fov") >= 0) {
2728 axes.setTo({
2729 "fov": options.fov
2730 });
2731 this.updatePanScale();
2732 }
2733
2734 this._updateControlScale();
2735 }
2736
2737 if (keys.some(function (key) {
2738 return key === "fovRange";
2739 })) {
2740 var fovRange = options.fovRange;
2741 var prevFov = axes.get().fov;
2742 var nextFov = axes.get().fov;
2743 glMatrix.vec2.copy(axes.axis.fov.range, fovRange);
2744
2745 if (nextFov < fovRange[0]) {
2746 nextFov = fovRange[0];
2747 } else if (prevFov > fovRange[1]) {
2748 nextFov = fovRange[1];
2749 }
2750
2751 if (prevFov !== nextFov) {
2752 axes.setTo({
2753 fov: nextFov
2754 }, 0);
2755
2756 this._updateControlScale();
2757
2758 this.updatePanScale();
2759 }
2760 }
2761
2762 if (keys.some(function (key) {
2763 return key === "gyroMode";
2764 }) && SUPPORT_DEVICEMOTION) {
2765 // Disconnect first
2766 if (this._axesTiltMotionInput) {
2767 this._axes.disconnect(this._axesTiltMotionInput);
2768
2769 this._axesTiltMotionInput.destroy();
2770
2771 this._axesTiltMotionInput = null;
2772 }
2773
2774 if (this._deviceQuaternion) {
2775 this._deviceQuaternion.destroy();
2776
2777 this._deviceQuaternion = null;
2778 }
2779
2780 if (isVR) {
2781 this._initDeviceQuaternion();
2782 } else if (isYawPitch) {
2783 this._axesTiltMotionInput = new TiltMotionInput(this._element);
2784
2785 this._axes.connect(["yaw", "pitch"], this._axesTiltMotionInput);
2786 }
2787
2788 this._axesPanInput.setUseRotation(isVR);
2789 }
2790
2791 if (keys.some(function (key) {
2792 return key === "useKeyboard";
2793 })) {
2794 var useKeyboard = options.useKeyboard;
2795
2796 if (useKeyboard) {
2797 axes.connect(["yaw", "pitch"], this._axesMoveKeyInput);
2798 } else {
2799 axes.disconnect(this._axesMoveKeyInput);
2800 }
2801 }
2802
2803 if (keys.some(function (key) {
2804 return key === "useZoom";
2805 })) {
2806 var useZoom = options.useZoom; // Disconnect first
2807
2808 axes.disconnect(this._axesWheelInput);
2809
2810 if (useZoom) {
2811 axes.connect(["fov"], this._axesWheelInput);
2812 }
2813 }
2814
2815 this._togglePinchInputByOption(options.touchDirection, options.useZoom);
2816
2817 if (keys.some(function (key) {
2818 return key === "touchDirection";
2819 }) && this._enabled) {
2820 this._enableTouch(touchDirection);
2821 }
2822 };
2823
2824 __proto._togglePinchInputByOption = function (touchDirection, useZoom) {
2825 if (this._axesPinchInput) {
2826 // disconnect first
2827 this._axes.disconnect(this._axesPinchInput); // If the touchDirection option is not ALL, pinchInput should be disconnected to make use of a native scroll.
2828
2829
2830 if (useZoom && touchDirection === TOUCH_DIRECTION_ALL && // TODO: Get rid of using private property of axes instance.
2831 this._axes._inputs.indexOf(this._axesPinchInput) === -1) {
2832 this._axes.connect(["fov"], this._axesPinchInput);
2833 }
2834 }
2835 };
2836
2837 __proto._enableTouch = function (direction) {
2838 // Disconnect first
2839 if (this._axesPanInput) {
2840 this._axes.disconnect(this._axesPanInput);
2841 }
2842
2843 var yawEnabled = direction & TOUCH_DIRECTION_YAW ? "yaw" : null;
2844 var pitchEnabled = direction & TOUCH_DIRECTION_PITCH ? "pitch" : null;
2845
2846 this._axes.connect([yawEnabled, pitchEnabled], this._axesPanInput);
2847 };
2848
2849 __proto._initDeviceQuaternion = function () {
2850 var _this = this;
2851
2852 this._deviceQuaternion = new DeviceQuaternion();
2853
2854 this._deviceQuaternion.on("change", function (e) {
2855 _this._triggerChange(e);
2856 });
2857 };
2858
2859 __proto._getValidYawRange = function (newYawRange, newFov, newAspectRatio) {
2860 var ratio = this._adjustAspectRatio(newAspectRatio || this.options.aspectRatio || 1);
2861
2862 var fov = newFov || this._axes.get().fov;
2863
2864 var horizontalFov = fov * ratio;
2865 var isValid = newYawRange[1] - newYawRange[0] >= horizontalFov;
2866
2867 if (isValid) {
2868 return newYawRange;
2869 } else {
2870 return this.options.yawRange || DEFAULT_YAW_RANGE;
2871 }
2872 };
2873
2874 __proto._getValidPitchRange = function (newPitchRange, newFov) {
2875 var fov = newFov || this._axes.get().fov;
2876
2877 var isValid = newPitchRange[1] - newPitchRange[0] >= fov;
2878
2879 if (isValid) {
2880 return newPitchRange;
2881 } else {
2882 return this.options.pitchRange || DEFAULT_PITCH_RANGE;
2883 }
2884 };
2885
2886 __proto._isCircular = function (range) {
2887 return range[1] - range[0] < 360 ? [false, false] : [true, true];
2888 };
2889 /**
2890 * Update yaw/pitch min/max by 5 factor
2891 *
2892 * 1. showPolePoint
2893 * 2. fov
2894 * 3. yawRange
2895 * 4. pitchRange
2896 * 5. aspectRatio
2897 *
2898 * If one of above is changed, call this function
2899 */
2900
2901
2902 __proto._updateControlScale = function (changeEvt) {
2903 var opt = this.options;
2904
2905 var fov = this._axes.get().fov;
2906
2907 var pRange = this._updatePitchRange(opt.pitchRange, fov, opt.showPolePoint);
2908
2909 var yRange = this._updateYawRange(opt.yawRange, fov, opt.aspectRatio); // TODO: If not changed!?
2910
2911
2912 var pos = this._axes.get();
2913
2914 var y = pos.yaw;
2915 var p = pos.pitch;
2916 glMatrix.vec2.copy(this._axes.axis.yaw.range, yRange);
2917 glMatrix.vec2.copy(this._axes.axis.pitch.range, pRange);
2918 this._axes.axis.yaw.circular = this._isCircular(yRange);
2919 this._axes.axis.pitch.circular = this._isCircular(pRange);
2920 /**
2921 * update yaw/pitch by it's range.
2922 */
2923
2924 if (y < yRange[0]) {
2925 y = yRange[0];
2926 } else if (y > yRange[1]) {
2927 y = yRange[1];
2928 }
2929
2930 if (p < pRange[0]) {
2931 p = pRange[0];
2932 } else if (p > pRange[1]) {
2933 p = pRange[1];
2934 }
2935
2936 if (changeEvt) {
2937 changeEvt.set({
2938 yaw: y,
2939 pitch: p
2940 });
2941 }
2942
2943 this._axes.setTo({
2944 yaw: y,
2945 pitch: p
2946 }, 0);
2947
2948 return this;
2949 };
2950
2951 __proto._updatePitchRange = function (pitchRange, fov, showPolePoint) {
2952 if (this.options.gyroMode === GYRO_MODE.VR) {
2953 // Circular pitch on VR
2954 return CIRCULAR_PITCH_RANGE;
2955 }
2956
2957 var verticalAngle = pitchRange[1] - pitchRange[0];
2958 var halfFov = fov / 2;
2959 var isPanorama = verticalAngle < 180;
2960
2961 if (showPolePoint && !isPanorama) {
2962 // Use full pinch range
2963 return pitchRange.concat();
2964 } // Round value as movableCood do.
2965
2966
2967 return [pitchRange[0] + halfFov, pitchRange[1] - halfFov];
2968 };
2969
2970 __proto._updateYawRange = function (yawRange, fov, aspectRatio) {
2971 if (this.options.gyroMode === GYRO_MODE.VR) {
2972 return DEFAULT_YAW_RANGE;
2973 }
2974
2975 var horizontalAngle = yawRange[1] - yawRange[0];
2976 /**
2977 * Full 360 Mode
2978 */
2979
2980 if (horizontalAngle >= 360) {
2981 // Don't limit yaw range on Full 360 mode.
2982 return yawRange.concat();
2983 }
2984 /**
2985 * Panorama mode
2986 */
2987 // Ref : https://github.com/naver/egjs-view360/issues/290
2988
2989
2990 var halfHorizontalFov = util.toDegree(Math.atan2(aspectRatio, 1 / Math.tan(glMatrix.glMatrix.toRadian(fov / 2)))); // Round value as movableCood do.
2991
2992 return [yawRange[0] + halfHorizontalFov, yawRange[1] - halfHorizontalFov];
2993 }; // TODO: update param type after Axes event type inference update
2994
2995
2996 __proto._triggerChange = function (evt) {
2997 var pos = this._axes.get();
2998
2999 var opt = this.options;
3000 var event = {
3001 targetElement: opt.element,
3002 isTrusted: evt.isTrusted,
3003 yaw: pos.yaw,
3004 pitch: pos.pitch,
3005 fov: pos.fov,
3006 quaternion: null
3007 };
3008
3009 if (opt.gyroMode === GYRO_MODE.VR && this._deviceQuaternion) {
3010 event.quaternion = this._deviceQuaternion.getCombinedQuaternion(pos.yaw);
3011 }
3012
3013 this.trigger(new Component.ComponentEvent("change", event));
3014 }; // TODO: makes constant to be logic
3015
3016
3017 __proto._adjustAspectRatio = function (input) {
3018 var inputRange = [0.520, 0.540, 0.563, 0.570, 0.584, 0.590, 0.609, 0.670, 0.702, 0.720, 0.760, 0.780, 0.820, 0.920, 0.970, 1.00, 1.07, 1.14, 1.19, 1.25, 1.32, 1.38, 1.40, 1.43, 1.53, 1.62, 1.76, 1.77, 1.86, 1.96, 2.26, 2.30, 2.60, 3.00, 5.00, 6.00];
3019 var outputRange = [0.510, 0.540, 0.606, 0.560, 0.628, 0.630, 0.647, 0.710, 0.736, 0.757, 0.780, 0.770, 0.800, 0.890, 0.975, 1.00, 1.07, 1.10, 1.15, 1.18, 1.22, 1.27, 1.30, 1.33, 1.39, 1.45, 1.54, 1.55, 1.58, 1.62, 1.72, 1.82, 1.92, 2.00, 2.24, 2.30];
3020 var rangeIdx = -1;
3021
3022 for (var i = 0; i < inputRange.length - 1; i++) {
3023 if (inputRange[i] <= input && inputRange[i + 1] >= input) {
3024 rangeIdx = i;
3025 break;
3026 }
3027 }
3028
3029 if (rangeIdx === -1) {
3030 if (inputRange[0] > input) {
3031 return outputRange[0];
3032 } else {
3033 // FIXME: this looks definitely wrong
3034 return outputRange[outputRange[0].length - 1];
3035 }
3036 }
3037
3038 var inputA = inputRange[rangeIdx];
3039 var inputB = inputRange[rangeIdx + 1];
3040 var outputA = outputRange[rangeIdx];
3041 var outputB = outputRange[rangeIdx + 1];
3042 return this._lerp(outputA, outputB, (input - inputA) / (inputB - inputA));
3043 };
3044
3045 __proto._lerp = function (a, b, fraction) {
3046 return a + fraction * (b - a);
3047 };
3048
3049 __proto._resetOrientation = function () {
3050 var opt = this.options;
3051
3052 this._axes.setTo({
3053 yaw: opt.yaw,
3054 pitch: opt.pitch,
3055 fov: opt.fov
3056 }, 0);
3057
3058 return this;
3059 };
3060
3061 YawPitchControl.VERSION = VERSION; // Expose DeviceOrientationControls sub module for test purpose
3062
3063 YawPitchControl.CONTROL_MODE_VR = CONTROL_MODE_VR;
3064 YawPitchControl.CONTROL_MODE_YAWPITCH = CONTROL_MODE_YAWPITCH;
3065 YawPitchControl.TOUCH_DIRECTION_ALL = TOUCH_DIRECTION_ALL;
3066 YawPitchControl.TOUCH_DIRECTION_YAW = TOUCH_DIRECTION_YAW;
3067 YawPitchControl.TOUCH_DIRECTION_PITCH = TOUCH_DIRECTION_PITCH;
3068 YawPitchControl.TOUCH_DIRECTION_NONE = TOUCH_DIRECTION_NONE;
3069 return YawPitchControl;
3070 }(Component);
3071
3072 /**
3073 * Constant value for errors
3074 * @ko 에러에 대한 상수 값
3075 * @namespace
3076 * @name ERROR_TYPE
3077 * @memberof eg.view360.PanoViewer
3078 */
3079
3080 var ERROR_TYPE = {
3081 /**
3082 * Unsupported device
3083 * @ko 미지원 기기
3084 * @name INVALID_DEVICE
3085 * @memberof eg.view360.PanoViewer.ERROR_TYPE
3086 * @constant
3087 * @type {Number}
3088 * @default 10
3089 */
3090 INVALID_DEVICE: 10,
3091
3092 /**
3093 * Webgl not support
3094 * @ko WEBGL 미지원
3095 * @name NO_WEBGL
3096 * @memberof eg.view360.PanoViewer.ERROR_TYPE
3097 * @constant
3098 * @type {Number}
3099 * @default 11
3100 */
3101 NO_WEBGL: 11,
3102
3103 /**
3104 * Failed to load image
3105 * @ko 이미지 로드 실패
3106 * @name FAIL_IMAGE_LOAD
3107 * @memberof eg.view360.PanoViewer.ERROR_TYPE
3108 * @constant
3109 * @type {Number}
3110 * @default 12
3111 */
3112 FAIL_IMAGE_LOAD: 12,
3113
3114 /**
3115 * Failed to bind texture
3116 * @ko 텍스쳐 바인딩 실패
3117 * @name FAIL_BIND_TEXTURE
3118 * @memberof eg.view360.PanoViewer.ERROR_TYPE
3119 * @constant
3120 * @type {Number}
3121 * @default 13
3122 */
3123 FAIL_BIND_TEXTURE: 13,
3124
3125 /**
3126 * Only one resource(image or video) should be specified
3127 * @ko 리소스 지정 오류 (image 혹은 video 중 하나만 지정되어야 함)
3128 * @name INVALID_RESOURCE
3129 * @memberof eg.view360.PanoViewer.ERROR_TYPE
3130 * @constant
3131 * @type {Number}
3132 * @default 14
3133 */
3134 INVALID_RESOURCE: 14,
3135
3136 /**
3137 * WebGL context lost occurred
3138 * @ko WebGL context lost 발생
3139 * @name RENDERING_CONTEXT_LOST
3140 * @memberof eg.view360.PanoViewer.ERROR_TYPE
3141 * @constant
3142 * @type {Number}
3143 * @default 15
3144 */
3145 RENDERING_CONTEXT_LOST: 15
3146 };
3147 /**
3148 * Constant value for events
3149 * @ko 이벤트에 대한 상수 값
3150 * @namespace
3151 * @name EVENTS
3152 * @memberof eg.view360.PanoViewer
3153 */
3154
3155 var PANOVIEWER_EVENTS = {
3156 /**
3157 * Events that is fired when PanoViewer is ready to show image and handle user interaction.
3158 * @ko PanoViewer 가 사용자의 인터렉션 및 렌더링이 준비되상태에 발생하는 이벤트
3159 * @name READY
3160 * @memberof eg.view360.PanoViewer.EVENTS
3161 * @constant
3162 * @type {String}
3163 * @default ready
3164 */
3165 READY: "ready",
3166
3167 /**
3168 * Events that is fired when direction or fov is changed.
3169 * @ko PanoViewer 에서 바라보고 있는 방향이나 FOV(화각)가 변경되었을때 발생하는 이벤트
3170 * @name VIEW_CHANGE
3171 * @memberof eg.view360.PanoViewer.EVENTS
3172 * @constant
3173 * @type {String}
3174 * @default viewChange
3175 */
3176 VIEW_CHANGE: "viewChange",
3177
3178 /**
3179 * Events that is fired when animation which is triggered by inertia is ended.
3180 * @ko 관성에 의한 애니메이션 동작이 완료되었을때 발생하는 이벤트
3181 * @name ANIMATION_END
3182 * @memberof eg.view360.PanoViewer.EVENTS
3183 * @constant
3184 * @type {String}
3185 * @default animationEnd
3186 */
3187 ANIMATION_END: "animationEnd",
3188
3189 /**
3190 * Events that is fired when error occurs
3191 * @ko 에러 발생 시 발생하는 이벤트
3192 * @name ERROR
3193 * @memberof eg.view360.PanoViewer.EVENTS
3194 * @constant
3195 * @type {String}
3196 * @default error
3197 */
3198 ERROR: "error"
3199 };
3200 /**
3201 * Constant value for projection type
3202 * @ko 프로젝션 타입 대한 상수 값
3203 * @namespace
3204 * @name PROJECTION_TYPE
3205 * @memberof eg.view360.PanoViewer
3206 */
3207
3208 var PROJECTION_TYPE = {
3209 /**
3210 * Constant value for equirectangular type.
3211 * @ko equirectangular 에 대한 상수 값.
3212 * @name EQUIRECTANGULAR
3213 * @memberof eg.view360.PanoViewer.PROJECTION_TYPE
3214 * @constant
3215 * @type {String}
3216 * @default equirectangular
3217 */
3218 EQUIRECTANGULAR: "equirectangular",
3219
3220 /**
3221 * Constant value for cubemap type.
3222 * @ko cubemap 에 대한 상수 값.
3223 * @name CUBEMAP
3224 * @memberof eg.view360.PanoViewer.PROJECTION_TYPE
3225 * @constant
3226 * @type {String}
3227 * @default cubemap
3228 */
3229 CUBEMAP: "cubemap",
3230
3231 /**
3232 * Constant value for cubestrip type.
3233 * Cubestrip is a format for a single image with a combination of six cube faces. It is almost identical to cubemap, but it is implemented in a different way. It aims at better performance and efficiency. In addition, it automatically detects and supports EAC.
3234 * @ko cubemap 에 대한 상수 값.Cubestrip 은 cube 면이 6개가 조합된 조합을 한장의 이미지를 위한 포맷이다. cubemap 과 사용방법이 거의 동일하지만 다른 방식으로 구현되었다. 보다 좋은 성능과 효율성을 목적으로 한다. 더불어 자동으로 EAC 를 감지하고 지원한다.
3235 * @name CUBESTRIP
3236 * @memberof eg.view360.PanoViewer.PROJECTION_TYPE
3237 * @constant
3238 * @type {String}
3239 * @default cubestrip
3240 */
3241 CUBESTRIP: "cubestrip",
3242
3243 /**
3244 * Constant value for PANORAMA type.
3245 *
3246 * PANORAMA is a format for a panorma image which is taken from smartphone.
3247 * @ko PANORAMA 에 대한 상수값. 파노라마는 스마트 폰에서 가져온 파노라마 이미지의 형식입니다.
3248 *
3249 * @name PANORAMA
3250 * @memberof eg.view360.PanoViewer.PROJECTION_TYPE
3251 * @constant
3252 * @type {String}
3253 * @default panorama
3254 */
3255 PANORAMA: "panorama",
3256
3257 /**
3258 * Constant value for EQUI_STEREOSCOPY type.
3259 *
3260 * Constant value for EQUI_STEREOSCOPY. Stereoscopy image format of EQUIRECTANGULAR. It is an experimental function to show a stereoscopic type equirectangular image on a plane. It does not support stereoscopic viewing function through special visual equipment at present.
3261 * @ko EQUI_STEREOSCOPY 에 대한 상수값. EQUIRECTANGULAR 의 Stereoscopy 이미지 형식입니다. Stereoscopic 형태의 equirectangular 이미지를 평면에 보여주기 위한 실험적인 기능으로 현재는 특수한 시각 장비를 통한 입체적인 보기 기능은 지원하지 않습니다.
3262 *
3263 * @name STEREOSCOPIC_EQUI
3264 * @memberof eg.view360.PanoViewer.PROJECTION_TYPE
3265 * @constant
3266 * @type {String}
3267 * @default stereoequi
3268 */
3269 STEREOSCOPIC_EQUI: "stereoequi"
3270 };
3271 /**
3272 * A constant value for the format of the stereoscopic equirectangular projection type.
3273 * @ko Stereoscopic equirectangular 프로젝션 타입의 포맷에 대한 상수 값
3274 * @namespace
3275 * @name STEREO_FORMAT
3276 * @memberof eg.view360.PanoViewer
3277 */
3278
3279 var STEREO_FORMAT = {
3280 /**
3281 * A constant value for format of top bottom stereoscopic 360 equirectangular projection.
3282 * @ko top bottom stereoscopic 360 equirectangular projection 콘텐츠 포맷에 대한 상수값.
3283 * @name TOP_BOTTOM
3284 * @memberof eg.view360.PanoViewer.STEREO_FORMAT
3285 * @constant
3286 * @type {String}
3287 * @default "3dv"
3288 */
3289 TOP_BOTTOM: "3dv",
3290
3291 /**
3292 * A constant value for format of left right stereoscopic 360 equirectangular projection.
3293 * @ko Left right stereoscopic 360 equirectangular projection 콘텐츠 포맷에 대한 상수값.
3294 * @name LEFT_RIGHT
3295 * @memberof eg.view360.PanoViewer.STEREO_FORMAT
3296 * @constant
3297 * @type {String}
3298 * @default "3dh"
3299 */
3300 LEFT_RIGHT: "3dh",
3301
3302 /**
3303 * A constant value specifying media is not in stereoscopic format.
3304 * @ko Stereoscopic 영상이 아닐 경우에 적용하는 상수값.
3305 * @name NONE
3306 * @memberof eg.view360.PanoViewer.STEREO_FORMAT
3307 * @constant
3308 * @type {String}
3309 * @default ""
3310 */
3311 NONE: ""
3312 }; // eslint-disable-next-line @typescript-eslint/no-unused-vars
3313
3314 var PANOVIEWER_OPTIONS = {
3315 image: true,
3316 video: true,
3317 projectionType: true,
3318 cubemapConfig: true,
3319 stereoFormat: true,
3320 width: true,
3321 height: true,
3322 yaw: true,
3323 pitch: true,
3324 fov: true,
3325 showPolePoint: true,
3326 useZoom: true,
3327 useKeyboard: true,
3328 gyroMode: true,
3329 yawRange: true,
3330 pitchRange: true,
3331 fovRange: true,
3332 touchDirection: true,
3333 canvasClass: true
3334 };
3335 var DEFAULT_CANVAS_CLASS = "view360-canvas";
3336
3337 var Constants = {
3338 __proto__: null,
3339 GYRO_MODE: GYRO_MODE,
3340 PANOVIEWER_EVENTS: PANOVIEWER_EVENTS,
3341 ERROR_TYPE: ERROR_TYPE,
3342 PROJECTION_TYPE: PROJECTION_TYPE,
3343 STEREO_FORMAT: STEREO_FORMAT,
3344 PANOVIEWER_OPTIONS: PANOVIEWER_OPTIONS,
3345 DEFAULT_CANVAS_CLASS: DEFAULT_CANVAS_CLASS
3346 };
3347
3348 var WEBGL_ERROR_CODE = {
3349 "0": "NO_ERROR",
3350 "1280": "INVALID_ENUM",
3351 "1281": "INVALID_VALUE",
3352 "1282": "INVALID_OPERATION",
3353 "1285": "OUT_OF_MEMORY",
3354 "1286": "INVALID_FRAMEBUFFER_OPERATION",
3355 "37442": "CONTEXT_LOST_WEBGL"
3356 };
3357 var webglAvailability = null; // eslint-disable-next-line @typescript-eslint/naming-convention
3358
3359 var WebGLUtils =
3360 /*#__PURE__*/
3361 function () {
3362 function WebGLUtils() {}
3363
3364 WebGLUtils.createShader = function (gl, type, source) {
3365 var shader = gl.createShader(type);
3366 gl.shaderSource(shader, source);
3367 gl.compileShader(shader);
3368 var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
3369
3370 if (success) {
3371 return shader;
3372 } // eslint-disable-next-line
3373
3374
3375 console.error(gl.getShaderInfoLog(shader));
3376 return null;
3377 };
3378
3379 WebGLUtils.createProgram = function (gl, vertexShader, fragmentShader) {
3380 var program = gl.createProgram();
3381 gl.attachShader(program, vertexShader);
3382 gl.attachShader(program, fragmentShader);
3383 gl.linkProgram(program);
3384 gl.deleteShader(vertexShader);
3385 gl.deleteShader(fragmentShader);
3386 var success = gl.getProgramParameter(program, gl.LINK_STATUS);
3387
3388 if (success) {
3389 return program;
3390 }
3391
3392 gl.deleteProgram(program);
3393 return null;
3394 };
3395
3396 WebGLUtils.initBuffer = function (gl, target
3397 /* bind point */
3398 , data, itemSize, attr) {
3399 var buffer = gl.createBuffer();
3400 gl.bindBuffer(target, buffer);
3401 gl.bufferData(target, data, gl.STATIC_DRAW);
3402
3403 if (buffer) {
3404 buffer.itemSize = itemSize;
3405 buffer.numItems = data.length / itemSize;
3406 }
3407
3408 if (attr !== undefined) {
3409 gl.enableVertexAttribArray(attr);
3410 gl.vertexAttribPointer(attr, buffer.itemSize, gl.FLOAT, false, 0, 0);
3411 }
3412
3413 return buffer;
3414 };
3415
3416 WebGLUtils.getWebglContext = function (canvas, userContextAttributes) {
3417 var e_1, _a;
3418
3419 var webglIdentifiers = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];
3420 var context = null;
3421
3422 var contextAttributes = __assign({
3423 preserveDrawingBuffer: false,
3424 antialias: false
3425 }, userContextAttributes);
3426
3427 var onWebglcontextcreationerror = function (e) {
3428 return e.statusMessage;
3429 };
3430
3431 canvas.addEventListener("webglcontextcreationerror", onWebglcontextcreationerror);
3432
3433 try {
3434 for (var webglIdentifiers_1 = __values(webglIdentifiers), webglIdentifiers_1_1 = webglIdentifiers_1.next(); !webglIdentifiers_1_1.done; webglIdentifiers_1_1 = webglIdentifiers_1.next()) {
3435 var identifier = webglIdentifiers_1_1.value;
3436
3437 try {
3438 context = canvas.getContext(identifier, contextAttributes);
3439 } catch (t) {} // eslint-disable-line no-empty
3440
3441
3442 if (context) {
3443 break;
3444 }
3445 }
3446 } catch (e_1_1) {
3447 e_1 = {
3448 error: e_1_1
3449 };
3450 } finally {
3451 try {
3452 if (webglIdentifiers_1_1 && !webglIdentifiers_1_1.done && (_a = webglIdentifiers_1.return)) _a.call(webglIdentifiers_1);
3453 } finally {
3454 if (e_1) throw e_1.error;
3455 }
3456 }
3457
3458 canvas.removeEventListener("webglcontextcreationerror", onWebglcontextcreationerror);
3459 return context;
3460 };
3461
3462 WebGLUtils.createTexture = function (gl, textureTarget) {
3463 var texture = gl.createTexture();
3464 gl.bindTexture(textureTarget, texture);
3465 gl.texParameteri(textureTarget, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
3466 gl.texParameteri(textureTarget, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
3467 gl.texParameteri(textureTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
3468 gl.texParameteri(textureTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
3469 gl.bindTexture(textureTarget, null);
3470 return texture;
3471 };
3472 /**
3473 * Returns the webgl availability of the current browser.
3474 * @method WebGLUtils#isWebGLAvailable
3475 * @retuen {Boolean} isWebGLAvailable
3476 */
3477
3478
3479 WebGLUtils.isWebGLAvailable = function () {
3480 if (webglAvailability === null) {
3481 var canvas = document.createElement("canvas");
3482 var webglContext = WebGLUtils.getWebglContext(canvas);
3483 webglAvailability = !!webglContext; // webglContext Resource forced collection
3484
3485 if (webglContext) {
3486 var loseContextExtension = webglContext.getExtension("WEBGL_lose_context");
3487
3488 if (loseContextExtension) {
3489 loseContextExtension.loseContext();
3490 }
3491 }
3492 }
3493
3494 return !!webglAvailability;
3495 };
3496 /**
3497 * Returns whether webgl is stable in the current browser.
3498 * @method WebGLUtils#isStableWebGL
3499 * @retuen {Boolean} isStableWebGL
3500 */
3501
3502
3503 WebGLUtils.isStableWebGL = function () {
3504 var agentInfo = agent$1();
3505 var isStableWebgl = true;
3506
3507 if (agentInfo.os.name === "android") {
3508 var version = parseFloat(agentInfo.os.version);
3509
3510 if (version <= 4.3 && version >= 1) {
3511 isStableWebgl = false;
3512 } else if (version === 4.4) {
3513 if (agentInfo.browser.name !== "chrome") {
3514 isStableWebgl = false;
3515 }
3516 }
3517 }
3518
3519 return isStableWebgl;
3520 };
3521
3522 WebGLUtils.getErrorNameFromWebGLErrorCode = function (code) {
3523 if (!(code in WEBGL_ERROR_CODE)) {
3524 return "UNKNOWN_ERROR";
3525 }
3526
3527 return WEBGL_ERROR_CODE[code];
3528 };
3529 /**
3530 * This function is wrapper for texImage2D to handle exceptions on texImage2D.
3531 * Purpose is to prevent service from being stopped by script error.
3532 */
3533
3534
3535 WebGLUtils.texImage2D = function (gl, target, pixels) {
3536 try {
3537 gl.texImage2D(target, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
3538 } catch (error) {
3539 /* eslint-disable no-console */
3540 console.error("WebGLUtils.texImage2D error:", error);
3541 /* eslint-enable no-console */
3542 }
3543 };
3544
3545 WebGLUtils.getMaxTextureSize = function (gl) {
3546 // WARN: MAX_TEXTURE_SIZE_FOR_TEST is used for test
3547 return gl.getParameter(gl.MAX_TEXTURE_SIZE);
3548 };
3549
3550 return WebGLUtils;
3551 }();
3552
3553 var agentInfo = agent$1();
3554 var isIE11 = agentInfo.browser.name === "ie" && agentInfo.browser.majorVersion === 11;
3555 var EVENTS = {
3556 ERROR: "error"
3557 };
3558 /**
3559 *
3560 * Extends Component for firing errors occurs internally.
3561 */
3562
3563 var Renderer =
3564 /*#__PURE__*/
3565 function (_super) {
3566 __extends(Renderer, _super);
3567
3568 function Renderer() {
3569 var _this = _super.call(this) || this;
3570
3571 _this._forceDimension = null;
3572 _this._pixelCanvas = null;
3573 _this._pixelContext = null;
3574 return _this;
3575 }
3576
3577 var __proto = Renderer.prototype;
3578
3579 __proto.render = function (_a) {
3580 var gl = _a.gl,
3581 shaderProgram = _a.shaderProgram,
3582 indexBuffer = _a.indexBuffer,
3583 mvMatrix = _a.mvMatrix,
3584 pMatrix = _a.pMatrix;
3585 gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
3586 gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
3587
3588 if (indexBuffer) {
3589 gl.drawElements(gl.TRIANGLES, indexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
3590 }
3591 }; // Define interface for Renderers
3592
3593 /**
3594 * Following MUST BE DEFINED on Child of Renderer
3595 *
3596 * DATA
3597 *
3598 * - getVertexPositionData
3599 * - getIndexData
3600 * - getTextureCoordData
3601 *
3602 * SOURCE
3603 *
3604 * - getVertexShaderSource
3605 * - getFragmentShaderSource
3606 *
3607 * TEXTURE
3608 *
3609 * - bindTexture
3610 */
3611
3612
3613 __proto.getDimension = function (pixelSource) {
3614 var width = pixelSource.naturalWidth || pixelSource.videoWidth;
3615 var height = pixelSource.naturalHeight || pixelSource.videoHeight;
3616 return {
3617 width: width,
3618 height: height
3619 };
3620 };
3621 /**
3622 * Update data used by shader
3623 */
3624
3625
3626 __proto.updateShaderData = function (param) {
3627 /*
3628 * Update following data in implementation layer.
3629 * If the data is not changed, it does not need to implement this function.
3630 *
3631 * - _VERTEX_POSITION_DATA
3632 * - _TEXTURE_COORD_DATA
3633 * - _INDEX_DATA
3634 */
3635 };
3636 /**
3637 *
3638 * @param {HTMLImageElement | HTMLVideoElement} image
3639 * @param {Object = {width, height}} forceDimension Forced dimension to resize
3640 */
3641
3642
3643 __proto._initPixelSource = function (image, forceDimension) {
3644 if (forceDimension === void 0) {
3645 forceDimension = null;
3646 }
3647
3648 var isIE11Video = isIE11 && image instanceof HTMLVideoElement;
3649
3650 if (isIE11Video || forceDimension) {
3651 var _a = forceDimension || this.getDimension(image),
3652 width = _a.width,
3653 height = _a.height;
3654
3655 this._pixelCanvas = document.createElement("canvas");
3656 this._pixelCanvas.width = width;
3657 this._pixelCanvas.height = height;
3658 this._pixelContext = this._pixelCanvas.getContext("2d");
3659 }
3660
3661 this._forceDimension = forceDimension;
3662 };
3663
3664 __proto._getPixelSource = function (image) {
3665 if (!this._pixelCanvas) {
3666 return image;
3667 }
3668 /**
3669 * IE11 && Video
3670 * or
3671 * Dimension is forced (Image is larger than texture size.)
3672 */
3673
3674
3675 var contentDimension = this.getDimension(image);
3676 var textureDimension = this._forceDimension || contentDimension;
3677
3678 if (this._pixelCanvas.width !== textureDimension.width) {
3679 this._pixelCanvas.width = textureDimension.width;
3680 }
3681
3682 if (this._pixelCanvas.height !== textureDimension.height) {
3683 this._pixelCanvas.height = textureDimension.height;
3684 }
3685
3686 if (this._forceDimension) {
3687 this._pixelContext.drawImage(image, 0, 0, contentDimension.width, contentDimension.height, 0, 0, textureDimension.width, textureDimension.height);
3688 } else {
3689 this._pixelContext.drawImage(image, 0, 0);
3690 }
3691
3692 return this._pixelCanvas;
3693 };
3694
3695 __proto._extractTileConfig = function (imageConfig) {
3696 var tileConfig = Array.isArray(imageConfig.tileConfig) ? imageConfig.tileConfig : Array.apply(void 0, __spread(Array(6))).map(function () {
3697 return imageConfig.tileConfig;
3698 });
3699 tileConfig = tileConfig.map(function (config) {
3700 return __assign({
3701 flipHorizontal: false,
3702 rotation: 0
3703 }, config);
3704 });
3705 return tileConfig;
3706 };
3707
3708 __proto._triggerError = function (error) {
3709 /* eslint-disable no-console */
3710 console.error("Renderer Error:", error);
3711 /* eslint-enable no-console */
3712
3713 this.trigger(new Component.ComponentEvent(EVENTS.ERROR, {
3714 message: typeof error === "string" ? error : error.message
3715 }));
3716 };
3717
3718 Renderer.EVENTS = EVENTS;
3719 return Renderer;
3720 }(Component);
3721
3722 var CubeRenderer =
3723 /*#__PURE__*/
3724 function (_super) {
3725 __extends(CubeRenderer, _super);
3726
3727 function CubeRenderer() {
3728 return _super !== null && _super.apply(this, arguments) || this;
3729 }
3730
3731 var __proto = CubeRenderer.prototype;
3732
3733 CubeRenderer.extractOrder = function (imageConfig) {
3734 return imageConfig.order || "RLUDBF";
3735 };
3736
3737 __proto.getVertexPositionData = function () {
3738 CubeRenderer._VERTEX_POSITION_DATA = CubeRenderer._VERTEX_POSITION_DATA !== null ? CubeRenderer._VERTEX_POSITION_DATA : [// back
3739 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, // front
3740 -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, // top
3741 -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, // bottom
3742 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, // right
3743 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, // left
3744 -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1];
3745 return CubeRenderer._VERTEX_POSITION_DATA;
3746 };
3747
3748 __proto.getIndexData = function () {
3749 if (CubeRenderer._INDEX_DATA) {
3750 return CubeRenderer._INDEX_DATA;
3751 }
3752
3753 var indexData = [];
3754 var vertexPositionData = this.getVertexPositionData();
3755
3756 for (var i = 0; i < vertexPositionData.length / 3; i += 4) {
3757 indexData.push(i, i + 2, i + 1, i, i + 3, i + 2);
3758 }
3759
3760 CubeRenderer._INDEX_DATA = indexData;
3761 return indexData;
3762 };
3763
3764 __proto.getTextureCoordData = function (_a) {
3765 var _this = this;
3766
3767 var image = _a.image,
3768 imageConfig = _a.imageConfig;
3769 var vertexOrder = "BFUDRL";
3770 var order = CubeRenderer.extractOrder(imageConfig);
3771 var base = this.getVertexPositionData();
3772
3773 var tileConfig = this._extractTileConfig(imageConfig);
3774
3775 var elemSize = 3;
3776 var vertexPerTile = 4;
3777 var trim = imageConfig.trim;
3778 var texCoords = vertexOrder.split("").map(function (face) {
3779 return tileConfig[order.indexOf(face)];
3780 }).map(function (config, i) {
3781 var rotation = Math.floor(config.rotation / 90);
3782 var ordermap = config.flipHorizontal ? [0, 1, 2, 3] : [1, 0, 3, 2];
3783
3784 for (var r = 0; r < Math.abs(rotation); r++) {
3785 if (config.flipHorizontal && rotation > 0 || !config.flipHorizontal && rotation < 0) {
3786 ordermap.push(ordermap.shift());
3787 } else {
3788 ordermap.unshift(ordermap.pop());
3789 }
3790 }
3791
3792 var elemPerTile = elemSize * vertexPerTile;
3793 var tileVertex = base.slice(i * elemPerTile, i * elemPerTile + elemPerTile);
3794 var tileTemp = [];
3795
3796 for (var j = 0; j < vertexPerTile; j++) {
3797 tileTemp[ordermap[j]] = tileVertex.splice(0, elemSize);
3798 }
3799
3800 return tileTemp;
3801 }).map(function (coord) {
3802 return _this._shrinkCoord({
3803 image: image,
3804 faceCoords: coord,
3805 trim: trim
3806 });
3807 }).reduce(function (acc, val) {
3808 return __spread(acc, val.reduce(function (coords, coord) {
3809 return __spread(coords, coord);
3810 }, []));
3811 }, []);
3812 return texCoords;
3813 };
3814
3815 __proto.getVertexShaderSource = function () {
3816 return "\nattribute vec3 aVertexPosition;\nattribute vec3 aTextureCoord;\nuniform mat4 uMVMatrix;\nuniform mat4 uPMatrix;\nvarying highp vec3 vVertexDirectionVector;\nvoid main(void) {\n vVertexDirectionVector = aTextureCoord;\n gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);\n}";
3817 };
3818
3819 __proto.getFragmentShaderSource = function () {
3820 return "\nprecision highp float;\nuniform samplerCube uSampler;\nvarying highp vec3 vVertexDirectionVector;\nvoid main(void) {\n gl_FragColor = textureCube(uSampler, vVertexDirectionVector);\n}";
3821 };
3822
3823 __proto.updateTexture = function (gl, image, imageConfig) {
3824 var baseOrder = "RLUDBF";
3825 var order = CubeRenderer.extractOrder(imageConfig);
3826 var orderMap = {};
3827 order.split("").forEach(function (v, i) {
3828 orderMap[v] = i;
3829 });
3830
3831 try {
3832 if (image instanceof Array) {
3833 for (var surfaceIdx = 0; surfaceIdx < 6; surfaceIdx++) {
3834 var tileIdx = orderMap[baseOrder[surfaceIdx]];
3835 WebGLUtils.texImage2D(gl, gl.TEXTURE_CUBE_MAP_POSITIVE_X + surfaceIdx, image[tileIdx]);
3836 }
3837 } else {
3838 var maxCubeMapTextureSize = this.getMaxCubeMapTextureSize(gl, image);
3839
3840 for (var surfaceIdx = 0; surfaceIdx < 6; surfaceIdx++) {
3841 var tileIdx = orderMap[baseOrder[surfaceIdx]];
3842 var tile = this.extractTileFromImage(image, tileIdx, maxCubeMapTextureSize);
3843 WebGLUtils.texImage2D(gl, gl.TEXTURE_CUBE_MAP_POSITIVE_X + surfaceIdx, tile);
3844 }
3845 }
3846 } catch (e) {
3847 this._triggerError(e);
3848 }
3849 };
3850
3851 __proto.bindTexture = function (gl, texture, image, imageConfig) {
3852 gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
3853 this.updateTexture(gl, image, imageConfig);
3854 };
3855
3856 __proto.getSourceTileSize = function (image) {
3857 var _a = this.getDimension(image),
3858 width = _a.width,
3859 height = _a.height;
3860
3861 var aspectRatio = width / height;
3862 var inputTextureSize;
3863
3864 if (aspectRatio === 1 / 6) {
3865 inputTextureSize = width;
3866 } else if (aspectRatio === 6) {
3867 inputTextureSize = height;
3868 } else if (aspectRatio === 2 / 3) {
3869 inputTextureSize = width / 2;
3870 } else {
3871 inputTextureSize = width / 3;
3872 }
3873
3874 return inputTextureSize;
3875 };
3876
3877 __proto.extractTileFromImage = function (image, tileIdx, outputTextureSize) {
3878 var width = this.getDimension(image).width;
3879 var inputTextureSize = this.getSourceTileSize(image);
3880 var canvas = document.createElement("canvas");
3881 canvas.width = outputTextureSize;
3882 canvas.height = outputTextureSize;
3883 var context = canvas.getContext("2d");
3884 var tilePerRow = width / inputTextureSize;
3885 var x = inputTextureSize * tileIdx % (inputTextureSize * tilePerRow);
3886 var y = Math.floor(tileIdx / tilePerRow) * inputTextureSize;
3887 context.drawImage(image, x, y, inputTextureSize, inputTextureSize, 0, 0, outputTextureSize, outputTextureSize);
3888 return canvas;
3889 };
3890
3891 __proto.getMaxCubeMapTextureSize = function (gl, image) {
3892 var agentInfo = agent$1();
3893 var maxCubeMapTextureSize = gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE);
3894 var imageWidth = this.getSourceTileSize(image);
3895
3896 if (agentInfo.browser.name === "ie" && agentInfo.browser.majorVersion === 11) {
3897 if (!util.isPowerOfTwo(imageWidth)) {
3898 for (var i = 1; i < maxCubeMapTextureSize; i *= 2) {
3899 if (i < imageWidth) {
3900 continue;
3901 } else {
3902 imageWidth = i;
3903 break;
3904 }
3905 }
3906 }
3907 }
3908
3909 if (agentInfo.os.name === "ios") {
3910 var majorVersion = agentInfo.os.majorVersion; // ios 9 의 경우 텍스쳐 최대사이즈는 1024 이다.
3911
3912 if (majorVersion === 9) {
3913 imageWidth = 1024;
3914 } // ios 8 의 경우 텍스쳐 최대사이즈는 512 이다.
3915
3916
3917 if (majorVersion === 8) {
3918 imageWidth = 512;
3919 }
3920 } // maxCubeMapTextureSize 보다는 작고, imageWidth 보다 큰 2의 승수 중 가장 작은 수
3921
3922
3923 return Math.min(maxCubeMapTextureSize, imageWidth);
3924 };
3925
3926 __proto._shrinkCoord = function (coordData) {
3927 var image = coordData.image,
3928 faceCoords = coordData.faceCoords,
3929 trim = coordData.trim;
3930 var inputTextureSize = Array.isArray(image) ? this.getDimension(image[0]).width : this.getSourceTileSize(image); // Shrink by "trim" px
3931
3932 var SHRINK_MULTIPLIER = 1 - trim * (2 / inputTextureSize);
3933 var axisMultipliers = [0, 1, 2].map(function (axisIndex) {
3934 var axisDir = util.sign(faceCoords[0][axisIndex]);
3935 var notSameDir = faceCoords.some(function (coord) {
3936 return util.sign(coord[axisIndex]) !== axisDir;
3937 });
3938 return notSameDir;
3939 }).map(function (notSameDir) {
3940 return notSameDir ? SHRINK_MULTIPLIER : 1;
3941 });
3942 return faceCoords.map(function (coords) {
3943 return coords.map(function (coord, axisIndex) {
3944 return coord * axisMultipliers[axisIndex];
3945 });
3946 });
3947 };
3948
3949 CubeRenderer._VERTEX_POSITION_DATA = null;
3950 CubeRenderer._INDEX_DATA = null;
3951 return CubeRenderer;
3952 }(Renderer);
3953
3954 var CubeStripRenderer =
3955 /*#__PURE__*/
3956 function (_super) {
3957 __extends(CubeStripRenderer, _super);
3958
3959 function CubeStripRenderer() {
3960 return _super !== null && _super.apply(this, arguments) || this;
3961 }
3962
3963 var __proto = CubeStripRenderer.prototype;
3964
3965 __proto.getVertexShaderSource = function () {
3966 return "\nattribute vec3 aVertexPosition;\nattribute vec2 aTextureCoord;\nuniform mat4 uMVMatrix;\nuniform mat4 uPMatrix;\nvarying highp vec2 vTextureCoord;\nvoid main(void) {\n vTextureCoord = aTextureCoord;\n gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);\n}";
3967 };
3968
3969 __proto.getFragmentShaderSource = function () {
3970 return "\n#define PI 3.14159265359\nprecision highp float;\nvarying highp vec2 vTextureCoord;\nuniform sampler2D uSampler;\nuniform bool uIsEAC;\nconst vec2 OPERATE_COORDS_RANGE = vec2(-1.0, 1.0);\nconst vec2 TEXTURE_COORDS_RANGE = vec2(0.0, 1.0);\n// vector type is used for initializing values instead of array.\nconst vec4 TEXTURE_DIVISION_X = vec4(0.0, 1.0 / 3.0, 2.0 / 3.0, 1.0);\nconst vec3 TEXTURE_DIVISION_Y = vec3(0.0, 1.0 / 2.0, 1.0);\nconst float EAC_CONST = 2.0 / PI;\nfloat scale(vec2 domainRange, vec2 targetRange, float val) {\n float unit = 1.0 / (domainRange[1] - domainRange[0]);\n return targetRange[0] + (targetRange[1] - targetRange[0]) * (val - domainRange[0]) * unit;\n}\nvoid main(void) {\n float transformedCoordX;\n float transformedCoordY;\n\n if (uIsEAC) {\n vec2 orgTextureRangeX;\n vec2 orgTextureRangeY;\n\n // Apply EAC transform\n if (vTextureCoord.s >= TEXTURE_DIVISION_X[2]) {\n orgTextureRangeX = vec2(TEXTURE_DIVISION_X[2], TEXTURE_DIVISION_X[3]);\n } else if (vTextureCoord.s >= TEXTURE_DIVISION_X[1]) {\n orgTextureRangeX = vec2(TEXTURE_DIVISION_X[1], TEXTURE_DIVISION_X[2]);\n } else {\n orgTextureRangeX = vec2(TEXTURE_DIVISION_X[0], TEXTURE_DIVISION_X[1]);\n }\n\n if (vTextureCoord.t >= TEXTURE_DIVISION_Y[1]) {\n orgTextureRangeY = vec2(TEXTURE_DIVISION_Y[1], TEXTURE_DIVISION_Y[2]);\n } else {\n orgTextureRangeY = vec2(TEXTURE_DIVISION_Y[0], TEXTURE_DIVISION_Y[1]);\n }\n\n // scaling coors by the coordinates following the range from -1.0 to 1.0.\n float px = scale(orgTextureRangeX, OPERATE_COORDS_RANGE, vTextureCoord.s);\n float py = scale(orgTextureRangeY, OPERATE_COORDS_RANGE, vTextureCoord.t);\n\n float qu = EAC_CONST * atan(px) + 0.5;\n float qv = EAC_CONST * atan(py) + 0.5;\n\n // re-scaling coors by original coordinates ranges\n transformedCoordX = scale(TEXTURE_COORDS_RANGE, orgTextureRangeX, qu);\n transformedCoordY = scale(TEXTURE_COORDS_RANGE, orgTextureRangeY, qv);\n } else {\n // normal cubemap\n transformedCoordX = vTextureCoord.s;\n transformedCoordY = vTextureCoord.t;\n }\n\n gl_FragColor = texture2D(uSampler, vec2(transformedCoordX, transformedCoordY));\n}";
3971 };
3972
3973 __proto.getVertexPositionData = function () {
3974 if (!this._vertices) {
3975 this._vertices = [// back
3976 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, // front
3977 -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, // up
3978 -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1, // down
3979 -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, -1, -1, // right
3980 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, // left
3981 -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1];
3982 }
3983
3984 return this._vertices;
3985 };
3986
3987 __proto.getIndexData = function () {
3988 var _this = this; // TODO: 한번만 계산하도록 수정하기
3989
3990
3991 var indices = function () {
3992 var indexData = [];
3993
3994 for (var i = 0; i < _this._vertices.length / 3; i += 4) {
3995 indexData.push(i, i + 1, i + 2, i, i + 2, i + 3);
3996 }
3997
3998 return indexData;
3999 }();
4000
4001 return indices;
4002 };
4003
4004 __proto.getTextureCoordData = function (_a) {
4005 var _this = this;
4006
4007 var image = _a.image,
4008 imageConfig = _a.imageConfig; // TODO: make it cols, rows as config.
4009
4010 var cols = 3;
4011 var rows = 2;
4012 var textureSize = this.getDimension(image);
4013 var trim = imageConfig.trim;
4014 var order = imageConfig.order || "RLUDFB";
4015 var coords = []; // 텍스쳐의 좌표는 윗쪽이 큰 값을 가지므로 row 는 역순으로 넣는다.
4016
4017 for (var r = rows - 1; r >= 0; r--) {
4018 for (var c = 0; c < cols; c++) {
4019 var coord = [c / cols, r / rows, (c + 1) / cols, r / rows, (c + 1) / cols, (r + 1) / rows, c / cols, (r + 1) / rows];
4020 coords.push(coord);
4021 }
4022 }
4023
4024 var tileConfigs = this._extractTileConfig(imageConfig); // Transform Coord By Flip & Rotation
4025
4026
4027 coords = coords // shrink coord to avoid pixel bleeding
4028 .map(function (coord) {
4029 return _this._shrinkCoord(coord, textureSize, trim);
4030 }).map(function (coord, i) {
4031 return _this._transformCoord(coord, tileConfigs[i]);
4032 }); // vertices 에서 지정된 순서대로 그대로 그리기 위해 vertex 의 순서를 BFUDRL 로 재배치
4033
4034 return "BFUDRL".split("").map(function (face) {
4035 return order.indexOf(face);
4036 }).map(function (index) {
4037 return coords[index];
4038 }).reduce(function (acc, val) {
4039 return acc.concat(val);
4040 }, []);
4041 };
4042
4043 __proto.updateTexture = function (gl, image) {
4044 WebGLUtils.texImage2D(gl, gl.TEXTURE_2D, this._getPixelSource(image));
4045 };
4046
4047 __proto.bindTexture = function (gl, texture, image) {
4048 // Make sure image isn't too big
4049 var _a = this.getDimension(image),
4050 width = _a.width,
4051 height = _a.height;
4052
4053 var size = Math.max(width, height);
4054 var maxSize = WebGLUtils.getMaxTextureSize(gl);
4055
4056 if (size > maxSize) {
4057 this._triggerError("Image width(" + width + ") exceeds device limit(" + maxSize + "))");
4058
4059 return;
4060 } // Pixel Source for IE11 & Video
4061
4062
4063 this._initPixelSource(image);
4064
4065 gl.activeTexture(gl.TEXTURE0);
4066 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
4067 gl.bindTexture(gl.TEXTURE_2D, texture);
4068 this.updateTexture(gl, image);
4069 };
4070
4071 __proto._transformCoord = function (coord, tileConfig) {
4072 var newCoord = coord.slice();
4073
4074 if (tileConfig.flipHorizontal) {
4075 newCoord = this._flipHorizontalCoord(newCoord);
4076 }
4077
4078 if (tileConfig.rotation) {
4079 newCoord = this._rotateCoord(newCoord, tileConfig.rotation);
4080 }
4081
4082 return newCoord;
4083 };
4084
4085 __proto._shrinkCoord = function (coord, textureSize, trim) {
4086 var width = textureSize.width,
4087 height = textureSize.height; // Shrink by "trim" px
4088
4089 var SHRINK_Y = trim * (1 / height);
4090 var SHRINK_X = trim * (1 / width);
4091 return [coord[0] + SHRINK_X, coord[1] + SHRINK_Y, coord[2] - SHRINK_X, coord[3] + SHRINK_Y, coord[4] - SHRINK_X, coord[5] - SHRINK_Y, coord[6] + SHRINK_X, coord[7] - SHRINK_Y];
4092 };
4093
4094 __proto._rotateCoord = function (coord, rotationAngle) {
4095 var SIZE = 2; // coord means x,y coordinates. Two values(x, y) makes a one coord.
4096
4097 var shiftCount = Math.floor(rotationAngle / 90) % 4;
4098
4099 if (shiftCount === 0) {
4100 return coord;
4101 }
4102
4103 var moved;
4104 var rotatedCoord = [];
4105
4106 if (shiftCount > 0) {
4107 moved = coord.splice(0, shiftCount * SIZE);
4108 rotatedCoord = coord.concat(moved);
4109 } else {
4110 moved = coord.splice((4 + shiftCount) * SIZE, -shiftCount * SIZE);
4111 rotatedCoord = moved.concat(coord);
4112 }
4113
4114 return rotatedCoord;
4115 };
4116
4117 __proto._flipHorizontalCoord = function (coord) {
4118 return [coord[2], coord[3], coord[0], coord[1], coord[6], coord[7], coord[4], coord[5]];
4119 };
4120
4121 return CubeStripRenderer;
4122 }(Renderer);
4123
4124 var latitudeBands = 60;
4125 var longitudeBands = 60;
4126 var radius = 2;
4127 var ANGLE_CORRECTION_FOR_CENTER_ALIGN = -0.5 * Math.PI;
4128 var textureCoordData = [];
4129 var vertexPositionData = [];
4130 var indexData = [];
4131 var latIdx;
4132 var lngIdx;
4133
4134 for (latIdx = 0; latIdx <= latitudeBands; latIdx++) {
4135 var theta = (latIdx / latitudeBands - 0.5) * Math.PI;
4136 var sinTheta = Math.sin(theta);
4137 var cosTheta = Math.cos(theta);
4138
4139 for (lngIdx = 0; lngIdx <= longitudeBands; lngIdx++) {
4140 var phi = (lngIdx / longitudeBands - 0.5) * 2 * Math.PI + ANGLE_CORRECTION_FOR_CENTER_ALIGN;
4141 var sinPhi = Math.sin(phi);
4142 var cosPhi = Math.cos(phi);
4143 var x = cosPhi * cosTheta;
4144 var y = sinTheta;
4145 var z = sinPhi * cosTheta;
4146 var u = lngIdx / longitudeBands;
4147 var v = latIdx / latitudeBands;
4148 textureCoordData.push(u, v);
4149 vertexPositionData.push(radius * x, radius * y, radius * z);
4150
4151 if (lngIdx !== longitudeBands && latIdx !== latitudeBands) {
4152 var a = latIdx * (longitudeBands + 1) + lngIdx;
4153 var b = a + longitudeBands + 1;
4154 indexData.push(a, b, a + 1, b, b + 1, a + 1);
4155 }
4156 }
4157 }
4158
4159 var SphereRenderer =
4160 /*#__PURE__*/
4161 function (_super) {
4162 __extends(SphereRenderer, _super);
4163
4164 function SphereRenderer(format) {
4165 var _this = _super.call(this) || this;
4166
4167 _this._stereoFormat = format;
4168 return _this;
4169 }
4170
4171 var __proto = SphereRenderer.prototype;
4172
4173 __proto.render = function (ctx) {
4174 var gl = ctx.gl,
4175 shaderProgram = ctx.shaderProgram;
4176 var leftEyeScaleOffset;
4177 var rightEyeScaleOffset;
4178
4179 switch (this._stereoFormat) {
4180 case STEREO_FORMAT.TOP_BOTTOM:
4181 leftEyeScaleOffset = [1, 0.5, 0, 0];
4182 rightEyeScaleOffset = [1, 0.5, 0, 0.5];
4183 break;
4184
4185 case STEREO_FORMAT.LEFT_RIGHT:
4186 leftEyeScaleOffset = [0.5, 1, 0, 0];
4187 rightEyeScaleOffset = [0.5, 1, 0.5, 0];
4188 break;
4189
4190 default:
4191 leftEyeScaleOffset = [1, 1, 0, 0];
4192 rightEyeScaleOffset = [1, 1, 0, 0];
4193 }
4194
4195 var uTexScaleOffset = gl.getUniformLocation(shaderProgram, "uTexScaleOffset");
4196 gl.uniform4fv(uTexScaleOffset, __spread(leftEyeScaleOffset, rightEyeScaleOffset));
4197
4198 _super.prototype.render.call(this, ctx);
4199 };
4200
4201 __proto.getVertexPositionData = function () {
4202 return SphereRenderer._VERTEX_POSITION_DATA;
4203 };
4204
4205 __proto.getIndexData = function () {
4206 return SphereRenderer._INDEX_DATA;
4207 };
4208
4209 __proto.getTextureCoordData = function () {
4210 return SphereRenderer._TEXTURE_COORD_DATA;
4211 };
4212
4213 __proto.getVertexShaderSource = function () {
4214 return "\nattribute vec3 aVertexPosition;\nattribute vec2 aTextureCoord;\nuniform mat4 uMVMatrix;\nuniform mat4 uPMatrix;\nuniform float uEye;\nuniform vec4 uTexScaleOffset[2];\nvarying highp vec2 vTextureCoord;\nvoid main(void) {\n vec4 scaleOffset = uTexScaleOffset[int(uEye)];\n vTextureCoord = aTextureCoord.xy * scaleOffset.xy + scaleOffset.zw;\n gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);\n}";
4215 };
4216
4217 __proto.getFragmentShaderSource = function () {
4218 return "\nprecision highp float;\nvarying highp vec2 vTextureCoord;\nuniform sampler2D uSampler;\nvoid main(void) {\n gl_FragColor = texture2D(uSampler, vTextureCoord.st);\n}";
4219 };
4220
4221 __proto.updateTexture = function (gl, image) {
4222 WebGLUtils.texImage2D(gl, gl.TEXTURE_2D, this._getPixelSource(image));
4223 };
4224
4225 __proto.bindTexture = function (gl, texture, image) {
4226 // Make sure image isn't too big
4227 var _a = this.getDimension(image),
4228 width = _a.width,
4229 height = _a.height;
4230
4231 var size = Math.max(width, height);
4232 var maxSize = WebGLUtils.getMaxTextureSize(gl);
4233
4234 if (size > maxSize) {
4235 this._triggerError("Image width(" + width + ") exceeds device limit(" + maxSize + "))");
4236
4237 return;
4238 } // Pixel Source for IE11 & Video
4239
4240
4241 this._initPixelSource(image);
4242
4243 gl.activeTexture(gl.TEXTURE0);
4244 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
4245 gl.bindTexture(gl.TEXTURE_2D, texture);
4246 this.updateTexture(gl, image);
4247 };
4248
4249 SphereRenderer._VERTEX_POSITION_DATA = vertexPositionData;
4250 SphereRenderer._TEXTURE_COORD_DATA = textureCoordData;
4251 SphereRenderer._INDEX_DATA = indexData;
4252 return SphereRenderer;
4253 }(Renderer);
4254
4255 var MIN_ASPECT_RATIO_FOR_FULL_PANORAMA = 6;
4256 var longitudeBands$1 = 60;
4257 var textureCoordData$1 = [];
4258 var vertexPositionData$1 = [];
4259 var indexData$1 = [];
4260
4261 var CylinderRenderer =
4262 /*#__PURE__*/
4263 function (_super) {
4264 __extends(CylinderRenderer, _super);
4265
4266 function CylinderRenderer() {
4267 return _super !== null && _super.apply(this, arguments) || this;
4268 }
4269
4270 var __proto = CylinderRenderer.prototype;
4271
4272 __proto.getVertexPositionData = function () {
4273 return CylinderRenderer._VERTEX_POSITION_DATA;
4274 };
4275
4276 __proto.getIndexData = function () {
4277 return CylinderRenderer._INDEX_DATA;
4278 };
4279
4280 __proto.getTextureCoordData = function () {
4281 return CylinderRenderer._TEXTURE_COORD_DATA;
4282 };
4283
4284 __proto.getVertexShaderSource = function () {
4285 return "\nattribute vec3 aVertexPosition;\nattribute vec2 aTextureCoord;\nuniform mat4 uMVMatrix;\nuniform mat4 uPMatrix;\nvarying highp vec2 vTextureCoord;\nvoid main(void) {\n vTextureCoord = aTextureCoord;\n gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);\n}";
4286 };
4287
4288 __proto.getFragmentShaderSource = function () {
4289 return "\nprecision highp float;\nvarying highp vec2 vTextureCoord;\nuniform sampler2D uSampler;\nvoid main(void) {\n gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));\n}";
4290 };
4291
4292 __proto.updateTexture = function (gl, image) {
4293 WebGLUtils.texImage2D(gl, gl.TEXTURE_2D, this._getPixelSource(image));
4294 };
4295
4296 __proto.bindTexture = function (gl, texture, image) {
4297 // Make sure image isn't too big
4298 var _a = this.getDimension(image),
4299 width = _a.width,
4300 height = _a.height;
4301
4302 var size = Math.max(width, height);
4303 var maxSize = WebGLUtils.getMaxTextureSize(gl);
4304 var resizeDimension;
4305
4306 if (size > maxSize) {
4307 this._triggerError("Image width(" + width + ") exceeds device texture limit(" + maxSize + "))"); // Request resizing texture.
4308
4309 /**
4310 * TODO: Is it need to apply on another projection type?
4311 */
4312
4313
4314 resizeDimension = width > height ? {
4315 width: maxSize,
4316 height: maxSize * height / width
4317 } : {
4318 width: maxSize * width / height,
4319 height: maxSize
4320 };
4321 } // Pixel Source for IE11 & Video or resizing needed
4322
4323
4324 this._initPixelSource(image, resizeDimension);
4325
4326 gl.activeTexture(gl.TEXTURE0);
4327 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
4328 gl.bindTexture(gl.TEXTURE_2D, texture);
4329 this.updateTexture(gl, image);
4330 };
4331
4332 __proto.updateShaderData = function (_a) {
4333 var _b = _a.imageAspectRatio,
4334 imageAspectRatio = _b === void 0 ? MIN_ASPECT_RATIO_FOR_FULL_PANORAMA : _b;
4335 var lngIdx;
4336 var cylinderMaxRadian;
4337 var halfCylinderY;
4338 var rotated;
4339 var aspectRatio; // Exception case: orientation is rotated.
4340
4341 if (imageAspectRatio < 1) {
4342 /**
4343 * If rotated is true, we assume that image is rotated counter clockwise.
4344 * TODO: If there's other rotation, it is need to implement by each rotation.
4345 */
4346 rotated = true;
4347 aspectRatio = 1 / imageAspectRatio;
4348 } else {
4349 rotated = false;
4350 aspectRatio = imageAspectRatio;
4351 }
4352
4353 if (aspectRatio >= MIN_ASPECT_RATIO_FOR_FULL_PANORAMA) {
4354 var fov = 360 / aspectRatio;
4355 cylinderMaxRadian = 2 * Math.PI; // 360 deg
4356
4357 halfCylinderY = Math.tan(glMatrix.glMatrix.toRadian(fov / 2));
4358 } else {
4359 cylinderMaxRadian = aspectRatio;
4360 halfCylinderY = 0.5; // Range of cylinder is [-0.5, 0.5] to make height to 1.
4361 } // initialize shader data before update
4362
4363
4364 textureCoordData$1.length = 0;
4365 vertexPositionData$1.length = 0;
4366 indexData$1.length = 0;
4367 var CYLIDER_Y = [-halfCylinderY, halfCylinderY];
4368 var startAngleForCenterAlign = Math.PI / 2 + (2 * Math.PI - cylinderMaxRadian) / 2; // Math.PI / 2 start point when cylinderMaxRadian is 2 phi(360)
4369 // console.log("cylinderMaxRadian:", glMatrix.toDegree(cylinderMaxRadian), "CYLIDER_Y", CYLIDER_Y, "start angle", glMatrix.toDegree(startAngleForCenterAlign));
4370
4371 for (var yIdx = 0, yLength = CYLIDER_Y.length; yIdx < yLength
4372 /* bottom & top */
4373 ; yIdx++) {
4374 for (lngIdx = 0; lngIdx <= longitudeBands$1; lngIdx++) {
4375 var angle = startAngleForCenterAlign + lngIdx / longitudeBands$1 * cylinderMaxRadian;
4376 var x = Math.cos(angle);
4377 var y = CYLIDER_Y[yIdx];
4378 var z = Math.sin(angle);
4379 var u = void 0;
4380 var v = void 0;
4381
4382 if (rotated) {
4383 // Rotated 90 degree (counter clock wise)
4384 u = 1 - yIdx; // yLength - yIdx;
4385
4386 v = lngIdx / longitudeBands$1;
4387 } else {
4388 // // Normal case (Not rotated)
4389 u = lngIdx / longitudeBands$1;
4390 v = yIdx;
4391 }
4392
4393 textureCoordData$1.push(u, v);
4394 vertexPositionData$1.push(x, y, z);
4395
4396 if (yIdx === 0 && lngIdx < longitudeBands$1) {
4397 var a = lngIdx;
4398 var b = a + longitudeBands$1 + 1;
4399 indexData$1.push(a, b, a + 1, b, b + 1, a + 1);
4400 }
4401 }
4402 }
4403 };
4404
4405 CylinderRenderer._VERTEX_POSITION_DATA = vertexPositionData$1;
4406 CylinderRenderer._TEXTURE_COORD_DATA = textureCoordData$1;
4407 CylinderRenderer._INDEX_DATA = indexData$1;
4408 return CylinderRenderer;
4409 }(Renderer);
4410
4411 var VR_DISPLAY_PRESENT_CHANGE = "vrdisplaypresentchange";
4412 var DEFAULT_LEFT_BOUNDS = [0, 0, 0.5, 1];
4413 var DEFAULT_RIGHT_BOUNDS = [0.5, 0, 0.5, 1];
4414 var EYES = {
4415 LEFT: "left",
4416 RIGHT: "right"
4417 };
4418
4419 var VRManager =
4420 /*#__PURE__*/
4421 function () {
4422 function VRManager() {
4423 var _this = this;
4424
4425 this.destroy = function () {
4426 var vrDisplay = _this._vrDisplay;
4427
4428 _this.removeEndCallback(_this.destroy);
4429
4430 if (vrDisplay && vrDisplay.isPresenting) {
4431 void vrDisplay.exitPresent();
4432 }
4433
4434 _this._clear();
4435 };
4436
4437 this._frameData = new window.VRFrameData();
4438
4439 this._clear();
4440 }
4441
4442 var __proto = VRManager.prototype;
4443 Object.defineProperty(__proto, "context", {
4444 get: function () {
4445 return this._vrDisplay;
4446 },
4447 enumerable: false,
4448 configurable: true
4449 });
4450
4451 __proto.canRender = function () {
4452 return Boolean(this._vrDisplay);
4453 };
4454
4455 __proto.beforeRender = function (gl) {
4456 // Render to the default backbuffer
4457 gl.bindFramebuffer(gl.FRAMEBUFFER, null);
4458 };
4459
4460 __proto.afterRender = function () {
4461 this._vrDisplay.submitFrame();
4462 };
4463
4464 __proto.getEyeParams = function (gl) {
4465 var display = this._vrDisplay;
4466 var halfWidth = gl.drawingBufferWidth * 0.5;
4467 var height = gl.drawingBufferHeight;
4468 var frameData = this._frameData;
4469 display.getFrameData(frameData);
4470 var leftMVMatrix = frameData.leftViewMatrix;
4471 var rightMVMatrix = frameData.rightViewMatrix;
4472 glMatrix.mat4.rotateY(leftMVMatrix, leftMVMatrix, this._yawOffset);
4473 glMatrix.mat4.rotateY(rightMVMatrix, rightMVMatrix, this._yawOffset);
4474 return [{
4475 viewport: [0, 0, halfWidth, height],
4476 mvMatrix: leftMVMatrix,
4477 pMatrix: frameData.leftProjectionMatrix
4478 }, {
4479 viewport: [halfWidth, 0, halfWidth, height],
4480 mvMatrix: rightMVMatrix,
4481 pMatrix: frameData.rightProjectionMatrix
4482 }];
4483 };
4484
4485 __proto.isPresenting = function () {
4486 return Boolean(this._vrDisplay && this._vrDisplay.isPresenting);
4487 };
4488
4489 __proto.addEndCallback = function (callback) {
4490 window.addEventListener(VR_DISPLAY_PRESENT_CHANGE, callback);
4491 };
4492
4493 __proto.removeEndCallback = function (callback) {
4494 window.removeEventListener(VR_DISPLAY_PRESENT_CHANGE, callback);
4495 };
4496
4497 __proto.requestPresent = function (canvas) {
4498 var _this = this;
4499
4500 return navigator.getVRDisplays().then(function (displays) {
4501 var vrDisplay = displays.length && displays[0];
4502
4503 if (!vrDisplay) {
4504 return Promise$1.reject(new Error("No displays available."));
4505 }
4506
4507 if (!vrDisplay.capabilities.canPresent) {
4508 return Promise$1.reject(new Error("Display lacking capability to present."));
4509 }
4510
4511 return vrDisplay.requestPresent([{
4512 source: canvas
4513 }]).then(function () {
4514 var leftEye = vrDisplay.getEyeParameters(EYES.LEFT);
4515 var rightEye = vrDisplay.getEyeParameters(EYES.RIGHT);
4516 canvas.width = Math.max(leftEye.renderWidth, rightEye.renderWidth) * 2;
4517 canvas.height = Math.max(leftEye.renderHeight, rightEye.renderHeight);
4518
4519 _this._setDisplay(vrDisplay);
4520 });
4521 });
4522 };
4523
4524 __proto.setYawOffset = function (offset) {
4525 this._yawOffset = offset;
4526 };
4527
4528 __proto._setDisplay = function (vrDisplay) {
4529 this._vrDisplay = vrDisplay;
4530 var layers = vrDisplay.getLayers();
4531
4532 if (layers.length) {
4533 var layer = layers[0];
4534 this._leftBounds = layer.leftBounds;
4535 this._rightBounds = layer.rightBounds;
4536 }
4537
4538 this.addEndCallback(this.destroy);
4539 };
4540
4541 __proto._clear = function () {
4542 this._vrDisplay = null;
4543 this._leftBounds = DEFAULT_LEFT_BOUNDS;
4544 this._rightBounds = DEFAULT_RIGHT_BOUNDS;
4545 this._yawOffset = 0;
4546 };
4547
4548 return VRManager;
4549 }();
4550
4551 var XR_REFERENCE_SPACE = "local";
4552
4553 var XRManager =
4554 /*#__PURE__*/
4555 function () {
4556 function XRManager(options) {
4557 var _this = this;
4558
4559 if (options === void 0) {
4560 options = {};
4561 }
4562
4563 this.destroy = function () {
4564 var xrSession = _this._xrSession;
4565
4566 _this.removeEndCallback(_this.destroy);
4567
4568 if (xrSession) {
4569 // Capture to avoid errors
4570 xrSession.end().then(function () {
4571 return void 0;
4572 }, function () {
4573 return void 0;
4574 });
4575 }
4576
4577 _this._clear();
4578 };
4579
4580 this._clear();
4581
4582 this._options = options;
4583 }
4584
4585 var __proto = XRManager.prototype;
4586 Object.defineProperty(__proto, "context", {
4587 get: function () {
4588 return this._xrSession;
4589 },
4590 enumerable: false,
4591 configurable: true
4592 });
4593
4594 __proto.canRender = function (frame) {
4595 var pose = frame.getViewerPose(this._xrRefSpace);
4596 return Boolean(pose);
4597 };
4598
4599 __proto.beforeRender = function (gl, frame) {
4600 var session = frame.session;
4601 var baseLayer = session.renderState.baseLayer;
4602 gl.bindFramebuffer(gl.FRAMEBUFFER, baseLayer.framebuffer);
4603 }; // eslint-disable-next-line @typescript-eslint/no-empty-function
4604
4605
4606 __proto.afterRender = function () {};
4607
4608 __proto.getEyeParams = function (gl, frame) {
4609 var _this = this;
4610
4611 var session = frame.session;
4612 var pose = frame.getViewerPose(this._xrRefSpace);
4613
4614 if (!pose) {
4615 // Can't render
4616 return null;
4617 }
4618
4619 var glLayer = session.renderState.baseLayer;
4620 return pose.views.map(function (view) {
4621 var viewport = glLayer.getViewport(view);
4622 var mvMatrix = view.transform.inverse.matrix;
4623
4624 if (IS_SAFARI_ON_DESKTOP) {
4625 glMatrix.mat4.rotateX(mvMatrix, mvMatrix, glMatrix.glMatrix.toRadian(180));
4626 }
4627
4628 glMatrix.mat4.rotateY(mvMatrix, mvMatrix, _this._yawOffset);
4629 return {
4630 viewport: [viewport.x, viewport.y, viewport.width, viewport.height],
4631 mvMatrix: mvMatrix,
4632 pMatrix: view.projectionMatrix
4633 };
4634 });
4635 };
4636
4637 __proto.isPresenting = function () {
4638 return this._presenting;
4639 };
4640
4641 __proto.addEndCallback = function (callback) {
4642 var _a;
4643
4644 (_a = this._xrSession) === null || _a === void 0 ? void 0 : _a.addEventListener("end", callback);
4645 };
4646
4647 __proto.removeEndCallback = function (callback) {
4648 var _a;
4649
4650 (_a = this._xrSession) === null || _a === void 0 ? void 0 : _a.removeEventListener("end", callback);
4651 };
4652
4653 __proto.requestPresent = function (canvas, gl) {
4654 return __awaiter(this, void 0, void 0, function () {
4655 var options, attributes;
4656
4657 var _this = this;
4658
4659 return __generator(this, function (_a) {
4660 switch (_a.label) {
4661 case 0:
4662 options = merge({
4663 requiredFeatures: [XR_REFERENCE_SPACE]
4664 }, this._options);
4665 attributes = gl.getContextAttributes();
4666 if (!(attributes && attributes.xrCompatible !== true)) return [3
4667 /*break*/
4668 , 2];
4669 return [4
4670 /*yield*/
4671 , gl.makeXRCompatible()];
4672
4673 case 1:
4674 _a.sent();
4675
4676 _a.label = 2;
4677
4678 case 2:
4679 return [2
4680 /*return*/
4681 , navigator.xr.requestSession("immersive-vr", options).then(function (session) {
4682 var xrLayer = new window.XRWebGLLayer(session, gl);
4683 session.updateRenderState({
4684 baseLayer: xrLayer
4685 });
4686 return session.requestReferenceSpace(XR_REFERENCE_SPACE).then(function (refSpace) {
4687 _this._setSession(session, xrLayer, refSpace);
4688 });
4689 })];
4690 }
4691 });
4692 });
4693 };
4694
4695 __proto.setYawOffset = function (offset) {
4696 this._yawOffset = offset;
4697 };
4698
4699 __proto._setSession = function (session, xrLayer, refSpace) {
4700 this._xrSession = session;
4701 this._xrLayer = xrLayer;
4702 this._xrRefSpace = refSpace;
4703 this._presenting = true;
4704 this.addEndCallback(this.destroy);
4705 };
4706
4707 __proto._clear = function () {
4708 this._xrSession = null;
4709 this._xrLayer = null;
4710 this._xrRefSpace = null;
4711 this._presenting = false;
4712 this._yawOffset = 0;
4713 this._options = {};
4714 };
4715
4716 return XRManager;
4717 }();
4718
4719 var WebGLAnimator =
4720 /*#__PURE__*/
4721 function () {
4722 function WebGLAnimator() {
4723 var _this = this;
4724 /**
4725 * There can be more than 1 argument when we use XRSession's raf
4726 */
4727
4728
4729 this._onLoop = function () {
4730 var args = [];
4731
4732 for (var _i = 0; _i < arguments.length; _i++) {
4733 args[_i] = arguments[_i];
4734 }
4735
4736 _this._callback.apply(_this, __spread(args));
4737
4738 _this._rafId = _this._context.requestAnimationFrame(_this._onLoop);
4739 };
4740 /**
4741 * MacOS X Safari Bug Fix
4742 * This code guarantees that rendering should be occurred.
4743 *
4744 * In MacOS X(10.14.2), Safari (12.0.2)
4745 * The requestAnimationFrame(RAF) callback is called just after previous RAF callback without term
4746 * only if requestAnimationFrame is called for next frame while updating frame is delayed (~over 2ms)
4747 * So browser cannot render the frame and may be freezing.
4748 */
4749
4750
4751 this._onLoopNextTick = function () {
4752 var args = [];
4753
4754 for (var _i = 0; _i < arguments.length; _i++) {
4755 args[_i] = arguments[_i];
4756 }
4757
4758 var before = performance.now();
4759
4760 _this._callback.apply(_this, __spread(args));
4761
4762 var diff = performance.now() - before;
4763
4764 if (_this._rafTimer >= 0) {
4765 clearTimeout(_this._rafTimer);
4766 _this._rafTimer = -1;
4767 }
4768 /* Use requestAnimationFrame only if current rendering could be possible over 60fps (1000/60) */
4769
4770
4771 if (diff < 16) {
4772 _this._rafId = _this._context.requestAnimationFrame(_this._onLoop);
4773 } else {
4774 /* Otherwise, Call setTimeout instead of requestAnimationFrame to gaurantee renering should be occurred */
4775 _this._rafTimer = window.setTimeout(_this._onLoop, 0);
4776 }
4777 };
4778
4779 this._callback = null;
4780 this._context = window;
4781 this._rafId = -1;
4782 this._rafTimer = -1;
4783 }
4784
4785 var __proto = WebGLAnimator.prototype;
4786
4787 __proto.setCallback = function (callback) {
4788 this._callback = callback;
4789 };
4790
4791 __proto.setContext = function (context) {
4792 this._context = context;
4793 };
4794
4795 __proto.start = function () {
4796 var context = this._context;
4797 var callback = this._callback; // No context / callback set
4798
4799 if (!context || !callback) return; // Animation already started
4800
4801 if (this._rafId >= 0 || this._rafTimer >= 0) return;
4802
4803 if (IS_SAFARI_ON_DESKTOP) {
4804 this._rafId = context.requestAnimationFrame(this._onLoopNextTick);
4805 } else {
4806 this._rafId = context.requestAnimationFrame(this._onLoop);
4807 }
4808 };
4809
4810 __proto.stop = function () {
4811 if (this._rafId >= 0) {
4812 this._context.cancelAnimationFrame(this._rafId);
4813 }
4814
4815 if (this._rafTimer >= 0) {
4816 clearTimeout(this._rafTimer);
4817 }
4818
4819 this._rafId = -1;
4820 this._rafTimer = -1;
4821 };
4822
4823 return WebGLAnimator;
4824 }();
4825
4826 var ImageType = PROJECTION_TYPE; // eslint-disable-next-line @typescript-eslint/naming-convention
4827
4828 var DEVICE_PIXEL_RATIO = devicePixelRatio || 1; // DEVICE_PIXEL_RATIO 가 2를 초과하는 경우는 리소스 낭비이므로 2로 맞춘다.
4829
4830 if (DEVICE_PIXEL_RATIO > 2) {
4831 DEVICE_PIXEL_RATIO = 2;
4832 } // define custom events name
4833
4834 /**
4835 * TODO: how to manage events/errortype with PanoViewer
4836 *
4837 * I think renderer events should be seperated from viewer events although it has same name.
4838 */
4839
4840
4841 var EVENTS$1 = {
4842 BIND_TEXTURE: "bindTexture",
4843 IMAGE_LOADED: "imageLoaded",
4844 ERROR: "error",
4845 RENDERING_CONTEXT_LOST: "renderingContextLost",
4846 RENDERING_CONTEXT_RESTORE: "renderingContextRestore"
4847 };
4848 var ERROR_TYPE$1 = {
4849 INVALID_DEVICE: 10,
4850 NO_WEBGL: 11,
4851 FAIL_IMAGE_LOAD: 12,
4852 RENDERER_ERROR: 13
4853 };
4854
4855 var PanoImageRenderer =
4856 /*#__PURE__*/
4857 function (_super) {
4858 __extends(PanoImageRenderer, _super);
4859
4860 function PanoImageRenderer(image, width, height, isVideo, container, canvasClass, sphericalConfig, renderingContextAttributes) {
4861 var _this = // Super constructor
4862 _super.call(this) || this;
4863
4864 _this.textureCoordBuffer = null;
4865 _this.vertexBuffer = null;
4866 _this.indexBuffer = null;
4867
4868 _this.exitVR = function () {
4869 var vr = _this._vr;
4870 var gl = _this.context;
4871 var animator = _this._animator;
4872 if (!vr) return;
4873 vr.removeEndCallback(_this.exitVR);
4874 vr.destroy();
4875 _this._vr = null; // Restore canvas & context on iOS
4876
4877 if (IS_IOS) {
4878 _this._restoreStyle();
4879 }
4880
4881 _this.updateViewportDimensions(_this.width, _this.height);
4882
4883 _this._updateViewport();
4884
4885 gl.bindFramebuffer(gl.FRAMEBUFFER, null);
4886
4887 _this._bindBuffers();
4888
4889 _this._shouldForceDraw = true;
4890 animator.stop();
4891 animator.setContext(window);
4892 animator.setCallback(_this._render.bind(_this));
4893 animator.start();
4894 };
4895
4896 _this._renderStereo = function (time, frame) {
4897 var e_1, _a;
4898
4899 var vr = _this._vr;
4900 var gl = _this.context;
4901 var eyeParams = vr.getEyeParams(gl, frame);
4902 if (!eyeParams) return;
4903 vr.beforeRender(gl, frame);
4904
4905 try {
4906 // Render both eyes
4907 for (var _b = __values([0, 1]), _c = _b.next(); !_c.done; _c = _b.next()) {
4908 var eyeIndex = _c.value;
4909 var eyeParam = eyeParams[eyeIndex];
4910 _this.mvMatrix = eyeParam.mvMatrix;
4911 _this.pMatrix = eyeParam.pMatrix;
4912 gl.viewport.apply(gl, __spread(eyeParam.viewport));
4913 gl.uniform1f(_this.shaderProgram.uEye, eyeIndex);
4914
4915 _this._bindBuffers();
4916
4917 _this._draw();
4918 }
4919 } catch (e_1_1) {
4920 e_1 = {
4921 error: e_1_1
4922 };
4923 } finally {
4924 try {
4925 if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
4926 } finally {
4927 if (e_1) throw e_1.error;
4928 }
4929 }
4930
4931 vr.afterRender();
4932 };
4933
4934 _this._onFirstVRFrame = function (time, frame) {
4935 var vr = _this._vr;
4936 var gl = _this.context;
4937 var animator = _this._animator; // If rendering is not ready, wait for next frame
4938
4939 if (!vr.canRender(frame)) return;
4940 var minusZDir = glMatrix.vec3.fromValues(0, 0, -1);
4941 var eyeParam = vr.getEyeParams(gl, frame)[0]; // Extract only rotation
4942
4943 var mvMatrix = glMatrix.mat3.fromMat4(glMatrix.mat3.create(), eyeParam.mvMatrix);
4944 var pMatrix = glMatrix.mat3.fromMat4(glMatrix.mat3.create(), eyeParam.pMatrix);
4945 var mvInv = glMatrix.mat3.invert(glMatrix.mat3.create(), mvMatrix);
4946 var pInv = glMatrix.mat3.invert(glMatrix.mat3.create(), pMatrix);
4947 var viewDir = glMatrix.vec3.transformMat3(glMatrix.vec3.create(), minusZDir, pInv);
4948 glMatrix.vec3.transformMat3(viewDir, viewDir, mvInv);
4949 var yawOffset = util.yawOffsetBetween(viewDir, glMatrix.vec3.fromValues(0, 0, 1));
4950
4951 if (yawOffset === 0) {
4952 // If the yawOffset is exactly 0, then device sensor is not ready
4953 // So read it again until it has any value in it
4954 return;
4955 }
4956
4957 vr.setYawOffset(yawOffset);
4958 animator.setCallback(_this._renderStereo);
4959 };
4960
4961 _this.sphericalConfig = sphericalConfig;
4962 _this.fieldOfView = sphericalConfig.fieldOfView;
4963 _this.width = width;
4964 _this.height = height;
4965 _this._lastQuaternion = null;
4966 _this._lastYaw = null;
4967 _this._lastPitch = null;
4968 _this._lastFieldOfView = null;
4969 _this.pMatrix = glMatrix.mat4.create();
4970 _this.mvMatrix = glMatrix.mat4.create(); // initialzie pMatrix
4971
4972 glMatrix.mat4.perspective(_this.pMatrix, glMatrix.glMatrix.toRadian(_this.fieldOfView), width / height, 0.1, 100);
4973 _this.textureCoordBuffer = null;
4974 _this.vertexBuffer = null;
4975 _this.indexBuffer = null;
4976 _this.canvas = _this._initCanvas(container, canvasClass, width, height);
4977
4978 _this._setDefaultCanvasStyle();
4979
4980 _this._wrapper = null; // canvas wrapper
4981
4982 _this._wrapperOrigStyle = null;
4983 _this._renderingContextAttributes = renderingContextAttributes;
4984 _this._image = null;
4985 _this._imageConfig = null;
4986 _this._imageIsReady = false;
4987 _this._shouldForceDraw = false;
4988 _this._keepUpdate = false; // Flag to specify 'continuous update' on video even when still.
4989
4990 _this._onContentLoad = _this._onContentLoad.bind(_this);
4991 _this._onContentError = _this._onContentError.bind(_this);
4992 _this._animator = new WebGLAnimator(); // VR/XR manager
4993
4994 _this._vr = null;
4995
4996 if (image) {
4997 _this.setImage({
4998 image: image,
4999 imageType: sphericalConfig.imageType,
5000 isVideo: isVideo,
5001 cubemapConfig: sphericalConfig.cubemapConfig
5002 });
5003 }
5004
5005 return _this;
5006 } // FIXME: Please refactor me to have more loose connection to yawpitchcontrol
5007
5008
5009 var __proto = PanoImageRenderer.prototype;
5010
5011 __proto.setYawPitchControl = function (yawPitchControl) {
5012 this._yawPitchControl = yawPitchControl;
5013 };
5014
5015 __proto.getContent = function () {
5016 return this._image;
5017 };
5018
5019 __proto.setImage = function (_a) {
5020 var image = _a.image,
5021 imageType = _a.imageType,
5022 _b = _a.isVideo,
5023 isVideo = _b === void 0 ? false : _b,
5024 cubemapConfig = _a.cubemapConfig;
5025 this._imageIsReady = false;
5026 this._isVideo = isVideo;
5027 this._imageConfig = __assign({
5028 /* RLUDBF is abnormal, we use it on CUBEMAP only */
5029 order: imageType === ImageType.CUBEMAP ? "RLUDBF" : "RLUDFB",
5030 tileConfig: {
5031 flipHorizontal: false,
5032 rotation: 0
5033 },
5034 trim: 0
5035 }, cubemapConfig);
5036
5037 this._setImageType(imageType);
5038
5039 if (this._contentLoader) {
5040 this._contentLoader.destroy();
5041 }
5042
5043 this._contentLoader = new ImReady().on("ready", this._onContentLoad).on("error", this._onContentError);
5044
5045 if (isVideo) {
5046 this._image = toVideoElement(image);
5047
5048 this._contentLoader.check([this._image]);
5049
5050 this._keepUpdate = true;
5051 } else {
5052 this._image = toImageElement(image);
5053
5054 this._contentLoader.check(Array.isArray(this._image) ? this._image : [this._image]);
5055
5056 this._keepUpdate = false;
5057 }
5058 };
5059
5060 __proto.isImageLoaded = function () {
5061 return !!this._image && this._imageIsReady && (!this._isVideo || this._image.readyState >= 2
5062 /* HAVE_CURRENT_DATA */
5063 );
5064 };
5065
5066 __proto.bindTexture = function () {
5067 var _this = this;
5068
5069 return new Promise$1(function (res, rej) {
5070 var contentLoader = _this._contentLoader;
5071
5072 if (!_this._image) {
5073 return rej("Image is not defined");
5074 }
5075
5076 if (!contentLoader) {
5077 return rej("ImageLoader is not initialized");
5078 }
5079
5080 if (contentLoader.isReady()) {
5081 _this._bindTexture();
5082
5083 res();
5084 } else {
5085 contentLoader.check(Array.isArray(_this._image) ? _this._image : [_this._image]);
5086 contentLoader.once("ready", function (e) {
5087 if (e.errorCount > 0) {
5088 rej("Failed to load images.");
5089 } else {
5090 _this._bindTexture();
5091
5092 res();
5093 }
5094 });
5095 }
5096 });
5097 }; // 부모 엘리먼트에 canvas 를 붙임
5098
5099
5100 __proto.attachTo = function (parentElement) {
5101 if (!this._hasExternalCanvas) {
5102 this.detach();
5103 parentElement.appendChild(this.canvas);
5104 }
5105
5106 this._wrapper = parentElement;
5107 };
5108
5109 __proto.forceContextLoss = function () {
5110 if (this.hasRenderingContext()) {
5111 var loseContextExtension = this.context.getExtension("WEBGL_lose_context");
5112
5113 if (loseContextExtension) {
5114 loseContextExtension.loseContext();
5115 }
5116 }
5117 }; // 부모 엘리먼트에서 canvas 를 제거
5118
5119
5120 __proto.detach = function () {
5121 if (!this._hasExternalCanvas && this.canvas.parentElement) {
5122 this.canvas.parentElement.removeChild(this.canvas);
5123 }
5124 };
5125
5126 __proto.destroy = function () {
5127 if (this._contentLoader) {
5128 this._contentLoader.destroy();
5129 }
5130
5131 this._animator.stop();
5132
5133 this.detach();
5134 this.forceContextLoss();
5135 this.off();
5136 this.canvas.removeEventListener("webglcontextlost", this._onWebglcontextlost);
5137 this.canvas.removeEventListener("webglcontextrestored", this._onWebglcontextrestored);
5138 };
5139
5140 __proto.hasRenderingContext = function () {
5141 var ctx = this.context;
5142
5143 if (!ctx || ctx.isContextLost() || !ctx.getProgramParameter(this.shaderProgram, ctx.LINK_STATUS)) {
5144 return false;
5145 }
5146
5147 return true;
5148 };
5149
5150 __proto.updateFieldOfView = function (fieldOfView) {
5151 this.fieldOfView = fieldOfView;
5152
5153 this._updateViewport();
5154 };
5155
5156 __proto.updateViewportDimensions = function (width, height) {
5157 var viewPortChanged = false;
5158 this.width = width;
5159 this.height = height;
5160 var w = width * DEVICE_PIXEL_RATIO;
5161 var h = height * DEVICE_PIXEL_RATIO;
5162
5163 if (w !== this.canvas.width) {
5164 this.canvas.width = w;
5165 viewPortChanged = true;
5166 }
5167
5168 if (h !== this.canvas.height) {
5169 this.canvas.height = h;
5170 viewPortChanged = true;
5171 }
5172
5173 if (!viewPortChanged) {
5174 return;
5175 }
5176
5177 this._updateViewport();
5178
5179 this._shouldForceDraw = true;
5180 };
5181
5182 __proto.keepUpdate = function (doUpdate) {
5183 if (doUpdate && this.isImageLoaded() === false) {
5184 // Force to draw a frame after image is loaded on render()
5185 this._shouldForceDraw = true;
5186 }
5187
5188 this._keepUpdate = doUpdate;
5189 };
5190
5191 __proto.startRender = function () {
5192 this._animator.setCallback(this._render.bind(this));
5193
5194 this._animator.start();
5195 };
5196
5197 __proto.stopRender = function () {
5198 this._animator.stop();
5199 };
5200
5201 __proto.renderWithQuaternion = function (quaternion, fieldOfView) {
5202 if (!this.isImageLoaded()) {
5203 return;
5204 }
5205
5206 if (this._keepUpdate === false && this._lastQuaternion && glMatrix.quat.exactEquals(this._lastQuaternion, quaternion) && this.fieldOfView && this.fieldOfView === fieldOfView && this._shouldForceDraw === false) {
5207 return;
5208 } // updatefieldOfView only if fieldOfView is changed.
5209
5210
5211 if (fieldOfView !== undefined && fieldOfView !== this.fieldOfView) {
5212 this.updateFieldOfView(fieldOfView);
5213 }
5214
5215 this.mvMatrix = glMatrix.mat4.fromQuat(glMatrix.mat4.create(), quaternion);
5216
5217 this._draw();
5218
5219 this._lastQuaternion = glMatrix.quat.clone(quaternion);
5220
5221 if (this._shouldForceDraw) {
5222 this._shouldForceDraw = false;
5223 }
5224 };
5225
5226 __proto.renderWithYawPitch = function (yaw, pitch, fieldOfView) {
5227 if (!this.isImageLoaded()) {
5228 return;
5229 }
5230
5231 if (this._keepUpdate === false && this._lastYaw !== null && this._lastYaw === yaw && this._lastPitch !== null && this._lastPitch === pitch && this.fieldOfView && this.fieldOfView === fieldOfView && this._shouldForceDraw === false) {
5232 return;
5233 } // fieldOfView 가 존재하면서 기존의 값과 다를 경우에만 업데이트 호출
5234
5235
5236 if (fieldOfView !== undefined && fieldOfView !== this.fieldOfView) {
5237 this.updateFieldOfView(fieldOfView);
5238 }
5239
5240 glMatrix.mat4.identity(this.mvMatrix);
5241 glMatrix.mat4.rotateX(this.mvMatrix, this.mvMatrix, -glMatrix.glMatrix.toRadian(pitch));
5242 glMatrix.mat4.rotateY(this.mvMatrix, this.mvMatrix, -glMatrix.glMatrix.toRadian(yaw));
5243
5244 this._draw();
5245
5246 this._lastYaw = yaw;
5247 this._lastPitch = pitch;
5248
5249 if (this._shouldForceDraw) {
5250 this._shouldForceDraw = false;
5251 }
5252 };
5253 /**
5254 * Returns projection renderer by each type
5255 */
5256
5257
5258 __proto.getProjectionRenderer = function () {
5259 return this._renderer;
5260 };
5261 /**
5262 * @return Promise
5263 */
5264
5265
5266 __proto.enterVR = function (options) {
5267 var vr = this._vr;
5268
5269 if (!WEBXR_SUPPORTED && !navigator.getVRDisplays) {
5270 return Promise$1.reject("VR is not available on this browser.");
5271 }
5272
5273 if (vr && vr.isPresenting()) {
5274 return Promise$1.resolve("VR already enabled.");
5275 }
5276
5277 return this._requestPresent(options);
5278 };
5279
5280 __proto._setImageType = function (imageType) {
5281 var _this = this;
5282
5283 if (!imageType || this._imageType === imageType) {
5284 return;
5285 }
5286
5287 this._imageType = imageType;
5288 this._isCubeMap = imageType === ImageType.CUBEMAP;
5289
5290 if (this._renderer) {
5291 this._renderer.off();
5292 }
5293
5294 switch (imageType) {
5295 case ImageType.CUBEMAP:
5296 this._renderer = new CubeRenderer();
5297 break;
5298
5299 case ImageType.CUBESTRIP:
5300 this._renderer = new CubeStripRenderer();
5301 break;
5302
5303 case ImageType.PANORAMA:
5304 this._renderer = new CylinderRenderer();
5305 break;
5306
5307 case ImageType.STEREOSCOPIC_EQUI:
5308 this._renderer = new SphereRenderer(this.sphericalConfig.stereoFormat);
5309 break;
5310
5311 default:
5312 this._renderer = new SphereRenderer(STEREO_FORMAT.NONE);
5313 break;
5314 }
5315
5316 this._renderer.on(Renderer.EVENTS.ERROR, function (e) {
5317 _this.trigger(new Component.ComponentEvent(EVENTS$1.ERROR, {
5318 type: ERROR_TYPE$1.RENDERER_ERROR,
5319 message: e.message
5320 }));
5321 });
5322
5323 this._initWebGL();
5324 };
5325
5326 __proto._initCanvas = function (container, canvasClass, width, height) {
5327 var canvasInContainer = container.querySelector("." + canvasClass);
5328
5329 var canvas = canvasInContainer || this._createCanvas(canvasClass);
5330
5331 this._hasExternalCanvas = !!canvasInContainer;
5332 canvas.width = width;
5333 canvas.height = height;
5334 this._onWebglcontextlost = this._onWebglcontextlost.bind(this);
5335 this._onWebglcontextrestored = this._onWebglcontextrestored.bind(this);
5336 canvas.addEventListener("webglcontextlost", this._onWebglcontextlost);
5337 canvas.addEventListener("webglcontextrestored", this._onWebglcontextrestored);
5338 return canvas;
5339 };
5340
5341 __proto._createCanvas = function (className) {
5342 var canvas = document.createElement("canvas");
5343 canvas.className = className;
5344 return canvas;
5345 };
5346
5347 __proto._setDefaultCanvasStyle = function () {
5348 var canvas = this.canvas;
5349 canvas.style.bottom = "0";
5350 canvas.style.left = "0";
5351 canvas.style.right = "0";
5352 canvas.style.top = "0";
5353 canvas.style.margin = "auto";
5354 canvas.style.maxHeight = "100%";
5355 canvas.style.maxWidth = "100%";
5356 canvas.style.outline = "none";
5357 canvas.style.position = "absolute";
5358 };
5359
5360 __proto._onContentError = function () {
5361 this._imageIsReady = false;
5362 this._image = null;
5363 this.trigger(new Component.ComponentEvent(EVENTS$1.ERROR, {
5364 type: ERROR_TYPE$1.FAIL_IMAGE_LOAD,
5365 message: "failed to load image"
5366 }));
5367 return false;
5368 };
5369
5370 __proto._triggerContentLoad = function () {
5371 this.trigger(new Component.ComponentEvent(EVENTS$1.IMAGE_LOADED, {
5372 content: this._image,
5373 isVideo: this._isVideo,
5374 projectionType: this._imageType
5375 }));
5376 };
5377
5378 __proto._onContentLoad = function (e) {
5379 if (e.errorCount > 0) return;
5380 this._imageIsReady = true;
5381
5382 this._triggerContentLoad();
5383 };
5384
5385 __proto._initShaderProgram = function () {
5386 var gl = this.context;
5387
5388 if (this.shaderProgram) {
5389 gl.deleteProgram(this.shaderProgram);
5390 this.shaderProgram = null;
5391 }
5392
5393 var renderer = this._renderer;
5394 var vsSource = renderer.getVertexShaderSource();
5395 var fsSource = renderer.getFragmentShaderSource();
5396 var vertexShader = WebGLUtils.createShader(gl, gl.VERTEX_SHADER, vsSource);
5397 var fragmentShader = WebGLUtils.createShader(gl, gl.FRAGMENT_SHADER, fsSource);
5398 var shaderProgram = WebGLUtils.createProgram(gl, vertexShader, fragmentShader);
5399
5400 if (!shaderProgram) {
5401 throw new Error("Failed to initialize shaders: " + WebGLUtils.getErrorNameFromWebGLErrorCode(gl.getError()));
5402 }
5403
5404 gl.useProgram(shaderProgram);
5405 shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
5406 shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
5407 shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
5408 shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
5409 shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
5410 shaderProgram.uEye = gl.getUniformLocation(shaderProgram, "uEye");
5411 gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
5412 gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute); // clear buffer
5413
5414 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); // Use TEXTURE0
5415
5416 gl.uniform1i(shaderProgram.samplerUniform, 0);
5417 this.shaderProgram = shaderProgram;
5418 };
5419
5420 __proto._onWebglcontextlost = function (e) {
5421 e.preventDefault();
5422 this.trigger(new Component.ComponentEvent(EVENTS$1.RENDERING_CONTEXT_LOST));
5423 };
5424
5425 __proto._onWebglcontextrestored = function () {
5426 this._initWebGL();
5427
5428 this.trigger(new Component.ComponentEvent(EVENTS$1.RENDERING_CONTEXT_RESTORE));
5429 };
5430
5431 __proto._updateViewport = function () {
5432 glMatrix.mat4.perspective(this.pMatrix, glMatrix.glMatrix.toRadian(this.fieldOfView), this.canvas.width / this.canvas.height, 0.1, 100);
5433 this.context.viewport(0, 0, this.context.drawingBufferWidth, this.context.drawingBufferHeight);
5434 };
5435
5436 __proto._initWebGL = function () {
5437 var gl; // TODO: Following code does need to be executed only if width/height, cubicStrip property is changed.
5438
5439 try {
5440 this._initRenderingContext();
5441
5442 gl = this.context;
5443 this.updateViewportDimensions(this.width, this.height);
5444
5445 this._initShaderProgram();
5446 } catch (e) {
5447 this.trigger(new Component.ComponentEvent(EVENTS$1.ERROR, {
5448 type: ERROR_TYPE$1.NO_WEBGL,
5449 message: "no webgl support"
5450 }));
5451 this.destroy();
5452 console.error(e); // eslint-disable-line no-console
5453
5454 return;
5455 } // 캔버스를 투명으로 채운다.
5456
5457
5458 gl.clearColor(0, 0, 0, 0);
5459 var textureTarget = this._isCubeMap ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D;
5460
5461 if (this.texture) {
5462 gl.deleteTexture(this.texture);
5463 }
5464
5465 this.texture = WebGLUtils.createTexture(gl, textureTarget);
5466
5467 if (this._imageType === ImageType.CUBESTRIP) {
5468 // TODO: Apply following options on other projection type.
5469 gl.enable(gl.CULL_FACE); // gl.enable(gl.DEPTH_TEST);
5470 }
5471 };
5472
5473 __proto._initRenderingContext = function () {
5474 if (this.hasRenderingContext()) {
5475 return;
5476 }
5477
5478 if (!window.WebGLRenderingContext) {
5479 throw new Error("WebGLRenderingContext not available.");
5480 }
5481
5482 this.context = WebGLUtils.getWebglContext(this.canvas, this._renderingContextAttributes);
5483
5484 if (!this.context) {
5485 throw new Error("Failed to acquire 3D rendering context");
5486 }
5487 };
5488
5489 __proto._initBuffers = function () {
5490 var image = this._image;
5491
5492 var vertexPositionData = this._renderer.getVertexPositionData();
5493
5494 var indexData = this._renderer.getIndexData();
5495
5496 var textureCoordData = this._renderer.getTextureCoordData({
5497 image: image,
5498 imageConfig: this._imageConfig
5499 });
5500
5501 var gl = this.context;
5502 this.vertexBuffer = WebGLUtils.initBuffer(gl, gl.ARRAY_BUFFER, new Float32Array(vertexPositionData), 3, this.shaderProgram.vertexPositionAttribute);
5503 this.indexBuffer = WebGLUtils.initBuffer(gl, gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexData), 1);
5504 this.textureCoordBuffer = WebGLUtils.initBuffer(gl, gl.ARRAY_BUFFER, new Float32Array(textureCoordData), this._isCubeMap ? 3 : 2, this.shaderProgram.textureCoordAttribute);
5505
5506 this._bindBuffers();
5507 };
5508
5509 __proto._bindTexture = function () {
5510 // Detect if it is EAC Format while CUBESTRIP mode.
5511 // We assume it is EAC if image is not 3/2 ratio.
5512 if (this._imageType === ImageType.CUBESTRIP) {
5513 var _a = this._renderer.getDimension(this._image),
5514 width = _a.width,
5515 height = _a.height;
5516
5517 var isEAC = width && height && width / height !== 1.5 ? 1 : 0;
5518 this.context.uniform1f(this.context.getUniformLocation(this.shaderProgram, "uIsEAC"), isEAC);
5519 } else if (this._imageType === ImageType.PANORAMA) {
5520 var _b = this._renderer.getDimension(this._image),
5521 width = _b.width,
5522 height = _b.height;
5523
5524 var imageAspectRatio = width && height && width / height;
5525
5526 this._renderer.updateShaderData({
5527 imageAspectRatio: imageAspectRatio
5528 });
5529 } // initialize shader buffers after image is loaded.(by updateShaderData)
5530 // because buffer may be differ by image size.(eg. CylinderRenderer)
5531
5532
5533 this._initBuffers();
5534
5535 this._renderer.bindTexture(this.context, this.texture, this._image, this._imageConfig);
5536
5537 this._shouldForceDraw = true;
5538 this.trigger(new Component.ComponentEvent(EVENTS$1.BIND_TEXTURE));
5539 };
5540
5541 __proto._updateTexture = function () {
5542 this._renderer.updateTexture(this.context, this._image, this._imageConfig);
5543 };
5544
5545 __proto._render = function () {
5546 var yawPitchControl = this._yawPitchControl;
5547 var fov = yawPitchControl.getFov();
5548
5549 if (yawPitchControl.shouldRenderWithQuaternion()) {
5550 var quaternion = yawPitchControl.getQuaternion();
5551 this.renderWithQuaternion(quaternion, fov);
5552 } else {
5553 var yawPitch = yawPitchControl.getYawPitch();
5554 this.renderWithYawPitch(yawPitch.yaw, yawPitch.pitch, fov);
5555 }
5556 };
5557
5558 __proto._bindBuffers = function () {
5559 var gl = this.context;
5560 var program = this.shaderProgram;
5561 var vertexBuffer = this.vertexBuffer;
5562 var textureCoordBuffer = this.textureCoordBuffer;
5563 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
5564 gl.enableVertexAttribArray(program.vertexPositionAttribute);
5565 gl.vertexAttribPointer(program.vertexPositionAttribute, vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
5566 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
5567 gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);
5568 gl.enableVertexAttribArray(program.textureCoordAttribute);
5569 gl.vertexAttribPointer(program.textureCoordAttribute, textureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
5570 };
5571
5572 __proto._draw = function () {
5573 if (this._isVideo && this._keepUpdate) {
5574 this._updateTexture();
5575 }
5576
5577 this._renderer.render({
5578 gl: this.context,
5579 shaderProgram: this.shaderProgram,
5580 indexBuffer: this.indexBuffer,
5581 mvMatrix: this.mvMatrix,
5582 pMatrix: this.pMatrix
5583 });
5584 };
5585
5586 __proto._requestPresent = function (options) {
5587 var _this = this;
5588
5589 var gl = this.context;
5590 var canvas = this.canvas;
5591 var animator = this._animator;
5592 this._vr = WEBXR_SUPPORTED ? new XRManager(options) : new VRManager();
5593 var vr = this._vr;
5594 animator.stop();
5595 return new Promise$1(function (resolve, reject) {
5596 vr.requestPresent(canvas, gl).then(function () {
5597 vr.addEndCallback(_this.exitVR);
5598 animator.setContext(vr.context);
5599 animator.setCallback(_this._onFirstVRFrame);
5600
5601 if (IS_IOS) {
5602 _this._setWrapperFullscreen();
5603 }
5604
5605 _this._shouldForceDraw = true;
5606 animator.start();
5607 resolve("success");
5608 }).catch(function (e) {
5609 vr.destroy();
5610 _this._vr = null;
5611 animator.start();
5612 reject(e);
5613 });
5614 });
5615 };
5616
5617 __proto._setWrapperFullscreen = function () {
5618 var wrapper = this._wrapper;
5619 if (!wrapper) return;
5620 this._wrapperOrigStyle = wrapper.getAttribute("style");
5621 var wrapperStyle = wrapper.style;
5622 wrapperStyle.width = "100vw";
5623 wrapperStyle.height = "100vh";
5624 wrapperStyle.position = "fixed";
5625 wrapperStyle.left = "0";
5626 wrapperStyle.top = "0";
5627 wrapperStyle.zIndex = "9999";
5628 };
5629
5630 __proto._restoreStyle = function () {
5631 var wrapper = this._wrapper;
5632 var canvas = this.canvas;
5633 if (!wrapper) return;
5634
5635 if (this._wrapperOrigStyle) {
5636 wrapper.setAttribute("style", this._wrapperOrigStyle);
5637 } else {
5638 wrapper.removeAttribute("style");
5639 }
5640
5641 this._wrapperOrigStyle = null; // Restore canvas style
5642
5643 canvas.removeAttribute("style");
5644
5645 this._setDefaultCanvasStyle();
5646 };
5647
5648 PanoImageRenderer.EVENTS = EVENTS$1;
5649 PanoImageRenderer.ERROR_TYPE = ERROR_TYPE$1;
5650 return PanoImageRenderer;
5651 }(Component);
5652
5653 /**
5654 * @memberof eg.view360
5655 * @extends eg.Component
5656 * PanoViewer
5657 */
5658
5659 var PanoViewer =
5660 /*#__PURE__*/
5661 function (_super) {
5662 __extends(PanoViewer, _super);
5663 /**
5664 * @classdesc 360 media viewer
5665 * @ko 360 미디어 뷰어
5666 *
5667 * @param container The container element for the renderer. <ko>렌더러의 컨테이너 엘리먼트</ko>
5668 * @param options
5669 *
5670 * @param {String|HTMLImageElement} options.image Input image url or element (Use only image property or video property)<ko>입력 이미지 URL 혹은 엘리먼트(image 와 video 둘 중 하나만 설정)</ko>
5671 * @param {String|HTMLVideoElement} options.video Input video url or element(Use only image property or video property)<ko>입력 비디오 URL 혹은 엘리먼트(image 와 video 둘 중 하나만 설정)</ko>
5672 * @param {String} [options.projectionType=equirectangular] The type of projection: equirectangular, cubemap <br/>{@link eg.view360.PanoViewer.PROJECTION_TYPE}<ko>Projection 유형 : equirectangular, cubemap <br/>{@link eg.view360.PanoViewer.PROJECTION_TYPE}</ko>
5673 * @param {Object} options.cubemapConfig Config cubemap projection layout. It is applied when projectionType is {@link eg.view360.PanoViewer.PROJECTION_TYPE.CUBEMAP} or {@link eg.view360.PanoViewer.PROJECTION_TYPE.CUBESTRIP}<ko>cubemap projection type 의 레이아웃을 설정한다. 이 설정은 ProjectionType이 {@link eg.view360.PanoViewer.PROJECTION_TYPE.CUBEMAP} 혹은 {@link eg.view360.PanoViewer.PROJECTION_TYPE.CUBESTRIP} 인 경우에만 적용된다.</ko>
5674 * @param {Object} [options.cubemapConfig.order = "RLUDBF"(ProjectionType === CUBEMAP) | "RLUDFB" (ProjectionType === CUBESTRIP)] Order of cubemap faces <ko>Cubemap 형태의 이미지가 배치된 순서</ko>
5675 * @param {Object} [options.cubemapConfig.tileConfig = { flipHorizontal:false, rotation: 0 }] Setting about rotation angle(degree) and whether to flip horizontal for each cubemap faces, if you put this object as a array, you can set each faces with different setting. For example, [{flipHorizontal:false, rotation:90}, {flipHorizontal: true, rotation: 180}, ...]<ko>각 Cubemap 면에 대한 회전 각도/좌우반전 여부 설정, 객체를 배열 형태로 지정하여 각 면에 대한 설정을 다르게 지정할 수도 있다. 예를 들어 [{flipHorizontal:false, rotation:90}, {flipHorizontal: true, rotation: 180}, ...]과 같이 지정할 수 있다.</ko>
5676 * @param {Number} [options.cubemapConfig.trim=0] A px distance to discard from each tile side. You can use this value to avoid graphical glitch at where tiles are connected. This option is available when there's only one texture.<ko>각 타일의 끝으로부터 폐기할 px 거리. 이 옵션을 사용하여 타일의 접합부에서 나타나는 그래픽 결함을 완화할 수 있습니다. 이 옵션은 한 개의 텍스쳐만 사용할 때 적용 가능합니다.</ko>
5677 * @param {String} [options.stereoFormat="3dv"] Contents format of the stereoscopic equirectangular projection.<br/>See {@link eg.view360.PanoViewer.STEREO_FORMAT}.<ko>Stereoscopic equirectangular projection type의 콘텐츠 포맷을 설정한다.<br/>{@link eg.view360.PanoViewer.STEREO_FORMAT} 참조.</ko>
5678 * @param {Number} [options.width=width of container] the viewer's width. (in px) <ko>뷰어의 너비 (px 단위)</ko>
5679 * @param {Number} [options.height=height of container] the viewer's height.(in px) <ko>뷰어의 높이 (px 단위)</ko>
5680 * @param {Number} [options.yaw=0] Initial Yaw of camera (in degree) <ko>카메라의 초기 Yaw (degree 단위)</ko>
5681 * @param {Number} [options.pitch=0] Initial Pitch of camera (in degree) <ko>카메라의 초기 Pitch (degree 단위)</ko>
5682 * @param {Number} [options.fov=65] Initial vertical field of view of camera (in degree) <ko>카메라의 초기 수직 field of view (degree 단위)</ko>
5683 * @param {Boolean} [options.showPolePoint=false] If false, the pole is not displayed inside the viewport <ko>false 인 경우, 극점은 뷰포트 내부에 표시되지 않습니다</ko>
5684 * @param {Boolean} [options.useZoom=true] When true, enables zoom with the wheel and Pinch gesture <ko>true 일 때 휠 및 집기 제스춰로 확대 / 축소 할 수 있습니다.</ko>
5685 * @param {Boolean} [options.useKeyboard=true] When true, enables the keyboard move key control: awsd, arrow keys <ko>true 이면 키보드 이동 키 컨트롤을 활성화합니다: awsd, 화살표 키</ko>
5686 * @param {String} [options.gyroMode=yawPitch] Enables control through device motion. ("none", "yawPitch", "VR") <br/>{@link eg.view360.PanoViewer.GYRO_MODE} <ko>디바이스 움직임을 통한 컨트롤을 활성화 합니다. ("none", "yawPitch", "VR") <br/>{@link eg.view360.PanoViewer.GYRO_MODE} </ko>
5687 * @param {Array} [options.yawRange=[-180, 180]] Range of controllable Yaw values <ko>제어 가능한 Yaw 값의 범위</ko>
5688 * @param {Array} [options.pitchRange=[-90, 90]] Range of controllable Pitch values <ko>제어 가능한 Pitch 값의 범위</ko>
5689 * @param {Array} [options.fovRange=[30, 110]] Range of controllable vertical field of view values <ko>제어 가능한 수직 field of view 값의 범위</ko>
5690 * @param {Number} [options.touchDirection= {@link eg.view360.PanoViewer.TOUCH_DIRECTION.ALL}(6)] Direction of touch that can be controlled by user <br/>{@link eg.view360.PanoViewer.TOUCH_DIRECTION}<ko>사용자가 터치로 조작 가능한 방향 <br/>{@link eg.view360.PanoViewer.TOUCH_DIRECTION}</ko>
5691 * @param {String} [options.canvasClass="view360-canvas"] A class name for the canvas element inside the container element. PanoViewer will use the canvas that has this class instead of creating one if it exists<ko>콘테이너 엘리먼트 내부의 캔버스 엘리먼트의 클래스 이름. PanoViewer는 해당 클래스를 갖는 캔버스 엘리먼트가 콘테이너 엘리먼트 내부에 존재할 경우, 새로 생성하는 대신 그 엘리먼트를 사용할 것입니다</ko>
5692 *
5693 * @example
5694 * ```
5695 * // PanoViewer Creation
5696 * // create PanoViewer with option
5697 * var PanoViewer = eg.view360.PanoViewer;
5698 * // Area where the image will be displayed(HTMLElement)
5699 * var container = document.getElementById("myPanoViewer");
5700 *
5701 * var panoViewer = new PanoViewer(container, {
5702 * // If projectionType is not specified, the default is "equirectangular".
5703 * // Specifies an image of the "equirectangular" type.
5704 * image: "/path/to/image/image.jpg"
5705 * });
5706 * ```
5707 *
5708 * @example
5709 * ```
5710 * // Cubemap Config Setting Example
5711 * // For support Youtube EAC projection, You should set cubemapConfig as follows.
5712 * cubemapConfig: {
5713 * order: "LFRDBU",
5714 * tileConfig: [{rotation: 0}, {rotation: 0}, {rotation: 0}, {rotation: 0}, {rotation: -90}, {rotation: 180}]
5715 * }
5716 * ```
5717 */
5718
5719
5720 function PanoViewer(container, options) {
5721 if (options === void 0) {
5722 options = {};
5723 }
5724
5725 var _this = _super.call(this) || this; // Raises the error event if webgl is not supported.
5726
5727
5728 if (!WebGLUtils.isWebGLAvailable()) {
5729 setTimeout(function () {
5730 _this.trigger(new Component.ComponentEvent(PANOVIEWER_EVENTS.ERROR, {
5731 type: ERROR_TYPE.NO_WEBGL,
5732 message: "no webgl support"
5733 }));
5734 }, 0);
5735 return _this;
5736 }
5737
5738 if (!WebGLUtils.isStableWebGL()) {
5739 setTimeout(function () {
5740 _this.trigger(new Component.ComponentEvent(PANOVIEWER_EVENTS.ERROR, {
5741 type: ERROR_TYPE.INVALID_DEVICE,
5742 message: "blacklisted browser"
5743 }));
5744 }, 0);
5745 return _this;
5746 }
5747
5748 if (!!options.image && !!options.video) {
5749 setTimeout(function () {
5750 _this.trigger(new Component.ComponentEvent(PANOVIEWER_EVENTS.ERROR, {
5751 type: ERROR_TYPE.INVALID_RESOURCE,
5752 message: "Specifying multi resouces(both image and video) is not valid."
5753 }));
5754 }, 0);
5755 return _this;
5756 } // Check XR support at not when imported, but when created.
5757 // This is intended to make polyfills easier to use.
5758
5759
5760 checkXRSupport();
5761 _this._container = container;
5762 _this._image = options.image || options.video;
5763 _this._isVideo = !!options.video;
5764 _this._projectionType = options.projectionType || PROJECTION_TYPE.EQUIRECTANGULAR;
5765 _this._cubemapConfig = __assign({
5766 /* RLUDBF is abnormal, we use it on CUBEMAP only for backward compatibility*/
5767 order: _this._projectionType === PROJECTION_TYPE.CUBEMAP ? "RLUDBF" : "RLUDFB",
5768 tileConfig: {
5769 flipHorizontal: false,
5770 rotation: 0
5771 },
5772 trim: 0
5773 }, options.cubemapConfig);
5774 _this._stereoFormat = options.stereoFormat || STEREO_FORMAT.TOP_BOTTOM; // If the width and height are not provided, will use the size of the container.
5775
5776 _this._width = options.width || parseInt(window.getComputedStyle(container).width, 10);
5777 _this._height = options.height || parseInt(window.getComputedStyle(container).height, 10);
5778 /**
5779 * Cache the direction for the performance in renderLoop
5780 *
5781 * This value should be updated by "change" event of YawPitchControl.
5782 */
5783
5784 _this._yaw = options.yaw || 0;
5785 _this._pitch = options.pitch || 0;
5786 _this._fov = options.fov || 65;
5787 _this._gyroMode = options.gyroMode || GYRO_MODE.YAWPITCH;
5788 _this._quaternion = null;
5789 _this._aspectRatio = _this._height !== 0 ? _this._width / _this._height : 1;
5790 _this._canvasClass = options.canvasClass || DEFAULT_CANVAS_CLASS;
5791 var fovRange = options.fovRange || [30, 110];
5792 var touchDirection = PanoViewer._isValidTouchDirection(options.touchDirection) ? options.touchDirection : YawPitchControl.TOUCH_DIRECTION_ALL;
5793
5794 var yawPitchConfig = __assign(__assign({}, options), {
5795 element: container,
5796 yaw: _this._yaw,
5797 pitch: _this._pitch,
5798 fov: _this._fov,
5799 gyroMode: _this._gyroMode,
5800 fovRange: fovRange,
5801 aspectRatio: _this._aspectRatio,
5802 touchDirection: touchDirection
5803 });
5804
5805 _this._isReady = false;
5806
5807 _this._initYawPitchControl(yawPitchConfig);
5808
5809 _this._initRenderer(_this._yaw, _this._pitch, _this._fov, _this._projectionType, _this._cubemapConfig);
5810
5811 return _this;
5812 }
5813 /**
5814 * Check whether the current environment can execute PanoViewer
5815 * @ko 현재 브라우저 환경에서 PanoViewer 실행이 가능한지 여부를 반환합니다.
5816 * @return PanoViewer executable <ko>PanoViewer 실행가능 여부</ko>
5817 */
5818
5819
5820 var __proto = PanoViewer.prototype;
5821
5822 PanoViewer.isSupported = function () {
5823 return WebGLUtils.isWebGLAvailable() && WebGLUtils.isStableWebGL();
5824 };
5825 /**
5826 * Check whether the current environment supports the WebGL
5827 * @ko 현재 브라우저 환경이 WebGL 을 지원하는지 여부를 확인합니다.
5828 * @return WebGL support <ko>WebGL 지원여부</ko>
5829 */
5830
5831
5832 PanoViewer.isWebGLAvailable = function () {
5833 return WebGLUtils.isWebGLAvailable();
5834 };
5835 /**
5836 * Check whether the current environment supports the gyro sensor.
5837 * @ko 현재 브라우저 환경이 자이로 센서를 지원하는지 여부를 확인합니다.
5838 * @param callback Function to take the gyro sensor availability as argument <ko>자이로 센서를 지원하는지 여부를 인자로 받는 함수</ko>
5839 */
5840
5841
5842 PanoViewer.isGyroSensorAvailable = function (callback) {
5843 if (!DeviceMotionEvent && callback) {
5844 callback(false);
5845 return;
5846 }
5847
5848 var onDeviceMotionChange;
5849
5850 var checkGyro = function () {
5851 return new Promise$1(function (res) {
5852 onDeviceMotionChange = function (deviceMotion) {
5853 var isGyroSensorAvailable = !(deviceMotion.rotationRate.alpha == null);
5854 res(isGyroSensorAvailable);
5855 };
5856
5857 window.addEventListener("devicemotion", onDeviceMotionChange);
5858 });
5859 };
5860
5861 var timeout = function () {
5862 return new Promise$1(function (res) {
5863 setTimeout(function () {
5864 return res(false);
5865 }, 1000);
5866 });
5867 };
5868
5869 Promise$1.race([checkGyro(), timeout()]).then(function (isGyroSensorAvailable) {
5870 window.removeEventListener("devicemotion", onDeviceMotionChange);
5871
5872 if (callback) {
5873 callback(isGyroSensorAvailable);
5874 }
5875
5876 PanoViewer.isGyroSensorAvailable = function (fb) {
5877 if (fb) {
5878 fb(isGyroSensorAvailable);
5879 }
5880
5881 return isGyroSensorAvailable;
5882 };
5883 });
5884 };
5885
5886 PanoViewer._isValidTouchDirection = function (direction) {
5887 return direction === PanoViewer.TOUCH_DIRECTION.NONE || direction === PanoViewer.TOUCH_DIRECTION.YAW || direction === PanoViewer.TOUCH_DIRECTION.PITCH || direction === PanoViewer.TOUCH_DIRECTION.ALL;
5888 };
5889 /**
5890 * Get the video element that the viewer is currently playing. You can use this for playback.
5891 * @ko 뷰어가 현재 사용 중인 비디오 요소를 얻습니다. 이 요소를 이용해 비디오의 컨트롤을 할 수 있습니다.
5892 * @return HTMLVideoElement<ko>HTMLVideoElement</ko>
5893 * @example
5894 * ```
5895 * var videoTag = panoViewer.getVideo();
5896 * videoTag.play(); // play the video!
5897 * ```
5898 */
5899
5900
5901 __proto.getVideo = function () {
5902 if (!this._isVideo) {
5903 return null;
5904 }
5905
5906 return this._photoSphereRenderer.getContent();
5907 };
5908 /**
5909 * Set the video information to be used by the viewer.
5910 * @ko 뷰어가 사용할 이미지 정보를 설정합니다.
5911 * @param {string|HTMLVideoElement|object} video Input video url or element or config object<ko>입력 비디오 URL 혹은 엘리먼트 혹은 설정객체를 활용(image 와 video 둘 중 하나만 설정)</ko>
5912 * @param {object} param
5913 * @param {string} [param.projectionType={@link eg.view360.PanoViewer.PROJECTION_TYPE.EQUIRECTANGULAR}("equirectangular")] Projection Type<ko>프로젝션 타입</ko>
5914 * @param {object} param.cubemapConfig config cubemap projection layout. <ko>cubemap projection type 의 레이아웃 설정</ko>
5915 * @param {string} [param.stereoFormat="3dv"] Contents format of the stereoscopic equirectangular projection. See {@link eg.view360.PanoViewer.STEREO_FORMAT}.<ko>Stereoscopic equirectangular projection type의 콘텐츠 포맷을 설정한다. {@link eg.view360.PanoViewer.STEREO_FORMAT} 참조.</ko>
5916 *
5917 * @return PanoViewer instance<ko>PanoViewer 인스턴스</ko>
5918 * @example
5919 * ```
5920 * panoViewer.setVideo("/path/to/video/video.mp4", {
5921 * projectionType: eg.view360.PanoViewer.PROJECTION_TYPE.EQUIRECTANGULAR
5922 * });
5923 * ```
5924 */
5925
5926
5927 __proto.setVideo = function (video, param) {
5928 if (param === void 0) {
5929 param = {};
5930 }
5931
5932 if (video) {
5933 this.setImage(video, {
5934 projectionType: param.projectionType,
5935 isVideo: true,
5936 cubemapConfig: param.cubemapConfig,
5937 stereoFormat: param.stereoFormat
5938 });
5939 }
5940
5941 return this;
5942 };
5943 /**
5944 * Get the image information that the viewer is currently using.
5945 * @ko 뷰어가 현재 사용하고있는 이미지 정보를 얻습니다.
5946 * @return Image Object<ko>이미지 객체</ko>
5947 * @example
5948 * var imageObj = panoViewer.getImage();
5949 */
5950
5951
5952 __proto.getImage = function () {
5953 if (this._isVideo) {
5954 return null;
5955 }
5956
5957 return this._photoSphereRenderer.getContent();
5958 };
5959 /**
5960 * Set the image information to be used by the viewer.
5961 * @ko 뷰어가 사용할 이미지 정보를 설정합니다.
5962 * @param {string|HTMLElement|object} image Input image url or element or config object<ko>입력 이미지 URL 혹은 엘리먼트 혹은 설정객체를 활용(image 와 video 둘 중 하나만 설정한다.)</ko>
5963 * @param {object} param Additional information<ko>이미지 추가 정보</ko>
5964 * @param {string} [param.projectionType="equirectangular"] Projection Type<ko>프로젝션 타입</ko>
5965 * @param {object} param.cubemapConfig config cubemap projection layout. <ko>cubemap projection type 레이아웃</ko>
5966 * @param {string} [param.stereoFormat="3dv"] Contents format of the stereoscopic equirectangular projection. See {@link eg.view360.PanoViewer.STEREO_FORMAT}.<ko>Stereoscopic equirectangular projection type의 콘텐츠 포맷을 설정한다. {@link eg.view360.PanoViewer.STEREO_FORMAT} 참조.</ko>
5967 * @param {boolean} [param.isVideo=false] Whether the given `imaage` is video or not.<ko>이미지가 비디오인지 여부</ko>
5968 *
5969 * @return PanoViewer instance<ko>PanoViewer 인스턴스</ko>
5970 * @example
5971 * ```
5972 * panoViewer.setImage("/path/to/image/image.png", {
5973 * projectionType: eg.view360.PanoViewer.PROJECTION_TYPE.CUBEMAP
5974 * });
5975 * ```
5976 */
5977
5978
5979 __proto.setImage = function (image, param) {
5980 if (param === void 0) {
5981 param = {};
5982 }
5983
5984 var cubemapConfig = __assign({
5985 order: "RLUDBF",
5986 tileConfig: {
5987 flipHorizontal: false,
5988 rotation: 0
5989 },
5990 trim: 0
5991 }, param.cubemapConfig);
5992
5993 var stereoFormat = param.stereoFormat || STEREO_FORMAT.TOP_BOTTOM;
5994 var isVideo = !!param.isVideo;
5995
5996 if (this._image && this._isVideo !== isVideo) {
5997 /* eslint-disable no-console */
5998 console.warn("PanoViewer is not currently supporting content type changes. (Image <--> Video)");
5999 /* eslint-enable no-console */
6000
6001 return this;
6002 }
6003
6004 if (image) {
6005 this._deactivate();
6006
6007 this._image = image;
6008 this._isVideo = isVideo;
6009 this._projectionType = param.projectionType || PROJECTION_TYPE.EQUIRECTANGULAR;
6010 this._cubemapConfig = cubemapConfig;
6011 this._stereoFormat = stereoFormat;
6012
6013 this._initRenderer(this._yaw, this._pitch, this._fov, this._projectionType, this._cubemapConfig);
6014 }
6015
6016 return this;
6017 };
6018 /**
6019 * Set whether the renderer always updates the texture and renders.
6020 * @ko 렌더러가 항상 텍스쳐를 갱신하고 화면을 렌더링 할지 여부를 설정할 수 있습니다.
6021 * @param doUpdate When true viewer will always update texture and render, when false viewer will not update texture and render only camera config is changed.<ko>true면 항상 텍스쳐를 갱신하고 화면을 그리는 반면, false면 텍스쳐 갱신은 하지 않으며, 카메라 요소에 변화가 있을 때에만 화면을 그립니다.</ko>
6022 * @return PanoViewer instance<ko>PanoViewer 인스턴스</ko>
6023 */
6024
6025
6026 __proto.keepUpdate = function (doUpdate) {
6027 this._photoSphereRenderer.keepUpdate(doUpdate);
6028
6029 return this;
6030 };
6031 /**
6032 * Get the current projection type (equirectangular/cube)
6033 * @ko 현재 프로젝션 타입(Equirectangular 혹은 Cube)을 반환합니다.
6034 * @return {@link eg.view360.PanoViewer.PROJECTION_TYPE}
6035 */
6036
6037
6038 __proto.getProjectionType = function () {
6039 return this._projectionType;
6040 };
6041 /**
6042 * Activate the device's motion sensor, and return the Promise whether the sensor is enabled
6043 * If it's iOS13+, this method must be used in the context of user interaction, like onclick callback on the button element.
6044 * @ko 디바이스의 모션 센서를 활성화하고, 활성화 여부를 담는 Promise를 리턴합니다.
6045 * iOS13+일 경우, 사용자 인터렉션에 의해서 호출되어야 합니다. 예로, 버튼의 onclick 콜백과 같은 콘텍스트에서 호출되어야 합니다.
6046 * @return Promise containing nothing when resolved, or string of the rejected reason when rejected.<ko>Promise. resolve되었을 경우 아무것도 반환하지 않고, reject되었을 경우 그 이유를 담고있는 string을 반환한다.</ko>
6047 */
6048
6049
6050 __proto.enableSensor = function () {
6051 return new Promise$1(function (resolve, reject) {
6052 if (DeviceMotionEvent && typeof DeviceMotionEvent.requestPermission === "function") {
6053 DeviceMotionEvent.requestPermission().then(function (permissionState) {
6054 if (permissionState === "granted") {
6055 resolve();
6056 } else {
6057 reject(new Error("permission denied"));
6058 }
6059 }).catch(function (e) {
6060 // This can happen when this method wasn't triggered by user interaction
6061 reject(e);
6062 });
6063 } else {
6064 resolve();
6065 }
6066 });
6067 };
6068 /**
6069 * Disable the device's motion sensor.
6070 * @ko 디바이스의 모션 센서를 비활성화합니다.
6071 * @deprecated
6072 * @return PanoViewer instance<ko>PanoViewer 인스턴스</ko>
6073 */
6074
6075
6076 __proto.disableSensor = function () {
6077 return this;
6078 };
6079 /**
6080 * Switch to VR stereo rendering mode which uses WebXR / WebVR API (WebXR is preferred).
6081 * This method must be used in the context of user interaction, like onclick callback on the button element.
6082 * It can be rejected when an enabling device sensor fails or image/video is still loading("ready" event not triggered).
6083 * @ko WebXR / WebVR API를 사용하는 VR 스테레오 렌더링 모드로 전환합니다. (WebXR을 더 선호합니다)
6084 * 이 메소드는 사용자 인터렉션에 의해서 호출되어야 합니다. 예로, 버튼의 onclick 콜백과 같은 콘텍스트에서 호출되어야 합니다.
6085 * 디바이스 센서 활성화에 실패시 혹은 아직 이미지/비디오가 로딩중인 경우("ready"이벤트가 아직 트리거되지 않은 경우)에는 Promise가 reject됩니다.
6086 * @param {object} [options={}] Additional options for WebXR session, see {@link https://developer.mozilla.org/en-US/docs/Web/API/XRSessionInit XRSessionInit}.<ko>WebXR용 추가 옵션, {@link https://developer.mozilla.org/en-US/docs/Web/API/XRSessionInit XRSessionInit}을 참조해주세요.</ko>
6087 * @return Promise containing either a string of resolved reason or an Error instance of rejected reason.<ko>Promise가 resolve된 이유(string) 혹은 reject된 이유(Error)</ko>
6088 */
6089
6090
6091 __proto.enterVR = function (options) {
6092 var _this = this;
6093
6094 if (options === void 0) {
6095 options = {};
6096 }
6097
6098 if (!this._isReady) {
6099 return Promise$1.reject(new Error("PanoViewer is not ready to show image."));
6100 }
6101
6102 return new Promise$1(function (resolve, reject) {
6103 _this.enableSensor().then(function () {
6104 return _this._photoSphereRenderer.enterVR(options);
6105 }).then(function (res) {
6106 return resolve(res);
6107 }).catch(function (e) {
6108 return reject(e);
6109 });
6110 });
6111 };
6112 /**
6113 * Exit VR stereo rendering mode.
6114 * @ko VR 스테레오 렌더링 모드에서 일반 렌더링 모드로 전환합니다.
6115 * @return PanoViewer instance<ko>PanoViewer 인스턴스</ko>
6116 */
6117
6118
6119 __proto.exitVR = function () {
6120 this._photoSphereRenderer.exitVR();
6121
6122 return this;
6123 };
6124 /**
6125 * When set true, enables zoom with the wheel or pinch gesture. However, in the case of touch, pinch works only when the touchDirection setting is {@link eg.view360.PanoViewer.TOUCH_DIRECTION.ALL}.
6126 * @ko true 로 설정 시 휠 혹은 집기 동작으로 확대/축소 할 수 있습니다. false 설정 시 확대/축소 기능을 비활성화 합니다. 단, 터치인 경우 touchDirection 설정이 {@link eg.view360.PanoViewer.TOUCH_DIRECTION.ALL} 인 경우에만 pinch 가 동작합니다.
6127 * @param useZoom
6128 * @return PanoViewer instance<ko>PanoViewer 인스턴스</ko>
6129 */
6130
6131
6132 __proto.setUseZoom = function (useZoom) {
6133 if (typeof useZoom === "boolean") {
6134 this._yawPitchControl.option("useZoom", useZoom);
6135 }
6136
6137 return this;
6138 };
6139 /**
6140 * When true, enables the keyboard move key control: awsd, arrow keys
6141 * @ko true이면 키보드 이동 키 컨트롤을 활성화합니다. (awsd, 화살표 키)
6142 * @param useKeyboard
6143 * @return PanoViewer instance<ko>PanoViewer 인스턴스</ko>
6144 */
6145
6146
6147 __proto.setUseKeyboard = function (useKeyboard) {
6148 this._yawPitchControl.option("useKeyboard", useKeyboard);
6149
6150 return this;
6151 };
6152 /**
6153 * Enables control through device motion. ("none", "yawPitch", "VR")
6154 * @ko 디바이스 움직임을 통한 컨트롤을 활성화 합니다. ("none", "yawPitch", "VR")
6155 * @param gyroMode {@link eg.view360.PanoViewer.GYRO_MODE}
6156 * @return PanoViewer instance<ko>PanoViewer 인스턴스</ko>
6157 * @example
6158 * ```
6159 * panoViewer.setGyroMode("yawPitch");
6160 * //equivalent
6161 * panoViewer.setGyroMode(eg.view360.PanoViewer.GYRO_MODE.YAWPITCH);
6162 * ```
6163 */
6164
6165
6166 __proto.setGyroMode = function (gyroMode) {
6167 this._yawPitchControl.option("gyroMode", gyroMode);
6168
6169 return this;
6170 };
6171 /**
6172 * Set the range of controllable FOV values
6173 * @ko 제어 가능한 FOV 구간을 설정합니다.
6174 * @param range
6175 * @return PanoViewer instance<ko>PanoViewer 인스턴스</ko>
6176 * @example
6177 * panoViewer.setFovRange([50, 90]);
6178 */
6179
6180
6181 __proto.setFovRange = function (range) {
6182 this._yawPitchControl.option("fovRange", range);
6183
6184 return this;
6185 };
6186 /**
6187 * Get the range of controllable FOV values
6188 * @ko 제어 가능한 FOV 구간을 반환합니다.
6189 * @return FOV range
6190 * @example
6191 * var range = panoViewer.getFovRange(); // [50, 90]
6192 */
6193
6194
6195 __proto.getFovRange = function () {
6196 return this._yawPitchControl.option("fovRange");
6197 };
6198 /**
6199 * Update size of canvas element by it's container element's or specified size. If size is not specified, the size of the container area is obtained and updated to that size.
6200 * @ko 캔버스 엘리먼트의 크기를 컨테이너 엘리먼트의 크기나 지정된 크기로 업데이트합니다. 만약 size 가 지정되지 않으면 컨테이너 영역의 크기를 얻어와 해당 크기로 갱신합니다.
6201 * @param {object} [size]
6202 * @param {number} [size.width=width of the container]
6203 * @param {number} [size.height=height of the container]
6204 * @return PanoViewer instance<ko>PanoViewer 인스턴스</ko>
6205 */
6206
6207
6208 __proto.updateViewportDimensions = function (size) {
6209 if (size === void 0) {
6210 size = {};
6211 }
6212
6213 if (!this._isReady) {
6214 return this;
6215 }
6216
6217 var containerSize;
6218
6219 if (size.width === undefined || size.height === undefined) {
6220 containerSize = window.getComputedStyle(this._container);
6221 }
6222
6223 var width = size.width || parseInt(containerSize.width, 10);
6224 var height = size.height || parseInt(containerSize.height, 10); // Skip if viewport is not changed.
6225
6226 if (width === this._width && height === this._height) {
6227 return this;
6228 }
6229
6230 this._width = width;
6231 this._height = height;
6232 this._aspectRatio = width / height;
6233
6234 this._photoSphereRenderer.updateViewportDimensions(width, height);
6235
6236 this._yawPitchControl.option("aspectRatio", this._aspectRatio);
6237
6238 this._yawPitchControl.updatePanScale({
6239 height: height
6240 });
6241
6242 this.lookAt({}, 0);
6243 return this;
6244 };
6245 /**
6246 * Get the current field of view(FOV)
6247 * @ko 현재 field of view(FOV) 값을 반환합니다.
6248 */
6249
6250
6251 __proto.getFov = function () {
6252 return this._fov;
6253 };
6254 /**
6255 * Get current yaw value
6256 * @ko 현재 yaw 값을 반환합니다.
6257 */
6258
6259
6260 __proto.getYaw = function () {
6261 return this._yaw;
6262 };
6263 /**
6264 * Get current pitch value
6265 * @ko 현재 pitch 값을 반환합니다.
6266 */
6267
6268
6269 __proto.getPitch = function () {
6270 return this._pitch;
6271 };
6272 /**
6273 * Get the range of controllable Yaw values
6274 * @ko 컨트롤 가능한 Yaw 구간을 반환합니다.
6275 */
6276
6277
6278 __proto.getYawRange = function () {
6279 return this._yawPitchControl.option("yawRange");
6280 };
6281 /**
6282 * Get the range of controllable Pitch values
6283 * @ko 컨트롤 가능한 Pitch 구간을 가져옵니다.
6284 */
6285
6286
6287 __proto.getPitchRange = function () {
6288 return this._yawPitchControl.option("pitchRange");
6289 };
6290 /**
6291 * Set the range of controllable yaw
6292 * @ko 컨트롤 가능한 Yaw 구간을 반환합니다.
6293 * @param {number[]} range
6294 * @return PanoViewer instance<ko>PanoViewer 인스턴스</ko>
6295 * @example
6296 * panoViewer.setYawRange([-90, 90]);
6297 */
6298
6299
6300 __proto.setYawRange = function (yawRange) {
6301 this._yawPitchControl.option("yawRange", yawRange);
6302
6303 return this;
6304 };
6305 /**
6306 * Set the range of controllable Pitch values
6307 * @ko 컨트롤 가능한 Pitch 구간을 설정합니다.
6308 * @param {number[]} range
6309 * @return PanoViewer instance<ko>PanoViewer 인스턴스</ko>
6310 * @example
6311 * panoViewer.setPitchRange([-40, 40]);
6312 */
6313
6314
6315 __proto.setPitchRange = function (pitchRange) {
6316 this._yawPitchControl.option("pitchRange", pitchRange);
6317
6318 return this;
6319 };
6320 /**
6321 * Specifies whether to display the pole by limiting the pitch range. If it is true, pole point can be displayed. If it is false, it is not displayed.
6322 * @ko pitch 범위를 제한하여 극점을 표시할지를 지정합니다. true 인 경우 극점까지 표현할 수 있으며 false 인 경우 극점까지 표시하지 않습니다.
6323 * @param showPolePoint
6324 * @return PanoViewer instance<ko>PanoViewer 인스턴스</ko>
6325 */
6326
6327
6328 __proto.setShowPolePoint = function (showPolePoint) {
6329 this._yawPitchControl.option("showPolePoint", showPolePoint);
6330
6331 return this;
6332 };
6333 /**
6334 * Set a new view by setting camera configuration. Any parameters not specified remain the same.
6335 * @ko 카메라 설정을 지정하여 화면을 갱신합니다. 지정되지 않은 매개 변수는 동일하게 유지됩니다.
6336 * @param {object} orientation
6337 * @param {number} orientation.yaw Target yaw in degree <ko>목표 yaw (degree 단위)</ko>
6338 * @param {number} orientation.pitch Target pitch in degree <ko>목표 pitch (degree 단위)</ko>
6339 * @param {number} orientation.fov Target vertical fov in degree <ko>목표 수직 fov (degree 단위)</ko>
6340 * @param {number} duration Animation duration in milliseconds <ko>애니메이션 시간 (밀리 초)</ko>
6341 * @return PanoViewer instance<ko>PanoViewer 인스턴스</ko>
6342 * @example
6343 * ```
6344 * // Change the yaw angle (absolute angle) to 30 degrees for one second.
6345 * panoViewer.lookAt({yaw: 30}, 1000);
6346 * ```
6347 */
6348
6349
6350 __proto.lookAt = function (orientation, duration) {
6351 if (duration === void 0) {
6352 duration = 0;
6353 }
6354
6355 if (!this._isReady) {
6356 return this;
6357 }
6358
6359 var yaw = orientation.yaw !== undefined ? orientation.yaw : this._yaw;
6360 var pitch = orientation.pitch !== undefined ? orientation.pitch : this._pitch;
6361
6362 var pitchRange = this._yawPitchControl.option("pitchRange");
6363
6364 var verticalAngleOfImage = pitchRange[1] - pitchRange[0];
6365 var fov = orientation.fov !== undefined ? orientation.fov : this._fov;
6366
6367 if (verticalAngleOfImage < fov) {
6368 fov = verticalAngleOfImage;
6369 }
6370
6371 this._yawPitchControl.lookAt({
6372 yaw: yaw,
6373 pitch: pitch,
6374 fov: fov
6375 }, duration);
6376
6377 if (duration === 0) {
6378 this._photoSphereRenderer.renderWithYawPitch(yaw, pitch, fov);
6379 }
6380
6381 return this;
6382 };
6383 /**
6384 * Set touch direction by which user can control.
6385 * @ko 사용자가 조작가능한 터치 방향을 지정합니다.
6386 * @param direction of the touch. {@link eg.view360.PanoViewer.TOUCH_DIRECTION}<ko>컨트롤 가능한 방향 {@link eg.view360.PanoViewer.TOUCH_DIRECTION}</ko>
6387 * @return PanoViewer instance
6388 * @example
6389 * ```
6390 * panoViewer = new PanoViewer(el);
6391 * // Limit the touch direction to the yaw direction only.
6392 * panoViewer.setTouchDirection(eg.view360.PanoViewer.TOUCH_DIRECTION.YAW);
6393 * ```
6394 */
6395
6396
6397 __proto.setTouchDirection = function (direction) {
6398 if (PanoViewer._isValidTouchDirection(direction)) {
6399 this._yawPitchControl.option("touchDirection", direction);
6400 }
6401
6402 return this;
6403 };
6404 /**
6405 * Returns touch direction by which user can control
6406 * @ko 사용자가 조작가능한 터치 방향을 반환한다.
6407 * @return direction of the touch. {@link eg.view360.PanoViewer.TOUCH_DIRECTION}<ko>컨트롤 가능한 방향 {@link eg.view360.PanoViewer.TOUCH_DIRECTION}</ko>
6408 * @example
6409 * ```
6410 * panoViewer = new PanoViewer(el);
6411 * // Returns the current touch direction.
6412 * var dir = panoViewer.getTouchDirection();
6413 * ```
6414 */
6415
6416
6417 __proto.getTouchDirection = function () {
6418 return this._yawPitchControl.option("touchDirection");
6419 };
6420 /**
6421 * Destroy viewer. Remove all registered event listeners and remove viewer canvas.
6422 * @ko 뷰어 인스턴스를 해제합니다. 모든 등록된 이벤트리스너를 제거하고 뷰어 캔버스를 삭제합니다.
6423 * @return PanoViewer instance<ko>PanoViewer 인스턴스</ko>
6424 */
6425
6426
6427 __proto.destroy = function () {
6428 this._deactivate();
6429
6430 if (this._yawPitchControl) {
6431 this._yawPitchControl.destroy();
6432
6433 this._yawPitchControl = null;
6434 }
6435
6436 return this;
6437 }; // TODO: Remove parameters as they're just using private values
6438
6439
6440 __proto._initRenderer = function (yaw, pitch, fov, projectionType, cubemapConfig) {
6441 var _this = this;
6442
6443 this._photoSphereRenderer = new PanoImageRenderer(this._image, this._width, this._height, this._isVideo, this._container, this._canvasClass, {
6444 initialYaw: yaw,
6445 initialPitch: pitch,
6446 fieldOfView: fov,
6447 imageType: projectionType,
6448 cubemapConfig: cubemapConfig,
6449 stereoFormat: this._stereoFormat
6450 });
6451
6452 this._photoSphereRenderer.setYawPitchControl(this._yawPitchControl);
6453
6454 this._bindRendererHandler();
6455
6456 this._photoSphereRenderer.bindTexture().then(function () {
6457 return _this._activate();
6458 }).catch(function () {
6459 _this.trigger(new Component.ComponentEvent(PANOVIEWER_EVENTS.ERROR, {
6460 type: ERROR_TYPE.FAIL_BIND_TEXTURE,
6461 message: "failed to bind texture"
6462 }));
6463 });
6464 };
6465 /**
6466 * @private
6467 * update values of YawPitchControl if needed.
6468 * For example, In Panorama mode, initial fov and pitchRange is changed by aspect ratio of image.
6469 *
6470 * This function should be called after isReady status is true.
6471 */
6472
6473
6474 __proto._updateYawPitchIfNeeded = function () {
6475 if (this._projectionType === PanoViewer.ProjectionType.PANORAMA) {
6476 // update fov by aspect ratio
6477 var image = this._photoSphereRenderer.getContent();
6478
6479 var imageAspectRatio = image.naturalWidth / image.naturalHeight;
6480 var yawSize = void 0;
6481 var maxFov = void 0; // If height is larger than width, then we assume it's rotated by 90 degree.
6482
6483 if (imageAspectRatio < 1) {
6484 // So inverse the aspect ratio.
6485 imageAspectRatio = 1 / imageAspectRatio;
6486 }
6487
6488 if (imageAspectRatio < 6) {
6489 yawSize = util.toDegree(imageAspectRatio); // 0.5 means ratio of half height of cylinder(0.5) and radius of cylider(1). 0.5/1 = 0.5
6490
6491 maxFov = util.toDegree(Math.atan(0.5)) * 2;
6492 } else {
6493 yawSize = 360;
6494 maxFov = 360 / imageAspectRatio; // Make it 5 fixed as axes does.
6495 } // console.log("_updateYawPitchIfNeeded", maxFov, "aspectRatio", image.naturalWidth, image.naturalHeight, "yawSize", yawSize);
6496
6497
6498 var minFov = this._yawPitchControl.option("fovRange")[0]; // this option should be called after fov is set.
6499
6500
6501 this._yawPitchControl.option({
6502 "fov": maxFov,
6503 "yawRange": [-yawSize / 2, yawSize / 2],
6504 "pitchRange": [-maxFov / 2, maxFov / 2],
6505 "fovRange": [minFov, maxFov]
6506 });
6507
6508 this.lookAt({
6509 fov: maxFov
6510 });
6511 }
6512 };
6513
6514 __proto._bindRendererHandler = function () {
6515 var _this = this;
6516
6517 this._photoSphereRenderer.on(PanoImageRenderer.EVENTS.ERROR, function (e) {
6518 _this.trigger(new Component.ComponentEvent(PANOVIEWER_EVENTS.ERROR, e));
6519 });
6520
6521 this._photoSphereRenderer.on(PanoImageRenderer.EVENTS.RENDERING_CONTEXT_LOST, function () {
6522 _this._deactivate();
6523
6524 _this.trigger(new Component.ComponentEvent(PANOVIEWER_EVENTS.ERROR, {
6525 type: ERROR_TYPE.RENDERING_CONTEXT_LOST,
6526 message: "webgl rendering context lost"
6527 }));
6528 });
6529 };
6530
6531 __proto._initYawPitchControl = function (yawPitchConfig) {
6532 var _this = this;
6533
6534 this._yawPitchControl = new YawPitchControl(yawPitchConfig);
6535
6536 this._yawPitchControl.on(PANOVIEWER_EVENTS.ANIMATION_END, function (e) {
6537 _this.trigger(new Component.ComponentEvent(PANOVIEWER_EVENTS.ANIMATION_END, e));
6538 });
6539
6540 this._yawPitchControl.on("change", function (e) {
6541 _this._yaw = e.yaw;
6542 _this._pitch = e.pitch;
6543 _this._fov = e.fov;
6544 _this._quaternion = e.quaternion;
6545
6546 _this.trigger(new Component.ComponentEvent(PANOVIEWER_EVENTS.VIEW_CHANGE, e));
6547 });
6548 };
6549
6550 __proto._activate = function () {
6551 this._photoSphereRenderer.attachTo(this._container);
6552
6553 this._yawPitchControl.enable();
6554
6555 this.updateViewportDimensions();
6556 this._isReady = true; // update yawPitchControl after isReady status is true.
6557
6558 this._updateYawPitchIfNeeded();
6559
6560 this.trigger(new Component.ComponentEvent(PANOVIEWER_EVENTS.READY));
6561
6562 this._photoSphereRenderer.startRender();
6563 };
6564 /**
6565 * Destroy webgl context and block user interaction and stop rendering
6566 */
6567
6568
6569 __proto._deactivate = function () {
6570 // Turn off the video if it has one
6571 var video = this.getVideo();
6572
6573 if (video) {
6574 video.pause();
6575 }
6576
6577 if (this._isReady) {
6578 this._photoSphereRenderer.stopRender();
6579
6580 this._yawPitchControl.disable();
6581
6582 this._isReady = false;
6583 }
6584
6585 if (this._photoSphereRenderer) {
6586 this._photoSphereRenderer.destroy();
6587
6588 this._photoSphereRenderer = null;
6589 }
6590 };
6591 /**
6592 * Version info string
6593 * @ko 버전정보 문자열
6594 * @name VERSION
6595 * @static
6596 * @type {String}
6597 * @example
6598 * eg.view360.PanoViewer.VERSION; // ex) 3.0.1
6599 * @memberof eg.view360.PanoViewer
6600 */
6601
6602
6603 PanoViewer.VERSION = VERSION;
6604 PanoViewer.ERROR_TYPE = ERROR_TYPE;
6605 PanoViewer.EVENTS = PANOVIEWER_EVENTS;
6606 PanoViewer.PROJECTION_TYPE = PROJECTION_TYPE;
6607 PanoViewer.GYRO_MODE = GYRO_MODE; // This should be deprecated!
6608 // eslint-disable-next-line @typescript-eslint/naming-convention
6609
6610 PanoViewer.ProjectionType = PROJECTION_TYPE;
6611 PanoViewer.STEREO_FORMAT = STEREO_FORMAT;
6612 /**
6613 * Constant value for touch directions
6614 * @ko 터치 방향에 대한 상수 값.
6615 * @namespace
6616 * @name TOUCH_DIRECTION
6617 */
6618
6619 PanoViewer.TOUCH_DIRECTION = {
6620 /**
6621 * Constant value for none direction.
6622 * @ko none 방향에 대한 상수 값.
6623 * @name NONE
6624 * @memberof eg.view360.PanoViewer.TOUCH_DIRECTION
6625 * @constant
6626 * @type {Number}
6627 * @default 1
6628 */
6629 NONE: YawPitchControl.TOUCH_DIRECTION_NONE,
6630
6631 /**
6632 * Constant value for horizontal(yaw) direction.
6633 * @ko horizontal(yaw) 방향에 대한 상수 값.
6634 * @name YAW
6635 * @memberof eg.view360.PanoViewer.TOUCH_DIRECTION
6636 * @constant
6637 * @type {Number}
6638 * @default 6
6639 */
6640 YAW: YawPitchControl.TOUCH_DIRECTION_YAW,
6641
6642 /**
6643 * Constant value for vertical direction.
6644 * @ko vertical(pitch) 방향에 대한 상수 값.
6645 * @name PITCH
6646 * @memberof eg.view360.PanoViewer.TOUCH_DIRECTION
6647 * @constant
6648 * @type {Number}
6649 * @default 24
6650 */
6651 PITCH: YawPitchControl.TOUCH_DIRECTION_PITCH,
6652
6653 /**
6654 * Constant value for all direction.
6655 * @ko all 방향에 대한 상수 값.
6656 * @name ALL
6657 * @memberof eg.view360.PanoViewer.TOUCH_DIRECTION
6658 * @constant
6659 * @type {Number}
6660 * @default 30
6661 */
6662 ALL: YawPitchControl.TOUCH_DIRECTION_ALL
6663 };
6664 return PanoViewer;
6665 }(Component);
6666
6667 /* eslint-disable @typescript-eslint/naming-convention */
6668 var PanoViewerModule = {
6669 PanoViewer: PanoViewer,
6670 VERSION: VERSION
6671 };
6672 merge(PanoViewerModule, Constants);
6673
6674 return PanoViewerModule;
6675
6676})));
6677//# sourceMappingURL=view360.panoviewer.js.map