diff --git a/.gitignore b/.gitignore index 5148e52..b592adc 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ jspm_packages # Optional REPL history .node_repl_history + +# Ignore new data in config file +config.js \ No newline at end of file diff --git a/README.md b/README.md index ab3df11..9fb4a12 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,79 @@ # OSWaldito +![oswaldito-front](images/oswaldito-front.jpg) + Robot Open Source que utilizamos en OSWeekends para gestionar redes sociales, telepresencia, etc.... + +**Redes Sociales** +- [Twitter (@OSWaldito_bot)](https://twitter.com/oswalditobot) + +### Equipo +![oswaldito-team](images/osweekends-team.jpg) + +OSWaldito es un proyecto de Open Source Weekends. Y es considerado un miembro más de la organización. + +### Importante + +**Arranque manual de FFMPEG** + +```bash +sudo ffmpeg -s 320x240 -y -f v4l2 -i /dev/video0 -update 1 -r 30 /home/pi/Desktop/oswaldito/public/cam.jpg +``` + + +### Software + +**Sistema Operativo** +- [Raspbian Jessie](https://www.raspberrypi.org/blog/raspbian-jessie-is-here/) +- [Nodejs](https://nodejs.org/) +- [FFMPEG](https://www.ffmpeg.org/) + +**Servidor** +- [pillars](https://www.npmjs.com/package/pillars) +- [rpio](https://www.npmjs.com/package/rpio) +- [socket.io](https://www.npmjs.com/package/socket.io) +- [hangouts-bot](https://www.npmjs.com/package/hangouts-bot) +- [scheduled](https://www.npmjs.com/package/scheduled) +- [slack-node](https://www.npmjs.com/package/slack-node) +- [twitter](https://www.npmjs.com/package/twitter) + + +**Cliente** +- [three.js](https://threejs.org/) + +### Funcionalidades + +**Robótica** +- Desplazamiento con WebSocket +- Simulación de VR en Cliente +- Simulación de voz en Cliente (parcial - En desarrollo) +- WebCam Stream frame a frame + +**Comunicación** +- Escucha activamente ciertos hashtags de Twitter. +- Escucha activamente conversaciones en Google Hangouts +- Envia mensajes en Google Hangouts +- Envia mensajes en Slack +- Envia mensajes al azar clasificados por prioridad en Slack +- Envia mensajes de Error y estado al administrador en Goolgle Hangouts +- Puede ser desplegado en multiples entornos (Raspbian, Linux, OSX, Windows, C9...) +- Permite desplegar multiples avatares y personalidades desde la configuración para comunicarse en Slack + +### Hardware +![hardware](images/hardware.jpg) + +**Componentes** +- [Raspberry Pi 3 modelo B](https://www.amazon.es/dp/B01CD5VC92) +- [Pantalla - SunFounder 7" HD 1024*600 TFT LCD](https://www.amazon.es/gp/product/B012ZRYDYY) +- [Chasis Robótico - ALSRobot 4WD Aluminum Mobile Robot Platform (Car)](https://www.amazon.com/ALSRobot-Aluminum-Mobile-Robot-Platform/dp/B00ME4FI2U/ref=pd_sbs_21_4?_encoding=UTF8&pd_rd_i=B00ME4FI2U&pd_rd_r=HDKE8MN2G3KJDYD8DKWG&pd_rd_w=w3u5s&pd_rd_wg=Sk1We&psc=1&refRID=HDKE8MN2G3KJDYD8DKWG) +- [WebCam - Logitech C525](https://www.amazon.es/Logitech-C525-Webcam-Micr%C3%B3fono-Negro/dp/B0050FBISI/ref=sr_1_5?s=computers&ie=UTF8&qid=1479673606&sr=1-5&keywords=webcam+logitech) +- [Puente H - Módulo L298N doble puente H DC Stepper para Arduino](https://www.amazon.es/gp/product/B00HNHUYSG) + +### Construcción + +![chasis-1](images/chasis-1.jpg) +![chasis-2](images/chasis-2.jpg) + +### Agradecimientos + +- [Reloj](public/clock.html) *Gracias a [dtinth en Codepen](http://codepen.io/dtinth/pen/tDihg)* +- [VR simulación](public/vr.html) *Gracias a [sgang007/vrStreamer en Github](https://github.com/sgang007/vrStreamer)* \ No newline at end of file diff --git a/config.js b/config.js new file mode 100644 index 0000000..c8d8eba --- /dev/null +++ b/config.js @@ -0,0 +1,40 @@ +var config = { + socialConnectionEnable: true, + hangouts: { + usuarioId: "", + usuarioAutorizado: "", + botEmail: "", + botPassword: "" + }, + slack: { + webhookUri: "", + channel: "", + defaultMessage: "", + username: "", + icon: "" + }, + twitter : { + consumer_key: "", + consumer_secret: "", + access_token_key: "", + access_token_secret: "" + }, + event: { + hashtags: [], + timerPriority: { + high: 0, + medium: 0, + low: 0, + }, + messagesByPriority: { + high: [], + medium: [], + low: [], + }, + messagesScheduled: [ + {cron: "", job: "", msg: ""} + ] + } +}; + +module.exports = config; \ No newline at end of file diff --git a/directo.js b/directo.js new file mode 100644 index 0000000..601f983 --- /dev/null +++ b/directo.js @@ -0,0 +1,132 @@ +var config = require('./config'), + credentials = require('./config').twitter, + eventConfig = require('./config').event, + SlackConfig = require('./config').slack, + Scheduled = require("scheduled"), + twitter = require('twitter'), + hangoutsBot = require("hangouts-bot"), + Slack = require('slack-node'); + +var twitterStream = new twitter(credentials), + hashtags = eventConfig.hashtags, + slack = new Slack(), + masterUserRegex = new RegExp(config.hangouts.usuarioId), + masterUser = config.hangouts.usuarioAutorizado, + bot = new hangoutsBot(config.hangouts.botEmail, config.hangouts.botPassword), + slackRegex = /^[Ss]lack /; + + +// Slack +slack.setWebhook(SlackConfig.webhookUri); + +function slackNotify(msg, details) { + details = details || {}; + slack.webhook({ + channel: details.channel || SlackConfig.channel, + text: msg || SlackConfig.defaultMessage, + username: details.username || SlackConfig.username, + icon_emoji: details.icon || SlackConfig.icon + }, function(err, response) { + if (err) { + bot.sendMessage(masterUser, "[Error] SLACK: " + err); + } + }); +} + +// Google Hangouts +bot.on('online', function() { + bot.sendMessage(masterUser, "Hola de nuevo, Jefe!"); +}); + +bot.on('message', function(from, message) { + if (message === "Quien soy?") { + bot.sendMessage(from, "Yo te conozco como " + from); + } else if (slackRegex.test(message)) { + if (masterUserRegex.test(from)) { + message = message.replace(slackRegex, ''); + slackNotify(message); + } else { + bot.sendMessage(from, 'Buen intento.. pero con esas zapatillas no envío mensajes!'); + bot.sendMessage(masterUser, 'Intento de lanzar un mensaje por parte de ' + from + '\n Contenido: ' + message); + } + } else { + bot.sendMessage(from, "No te entiendo... Explicate mejor, Humano!"); + } +}); + + +// Twitter +twitterStream.stream('statuses/filter', { + track: hashtags.join(',') +}, function(stream) { + + stream.on('data', function(tweet) { + if (tweet.text !== undefined) { + + var data = { + username: "Twitter", + icon: "https://cdn3.iconfinder.com/data/icons/social-icons-5/607/Twitterbird.png" + }; + + slackNotify('https://twitter.com/' + tweet.user.screen_name + '/status/' + tweet.id_str, data); + } + }); + + stream.on('error', function(error, code) { + bot.sendMessage(masterUser, "[ERROR][Twitter-Stream] Código: " + code + ". Detalle: " + error + ". CC: @ulisesgascon"); + }); + +}); + + +// Bienvenida +slackNotify("Ya estoy Aquí! Se abren las notificaciones automáticas.\n¡Salvemos el mundo haciendo Software Libre!"); + + +// Temporización via config.js +var messageManager = []; + +for (var messages in eventConfig.messagesByPriority){ + setTimer(messages); +} + +function setTimer (key) { + if(eventConfig.timerPriority[key] && eventConfig.messagesByPriority[key]){ + setInterval(function(){ + slackNotify(eventConfig.messagesByPriority[key][Math.floor(Math.random() * eventConfig.messagesByPriority[key].length)]); + }, eventConfig.timerPriority[key]); + } + +} + +for (var item in eventConfig.messagesScheduled){ + if(item.cron && item.job && item.msg){ + messageManager.push(new Scheduled({ + id: item.job, + pattern: item.cron, + task: function(){ + slackNotify(item.msg); + } + }).start()); + } +} + + +// Eventos +process.on('SIGINT', function() { + slackNotify("Me expulsan del canal! Ya no me quereis como antes... \ncc: @ulisesgascon te portas mal conmigo."); + bot.sendMessage(masterUser, "Me piro! Ya no me quieres como antes..."); + process.exit(); +}); + +process.on('exit', function() { + slackNotify("Salgo del canal! Pasarlo bien amigos.... \n¡Paz, amor y Open Source! \ncc: @ulisesgascon"); + bot.sendMessage(masterUser, "Me piro! Vaciones por fín!"); + process.exit(); +}); + +process.on('uncaughtException', function() { + slackNotify("Salgo del canal! Cierre inesperado... \n¡Paz, amor y Open Source!\ncc: @ulisesgascon"); + bot.sendMessage(masterUser, "Me piro! Cierre inesperado..."); + process.exit(); +}); \ No newline at end of file diff --git a/images/chasis-1.jpg b/images/chasis-1.jpg new file mode 100644 index 0000000..258b376 Binary files /dev/null and b/images/chasis-1.jpg differ diff --git a/images/chasis-2.jpg b/images/chasis-2.jpg new file mode 100644 index 0000000..926cc0e Binary files /dev/null and b/images/chasis-2.jpg differ diff --git a/images/hardware.jpg b/images/hardware.jpg new file mode 100644 index 0000000..41ec265 Binary files /dev/null and b/images/hardware.jpg differ diff --git a/images/oswaldito-front.jpg b/images/oswaldito-front.jpg new file mode 100644 index 0000000..328f9c0 Binary files /dev/null and b/images/oswaldito-front.jpg differ diff --git a/images/osweekends-team.jpg b/images/osweekends-team.jpg new file mode 100644 index 0000000..e293fd1 Binary files /dev/null and b/images/osweekends-team.jpg differ diff --git a/package.json b/package.json new file mode 100644 index 0000000..6e22f9b --- /dev/null +++ b/package.json @@ -0,0 +1,43 @@ +{ + "name": "oswaldito", + "version": "0.1.0", + "description": "Robot Open Source que utilizamos en OSWeekends para gestionar redes sociales, telepresencia, etc....", + "main": "server", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/OSWeekends/OSWaldito.git" + }, + "keywords": [ + "robot", + "raspberry", + "pi", + "iot", + "hardware", + "slack", + "socialbot" + ], + "author": "Open Source Weekends", + "contributors": [ + { + "name": "Ulises Gascón", + "url": "https://github.com/ulisesGascon" + } + ], + "license": "GPL-3.0", + "bugs": { + "url": "https://github.com/OSWeekends/OSWaldito/issues" + }, + "homepage": "https://github.com/OSWeekends/OSWaldito#readme", + "dependencies": { + "pillars": "0.3.7", + "rpio": "0.9.12", + "socket.io": "1.5.0", + "hangouts-bot": "0.2.1", + "scheduled": "0.4.5", + "slack-node": "0.1.8", + "twitter": "1.4.0" + } +} diff --git a/public/clock.html b/public/clock.html new file mode 100644 index 0000000..964a1e6 --- /dev/null +++ b/public/clock.html @@ -0,0 +1,264 @@ + + +
+ +f;f++){a.__webglFramebuffer[f]=m.createFramebuffer();a.__webglRenderbuffer[f]=m.createRenderbuffer();m.texImage2D(m.TEXTURE_CUBE_MAP_POSITIVE_X+f,0,d,a.width,a.height,0,d,e,null);var g=a,h=m.TEXTURE_CUBE_MAP_POSITIVE_X+f;m.bindFramebuffer(m.FRAMEBUFFER,
+a.__webglFramebuffer[f]);m.framebufferTexture2D(m.FRAMEBUFFER,m.COLOR_ATTACHMENT0,h,g.__webglTexture,0);G(a.__webglRenderbuffer[f],a)}c&&m.generateMipmap(m.TEXTURE_CUBE_MAP)}else a.__webglFramebuffer=m.createFramebuffer(),a.__webglRenderbuffer=a.shareDepthFrom?a.shareDepthFrom.__webglRenderbuffer:m.createRenderbuffer(),m.bindTexture(m.TEXTURE_2D,a.__webglTexture),A(m.TEXTURE_2D,a,c),m.texImage2D(m.TEXTURE_2D,0,d,a.width,a.height,0,d,e,null),d=m.TEXTURE_2D,m.bindFramebuffer(m.FRAMEBUFFER,a.__webglFramebuffer),
+m.framebufferTexture2D(m.FRAMEBUFFER,m.COLOR_ATTACHMENT0,d,a.__webglTexture,0),a.shareDepthFrom?a.depthBuffer&&!a.stencilBuffer?m.framebufferRenderbuffer(m.FRAMEBUFFER,m.DEPTH_ATTACHMENT,m.RENDERBUFFER,a.__webglRenderbuffer):a.depthBuffer&&a.stencilBuffer&&m.framebufferRenderbuffer(m.FRAMEBUFFER,m.DEPTH_STENCIL_ATTACHMENT,m.RENDERBUFFER,a.__webglRenderbuffer):G(a.__webglRenderbuffer,a),c&&m.generateMipmap(m.TEXTURE_2D);b?m.bindTexture(m.TEXTURE_CUBE_MAP,null):m.bindTexture(m.TEXTURE_2D,null);m.bindRenderbuffer(m.RENDERBUFFER,
+null);m.bindFramebuffer(m.FRAMEBUFFER,null)}a?(b=b?a.__webglFramebuffer[a.activeCubeFace]:a.__webglFramebuffer,c=a.width,a=a.height,e=d=0):(b=null,c=pb,a=qb,d=ib,e=bb);b!==ab&&(m.bindFramebuffer(m.FRAMEBUFFER,b),m.viewport(d,e,c,a),ab=b);Xb=c;fc=a};this.readRenderTargetPixels=function(a,b,c,d,e,f){if(!(a instanceof THREE.WebGLRenderTarget))console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");else if(a.__webglFramebuffer)if(a.format!==THREE.RGBAFormat)console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA format. readPixels can read only RGBA format.");
+else{var g=!1;a.__webglFramebuffer!==ab&&(m.bindFramebuffer(m.FRAMEBUFFER,a.__webglFramebuffer),g=!0);m.checkFramebufferStatus(m.FRAMEBUFFER)===m.FRAMEBUFFER_COMPLETE?m.readPixels(b,c,d,e,m.RGBA,m.UNSIGNED_BYTE,f):console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.");g&&m.bindFramebuffer(m.FRAMEBUFFER,ab)}};this.initMaterial=function(){THREE.warn("THREE.WebGLRenderer: .initMaterial() has been removed.")};this.addPrePlugin=function(){THREE.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed.")};
+this.addPostPlugin=function(){THREE.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed.")};this.updateShadowMap=function(){THREE.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed.")}};
+THREE.WebGLRenderTarget=function(a,b,c){this.width=a;this.height=b;c=c||{};this.wrapS=void 0!==c.wrapS?c.wrapS:THREE.ClampToEdgeWrapping;this.wrapT=void 0!==c.wrapT?c.wrapT:THREE.ClampToEdgeWrapping;this.magFilter=void 0!==c.magFilter?c.magFilter:THREE.LinearFilter;this.minFilter=void 0!==c.minFilter?c.minFilter:THREE.LinearMipMapLinearFilter;this.anisotropy=void 0!==c.anisotropy?c.anisotropy:1;this.offset=new THREE.Vector2(0,0);this.repeat=new THREE.Vector2(1,1);this.format=void 0!==c.format?c.format:
+THREE.RGBAFormat;this.type=void 0!==c.type?c.type:THREE.UnsignedByteType;this.depthBuffer=void 0!==c.depthBuffer?c.depthBuffer:!0;this.stencilBuffer=void 0!==c.stencilBuffer?c.stencilBuffer:!0;this.generateMipmaps=!0;this.shareDepthFrom=void 0!==c.shareDepthFrom?c.shareDepthFrom:null};
+THREE.WebGLRenderTarget.prototype={constructor:THREE.WebGLRenderTarget,setSize:function(a,b){this.width=a;this.height=b},clone:function(){var a=new THREE.WebGLRenderTarget(this.width,this.height);a.wrapS=this.wrapS;a.wrapT=this.wrapT;a.magFilter=this.magFilter;a.minFilter=this.minFilter;a.anisotropy=this.anisotropy;a.offset.copy(this.offset);a.repeat.copy(this.repeat);a.format=this.format;a.type=this.type;a.depthBuffer=this.depthBuffer;a.stencilBuffer=this.stencilBuffer;a.generateMipmaps=this.generateMipmaps;
+a.shareDepthFrom=this.shareDepthFrom;return a},dispose:function(){this.dispatchEvent({type:"dispose"})}};THREE.EventDispatcher.prototype.apply(THREE.WebGLRenderTarget.prototype);THREE.WebGLRenderTargetCube=function(a,b,c){THREE.WebGLRenderTarget.call(this,a,b,c);this.activeCubeFace=0};THREE.WebGLRenderTargetCube.prototype=Object.create(THREE.WebGLRenderTarget.prototype);THREE.WebGLRenderTargetCube.prototype.constructor=THREE.WebGLRenderTargetCube;
+THREE.WebGLExtensions=function(a){var b={};this.get=function(c){if(void 0!==b[c])return b[c];var d;switch(c){case "EXT_texture_filter_anisotropic":d=a.getExtension("EXT_texture_filter_anisotropic")||a.getExtension("MOZ_EXT_texture_filter_anisotropic")||a.getExtension("WEBKIT_EXT_texture_filter_anisotropic");break;case "WEBGL_compressed_texture_s3tc":d=a.getExtension("WEBGL_compressed_texture_s3tc")||a.getExtension("MOZ_WEBGL_compressed_texture_s3tc")||a.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc");
+break;case "WEBGL_compressed_texture_pvrtc":d=a.getExtension("WEBGL_compressed_texture_pvrtc")||a.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc");break;default:d=a.getExtension(c)}null===d&&THREE.warn("THREE.WebGLRenderer: "+c+" extension not supported.");return b[c]=d}};
+THREE.WebGLProgram=function(){var a=0;return function(b,c,d,e){var f=b.context,g=d.defines,h=d.__webglShader.uniforms,k=d.attributes,l=d.__webglShader.vertexShader,p=d.__webglShader.fragmentShader,q=d.index0AttributeName;void 0===q&&!0===e.morphTargets&&(q="position");var n="SHADOWMAP_TYPE_BASIC";e.shadowMapType===THREE.PCFShadowMap?n="SHADOWMAP_TYPE_PCF":e.shadowMapType===THREE.PCFSoftShadowMap&&(n="SHADOWMAP_TYPE_PCF_SOFT");var t="ENVMAP_TYPE_CUBE",r="ENVMAP_MODE_REFLECTION",s="ENVMAP_BLENDING_MULTIPLY";
+if(e.envMap){switch(d.envMap.mapping){case THREE.CubeReflectionMapping:case THREE.CubeRefractionMapping:t="ENVMAP_TYPE_CUBE";break;case THREE.EquirectangularReflectionMapping:case THREE.EquirectangularRefractionMapping:t="ENVMAP_TYPE_EQUIREC";break;case THREE.SphericalReflectionMapping:t="ENVMAP_TYPE_SPHERE"}switch(d.envMap.mapping){case THREE.CubeRefractionMapping:case THREE.EquirectangularRefractionMapping:r="ENVMAP_MODE_REFRACTION"}switch(d.combine){case THREE.MultiplyOperation:s="ENVMAP_BLENDING_MULTIPLY";
+break;case THREE.MixOperation:s="ENVMAP_BLENDING_MIX";break;case THREE.AddOperation:s="ENVMAP_BLENDING_ADD"}}var u=0 t&&(.2>d&&(b[0].x+=1),.2>a&&(b[1].x+=1),.2>q&&(b[2].x+=1));l=0;for(p=this.vertices.length;l c.y?this.quaternion.set(1,0,0,0):(a.set(c.z,0,-c.x).normalize(),b=Math.acos(c.y),this.quaternion.setFromAxisAngle(a,b))}}();THREE.ArrowHelper.prototype.setLength=function(a,b,c){void 0===b&&(b=.2*a);void 0===c&&(c=.2*b);this.line.scale.set(1,a-b,1);this.line.updateMatrix();this.cone.scale.set(c,b,c);this.cone.position.y=a;this.cone.updateMatrix()};
+THREE.ArrowHelper.prototype.setColor=function(a){this.line.material.color.set(a);this.cone.material.color.set(a)};THREE.BoxHelper=function(a){var b=new THREE.BufferGeometry;b.addAttribute("position",new THREE.BufferAttribute(new Float32Array(72),3));THREE.Line.call(this,b,new THREE.LineBasicMaterial({color:16776960}),THREE.LinePieces);void 0!==a&&this.update(a)};THREE.BoxHelper.prototype=Object.create(THREE.Line.prototype);THREE.BoxHelper.prototype.constructor=THREE.BoxHelper;
+THREE.BoxHelper.prototype.update=function(a){var b=a.geometry;null===b.boundingBox&&b.computeBoundingBox();var c=b.boundingBox.min,b=b.boundingBox.max,d=this.geometry.attributes.position.array;d[0]=b.x;d[1]=b.y;d[2]=b.z;d[3]=c.x;d[4]=b.y;d[5]=b.z;d[6]=c.x;d[7]=b.y;d[8]=b.z;d[9]=c.x;d[10]=c.y;d[11]=b.z;d[12]=c.x;d[13]=c.y;d[14]=b.z;d[15]=b.x;d[16]=c.y;d[17]=b.z;d[18]=b.x;d[19]=c.y;d[20]=b.z;d[21]=b.x;d[22]=b.y;d[23]=b.z;d[24]=b.x;d[25]=b.y;d[26]=c.z;d[27]=c.x;d[28]=b.y;d[29]=c.z;d[30]=c.x;d[31]=b.y;
+d[32]=c.z;d[33]=c.x;d[34]=c.y;d[35]=c.z;d[36]=c.x;d[37]=c.y;d[38]=c.z;d[39]=b.x;d[40]=c.y;d[41]=c.z;d[42]=b.x;d[43]=c.y;d[44]=c.z;d[45]=b.x;d[46]=b.y;d[47]=c.z;d[48]=b.x;d[49]=b.y;d[50]=b.z;d[51]=b.x;d[52]=b.y;d[53]=c.z;d[54]=c.x;d[55]=b.y;d[56]=b.z;d[57]=c.x;d[58]=b.y;d[59]=c.z;d[60]=c.x;d[61]=c.y;d[62]=b.z;d[63]=c.x;d[64]=c.y;d[65]=c.z;d[66]=b.x;d[67]=c.y;d[68]=b.z;d[69]=b.x;d[70]=c.y;d[71]=c.z;this.geometry.attributes.position.needsUpdate=!0;this.geometry.computeBoundingSphere();this.matrix=a.matrixWorld;
+this.matrixAutoUpdate=!1};THREE.BoundingBoxHelper=function(a,b){var c=void 0!==b?b:8947848;this.object=a;this.box=new THREE.Box3;THREE.Mesh.call(this,new THREE.BoxGeometry(1,1,1),new THREE.MeshBasicMaterial({color:c,wireframe:!0}))};THREE.BoundingBoxHelper.prototype=Object.create(THREE.Mesh.prototype);THREE.BoundingBoxHelper.prototype.constructor=THREE.BoundingBoxHelper;THREE.BoundingBoxHelper.prototype.update=function(){this.box.setFromObject(this.object);this.box.size(this.scale);this.box.center(this.position)};
+THREE.CameraHelper=function(a){function b(a,b,d){c(a,d);c(b,d)}function c(a,b){d.vertices.push(new THREE.Vector3);d.colors.push(new THREE.Color(b));void 0===f[a]&&(f[a]=[]);f[a].push(d.vertices.length-1)}var d=new THREE.Geometry,e=new THREE.LineBasicMaterial({color:16777215,vertexColors:THREE.FaceColors}),f={};b("n1","n2",16755200);b("n2","n4",16755200);b("n4","n3",16755200);b("n3","n1",16755200);b("f1","f2",16755200);b("f2","f4",16755200);b("f4","f3",16755200);b("f3","f1",16755200);b("n1","f1",16755200);
+b("n2","f2",16755200);b("n3","f3",16755200);b("n4","f4",16755200);b("p","n1",16711680);b("p","n2",16711680);b("p","n3",16711680);b("p","n4",16711680);b("u1","u2",43775);b("u2","u3",43775);b("u3","u1",43775);b("c","t",16777215);b("p","c",3355443);b("cn1","cn2",3355443);b("cn3","cn4",3355443);b("cf1","cf2",3355443);b("cf3","cf4",3355443);THREE.Line.call(this,d,e,THREE.LinePieces);this.camera=a;this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1;this.pointMap=f;this.update()};
+THREE.CameraHelper.prototype=Object.create(THREE.Line.prototype);THREE.CameraHelper.prototype.constructor=THREE.CameraHelper;
+THREE.CameraHelper.prototype.update=function(){var a,b,c=new THREE.Vector3,d=new THREE.Camera,e=function(e,g,h,k){c.set(g,h,k).unproject(d);e=b[e];if(void 0!==e)for(g=0,h=e.length;ga&&(this.currentTime%=a);this.currentTime=Math.min(this.currentTime,a);a=0;for(var b=this.hierarchy.length;af.index;)f=g,g=e[f.index+1];d.prevKey=f;d.nextKey=g}g.time>=this.currentTime?f.interpolate(g,this.currentTime):
+f.interpolate(g,g.time);this.data.hierarchy[a].node.updateMatrix();c.matrixWorldNeedsUpdate=!0}}}},getNextKeyWith:function(a,b,c){b=this.data.hierarchy[b].keys;for(c%=b.length;c