diff --git a/applications/cam360/cam360.html b/applications/cam360/cam360.html new file mode 100644 index 0000000..519a82d --- /dev/null +++ b/applications/cam360/cam360.html @@ -0,0 +1,406 @@ + + + + + + + + + + + + + + + + + + + + + + +
+

CAM360 v2.0

+
+
+
+
+
+
+
+   + +
+ +   +
+
+ + + + + +
+
+ Throw the camera in the air to take a 360 degrees snapshot! +
+
+
  
+
+
  
+

You can also click on the thumbstick
on your right controller.
+
+
+
+ +
+ + + diff --git a/applications/cam360/cam360.js b/applications/cam360/cam360.js new file mode 100644 index 0000000..fb42aec --- /dev/null +++ b/applications/cam360/cam360.js @@ -0,0 +1,605 @@ +"use strict"; +// +// cam360.js +// +// Created by Zach Fox on 2018-10-26 +// Copyright 2018 High Fidelity, Inc. +// Copyright 2022, Overte e.V. +// +// Application to take 360 degrees photo by throwing a camera in the air (as in Ready Player One (RPO)) or as a standard positionned camera. +// version 2.0 +// +// Distributed under the Apache License, Version 2.0 +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function () { // BEGIN LOCAL_SCOPE + + // Function Name: inFrontOf() + // Description: + // - Returns the position in front of the given "position" argument, where the forward vector is based off + // the "orientation" argument and the amount in front is based off the "distance" argument. + function inFrontOf(distance, position, orientation) { + return Vec3.sum(position || MyAvatar.position, + Vec3.multiply(distance, Quat.getForward(orientation || MyAvatar.orientation))); + } + + // Function Name: rpo360On() + var CAMERA_NAME = "CAM360 Camera"; + var SETTING_LAST_360_CAPTURE = "overte_app_cam360_last_capture"; + var secondaryCameraConfig = Render.getConfig("SecondaryCamera"); + var camera = false; + var cameraRotation; + var cameraPosition; + var cameraGravity = {x: 0, y: -5, z: 0}; + var velocityLoopInterval = false; + var isThrowMode = true; + + function rpo360On() { + // Rez the camera model, and attach + // the secondary camera to the rezzed model. + cameraRotation = MyAvatar.orientation; + cameraPosition = inFrontOf(1.0, Vec3.sum(MyAvatar.position, { x: 0, y: 0.3, z: 0 })); + var properties; + var hostType = ""; + if (isThrowMode) { + properties = { + "angularDamping": 0.08, + "canCastShadow": false, + "damping": 0.01, + "collisionMask": 7, + "modelURL": Script.resolvePath("resources/models/cam360white.fst"), + "name": CAMERA_NAME, + "rotation": cameraRotation, + "position": cameraPosition, + "shapeType": "simple-compound", + "type": "Model", + "grab": { + "grabbable": true + }, + "script": Script.resolvePath("grabDetection.js"), + "userData": "", + "isVisibleInSecondaryCamera": false, + "gravity": cameraGravity, + "dynamic": true + }; + hostType = "local"; + } else { + properties = { + "canCastShadow": false, + "collisionMask": 7, + "modelURL": Script.resolvePath("resources/models/cam360black.fst"), + "name": CAMERA_NAME, + "rotation": cameraRotation, + "position": cameraPosition, + "shapeType": "sphere", + "type": "Model", + "grab": { + "grabbable": true + }, + "userData": "", + "isVisibleInSecondaryCamera": false + }; + hostType = "avatar"; + } + + camera = Entities.addEntity(properties, hostType); + secondaryCameraConfig.attachedEntityId = camera; + + // Play a little sound to let the user know we've rezzed the camera + Audio.playSound(SOUND_CAMERA_ON, { + "volume": 0.15, + "position": cameraPosition, + "localOnly": true + }); + + // Remove the existing camera model from the domain if one exists. + // It's easy for this to happen if the user crashes while the RPO360 Camera is on. + // We do this down here (after the new one is rezzed) so that we don't accidentally delete + // the newly-rezzed model. + var entityIDs = Entities.findEntitiesByName(CAMERA_NAME, MyAvatar.position, 100, false); + entityIDs.forEach(function (currentEntityID) { + var currentEntityOwner = Entities.getEntityProperties(currentEntityID, ['owningAvatarID']).owningAvatarID; + if (currentEntityOwner === MyAvatar.sessionUUID && currentEntityID !== camera) { + Entities.deleteEntity(currentEntityID); + } + }); + + setTakePhotoControllerMappingStatus(); + + // Start the velocity loop interval at 70ms + // This is used to determine when the 360 photo should be snapped + if (isThrowMode) { + velocityLoopInterval = Script.setInterval(velocityLoop, 70); + } + } + + // Function Name: velocityLoop() + var hasBeenThrown = false; + var snapshotVelocity = false; + var snapshotAngularVelocity = false; + var velocityWasPositive = false; + var cameraReleaseTime = false; + var MIN_AIRTIME_MS = 500; + var flash = false; + var useFlash = false; + function velocityLoop() { + // Get the velocity and angular velocity of the camera model + var properties = Entities.getEntityProperties(camera, ["velocity", "angularVelocity", "userData"]); + var velocity = properties.velocity; + var angularVelocity = properties.angularVelocity; + var releasedState = properties.userData; + + if (releasedState === "RELEASED" && !hasBeenThrown) { + hasBeenThrown = true; + // Record the time at which a user has thrown the camera + cameraReleaseTime = Date.now(); + } + + // If we've been thrown UP... + if (hasBeenThrown && velocity.y > 0) { + // Set this flag to true + velocityWasPositive = true; + } + + // If we've been thrown UP in the past, but now we're coming DOWN... + if (hasBeenThrown && velocityWasPositive && velocity.y < 0) { + // Reset the state machine + hasBeenThrown = false; + velocityWasPositive = false; + // Don't take a snapshot if the camera hasn't been in the air for very long + if (Date.now() - cameraReleaseTime <= MIN_AIRTIME_MS) { + Entities.editEntity(camera, { + "userData": "" + }); + return; + } + // Save these properties so that the camera falls realistically + // after it's taken the 360 snapshot + snapshotVelocity = velocity; + snapshotAngularVelocity = angularVelocity; + // Freeze the camera model and make it not grabbable + Entities.editEntity(camera, { + "velocity": {"x": 0, "y": 0, "z": 0}, + "angularVelocity": {"x": 0, "y": 0, "z": 0}, + "gravity": {"x": 0, "y": 0, "z": 0}, + "grab": { + "grabbable": false + }, + "userData": "" + }); + // Add a "flash" to the camera that illuminates the ground below the camera + if (useFlash) { + flash = Entities.addEntity({ + "collidesWith": "", + "collisionMask": 0, + "color": { + "blue": 173, + "green": 252, + "red": 255 + }, + "dimensions": { + "x": 100, + "y": 100, + "z": 100 + }, + "dynamic": false, + "falloffRadius": 10, + "intensity": 1, + "isSpotlight": false, + "localRotation": { w: 1, x: 0, y: 0, z: 0 }, + "name": CAMERA_NAME + "_Flash", + "type": "Light", + "parentID": camera + }, "local"); + } + // Take the snapshot! + maybeTake360Snapshot(); + } + } + + function capture() { + if (!isThrowMode) { + if (useFlash) { + flash = Entities.addEntity({ + "collidesWith": "", + "collisionMask": 0, + "color": { + "blue": 173, + "green": 252, + "red": 255 + }, + "dimensions": { + "x": 100, + "y": 100, + "z": 100 + }, + "dynamic": false, + "falloffRadius": 10, + "intensity": 1, + "isSpotlight": false, + "localRotation": { w: 1, x: 0, y: 0, z: 0 }, + "name": CAMERA_NAME + "_Flash", + "type": "Light", + "parentID": camera + }, "avatar"); + } + // Take the snapshot! + maybeTake360Snapshot(); + } + } + + // Function Name: rpo360Off() + var WAIT_AFTER_DOMAIN_SWITCH_BEFORE_CAMERA_DELETE_MS = 1 * 1000; + function rpo360Off(isChangingDomains) { + if (velocityLoopInterval) { + Script.clearInterval(velocityLoopInterval); + velocityLoopInterval = false; + } + + function deleteCamera() { + if (flash) { + Entities.deleteEntity(flash); + flash = false; + } + if (camera) { + Entities.deleteEntity(camera); + camera = false; + } + //buttonActive(ui.isOpen); + } + + secondaryCameraConfig.attachedEntityId = false; + if (camera) { + // Workaround for Avatar Entities not immediately having properties after + // the "Window.domainChanged()" signal is emitted. + // May no longer be necessary; untested... + if (isChangingDomains) { + Script.setTimeout(function () { + deleteCamera(); + rpo360On(); + }, WAIT_AFTER_DOMAIN_SWITCH_BEFORE_CAMERA_DELETE_MS); + } else { + deleteCamera(); + } + } + setTakePhotoControllerMappingStatus(); + } + + var isCurrentlyTaking360Snapshot = false; + var processing360Snapshot = false; + function maybeTake360Snapshot() { + // Don't take a snapshot if we're currently in the middle of taking one + // or if the camera entity doesn't exist + if (!isCurrentlyTaking360Snapshot && camera) { + isCurrentlyTaking360Snapshot = true; + var currentCameraPosition = Entities.getEntityProperties(camera, ["position"]).position; + // Play a sound at the current camera position + Audio.playSound(SOUND_SNAPSHOT, { + "position": { "x": currentCameraPosition.x, "y": currentCameraPosition.y, "z": currentCameraPosition.z }, + "localOnly": isThrowMode, + "volume": 0.8 + }); + Window.takeSecondaryCamera360Snapshot(currentCameraPosition); + used360AppToTakeThisSnapshot = true; + processing360Snapshot = true; + + // Let the UI know we're processing a 360 snapshot now + tablet.emitScriptEvent(JSON.stringify({ + "channel": channel, + "method": "startedProcessing360Snapshot" + })); + } + } + + function on360SnapshotTaken(path) { + isCurrentlyTaking360Snapshot = false; + // Make the camera fall back to the ground with the same + // physical properties as when it froze in the air + if (isThrowMode) { + Entities.editEntity(camera, { + "velocity": snapshotVelocity, + "angularVelocity": snapshotAngularVelocity, + "gravity": cameraGravity, + "grab": { + "grabbable": true + } + }); + } + // Delete the flash entity + if (flash) { + Entities.deleteEntity(flash); + flash = false; + } + //console.log('360 Snapshot taken. Path: ' + path); + + //update UI + tablet.emitScriptEvent(JSON.stringify({ + "channel": channel, + "method": "last360ThumbnailURL", + "last360ThumbnailURL": path + })); + last360ThumbnailURL = path; + Settings.setValue(SETTING_LAST_360_CAPTURE, last360ThumbnailURL); + processing360Snapshot = false; + tablet.emitScriptEvent(JSON.stringify({ + "channel": channel, + "method": "finishedProcessing360Snapshot" + })); + } + + + var last360ThumbnailURL = Settings.getValue(SETTING_LAST_360_CAPTURE, ""); + var used360AppToTakeThisSnapshot = false; + + function onDomainChanged() { + rpo360Off(true); + } + + // These functions will be called when the script is loaded. + var SOUND_CAMERA_ON = SoundCache.getSound(Script.resolvePath("resources/sounds/cameraOn.wav")); + var SOUND_SNAPSHOT = SoundCache.getSound(Script.resolvePath("resources/sounds/snap.wav")); + + + var jsMainFileName = "cam360.js"; + var ROOT = Script.resolvePath('').split(jsMainFileName)[0]; + + var APP_NAME = "CAM360"; + var APP_URL = ROOT + "cam360.html"; + var APP_ICON_INACTIVE = ROOT + "resources/images/icons/cam360-i.svg"; + var APP_ICON_ACTIVE = ROOT + "resources/images/icons/cam360-a.svg"; + var appStatus = false; + var channel = "cam360.ak.overte.com"; + + var timestamp = 0; + var INTERCALL_DELAY = 200; //0.3 sec + var DEG_TO_RAD = Math.PI/180; + + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + + Window.domainChanged.connect(onDomainChanged); + Window.snapshot360Taken.connect(on360SnapshotTaken); + HMD.displayModeChanged.connect(onHMDChanged); + + camera = false; + + tablet.screenChanged.connect(onScreenChanged); + + var button = tablet.addButton({ + text: APP_NAME, + icon: APP_ICON_INACTIVE, + activeIcon: APP_ICON_ACTIVE + }); + + + function clicked(){ + if (appStatus === true) { + tablet.webEventReceived.disconnect(onAppWebEventReceived); + tablet.gotoHomeScreen(); + appStatus = false; + }else{ + tablet.gotoWebScreen(APP_URL); + tablet.webEventReceived.connect(onAppWebEventReceived); + appStatus = true; + } + + button.editProperties({ + isActive: appStatus || camera + }); + } + + button.clicked.connect(clicked); + + + function onAppWebEventReceived(message){ + var d = new Date(); + var n = d.getTime(); + var messageObj = JSON.parse(message); + if (messageObj.channel === channel) { + if (messageObj.method === "rpo360On" && (n - timestamp) > INTERCALL_DELAY) { + d = new Date(); + timestamp = d.getTime(); + rpo360On(); + + } else if (messageObj.method === "rpo360Off" && (n - timestamp) > INTERCALL_DELAY) { + d = new Date(); + timestamp = d.getTime(); + rpo360Off(); + + } else if (messageObj.method === "openSettings" && (n - timestamp) > INTERCALL_DELAY) { + d = new Date(); + timestamp = d.getTime(); + if ((HMD.active && Settings.getValue("hmdTabletBecomesToolbar", false)) || (!HMD.active && Settings.getValue("desktopTabletBecomesToolbar", true))) { + Desktop.show("hifi/dialogs/GeneralPreferencesDialog.qml", "GeneralPreferencesDialog"); + } else { + tablet.pushOntoStack("hifi/tablet/TabletGeneralPreferences.qml"); + } + + } else if (messageObj.method === "disableFlash" && (n - timestamp) > INTERCALL_DELAY) { + d = new Date(); + timestamp = d.getTime(); + useFlash = false; + + } else if (messageObj.method === "enableFlash" && (n - timestamp) > INTERCALL_DELAY) { + d = new Date(); + timestamp = d.getTime(); + useFlash = true; + + } else if (messageObj.method === "uiReady" && (n - timestamp) > INTERCALL_DELAY) { + d = new Date(); + timestamp = d.getTime(); + tablet.emitScriptEvent(JSON.stringify({ + "channel": channel, + "method": "initializeUI", + "masterSwitchOn": !!camera, + "last360ThumbnailURL": last360ThumbnailURL, + "processing360Snapshot": processing360Snapshot, + "useFlash": useFlash, + "isThrowMode": isThrowMode + })); + + } else if (messageObj.method === "ThrowMode" && (n - timestamp) > INTERCALL_DELAY) { + d = new Date(); + timestamp = d.getTime(); + isThrowMode = true; + if (camera) { + rpo360Off(); + rpo360On(); + } + } else if (messageObj.method === "PositionMode" && (n - timestamp) > INTERCALL_DELAY) { + d = new Date(); + timestamp = d.getTime(); + isThrowMode = false; + if (camera) { + rpo360Off(); + rpo360On(); + } + } else if (messageObj.method === "Capture" && (n - timestamp) > INTERCALL_DELAY) { + d = new Date(); + timestamp = d.getTime(); + if (camera) { + capture(); + } + } + + } + } + var udateSignateDisconnected = true; + function onScreenChanged(type, url) { + if (type === "Web" && url.indexOf(APP_URL) !== -1) { + appStatus = true; + Script.update.connect(myTimer); + udateSignateDisconnected = false; + } else { + appStatus = false; + if (!udateSignateDisconnected) { + Script.update.disconnect(myTimer); + udateSignateDisconnected = true; + } + } + + button.editProperties({ + isActive: appStatus || camera + }); + } + + function myTimer(deltaTime) { + var yaw = 0.0; + var pitch = 0.0; + var roll = 0.0; + var euler; + if (!HMD.active) { + //Use cuser camera for destop + euler = Quat.safeEulerAngles(Camera.orientation); + yaw = -euler.y; + pitch = -euler.x; + roll = -euler.z; + } else { + //Use Tablet orientation for HMD + var tabletRotation = Entities.getEntityProperties(HMD.tabletID, ["rotation"]).rotation; + var noRoll = Quat.cancelOutRoll(tabletRotation); //Pushing the roll is getting quite complexe + euler = Quat.safeEulerAngles(noRoll); + yaw = euler.y - 180; + if (yaw < -180) { yaw = yaw + 360;} + yaw = -yaw; + pitch = euler.x; + roll = 0; + } + + tablet.emitScriptEvent(JSON.stringify({ + "channel": channel, + "method": "yawPitchRoll", + "yaw": yaw, + "pitch": pitch, + "roll": roll + })); + + } + + function cleanup() { + + if (appStatus) { + tablet.gotoHomeScreen(); + tablet.webEventReceived.disconnect(onAppWebEventReceived); + if (!udateSignateDisconnected) { + Script.update.disconnect(myTimer); + udateSignateDisconnected = true; + } + } + + tablet.screenChanged.disconnect(onScreenChanged); + tablet.removeButton(button); + + rpo360Off(); + + if (takePhotoControllerMapping) { + takePhotoControllerMapping.disable(); + } + + Window.domainChanged.disconnect(onDomainChanged); + Window.snapshot360Taken.disconnect(on360SnapshotTaken); + HMD.displayModeChanged.disconnect(onHMDChanged); + } + + Script.scriptEnding.connect(cleanup); + + //controller + function setTakePhotoControllerMappingStatus() { + if (!takePhotoControllerMapping) { + return; + } + if (!isThrowMode) { + takePhotoControllerMapping.enable(); + } else { + takePhotoControllerMapping.disable(); + } + } + + var takePhotoControllerMapping; + var takePhotoControllerMappingName = 'Overte-cam360-Mapping-Capture'; + function registerTakePhotoControllerMapping() { + takePhotoControllerMapping = Controller.newMapping(takePhotoControllerMappingName); + if (controllerType === "OculusTouch") { + takePhotoControllerMapping.from(Controller.Standard.RS).to(function (value) { + if (value === 1.0) { + if (camera) { + capture(); + } + } + return; + }); + } else if (controllerType === "Vive") { + takePhotoControllerMapping.from(Controller.Standard.RightPrimaryThumb).to(function (value) { + if (value === 1.0) { + if (camera) { + capture(); + } + } + return; + }); + } + } + + var controllerType = "Other"; + function registerButtonMappings() { + var VRDevices = Controller.getDeviceNames().toString(); + if (VRDevices) { + if (VRDevices.indexOf("Vive") !== -1) { + controllerType = "Vive"; + } else if (VRDevices.indexOf("OculusTouch") !== -1) { + controllerType = "OculusTouch"; + } else { + return; // Neither Vive nor Touch detected + } + } + + if (!takePhotoControllerMapping) { + registerTakePhotoControllerMapping(); + } + } + + function onHMDChanged(isHMDMode) { + registerButtonMappings(); + } + +}()); diff --git a/applications/cam360/grabDetection.js b/applications/cam360/grabDetection.js new file mode 100644 index 0000000..4f69709 --- /dev/null +++ b/applications/cam360/grabDetection.js @@ -0,0 +1,50 @@ +"use strict"; +// +// grabDetection.js +// +// Created by Alezia Kurdis on August 26th, 2022 +// Copyright 2022 Overte e.V. +// +// Distributed under the Apache License, Version 2.0 +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +(function(){ + + var _this; + + DetectGrabbed = function() { + _this = this; + }; + + DetectGrabbed.prototype = { + setRightHand: function () { + //print("I am being held in a right hand... entity:" + this.entityID); + }, + setLeftHand: function () { + //print("I am being held in a left hand... entity:" + this.entityID); + }, + startDistantGrab: function () { + //print("I am being distance held... entity:" + this.entityID); + }, + continueDistantGrab: function () { + //print("I continue to be distance held... entity:" + this.entityID); + }, + startNearGrab: function () { + //print("I was just grabbed... entity:" + this.entityID); + }, + continueNearGrab: function () { + //print("I am still being grabbed... entity:" + this.entityID); + }, + + releaseGrab: function () { + //print("I was released... entity:" + this.entityID); + Entities.editEntity(this.entityID, {"userData": "RELEASED"}); + }, + + preload: function(entityID) { + this.entityID = entityID; + }, + }; + + return new DetectGrabbed(); +}) diff --git a/applications/cam360/resources/fonts/FiraSans-SemiBold.ttf b/applications/cam360/resources/fonts/FiraSans-SemiBold.ttf new file mode 100644 index 0000000..821a43d Binary files /dev/null and b/applications/cam360/resources/fonts/FiraSans-SemiBold.ttf differ diff --git a/applications/cam360/resources/images/default.jpg b/applications/cam360/resources/images/default.jpg new file mode 100644 index 0000000..02ddb22 Binary files /dev/null and b/applications/cam360/resources/images/default.jpg differ diff --git a/applications/cam360/resources/images/icons/cam360-a.svg b/applications/cam360/resources/images/icons/cam360-a.svg new file mode 100644 index 0000000..f20774c --- /dev/null +++ b/applications/cam360/resources/images/icons/cam360-a.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + diff --git a/applications/cam360/resources/images/icons/cam360-i.svg b/applications/cam360/resources/images/icons/cam360-i.svg new file mode 100644 index 0000000..8bfab42 --- /dev/null +++ b/applications/cam360/resources/images/icons/cam360-i.svg @@ -0,0 +1,64 @@ + + + + + + + + + + + + diff --git a/applications/cam360/resources/images/off.png b/applications/cam360/resources/images/off.png new file mode 100644 index 0000000..caf17ab Binary files /dev/null and b/applications/cam360/resources/images/off.png differ diff --git a/applications/cam360/resources/images/on.png b/applications/cam360/resources/images/on.png new file mode 100644 index 0000000..a3e279f Binary files /dev/null and b/applications/cam360/resources/images/on.png differ diff --git a/applications/cam360/resources/images/processing.gif b/applications/cam360/resources/images/processing.gif new file mode 100644 index 0000000..77caa12 Binary files /dev/null and b/applications/cam360/resources/images/processing.gif differ diff --git a/applications/cam360/resources/marzipano/LICENSE.txt b/applications/cam360/resources/marzipano/LICENSE.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/applications/cam360/resources/marzipano/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/applications/cam360/resources/marzipano/marzipano.js b/applications/cam360/resources/marzipano/marzipano.js new file mode 100644 index 0000000..3e84187 --- /dev/null +++ b/applications/cam360/resources/marzipano/marzipano.js @@ -0,0 +1,16 @@ +// Marzipano - a 360° media viewer for the modern web (v0.10.2) +// +// Copyright 2016 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +!function(t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).Marzipano=t()}(function(){var Vt;return function r(n,o,s){function a(e,t){if(!o[e]){if(!n[e]){var i="function"==typeof require&&require;if(!t&&i)return i(e,!0);if(h)return h(e,!0);throw(i=new Error("Cannot find module '"+e+"'")).code="MODULE_NOT_FOUND",i}i=o[e]={exports:{}},n[e][0].call(i.exports,function(t){return a(n[e][1][t]||t)},i,i.exports,r,n,o,s)}return o[e].exports}for(var h="function"==typeof require&&require,t=0;te[1][i])return 1;if(e[0][i]!==e[1][i])return-1;if(0===i)return 0}}function o(t,e,i){var r=a;"string"==typeof e&&(i=e,e=void 0),void 0===e&&(e=!1),i&&(r=s(i));var n,o=""+r.version;for(n in t)if(t.hasOwnProperty(n)&&r[n]){if("string"!=typeof t[n])throw new Error("Browser version in the minVersion map should be a string: "+n+": "+String(t));return x([o,t[n]])<0}return e}return a.test=function(t){for(var e=0;ef.EPSILON?(t[0]=e[0]/r,t[1]=e[1]/r,t[2]=e[2]/r):(t[0]=1,t[1]=0,t[2]=0);return i},i.getAngle=function(t,e){e=y(t,e);return Math.acos(2*e*e-1)},i.multiply=c,i.rotateX=function(t,e,i){i*=.5;var r=e[0],n=e[1],o=e[2],s=e[3],e=Math.sin(i),i=Math.cos(i);return t[0]=r*i+s*e,t[1]=n*i+o*e,t[2]=o*i-n*e,t[3]=s*i-r*e,t},i.rotateY=function(t,e,i){i*=.5;var r=e[0],n=e[1],o=e[2],s=e[3],e=Math.sin(i),i=Math.cos(i);return t[0]=r*i-o*e,t[1]=n*i+s*e,t[2]=o*i+r*e,t[3]=s*i-n*e,t},i.rotateZ=function(t,e,i){i*=.5;var r=e[0],n=e[1],o=e[2],s=e[3],e=Math.sin(i),i=Math.cos(i);return t[0]=r*i+n*e,t[1]=n*i-r*e,t[2]=o*i+s*e,t[3]=s*i-o*e,t},i.calculateW=function(t,e){var i=e[0],r=e[1],e=e[2];return t[0]=i,t[1]=r,t[2]=e,t[3]=Math.sqrt(Math.abs(1-i*i-r*r-e*e)),t},i.exp=p,i.ln=d,i.pow=function(t,e,i){return d(t,e),_(t,t,i),p(t,t),t},i.slerp=m,i.random=function(t){var e=f.RANDOM(),i=f.RANDOM(),r=f.RANDOM(),n=Math.sqrt(1-e),e=Math.sqrt(e);return t[0]=n*Math.sin(2*Math.PI*i),t[1]=n*Math.cos(2*Math.PI*i),t[2]=e*Math.sin(2*Math.PI*r),t[3]=e*Math.cos(2*Math.PI*r),t},i.invert=function(t,e){var i=e[0],r=e[1],n=e[2],o=e[3],e=i*i+r*r+n*n+o*o,e=e?1/e:0;return t[0]=-i*e,t[1]=-r*e,t[2]=-n*e,t[3]=o*e,t},i.conjugate=function(t,e){return t[0]=-e[0],t[1]=-e[1],t[2]=-e[2],t[3]=e[3],t},i.fromMat3=v,i.fromEuler=function(t,e,i,r){var n=.5*Math.PI/180;e*=n,i*=n,r*=n;var o=Math.sin(e),s=Math.cos(e),n=Math.sin(i),e=Math.cos(i),i=Math.sin(r),r=Math.cos(r);return t[0]=o*e*r-s*n*i,t[1]=s*n*r+o*e*i,t[2]=s*e*i-o*n*r,t[3]=s*e*r+o*n*i,t},i.str=function(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},i.setAxes=i.sqlerp=i.rotationTo=i.equals=i.exactEquals=i.normalize=i.sqrLen=i.squaredLength=i.len=i.length=i.lerp=i.dot=i.scale=i.mul=i.add=i.set=i.copy=i.fromValues=i.clone=void 0;var f=h(t("./common.js")),r=h(t("./mat3.js")),n=h(t("./vec3.js")),o=h(t("./vec4.js"));function a(){if("function"!=typeof WeakMap)return null;var t=new WeakMap;return a=function(){return t},t}function h(t){if(t&&t.__esModule)return t;if(null===t||"object"!==s(t)&&"function"!=typeof t)return{default:t};var e=a();if(e&&e.has(t))return e.get(t);var i,r,n={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(i in t)Object.prototype.hasOwnProperty.call(t,i)&&((r=o?Object.getOwnPropertyDescriptor(t,i):null)&&(r.get||r.set)?Object.defineProperty(n,i,r):n[i]=t[i]);return n.default=t,e&&e.set(t,n),n}function u(){var t=new f.ARRAY_TYPE(4);return f.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t[3]=1,t}function l(t,e,i){i*=.5;var r=Math.sin(i);return t[0]=r*e[0],t[1]=r*e[1],t[2]=r*e[2],t[3]=Math.cos(i),t}function c(t,e,i){var r=e[0],n=e[1],o=e[2],s=e[3],a=i[0],h=i[1],e=i[2],i=i[3];return t[0]=r*i+s*a+n*e-o*h,t[1]=n*i+s*h+o*a-r*e,t[2]=o*i+s*e+r*h-n*a,t[3]=s*i-r*a-n*h-o*e,t}function p(t,e){var i=e[0],r=e[1],n=e[2],o=e[3],s=Math.sqrt(i*i+r*r+n*n),e=Math.exp(o),o=0f.EPSILON?(i=Math.acos(e),e=Math.sin(i),n=Math.sin((1-r)*i)/e,Math.sin(r*i)/e):(n=1-r,r),t[0]=n*o+r*u,t[1]=n*s+r*l,t[2]=n*a+r*c,t[3]=n*h+r*p,t}function v(t,e){var i,r,n,o=e[0]+e[4]+e[8];return 0e[0]&&(i=1),e[8]>e[3*i+i]&&(i=2),r=(i+1)%3,o=(i+2)%3,n=Math.sqrt(e[3*i+i]-e[3*r+r]-e[3*o+o]+1),t[i]=.5*n,n=.5/n,t[3]=(e[3*r+o]-e[3*o+r])*n,t[r]=(e[3*r+i]+e[3*i+r])*n,t[o]=(e[3*o+i]+e[3*i+o])*n),t}t=o.clone;i.clone=t;t=o.fromValues;i.fromValues=t;t=o.copy;i.copy=t;t=o.set;i.set=t;t=o.add;i.add=t,i.mul=c;var _=o.scale;i.scale=_;var y=o.dot;i.dot=y;t=o.lerp;i.lerp=t;t=o.length;i.length=t,i.len=t;t=o.squaredLength;i.squaredLength=t,i.sqrLen=t;var g=o.normalize;i.normalize=g;t=o.exactEquals;i.exactEquals=t;o=o.equals;i.equals=o;var w,b,M,o=(w=n.create(),b=n.fromValues(1,0,0),M=n.fromValues(0,1,0),function(t,e,i){var r=n.dot(e,i);return r<-.999999?(n.cross(w,b,e),n.len(w)<1e-6&&n.cross(w,M,e),n.normalize(w,w),l(t,w,Math.PI),t):.999999e[i]}):r.sort()),r}function P(t,e){for(var i,r=e[0].toUpperCase()+e.slice(1),n=0;nl(s.y)?s.x:s.y,o=J(n,o),t.lastInterval=e):(s=a.velocity,i=a.velocityX,r=a.velocityY,o=a.direction),e.velocity=s,e.velocityX=i,e.velocityY=r,e.direction=o}(i,e),t=t.element,w(e.srcEvent.target,t)&&(t=e.srcEvent.target),e.target=t}(t,i),t.emit("hammer.input",i),t.recognize(i),t.session.prevInput=i}function Z(t){for(var e=[],i=0;i=l(e)?0e.threshold&&n&e.direction},attrTest:function(t){return It.prototype.attrTest.call(this,t)&&(this.state&Tt||!(this.state&Tt)&&this.directionTest(t))},emit:function(t){this.pX=t.deltaX,this.pY=t.deltaY;var e=At(t.direction);e&&this.manager.emit(this.options.event+e,t),this._super.emit.call(this,t)}}),d(Dt,It,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[wt]},attrTest:function(t){return this._super.attrTest.call(this,t)&&(Math.abs(t.scale-1)>this.options.threshold||this.state&Tt)},emit:function(t){var e;this._super.emit.call(this,t),1!==t.scale&&(e=t.scale<1?"in":"out",this.manager.emit(this.options.event+e,t))}}),d(jt,Rt,{defaults:{event:"press",pointers:1,time:500,threshold:5},getTouchAction:function(){return["auto"]},process:function(t){var e=this.options,i=t.pointers.length===e.pointers,r=t.distancee.time;if(this._input=t,!r||!i||t.eventType&(j|H)&&!n)this.reset();else if(t.eventType&D)this.reset(),this._timer=c(function(){this.state=St,this.tryEmit()},e.time,this);else if(t.eventType&j)return St;return 32},reset:function(){clearTimeout(this._timer)},emit:function(t){this.state===St&&(t&&t.eventType&j?this.manager.emit(this.options.event+"up",t):(this._input.timeStamp=h(),this.manager.emit(this.options.event,this._input)))}}),d(Ht,It,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[wt]},attrTest:function(t){return this._super.attrTest.call(this,t)&&(Math.abs(t.rotation)>this.options.threshold||this.state&Tt)}}),d(kt,It,{defaults:{event:"swipe",threshold:10,velocity:.65,direction:W|V,pointers:1},getTouchAction:function(){return zt.prototype.getTouchAction.call(this)},attrTest:function(t){var e,i=this.options.direction;return i&(W|V)?e=t.velocity:i&W?e=t.velocityX:i&V&&(e=t.velocityY),this._super.attrTest.call(this,t)&&i&t.direction&&t.distance>this.options.threshold&&l(e)>this.options.velocity&&t.eventType&j},emit:function(t){var e=At(t.direction);e&&this.manager.emit(this.options.event+e,t),this.manager.emit(this.options.event,t)}}),d(Yt,Rt,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:2,posThreshold:10},getTouchAction:function(){return[gt]},process:function(t){var e=this.options,i=t.pointers.length===e.pointers,r=t.distance=this._geometry.levelList.length||t<0))throw new Error("Level index out of range: "+t);this._fixedLevelIndex=t,this.emit("fixedLevelChange",this._fixedLevelIndex)}},s.prototype._selectLevel=function(){var t=null!=this._fixedLevelIndex?this._geometry.levelList[this._fixedLevelIndex]:this._view.selectLevel(this._geometry.selectableLevelList);return t},s.prototype.visibleTiles=function(t){var e=this._selectLevel();return this._geometry.visibleTiles(this._view,e,t)},s.prototype.pinLevel=function(t){for(var t=this._geometry.levelList[t],e=this._geometry.levelTiles(t),i=0;io.length)for(t=0;ti?Math.min(h+o*v,f):Math.max(h-o*v,0),n=l*o,yt.pitch&&(t.pitch=Math.min(y,t.pitch+n))),null!=g&&t.fov!==y&&(i=.5*u*u/_,c=Math.abs(g-t.fov)>i?Math.min(u+o*_,d):Math.max(u-o*_,0),o=c*o,gt.fov&&(t.fov=Math.min(g,t.fov+o))),s=e,a=r,h=l,u=c,t}}}},{"./util/defaults":81}],28:[function(t,e,i){"use strict";var r=t("../util/mod");function n(t){if(!isFinite(t)||Math.floor(t)!==t||t<0)throw new Error("LruMap: invalid capacity");this._capacity=t,this._keys=new Array(this._capacity),this._values=new Array(this._capacity),this._start=0,this._size=0}n.prototype._index=function(t){return r(this._start+t,this._capacity)},n.prototype.get=function(t){for(var e=0;ethis._layers.length)throw new Error("Invalid layer position");this.validateLayer(t);var i=t.geometry().type,r=t.view().type,n=this._rendererRegistry.get(i,r);if(!n)throw new Error("No "+this.type+" renderer avaiable for "+i+" geometry and "+r+" view");n=this.createRenderer(n);this._layers.splice(e,0,t),this._renderers.splice(e,0,n),t.addEventListener("viewChange",this._emitRenderInvalid),t.addEventListener("effectsChange",this._emitRenderInvalid),t.addEventListener("fixedLevelChange",this._emitRenderInvalid),t.addEventListener("textureStoreChange",this._emitRenderInvalid),this._emitRenderInvalid()},c.prototype.moveLayer=function(t,e){var i=this._layers.indexOf(t);if(i<0)throw new Error("No such layer in stage");if(e<0||e>=this._layers.length)throw new Error("Invalid layer position");t=this._layers.splice(i,1)[0];i=this._renderers.splice(i,1)[0];this._layers.splice(e,0,t),this._renderers.splice(e,0,i),this._emitRenderInvalid()},c.prototype.removeLayer=function(t){var e=this._layers.indexOf(t);if(e<0)throw new Error("No such layer in stage");t=this._layers.splice(e,1)[0],e=this._renderers.splice(e,1)[0];this.destroyRenderer(e),t.removeEventListener("viewChange",this._emitRenderInvalid),t.removeEventListener("effectsChange",this._emitRenderInvalid),t.removeEventListener("fixedLevelChange",this._emitRenderInvalid),t.removeEventListener("textureStoreChange",this._emitRenderInvalid),this._emitRenderInvalid()},c.prototype.removeAllLayers=function(){for(;0>6}return t+=t<<3,t^=t>>11,0<=(t+=t<<15)?t:-t}},{}],89:[function(t,e,i){"use strict";e.exports=function(t,e){function i(){}t.super_=e,i.prototype=e.prototype,t.prototype=new i,t.prototype.constructor=t}},{}],90:[function(t,e,i){"use strict";e.exports=function(t){return 0==(t&t-1)}},{}],91:[function(t,e,i){"use strict";e.exports=function(t,e){return(+t%(e=+e)+e)%e}},{}],92:[function(t,e,i){"use strict";e.exports=function(){}},{}],93:[function(t,e,i){"use strict";e.exports="undefined"!=typeof performance&&performance.now?function(){return performance.now()}:function(){return Date.now()}},{}],94:[function(t,e,i){"use strict";e.exports=function(t){var e,i=!1;return function(){return i||(i=!0,e=t.apply(null,arguments)),e}}},{}],95:[function(t,e,i){"use strict";e.exports=function(){if("undefined"!=typeof window){if(window.devicePixelRatio)return window.devicePixelRatio;var t=window.screen;if(t&&t.deviceXDPI&&t.logicalXDPI)return t.deviceXDPI/t.logicalXDPI;if(t&&t.systemXDPI&&t.logicalXDPI)return t.systemXDPI/t.logicalXDPI}return 1}},{}],96:[function(t,e,i){"use strict";var n=t("./dom").setTransform,o=t("./decimal");e.exports=function(t,e,i,r){r=r||"",r="translateX("+o(e)+"px) translateY("+o(i)+"px) translateZ(0) "+r,n(t,r)}},{"./decimal":80,"./dom":85}],97:[function(t,e,i){"use strict";e.exports=function(t){return 180*t/Math.PI}},{}],98:[function(t,e,i){"use strict";e.exports=function(t){return"number"==typeof t&&isFinite(t)}},{}],99:[function(t,e,i){"use strict";var s=t("./noop");e.exports=function(o){return function(){var t=arguments.length?Array.prototype.slice.call(arguments,0,arguments.length-1):[],e=arguments.length?arguments[arguments.length-1]:s,i=null,r=!1;function n(){!arguments[0]||r?e.apply(null,arguments):i=o.apply(null,t)}return t.push(n),n(!0),function(){r=!0,i.apply(null,arguments)}}}},{"./noop":92}],100:[function(t,e,i){"use strict";var a=t("./now");e.exports=function(i,r,n){var o=!1,s=a();return r(0),requestAnimationFrame(function t(){var e;o||((e=(a()-s)/i)<1?(r(e),requestAnimationFrame(t)):(r(1),n()))}),function(){o=!0,n.apply(null,arguments)}}},{"./now":93}],101:[function(t,e,i){"use strict";e.exports=function(t){var e=typeof t;if("object"==e){if(null===t)return"null";if("[object Array]"===Object.prototype.toString.call(t))return"array";if("[object RegExp]"===Object.prototype.toString.call(t))return"regexp"}return e}},{}],102:[function(t,e,i){"use strict";var r=t("minimal-event-emitter"),u=t("gl-matrix").mat4,a=t("gl-matrix").vec4,o=t("../util/pixelRatio"),p=t("../util/real"),f=t("../util/clamp"),n=t("../util/clearOwnProperties"),l=[1,0,1,0],c=[-1,-1,1,1];function s(t,e){if(!t||null==t.mediaAspectRatio)throw new Error("mediaAspectRatio must be defined");this._x=t&&null!=t.x?t.x:.5,this._y=t&&null!=t.y?t.y:.5,this._zoom=t&&null!=t.zoom?t.zoom:1,this._mediaAspectRatio=t.mediaAspectRatio,this._width=t&&null!=t.width?t.width:0,this._height=t&&null!=t.height?t.height:0,this._limiter=e||null,this._projMatrix=u.create(),this._invProjMatrix=u.create(),this._frustum=[0,0,0,0],this._projectionChanged=!0,this._params={},this._vec=a.create(),this._update()}r(s),s.prototype.destroy=function(){n(this)},s.prototype.x=function(){return this._x},s.prototype.y=function(){return this._y},s.prototype.zoom=function(){return this._zoom},s.prototype.mediaAspectRatio=function(){return this._mediaAspectRatio},s.prototype.width=function(){return this._width},s.prototype.height=function(){return this._height},s.prototype.size=function(t){return(t=t||{}).width=this._width,t.height=this._height,t},s.prototype.parameters=function(t){return(t=t||{}).x=this._x,t.y=this._y,t.zoom=this._zoom,t.mediaAspectRatio=this._mediaAspectRatio,t},s.prototype.limiter=function(){return this._limiter},s.prototype.setX=function(t){this._resetParams(),this._params.x=t,this._update(this._params)},s.prototype.setY=function(t){this._resetParams(),this._params.y=t,this._update(this._params)},s.prototype.setZoom=function(t){this._resetParams(),this._params.zoom=t,this._update(this._params)},s.prototype.offsetX=function(t){this.setX(this._x+t)},s.prototype.offsetY=function(t){this.setY(this._y+t)},s.prototype.offsetZoom=function(t){this.setZoom(this._zoom+t)},s.prototype.setMediaAspectRatio=function(t){this._resetParams(),this._params.mediaAspectRatio=t,this._update(this._params)},s.prototype.setSize=function(t){this._resetParams(),this._params.width=t.width,this._params.height=t.height,this._update(this._params)},s.prototype.setParameters=function(t){this._resetParams(),this._params.x=t.x,this._params.y=t.y,this._params.zoom=t.zoom,this._params.mediaAspectRatio=t.mediaAspectRatio,this._update(this._params)},s.prototype.setLimiter=function(t){this._limiter=t||null,this._update()},s.prototype._resetParams=function(){var t=this._params;t.x=null,t.y=null,t.zoom=null,t.mediaAspectRatio=null,t.width=null,t.height=null},s.prototype._update=function(t){null==t&&(this._resetParams(),t=this._params);var e=this._x,i=this._y,r=this._zoom,n=this._mediaAspectRatio,o=this._width,s=this._height;if(t.x=null!=t.x?t.x:e,t.y=null!=t.y?t.y:i,t.zoom=null!=t.zoom?t.zoom:r,t.mediaAspectRatio=null!=t.mediaAspectRatio?t.mediaAspectRatio:n,t.width=null!=t.width?t.width:o,t.height=null!=t.height?t.height:s,this._limiter&&!(t=this._limiter(t)))throw new Error("Bad view limiter");var a=t.x,h=t.y,u=t.zoom,l=t.mediaAspectRatio,c=t.width,t=t.height;if(!(p(a)&&p(h)&&p(u)&&p(l)&&p(c)&&p(t)))throw new Error("Bad view - suspect a broken limiter");u=f(u,1e-6,1/0),this._x=a,this._y=h,this._zoom=u,this._mediaAspectRatio=l,this._width=c,this._height=t,a===e&&h===i&&u===r&&l===n&&c===o&&t===s||(this._projectionChanged=!0,this.emit("change")),c===o&&t===s||this.emit("resize")},s.prototype._zoomX=function(){return this._zoom},s.prototype._zoomY=function(){var t=this._mediaAspectRatio,e=this._width/this._height,i=this._zoom,e=i*t/e;return isNaN(e)&&(e=i),e},s.prototype.updateWithControlParameters=function(t){var e=this.zoom(),i=this._zoomX(),r=this._zoomY();this.offsetX(t.axisScaledX*i+t.x*e),this.offsetY(t.axisScaledY*r+t.y*e),this.offsetZoom(t.zoom*e)},s.prototype._updateProjection=function(){var t,e,i,r,n,o,s=this._projMatrix,a=this._invProjMatrix,h=this._frustum;this._projectionChanged&&(t=this._x,e=this._y,o=this._zoomX(),n=this._zoomY(),i=h[0]=.5-e+.5*n,r=h[1]=t-.5+.5*o,n=h[2]=.5-e-.5*n,o=h[3]=t-.5-.5*o,u.ortho(s,o,r,n,i,-1,1),u.invert(a,s),this._projectionChanged=!1)},s.prototype.projection=function(){return this._updateProjection(),this._projMatrix},s.prototype.inverseProjection=function(){return this._updateProjection(),this._invProjMatrix},s.prototype.intersects=function(t){this._updateProjection();for(var e=this._frustum,i=0;ir){s=!0;break}}if(!s)return!1}return!0},s.prototype.selectLevel=function(t){for(var e=o()*this.width(),i=this._zoom,r=0;r=e)return n}return t[t.length-1]},s.prototype.coordinatesToScreen=function(t,e){var i=this._vec;e=e||{};var r=this._width,n=this._height;if(r<=0||n<=0)return e.x=null,e.y=null;var o=t&&null!=t.x?t.x:.5,t=t&&null!=t.y?t.y:.5;a.set(i,o-.5,.5-t,-1,1),a.transformMat4(i,i,this.projection());for(var s=0;s<3;s++)i[s]/=i[3];return e.x=r*(i[0]+1)/2,e.y=n*(1-i[1])/2,e},s.prototype.screenToCoordinates=function(t,e){var i=this._vec;e=e||{};var r=this._width,n=this._height,r=2*t.x/r-1,n=1-2*t.y/n;return a.set(i,r,n,1,1),a.transformMat4(i,i,this.inverseProjection()),e.x=.5+i[0],e.y=.5-i[1],e},s.limit={x:function(e,i){return function(t){return t.x=f(t.x,e,i),t}},y:function(e,i){return function(t){return t.y=f(t.y,e,i),t}},zoom:function(e,i){return function(t){return t.zoom=f(t.zoom,e,i),t}},resolution:function(i){return function(t){if(t.width<=0||t.height<=0)return t;var e=t.width,e=o()*e/i;return t.zoom=f(t.zoom,e,1/0),t}},visibleX:function(r,n){return function(t){var e=n-r;t.zoom>e&&(t.zoom=e);var i=r+.5*t.zoom,e=n-.5*t.zoom;return t.x=f(t.x,i,e),t}},visibleY:function(r,n){return function(t){if(t.width<=0||t.height<=0)return t;var e=t.width/t.height/t.mediaAspectRatio,i=(n-r)*e;t.zoom>i&&(t.zoom=i);i=r+.5*t.zoom/e,e=n-.5*t.zoom/e;return t.y=f(t.y,i,e),t}},letterbox:function(){return function(t){if(t.width<=0||t.height<=0)return t;var e,i,r,n,o=t.width/t.height,s=o/t.mediaAspectRatio;return t.mediaAspectRatio>=o&&(t.zoom=Math.min(t.zoom,1)),t.mediaAspectRatio<=o&&(t.zoom=Math.min(t.zoom,s)),1s?r=n=.5:(r=0+.5*t.zoom/s,n=1-.5*t.zoom/s),t.x=f(t.x,e,i),t.y=f(t.y,r,n),t}}},s.type=s.prototype.type="flat",e.exports=s},{"../util/clamp":75,"../util/clearOwnProperties":76,"../util/pixelRatio":95,"../util/real":98,"gl-matrix":3,"minimal-event-emitter":14}],103:[function(t,e,i){"use strict";var r=t("minimal-event-emitter"),u=t("gl-matrix").mat4,h=t("gl-matrix").vec4,o=t("../util/pixelRatio"),l=t("../util/convertFov"),n=t("../util/mod"),v=t("../util/real"),s=t("../util/clamp"),a=t("../util/decimal"),c=t("../util/compose"),p=t("../util/clearOwnProperties"),f=Math.PI/4;function d(t,e){this._yaw=t&&null!=t.yaw?t.yaw:0,this._pitch=t&&null!=t.pitch?t.pitch:0,this._roll=t&&null!=t.roll?t.roll:0,this._fov=t&&null!=t.fov?t.fov:f,this._width=t&&null!=t.width?t.width:0,this._height=t&&null!=t.height?t.height:0,this._projectionCenterX=t&&null!=t.projectionCenterX?t.projectionCenterX:0,this._projectionCenterY=t&&null!=t.projectionCenterY?t.projectionCenterY:0,this._limiter=e||null,this._projMatrix=u.create(),this._invProjMatrix=u.create(),this._frustum=[h.create(),h.create(),h.create(),h.create(),h.create()],this._projectionChanged=!0,this._params={},this._fovs={},this._tmpVec=h.create(),this._update()}r(d),d.prototype.destroy=function(){p(this)},d.prototype.yaw=function(){return this._yaw},d.prototype.pitch=function(){return this._pitch},d.prototype.roll=function(){return this._roll},d.prototype.projectionCenterX=function(){return this._projectionCenterX},d.prototype.projectionCenterY=function(){return this._projectionCenterY},d.prototype.fov=function(){return this._fov},d.prototype.width=function(){return this._width},d.prototype.height=function(){return this._height},d.prototype.size=function(t){return(t=t||{}).width=this._width,t.height=this._height,t},d.prototype.parameters=function(t){return(t=t||{}).yaw=this._yaw,t.pitch=this._pitch,t.roll=this._roll,t.fov=this._fov,t},d.prototype.limiter=function(){return this._limiter},d.prototype.setYaw=function(t){this._resetParams(),this._params.yaw=t,this._update(this._params)},d.prototype.setPitch=function(t){this._resetParams(),this._params.pitch=t,this._update(this._params)},d.prototype.setRoll=function(t){this._resetParams(),this._params.roll=t,this._update(this._params)},d.prototype.setFov=function(t){this._resetParams(),this._params.fov=t,this._update(this._params)},d.prototype.setProjectionCenterX=function(t){this._resetParams(),this._params.projectionCenterX=t,this._update(this._params)},d.prototype.setProjectionCenterY=function(t){this._resetParams(),this._params.projectionCenterY=t,this._update(this._params)},d.prototype.offsetYaw=function(t){this.setYaw(this._yaw+t)},d.prototype.offsetPitch=function(t){this.setPitch(this._pitch+t)},d.prototype.offsetRoll=function(t){this.setRoll(this._roll+t)},d.prototype.offsetFov=function(t){this.setFov(this._fov+t)},d.prototype.setSize=function(t){this._resetParams(),this._params.width=t.width,this._params.height=t.height,this._update(this._params)},d.prototype.setParameters=function(t){this._resetParams(),this._params.yaw=t.yaw,this._params.pitch=t.pitch,this._params.roll=t.roll,this._params.fov=t.fov,this._params.projectionCenterX=t.projectionCenterX,this._params.projectionCenterY=t.projectionCenterY,this._update(this._params)},d.prototype.setLimiter=function(t){this._limiter=t||null,this._update()},d.prototype._resetParams=function(){var t=this._params;t.yaw=null,t.pitch=null,t.roll=null,t.fov=null,t.width=null,t.height=null},d.prototype._update=function(t){null==t&&(this._resetParams(),t=this._params);var e=this._yaw,i=this._pitch,r=this._roll,n=this._fov,o=this._projectionCenterX,s=this._projectionCenterY,a=this._width,h=this._height;if(t.yaw=null!=t.yaw?t.yaw:e,t.pitch=null!=t.pitch?t.pitch:i,t.roll=null!=t.roll?t.roll:r,t.fov=null!=t.fov?t.fov:n,t.width=null!=t.width?t.width:a,t.height=null!=t.height?t.height:h,t.projectionCenterX=null!=t.projectionCenterX?t.projectionCenterX:o,t.projectionCenterY=null!=t.projectionCenterY?t.projectionCenterY:s,this._limiter&&!(t=this._limiter(t)))throw new Error("Bad view limiter");var u=(t=this._normalize(t)).yaw,l=t.pitch,c=t.roll,p=t.fov,f=t.width,d=t.height,m=t.projectionCenterX,t=t.projectionCenterY;if(!(v(u)&&v(l)&&v(c)&&v(p)&&v(f)&&v(d)&&v(m)&&v(t)))throw new Error("Bad view - suspect a broken limiter");this._yaw=u,this._pitch=l,this._roll=c,this._fov=p,this._width=f,this._height=d,this._projectionCenterX=m,this._projectionCenterY=t,u===e&&l===i&&c===r&&p===n&&f===a&&d===h&&m===o&&t===s||(this._projectionChanged=!0,this.emit("change")),f===a&&d===h||this.emit("resize")},d.prototype._normalize=function(t){this._normalizeCoordinates(t);var e=l.htov(Math.PI,t.width,t.height),e=isNaN(e)?Math.PI:Math.min(Math.PI,e);return t.fov=s(t.fov,1e-6,e-1e-6),t},d.prototype._normalizeCoordinates=function(t){return"yaw"in t&&(t.yaw=n(t.yaw-Math.PI,-2*Math.PI)+Math.PI),"pitch"in t&&(t.pitch=n(t.pitch-Math.PI,-2*Math.PI)+Math.PI),"roll"in t&&(t.roll=n(t.roll-Math.PI,-2*Math.PI)+Math.PI),t},d.prototype.normalizeToClosest=function(t,e){var i=this._yaw,r=this._pitch,n=t.yaw,o=t.pitch,s=n-2*Math.PI,t=n+2*Math.PI;Math.abs(s-i)=e)return n}return t[t.length-1]},d.prototype.coordinatesToScreen=function(t,e){var i=this._tmpVec;e=e||{};var r=this._width,n=this._height;if(r<=0||n<=0)return e.x=null,e.y=null;var o=t.yaw,s=t.pitch,a=Math.sin(o)*Math.cos(s),t=-Math.sin(s),s=-Math.cos(o)*Math.cos(s);return h.set(i,a,t,s,1),h.transformMat4(i,i,this.projection()),0<=i[3]?(e.x=r*(i[0]/i[3]+1)/2,e.y=n*(1-i[1]/i[3])/2,e):(e.x=null,e.y=null)},d.prototype.screenToCoordinates=function(t,e){var i=this._tmpVec;e=e||{};var r=this._width,n=this._height,r=2*t.x/r-1,n=1-2*t.y/n;h.set(i,r,n,1,1),h.transformMat4(i,i,this.inverseProjection());n=Math.sqrt(i[0]*i[0]+i[1]*i[1]+i[2]*i[2]);return e.yaw=Math.atan2(i[0],-i[2]),e.pitch=Math.acos(i[1]/n)-Math.PI/2,this._normalizeCoordinates(e),e},d.prototype.coordinatesToPerspectiveTransform=function(t,e,i){i=i||"";var r=this._height,n=this._width,o=this._fov,s=.5*r/Math.tan(o/2),o="";return o+="translateX("+a(n/2)+"px) ",o+="translateY("+a(r/2)+"px) ",o+="translateX(-50%) translateY(-50%) ",o+="perspective("+a(s)+"px) ",o+="translateZ("+a(s)+"px) ",o+="rotateZ("+a(-this._roll)+"rad) ",o+="rotateX("+a(-this._pitch)+"rad) ",o+="rotateY("+a(this._yaw)+"rad) ",o+="rotateY("+a(-t.yaw)+"rad) ",o+="rotateX("+a(t.pitch)+"rad) ",o+="translateZ("+a(-e)+"px) ",o+=i+" "},d.limit={yaw:function(e,i){return function(t){return t.yaw=s(t.yaw,e,i),t}},pitch:function(e,i){return function(t){return t.pitch=s(t.pitch,e,i),t}},roll:function(e,i){return function(t){return t.roll=s(t.roll,e,i),t}},hfov:function(n,o){return function(t){var e,i=t.width,r=t.height;return 0- The 'Throw' mode: Rez the cam, grab it, then toss it high in the sky to capture an incredible view of of any event.
- The 'Position' mode: Position then click to capture. Ideal to generate an ambient light texture for any scene.
This includes a 360° media viewer.", + "jsfile": "cam360/cam360.js", + "icon": "cam360/resources/images/icons/cam360-i.svg", + "caption": "CAM360" } ] };