tag:blogger.com,1999:blog-136983762024-03-13T21:44:13.954-07:00El blog tecnológico de Blag.Blog sobre Lenguajes Script, Tecnología y SAP.Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.comBlogger461125tag:blogger.com,1999:blog-13698376.post-65567875024176408082022-05-18T03:55:00.004-07:002022-05-18T03:55:30.037-07:00¿Qué es Nylas, por qué me uní y porque debería importarles?<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgM3Za3fXBOLYaGYUIZPhonfWuVouQjDXZVm7vIsoMBuToX4_dbnM2hmQ3vB6QNVOfaLn_0d7ZJPtuUjBpsE_m6wk_JZ8BOkcrwypx5BIhb6VCnlzqbXevXTlU6G9GyFPPsSaCWK1rDOfAO3aAlLzM9_4u1LHAnqzDxjfnaCQDKe_Ma0RvFPg/s2160/Office_Augmented.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1144" data-original-width="2160" height="338" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgM3Za3fXBOLYaGYUIZPhonfWuVouQjDXZVm7vIsoMBuToX4_dbnM2hmQ3vB6QNVOfaLn_0d7ZJPtuUjBpsE_m6wk_JZ8BOkcrwypx5BIhb6VCnlzqbXevXTlU6G9GyFPPsSaCWK1rDOfAO3aAlLzM9_4u1LHAnqzDxjfnaCQDKe_Ma0RvFPg/w640-h338/Office_Augmented.png" width="640" /></a></div><div><br /></div><br /> Me uní a Nylas más o menos hace 4 meses como su primer Senior Developer Advocate. Puede parecer un tiempo muy corto, pero en el mundo de los startups, el tiempo se mueve de forma diferente.<br /><br /><b><i>Aviso: Este post está escrito en Español, pensado en desarrolladores de habla hispana, pero no significa que Nylas ofrezca contenido, soporte o servicios en Español. Todo servicio es ofrecido únicamente en Inglés.</i></b><div><b><i><br /></i></b><div><h2 style="text-align: left;">¿Qué es Nylas?</h2><br />En palabras sencillas, Nylas es un grupo de APIs que nos permiten conectarnos de manera sencilla con cualquier proveedor de correo, calendario y contactos sin tener que pasar por ningún tipo de proceso largo y tedioso.<br /><br />En otras palabras, hacemos el trabajo pesado para que ustedes puedan enfocarse en tu negocio.</div><div><br /><div style="text-align: center;"><img height="296" src="https://media-exp1.licdn.com/dms/image/C5612AQF7ZKpTTkthJg/article-inline_image-shrink_1500_2232/0/1652867679983?e=1658361600&v=beta&t=-v_Py8LYWanVp83FAckLVjHQNRHuwCwC4HJY4dfEzn4" width="400" /></div><br />Si hablamos de Email, ofrecemos “Un API de Correo para cualquier proveedor”. Esto significa que nuestro API de Correo es universal y puedes conectarte con varios proveedores sin tener la necesidad de escribir código específico para cada uno de ellos. Además, proveemos capacidades de tiempo real, sincronización bi-direccional, y full CRUD (Crear, Leer, Actualizar, Borrar). Si quieres saber más, solo tienes que ir a nuestra página <a href="https://www.nylas.com/products/email-api/">Universal Email API</a>.</div><div><br /><div style="text-align: center;"><img height="258" src="https://media-exp1.licdn.com/dms/image/C5612AQEAi8pjV7Pk7w/article-inline_image-shrink_1000_1488/0/1652867723267?e=1658361600&v=beta&t=3S8NQjgx9vouNNk8C5T9RsVXxcqgT9sQ5PO-PW4M0iE" width="400" /></div><br />Cuando se trata de Calendarios, ofrecemos las mismas capacidades que en nuestro API de Correo API, pero también sincronización de conferencias, metadata de eventos, y webhooks programables. Si quieres saber más, solo tienes que ir a nuestra página <a href="https://www.nylas.com/products/calendar-api/">Universal Calendar API</a>.</div><div><br /><div style="text-align: center;"><img height="323" src="https://media-exp1.licdn.com/dms/image/C5612AQFdMJtRIEWOKg/article-inline_image-shrink_1000_1488/0/1652867746668?e=1658361600&v=beta&t=CKlnZ3gvoaWNhCVTM_IUYpkNJCO1Dmx3tO7TMpFZoUI" width="400" /></div><br />Si hablamos de Contactos, podemos crear, actualizar o eliminar contactos en cualquier proveedor compatible. Si quieres saber más, solo tienes que ir a nuestra página <a href="https://www.nylas.com/products/contacts-api/">Universal Contacts API</a>.</div><div><br /><div style="text-align: center;"><img height="323" src="https://media-exp1.licdn.com/dms/image/C5612AQFiP5tF684BLQ/article-inline_image-shrink_1000_1488/0/1652867777442?e=1658361600&v=beta&t=Mr9Y2eN2BHCTHF_ZUaGKJXxaPXCdyuqazNUmXlXfl6w" width="400" /></div><br />Con APIs para Correos, Calendarios, y Contactos, solo estamos comenzando. Miren que más ofrecemos por encima de esos APIs para proveer a los desarrolladores aún más poder.<br /><br />Con Neural API, explorar el mundo de AI y ML es muy sencillo, con modelos listos para utilizar que pueden ser utilizados para limpiar conversaciones, extraer firmas, realizar OCR, y ejecutar análisis de sentimientos. Si quieres saber más, solo tienes que ir a nuestra página <a href="https://www.nylas.com/products/neural-api/">Neural API</a>.With Neural API, exploring the world of AI and ML is easy, with ready-made models that can be used to clean conversations, extract signatures, perform OCR, and run sentiment analysis. If you want to learn more, just go to our <a href="https://www.nylas.com/products/neural-api/">Neural API</a> page.</div><div><br /><div style="text-align: center;"><img height="323" src="https://media-exp1.licdn.com/dms/image/C5612AQFSQJnlDP2YfA/article-inline_image-shrink_1000_1488/0/1652867815905?e=1658361600&v=beta&t=s4DUgb_dlATjuhJ9WLjLpyNk-Ol1C_RSC7b_5Z6ylUU" width="400" /></div><br />Nylas Streams es nuestra solución ETL que requiere muy poco o nada de código para transformar y consumir datos de comunicaciones para E-Commerce, Ventas, Fintech y Customer Success. Si quieres saber más, solo tienes que ir a nuestra página <a href="https://www.nylas.com/products/nylas-streams">Nylas Streams</a>.</div><div><br /><div style="text-align: center;"><img height="322" src="https://media-exp1.licdn.com/dms/image/C5612AQHqS_F54dF_cg/article-inline_image-shrink_1500_2232/0/1652867850468?e=1658361600&v=beta&t=RDHcVa9oLq7wYT7ffRyv606ENAsfwav5vlzQ-v9Jzn8" width="400" /></div><br />Scheduler, un organizador completo con UI configurable. Si quieres saber más, solo tienes que ir a nuestra página <a href="https://www.nylas.com/products/nylas-scheduler">Scheduler</a>.</div><div><br /><div style="text-align: center;"><img height="323" src="https://media-exp1.licdn.com/dms/image/C5612AQFcG0BaesFxsQ/article-inline_image-shrink_1000_1488/0/1652867881802?e=1658361600&v=beta&t=xZQNkFK55OfDbOSBNHWLR7ItDaWGdBvLtxh3g2KeZNc" width="400" /></div><br />Components, widgets UI/UX completamente flexibles y listos para utilizar inmediatamente. Si quieres saber más, solo tienes que ir a nuestra página <a href="https://www.nylas.com/products/components/">Components</a>.</div><div><br /><div style="text-align: center;"><img height="323" src="https://media-exp1.licdn.com/dms/image/C5612AQGS0AQC3LXUkA/article-inline_image-shrink_1000_1488/0/1652867929550?e=1658361600&v=beta&t=meQiVpWSvx4bnd-TRA-dkzNyCZJtkqvs5rk9MjTwLl4" width="400" /></div><div style="text-align: center;"><br /></div><h2 style="text-align: left;">¿Por qué me uní?</h2><br />Lo primero que llamó mi atención fue el hecho de que Nylas provee APIs que hacen que el manejo de comunicaciones sea mucho más sencillo. Manejar Correo, Calendarios y Contactos brinda mucho espacio para poder crear aplicaciones impresionantes.<br /><br />Lo segundo que me atrajo de Nylas fue la cultura de la compañía. Todos se ven comprometidos los unos a los otros, puesto que vi mucho apoyo mutuo cuando la cuenta de Nylas posteaba algo en las redes sociales, y también mis entrevistas fueron más una conversación entre amigos que una entrevista de trabajo.<br /><br />Lo tercero y probablemente más importante para mí, fue poder regresar a Developer Advocacy, que es algo que realmente me apasiona. Poder compartir con la comunidad de desarrolladores es algo que me hace sentir bien y que me hace una mejor persona y un mejor desarrollador.<br /><br />Obviamente, estas 3 cosas no van a ser suficientes para convencerlos, así que he creado un par de pequeños ejemplos.<br /><br />Para esto voy a utilizar uno de mis lenguajes de programación favoritos– <a href="https://www.r-project.org/">R</a>. Y aunque no soy un profesional de R ni tampoco un experto, es algo que me encanta, así que tenganme paciencia, deben hacer una manera mucho mejor de hacer esto.<br /><br />Lo que vamos a hacer, es simplemente leer los primeros tres mensajes de nuestro inbox e imprimir los títulos.</div><div><br /><div style="text-align: center;"><img height="427" src="https://media-exp1.licdn.com/dms/image/C5612AQEnzgBccLsBAA/article-inline_image-shrink_1500_2232/0/1652868006937?e=1658361600&v=beta&t=7elKGO-GTYeHsDuAF_S_2pb3lhXn6UwNmK_h2hleWvA" width="640" /></div><br />El resultado va a ser:</div><div><br /></div><div><blockquote>A Nylas email </blockquote><blockquote>
Invitation: RRSP Webinar @ Tue Feb 8, 2022 1pm - 2pm (EST) (alvaro.t@nylas.com) </blockquote><blockquote>
Start here: Welcome to Clockwise</blockquote><br /><br />Obviamente, aunque R es un lenguaje asombroso utilizado por Estatistas y Data Scientists alrededor del mundo, no es un lenguaje utilizado por las masas.<br /><br />Es por esto que Nylas provee SDKs para <a href="https://developer.nylas.com/docs/developer-tools/sdk/python-sdk/">Python</a>, <a href="https://developer.nylas.com/docs/developer-tools/sdk/ruby-sdk/">Ruby</a>, <a href="https://developer.nylas.com/docs/developer-tools/sdk/node-sdk/">Node</a>, y <a href="https://developer.nylas.com/docs/developer-tools/sdk/java-sdk/">Java</a>.<br /><br />Veamos como podemos leer nuestro inbox utilizando el SDK de <a href="https://www.python.org/">Python</a>. Si quieres saber como instalar el SDK, puedes leer nuestra documentación <a href="https://developer.nylas.com/docs/developer-tools/sdk/python-sdk/">Python Documentation</a>.</div><div><br /><div style="text-align: center;"><img height="445" src="https://media-exp1.licdn.com/dms/image/C5612AQH_mPr89R473w/article-inline_image-shrink_1000_1488/0/1652868076196?e=1658361600&v=beta&t=UWrEn7WWme4y3STV3Xj0ub9nQ7rgnNPbWhmlkThEfaY" width="640" /></div><br />El resultado va a ser:</div><div><br /></div><div><blockquote>A Nylas email </blockquote><blockquote>
Invitation: RRSP Webinar @ Tue Feb 8, 2022 1pm - 2pm (EST) (alvaro.t@nylas.com) </blockquote><blockquote>
Start here: Welcome to Clockwise</blockquote><br />Como podemos ver, utilizar el SDK es mucho más sencillo, porque no tenemos que navegar la respuesta JSON ni tampoco averiguar que número de elemento tenemos que imprimir. Además, si algo cambia internamente podemos estar tranquilos y saber que el SDK va a ser actualizado para reflejar cualquier cambio mientras que llamar directamente al API va a requerir trabajo manual extra. Y lo mismo sucede con el Calendario, Contactos y el resto de nuestras propuestas.</div><div><br /><h2 style="text-align: left;">¿Por qué debería importarles?</h2><br />Bueno, para empezar, soy su amigable Developer Advocate así que ya saben que voy a mantenerlos informados con información constante e interesante acerca de las APIs de Nylas. Además, crearse una cuenta en Nylas es sencillo, no se necesita tarjeta de crédito y obtenemos 14 días para probar el producto. Listos para comenzar? Solo deben ir a <a href="https://dashboard.nylas.com/register">https://dashboard.nylas.com/register</a> y seguir las instrucciones.<br /><br />Además, estoy trabajando en una serie de posts para ayudarlos a dar sus primeros pasos, hacer sus primeras llamadas a las APIs y en general, ayudarlos a obtener lo mejor de su experiencia con Nylas.<br /><br />Pueden comenzar leyendo mi post <a href="https://www.nylas.com/blog/how-to-send-emails-with-the-nylas-python-sdk/">How to Send Emails with the Nylas Python SDK</a><br /><br />¿Piensas que Nylas no es para ti? Bueno, entonces recomiéndanos con tus amigos. Puedes conocer a alguien que se puede beneficiar de tener una manera rápida, sencilla y conveniente de acceder a APIs universales de comunicación.</div></div>Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-63258526296737276142019-01-26T04:35:00.001-08:002019-01-26T04:35:19.573-08:00Un Vector Interior<div style="text-align: justify;">
Cuando terminé mi blog llamado “<a href="http://atejada.blogspot.com/2018/12/hey-vector-quien-me-parezco.html" target="_blank">Hey Vector, a quien me parezco?</a>” utilizando el Vector de Anki y los API's de Machine Learning de SAP Leonardo…Comencé a pensar en que debería empezar a trabajar…primero…obviamente me estaba quedándo sin ideas…pero después…de pronto…se ocurrió una buena idea…que pasaría si pudieramos controlar a Vector…desde adentro? Es decir…que pasaría si pudieramos simular que estamos adentro de Vector y que pudieramos ver a través de sus ojos y pudieramos controlarlo…</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Así como el proyecto se inició 😉</div>
<div>
<h2 style="text-align: justify;">
<b><br /></b></h2>
<h2 style="text-align: justify;">
<b>La Idea</b></h2>
</div>
<div style="text-align: justify;">
<br />
Así que, sabía que quería poder controlar a Vector…podía utilizar Amazon Alexa, pero por supuesto…eso dejaría fuera la parte “interior”…así que no era una opción…luego pensé en utilizar Unreal Engine…puesto que ya lo había utilizado para mi blog <a href="http://atejada.blogspot.com/2018/09/apis-de-sap-leonardo-machine-learning.html" target="_blank">“API's de SAP Leonardo Machine Learning en el Go”</a> donde utilicé las APIs de Machine Learning de SAP Machine, Unreal Engine y Oculus Go para poder demostrar las APIs de SAP Leonardo. Utilizar Unreal Engine y Oculus Go sonaba como la combinación perfecta, así que comencé a trabajar en eso.</div>
<div style="text-align: justify;">
<h2>
<b><br /></b></h2>
<h2>
<b>Que vamos a utilizar?</b></h2>
<div>
<br />
Yo se que he mencionado muchas cosas…así que vamos a obtener un poco más de información 😉</div>
<div>
<br /></div>
<h3>
<b>Vector</b></h3>
<div>
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;">La evolución de <a href="https://www.anki.com/en-ca/cozmo" target="_blank">Cozmo</a> por parte de Anki</span><span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;">. <a href="https://www.anki.com/en-ca/vector" target="_blank">Vector</a> no solo trae más potencia y más independencia pero también un micrófono, así que finalmente puedes hablarle </span><span style="font-family: "segoe ui emoji" , sans-serif; font-size: 12.0pt; line-height: 107%;">😉</span><span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"> y además viene con Amazon Alexa…así que es un impresionante robotito…</span></div>
<div>
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-YMSiAtc9tP0/XEpuN4UDnKI/AAAAAAAAFiA/RZvdmj-oWHYpEjx5iDdcdgjJmEQIgTvdACLcBGAs/s1600/AnkiVector.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="320" data-original-width="320" height="200" src="https://4.bp.blogspot.com/-YMSiAtc9tP0/XEpuN4UDnKI/AAAAAAAAFiA/RZvdmj-oWHYpEjx5iDdcdgjJmEQIgTvdACLcBGAs/s200/AnkiVector.jpg" width="200" /></a></div>
<h3>
<span style="font-family: "calibri" , sans-serif; font-size: 12pt; line-height: 107%;">Unreal Engine</span></h3>
<div>
<span style="line-height: 107%;"><span style="font-family: "calibri" , sans-serif;"><a href="https://www.unrealengine.com/en-US/what-is-unreal-engine-4" target="_blank">Unreal Engine</a> es sin lugar a dudas “El motor de creación más potente”. Puede ser programado utilizándo C++ o Blueprints (Programación Visual) y lo mejor de todo…es totalmente gratuito! A menos que hagas juegos comerciales que se vendan…en ese caso ellos solo piden el 5%</span></span></div>
<div>
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-Fa4nHwq4Bp8/XEpuleRo6LI/AAAAAAAAFiI/ieej66Q1EmkuKnuJ702nXWibF3hHF0YvgCLcBGAs/s1600/ue4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="720" data-original-width="1280" height="180" src="https://1.bp.blogspot.com/-Fa4nHwq4Bp8/XEpuleRo6LI/AAAAAAAAFiI/ieej66Q1EmkuKnuJ702nXWibF3hHF0YvgCLcBGAs/s320/ue4.jpg" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<h3 style="clear: both; text-align: justify;">
<b>Blender</b></h3>
<div class="separator" style="clear: both; text-align: justify;">
<a href="https://www.blender.org/" target="_blank">Blender</a> es un paquete de creación de 3D Open Source. Modelamiento, rigging, animación, simulación, renderizado y un largo etcetera…incluído en la version 2.8, viene EEVEE (Extra Easy Virtual Environment Engine) un motor de renderizado en tiempo real.</div>
<div>
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-LNMGxjSRdc0/XEpu4ZavdZI/AAAAAAAAFiQ/3pk7mKFtEXkBYZzRYH6DZdRTOjTjRBBjQCLcBGAs/s1600/Blender28Beta.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="865" data-original-width="1600" height="173" src="https://1.bp.blogspot.com/-LNMGxjSRdc0/XEpu4ZavdZI/AAAAAAAAFiQ/3pk7mKFtEXkBYZzRYH6DZdRTOjTjRBBjQCLcBGAs/s320/Blender28Beta.jpg" width="320" /></a></div>
<div>
<br /></div>
<div>
<h3>
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"><b>HANA Cloud Platform</b></span></h3>
</div>
<div>
<span style="line-height: 107%;"><span style="font-family: "calibri" , sans-serif;">Base de Datos In-Memory orientada a columnas de SAP que corre en la nube. Que incluye Predictive Analysis, Spatial Data Processing, Text Analysis y mucho más. Además, es extremadamente veloz 😉</span></span><br />
<span style="line-height: 107%;"><span style="font-family: "calibri" , sans-serif;"><br /></span></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-CMyxlZtS5dA/XEpvLR6IsWI/AAAAAAAAFiY/MZM92_C3rLsCa0k4rUsvMcGro18u8sFtgCLcBGAs/s1600/SAPHANA.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="435" data-original-width="800" height="174" src="https://3.bp.blogspot.com/-CMyxlZtS5dA/XEpvLR6IsWI/AAAAAAAAFiY/MZM92_C3rLsCa0k4rUsvMcGro18u8sFtgCLcBGAs/s320/SAPHANA.png" width="320" /></a></div>
<h3>
<span style="line-height: 107%;"><span style="font-family: "calibri" , sans-serif;"><b>Python3</b></span></span></h3>
<span style="line-height: 107%;"><span style="font-family: "calibri" , sans-serif;">Lenguaje de programación interpretado de alto nivel y multipropósito. Es el lenguaje elegido para programar a Vector.</span></span><br />
<span style="line-height: 107%;"><span style="font-family: "calibri" , sans-serif;"><br /></span></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-1hy0CC_AzJ8/XEpvUUeLYsI/AAAAAAAAFig/JG7lYSLSWZoJAHBw4BbMBg9x-itd4WNbQCLcBGAs/s1600/PythonLogo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="203" data-original-width="601" height="108" src="https://4.bp.blogspot.com/-1hy0CC_AzJ8/XEpvUUeLYsI/AAAAAAAAFig/JG7lYSLSWZoJAHBw4BbMBg9x-itd4WNbQCLcBGAs/s320/PythonLogo.png" width="320" /></a></div>
<h2>
<span style="line-height: 107%;"><span style="font-family: "calibri" , sans-serif;"><br /></span></span></h2>
<h2>
<span style="line-height: 107%;"><span style="font-family: "calibri" , sans-serif;">El Primer Problema</span></span></h2>
<span style="font-family: "calibri" , sans-serif;"><br /></span>
<span style="font-family: "calibri" , sans-serif;">Como quería ver a través de los ojos de Vector…necesitaba mostrar el canal de video de Vector dentro de Unreal Engine…me la pasé buen tiempo pensándo como podía hacer eso…pero al final recordé que cuando utilizar el Microsoft HoloLens y lo enlazas a tu laptop para “<b>streaming</b>”, siempre hay un retrazo de 1 o 2 segundos…luego por supuesto recordé que los videos son solo cientos de imágenes que se muestran en secuencia a una velocidad muy alta…no me importa mucho la velocidad o estar cerca de tiempo real…tener un retrazo de 1 o 2 segundos…no algo tan malo...para nada…</span><br />
<span style="font-family: "calibri" , sans-serif;"><br /></span>
<span style="font-family: "calibri" , sans-serif;">Bueno…los problemas continuaron…Sabía que quería que Vector tome un foto cada 1 o 2 segundos…y luego esa imagen debería llegar a Unreal Engine…y fuera del hecho de que programo en Python en mi Ubuntu de mi Maquina Virtual y Unreal Engine en mi laptop Windows…no me convencía mucho la idea de mandar la imagen como un archivo…así que…Se me ocurrió codificar la imagen en Base 64 (Lo cual, sí…incrementa el tamaño…pero por lo menos te brinda un extremadamete largo String el cual puedes manipular) y enviarlo…pero cómo? Bueno…SAP Cloud Platform es un Base de Datos In-Memory…así que muy rápida…por qué no crear algunos REST APIs para poder manejar la creación , visualización y eliminación de los registros?…</span><br />
<span style="font-family: "calibri" , sans-serif;"><br /></span>
<span style="line-height: 107%;"><span style="font-family: "calibri" , sans-serif;"></span></span><br />
<span style="font-family: "calibri" , sans-serif;">Cada vez que Vector toma una imagen...se convierte a Base 64 y luego se envía a la nube, luego Unreal Engine lee el API, decodifica el Base 64 de nuevo a una imagen y lo muestra…eso me llevó a un segundo problema…</span></div>
<h2>
</h2>
<h2>
El Segundo Problema</h2>
<div>
<div>
<br />
Como codifico una imagen a Base 64 en Unreal Engine? Si bien se utilizar C++ bastante bien…cuando se trata de Unreal Engine por lo general utilizo Blueprints, que es programación visual y aunque en el fondo es C++, no todo está implementado…</div>
<div>
<br /></div>
<div>
Afortunadamente, una rápido paseo por la documentación de Unreal me dió la respuesta en la forma de una función que codifica/decodifica en Base 64…pero en C++ por supuesto…</div>
<div>
<br /></div>
<div>
Pero…lo bueno de Unreal Engine es que puedes crear un proyecto en C++…implementar tu clase de codificación de Base 64 y comenzar a crear Blueprints para consumirla…otro problema resuelto…</div>
<div>
<br /></div>
<div>
Eso fué lo que pensé…pero luego me di cuenta de que no era solo cuestión de tener la imagen de nuevo como una imagen…En realidad necesitaba un material dinámico donde pudiera mostrar las imágenes…busqué en la web y encontré algunos artículos interesántes…pero nada que pudiera ayudarme en realidad…al final…tomé partes de aquí y allá y lo mezcle con mi propia investigación…para finalmente hacerlo funcionar…</div>
<div>
<br /></div>
<div>
Y sí…si le estan preguntándo…había otro problema…</div>
</div>
<h2>
</h2>
<h2>
El Tercer Problema</h2>
<div>
<div>
<br />
Todo estaba funcionando bien y bonito…probé mi aplicación…inicialmente pasándo algunas imágenes en sequencia a la nube y luego a Unreal y luego utilizándo Vector…con un retrazo de 1 o 2 segundos…se vería como un video corriendo en un celular antiguo con una conexion a Internet barata en medio del desierto…lo suficientemente bueno para mí 😉</div>
<div>
<br /></div>
<div>
Pero…se supone que estamos dentro de Vector, no? Como se suponía que podía simular eso? Luego de no pensarlo por mucho tiempo…decidí utilizar Blender 2.8 el cual está actualmente en Beta 😊 y que viene con EEVEE (Que es un renderizador en tiempo real asombrozo). Hice un pequeño cuarto de control…con algunos botones y paneles llamativos…una silla donde poder sentarse mientras se está controlándo a Vector y una pantalla para poder vez lo que Vector está mirándo….</div>
<div>
<br /></div>
<div>
El Baking no está funcionándo en este momento…o por lo menos soy muy torpe para poder darme cuenta de como se utiliza en 2.8, así que utlizar texturas en Cycles estaba descartado…entonces…hice una prueba en EEVEE utilizándo solo materiales simples…la exporté a .FBX y funcionó perfectamente! Así que, comencé a trabajar y a probarlo en Unreal…por supuesto…no soy un experto en Blender, así que a pesar de que todo se ve bien…no todos los colores fueron renderizados corractement ☹ At least it looks fairly decent 😉</div>
</div>
<h2>
</h2>
<h2>
No más problemas</h2>
<div>
<br />
Así es…no es que todo haya funcionado bien y sin errores…pero por lo menos esos fueron los errores o problemas más críticos…así que finalmente podemos empezar con el post -:P </div>
<h2>
</h2>
<h2>
Blender y El Cuarto de Control</h2>
<div>
<div>
<br />
Como ya lo dije…utilicé el renderizado de EEVEE en Blender 2.8 Beta para crear un cuarto de control que pudiera, de alguna manera, dar la impresión de estar dentro de Vector…por supuesto…una versión total y completamente poética porque a) Quién sabe como se ve Vector por adentro? b) No creo que haya suficiente espacio dentro de Vector como para poder acomodar nada más.</div>
<div>
<br /></div>
<div>
Primero, comencé poniendo algunos botones, perillas y teclados a un panel…luego añadí una especie de radares con deslizadores…</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-CnSBVmdHzqM/XEpwZA-93NI/AAAAAAAAFiw/-0SiIo8Tp78z_gEorTl8IY05Uv7LY4s5wCLcBGAs/s1600/InsideCozmoPanels.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://4.bp.blogspot.com/-CnSBVmdHzqM/XEpwZA-93NI/AAAAAAAAFiw/-0SiIo8Tp78z_gEorTl8IY05Uv7LY4s5wCLcBGAs/s320/InsideCozmoPanels.png" width="320" /></a></div>
<div>
<br /></div>
<div>
Luego, pensé que algunos interruptores y botones de varios colores serían una interesánte adición…</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-P9H5VN8Ja1w/XEpwgdYgUlI/AAAAAAAAFi0/qRDCmoDiXWsXpk2X7VX-21NTJ8bCXQfLACLcBGAs/s1600/InsideCozmoControls.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://1.bp.blogspot.com/-P9H5VN8Ja1w/XEpwgdYgUlI/AAAAAAAAFi0/qRDCmoDiXWsXpk2X7VX-21NTJ8bCXQfLACLcBGAs/s320/InsideCozmoControls.png" width="320" /></a></div>
<div>
<br /></div>
<div>
Finalmente, agregué una silla…porque tenemos qu entarnos en algun lugar, no?</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-1agBPk3B7TM/XEpwnFC0iFI/AAAAAAAAFi4/o2IUi2cFvDQmSpnFG2HQclJ_aczBzfvIACLcBGAs/s1600/InsideCozmoChair.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://4.bp.blogspot.com/-1agBPk3B7TM/XEpwnFC0iFI/AAAAAAAAFi4/o2IUi2cFvDQmSpnFG2HQclJ_aczBzfvIACLcBGAs/s320/InsideCozmoChair.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
La pantalla es simplemente un espacio vacio…casi igual que en un cine…</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-SLdQMoROT7c/XEpwvmRQxBI/AAAAAAAAFjA/6NvyOM-_TfcA6Sp4zkoKL4iWeDhI7-euQCLcBGAs/s1600/InsideCozmoScreen.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://3.bp.blogspot.com/-SLdQMoROT7c/XEpwvmRQxBI/AAAAAAAAFjA/6NvyOM-_TfcA6Sp4zkoKL4iWeDhI7-euQCLcBGAs/s320/InsideCozmoScreen.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Miren, muy bonito en Blender, no? Bueno…no mucho en Unreal…no feo…ciertamente no optimizado…probablemente debido al echo de que uní todo y lo exporte como un bloque grande…pero eso está bien…no pienso cambiar eso…soy flojo 😊</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-5OFF3kK-9j0/XEpw72LOQ7I/AAAAAAAAFjM/1qy8DjVQV7cyXc3vszqwH0lD2i3vTGrnACLcBGAs/s1600/UnrealVector.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="666" data-original-width="1287" height="165" src="https://1.bp.blogspot.com/-5OFF3kK-9j0/XEpw72LOQ7I/AAAAAAAAFjM/1qy8DjVQV7cyXc3vszqwH0lD2i3vTGrnACLcBGAs/s320/UnrealVector.jpg" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both;">
Ya ven…no perfecto…pero tampoco mal 😉</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
Aquí está el archivo <a href="http://blagarts.com/files/InsideCozmo.fbx" target="_blank">.FBX</a></div>
<h2 style="clear: both;">
</h2>
<h2 style="clear: both;">
Creándo las tablas y APIS en HANA Cloud Platform</h2>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
El siguiente paso…Creé dos tablas en HANA Cloud Platform…Llamé a la primera tabla <b>“VECTOREYES”</b> porque es la tabla que va a contener las imágenes en Base 64. Aquí está el script para crear la tabla…</div>
<div class="separator" style="clear: both;">
<br /></div>
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC"></th>
</tr>
<tr>
<td><pre>CREATE COLUMN TABLE "I830502"."VECTOREYES"(
"TIMESTAMP" LONGDATE CS_LONGDATE NOT NULL,
"VECTOREYE" CLOB MEMORY THRESHOLD 3000,
PRIMARY KEY (
"TIMESTAMP"
)
) UNLOAD PRIORITY 5 AUTO MERGE;
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Para la llave primaria utilicé un TIMESTAMP basicamente porque quería que si algo pasara en terminos de conexión no hubieran errores de llave repetida…<br />
<br />
La siguiente tabla se llamará <b>“VECTORCOMMAND”</b> y va a contener…los comandos que vamos a enviar a Vector…<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC"></th>
</tr>
<tr>
<td><pre>CREATE COLUMN TABLE "I830502"."VECTORCOMMAND"(
"NID" INTEGER CS_INT,
"COMMAND" NVARCHAR(50),
PRIMARY KEY (
"NID"
)
) UNLOAD PRIORITY 5 AUTO MERGE;
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
En este caso…siempre va a existir un solo comando…así que utilicé una llave primaria simple de número entero.<br />
<br />
Con las tablas creadas, podemos generar nuestro paquete de XS Engine…y simplemente llamarlo <b>“VectorEyes”</b>.<br />
<br />
Debemos crear los siguientes archivos…<br />
<div>
<br /></div>
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">.xsaccess</th>
</tr>
<tr>
<td><pre>{
"exposed" : true,
"authentication" :
{
"method": "Basic"
},
"cache_control" : "must-revalidate",
"cors" :
{
"enabled" : true,
"allowMethods": [
"GET",
"POST",
"HEAD",
"OPTIONS"
]
},
"enable_etags" : false,
"force_ssl" : false,
"prevent_xsrf" : false
}
</pre>
<pre></pre>
<pre></pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">.xsapp</th>
</tr>
<tr>
<td><pre></pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Sip…no es un error…este archivo está total y completamente vacío…
<br />
<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">AddVectorEye.xsjs</th>
</tr>
<tr>
<td><pre>$.response.contentType = "text/html";
var conn = $.db.getConnection();
var content = $.request.body.asString();
content = JSON.parse(content);
var st = conn.prepareStatement("INSERT INTO \"YourSchema\".\"VECTOREYES\" values(?,?)");
st.setString(1,content.timestamp);
st.setString(2,content.vectoreye);
st.execute();
conn.commit();
st.close();
conn.close();
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">GetAddVectorEye.xsodata</th>
</tr>
<tr>
<td><pre>service namespace "YourSchema"{
"YourSchema"."VECTOREYES" as "vectoreye";
}
</pre>
<pre></pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">DeleteVectorEye.xsjs</th>
</tr>
<tr>
<td><pre>$.response.contentType = "text/html";
var conn = $.db.getConnection();
var st = conn.prepareStatement("DELETE FROM \"YourSchema\".\"VECTOREYES\"");
st.execute();
conn.commit();
st.close();
conn.close();
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Con esto, podemos insertar, leer o borrar la tabla <b>VECTOREYES</b>. Continuemos con los archivos para la tabla <b>VECTORCOMMAND</b>…
<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">AddVectorCommand.xsjs</th>
</tr>
<tr>
<td><pre>$.response.contentType = "text/html";
var nid = $.request.parameters.get("nid");
var command = $.request.parameters.get("command");
var conn = $.db.getConnection();
var st = conn.prepareStatement("INSERT INTO \"YourSchema\".\"VECTORCOMMAND\" values(?,?)");
st.setString(1,nid);
st.setString(2,command);
st.execute();
conn.commit();
st.close();
conn.close();
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">GetVectorCommand.xsodata</th>
</tr>
<tr>
<td><pre>service namespace "YourSchema"{
"YourSchema"."VECTORCOMMAND" as "vectorcommand";
}
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">DeleteVectorCommand.xsjs</th>
</tr>
<tr>
<td><pre>$.response.contentType = "text/html";
var conn = $.db.getConnection();
var st = conn.prepareStatement("DELETE FROM \"YourSchema\".\"VECTORCOMMAND\"");
st.execute();
conn.commit();
st.close();
conn.close();
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Eso es todo 😊 Simplemente debemos activarlos y probarlos…de hecho que <a href="https://www.getpostman.com/" target="_blank">Postman</a> es la mejor herramienta para esto 😉
<br />
<br />
<h2>
Creándo nuestro proyecto en Unreal Engine</h2>
<br />
Como mencioné anteriormente...Creé un proyecto vació de C++ utilizándo Mobile/Tablet, Scalable 3D or 2D y No starter content. Utilicé Unreal Engine versión 4.21.1 y llamé al proyecto <b>“VectorOculusGo”</b><br />
<b><br /></b>
<br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-7AvRFAEP4kU/XEp2dWul5xI/AAAAAAAAFjY/fc9olwhZrEAbqbNA8kghRttJIVlmhymCwCLcBGAs/s1600/UE_SetupProject.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="635" data-original-width="797" height="254" src="https://1.bp.blogspot.com/-7AvRFAEP4kU/XEp2dWul5xI/AAAAAAAAFjY/fc9olwhZrEAbqbNA8kghRttJIVlmhymCwCLcBGAs/s320/UE_SetupProject.jpg" width="320" /></a></div>
<div>
<br /></div>
<div>
Cuando el proyecto está abierto, seleccioné <b>“File --> New C++ Class”</b>, y escogí <b>“Actor”</b>.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-pzhDHQ0irlc/XEp3JivLCyI/AAAAAAAAFjk/3Z5C0pO7H80V88Ld2cBghPavvVevSiNpgCLcBGAs/s1600/UE_ActorClass.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="568" data-original-width="946" height="192" src="https://4.bp.blogspot.com/-pzhDHQ0irlc/XEp3JivLCyI/AAAAAAAAFjk/3Z5C0pO7H80V88Ld2cBghPavvVevSiNpgCLcBGAs/s320/UE_ActorClass.jpg" width="320" /></a></div>
<div>
<br /></div>
<div>
Llamé a la clase, <b>“ImageParser”</b> y utilicé los siguientes códigos para <b>“ImageParser.h”</b> y <b>“ImageParser.cpp”</b></div>
<div>
<b><br /></b></div>
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">ImageParser.h</th>
</tr>
<tr>
<td><pre>#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ImageParser.generated.h"
UCLASS()
class VECTOROCULUSGO_API AImageParser : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AImageParser();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UFUNCTION(BlueprintCallable, Category = "ImageParser")
void ParseImage(FString encoded, TArray<uint8> &decoded);
};
</uint8></pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Aquí estamos creándo una función que será llamada desde Blueprints. Va a recibir un String y va a retornar un arreglo de Enteros.<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">ImageParser.cpp</th>
</tr>
<tr>
<td><pre>#include "ImageParser.h"
#include "Misc/Base64.h"
// Sets default values
AImageParser::AImageParser()
{
// Set this actor to call Tick() every frame. </pre>
<pre> // You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void AImageParser::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AImageParser::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AImageParser::ParseImage(FString encoded, TArray<uint8> &decoded)
{
FBase64::Decode(encoded, decoded);
}
</uint8></pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Aquí, simplemente llamamos al método Decode de la librería Base64. Esta va a tomar el String en Base 64 y convertirlo de vuelta a una imagen.<br />
<br />
Para poder compilarlo, solo debemos hacer click derecho en el nombre del proyecto y seleccionar <b>“Debug --> Start new instance”</b>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-aBn3ydy0kiU/XErY_KtI8oI/AAAAAAAAFj0/hHKZ9mxCRGYdfPXZ0wX1FOmJpt5ESiOWACLcBGAs/s1600/Compile.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="73" data-original-width="563" height="41" src="https://4.bp.blogspot.com/-aBn3ydy0kiU/XErY_KtI8oI/AAAAAAAAFj0/hHKZ9mxCRGYdfPXZ0wX1FOmJpt5ESiOWACLcBGAs/s320/Compile.jpg" width="320" /></a></div>
<br />
Una vez que la compilación termino, podemos simplemente para el debugging.<br />
<br />
Antes de continuar…debemos descargar una librería para poder manajer APIs…se llama <a href="https://github.com/Stefander/JSONQuery" target="_blank">JSONQuery</a> y es impresionante!<br />
<br />
Simplemente, debemos cerrar Unreal, ir a la carpeta del proyecto y crear una nueva carpeta llamada <b>“Plugins”</b>, luego descargamos el .zip, lo descomprimos dentro de la carpeta <b>“Plugins”</b> y borramos las carpetas <b>Binaries</b> and <b>Intermediate</b>.<br />
<br />
Luego, deberemos cambiar el código fuente un poco…<br />
<br />
Dentro de la carpeta <b>“JSONQuery”</b>, debemos ir a <b>“Source --> JSONQuery --> Classes --> JsonFieldData.h”</b> y buscar <b>“GetRequest”</b>.<br />
<br />
Luego de <b><i>const FString& url</i></b> debemos agregar <b><i>const FString& auth</i></b><br />
<br />
Luego debemos abrir <b>“Source --> JSONQuery --> Private --> jsonfielddata.cpp”</b> y volver a buscar <b>“GetRequest”</b>.<br />
<br />
Aquí, agregamos el mismo <b><i>const FString& auth</i></b>.<br />
<br />
Luego de <b><i>HttpRequest->SetURL(CreateURL(url));</i></b> debemos agregar lo siguiente…<br />
<br />
<b><i>HttpRequest->SetHeader(TEXT("Authorization"), auth);</i></b><br />
<br />
Guardamos ambos archivos y abrimos nuestro proyecto. Vamos a recibir un mensaje diciendo que parte de nuestro código necesita ser recompilado. Así que simplemente acepta y espera un poco hasta que todo haya sido compilado 😊<br />
<br />
Para revisar que todo está bien, debemos ir a <b>“Settings --> Plugins”</b> e ir hasta el final hasta encontrar <b>“Project --> Web”</b> y <b>JSON Query</b> debería estar seleccionado. 😉<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-WjEPSTnyi-Q/XErZ3iY5cYI/AAAAAAAAFj8/_zMWZvFScZY0-_m0ex6lDvtpwB2BC2wiwCLcBGAs/s1600/JSONQuery.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="222" data-original-width="618" height="114" src="https://2.bp.blogspot.com/-WjEPSTnyi-Q/XErZ3iY5cYI/AAAAAAAAFj8/_zMWZvFScZY0-_m0ex6lDvtpwB2BC2wiwCLcBGAs/s320/JSONQuery.jpg" width="320" /></a></div>
<br />
<div>
<br /></div>
<div>
<div>
Excelente, ahora podemos continuar.</div>
<div>
<br /></div>
<div>
Para poder hacer que nuestro proyecto funciones con el Oculus Go, debemos configurar un par de cosas.</div>
</div>
<div>
<br /></div>
<h2>
Configurándo el Oculus Go</h2>
<div>
<br /></div>
<div>
Tu debes querer configurar tu Oculus si es que no lo haz hecho todavía 😊 Aquí le dejo un <a href="https://support.oculus.com/183135912238400/" target="_blank">enlace</a> muy interesánte con toda la explicación necesaria …</div>
<div>
<br /></div>
<div>
<h2>
Configurándo Unreal para Oculus Go</h2>
</div>
<div>
<div>
<br />
Necesitamos instalar <b>“Code Works for Android”</b> que en realidad está incluído dentro de la instalación de Unreal. Así que, vamos a <b>“Program Files --> Epic Games --> UE_4.21 --> Engine --> Extras --> AndroidWorks --> Win64”</b> y ejecutamos <b>“CodeWorksforAndroid-1R7u1-windows.exe”</b>.</div>
<div>
<br /></div>
<div>
Nos daremos cuenta de que estamos dentro de la carpeta C++ Classes, así que haz click en el ícono de carpeta que está al lado y selecciona <b>“Content”</b>.</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-L_3ZlKD94io/XEri2CWaEmI/AAAAAAAAFkI/Uf-8dZWbyGUwt8GyGoSGL-91zfvWxbXhACLcBGAs/s1600/UE_Folders.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="525" data-original-width="193" height="320" src="https://2.bp.blogspot.com/-L_3ZlKD94io/XEri2CWaEmI/AAAAAAAAFkI/Uf-8dZWbyGUwt8GyGoSGL-91zfvWxbXhACLcBGAs/s320/UE_Folders.jpg" width="117" /></a></div>
<div>
<br /></div>
<div>
<div>
<br /></div>
<div>
No prestemos atención a las carpetas por el momento.</div>
<div>
<br /></div>
<div>
Primero, grabemos el mapa por defecto y lo llamaremos <b>“MainMap”</b>. Luego vamos a <b>“Edit --> Project Settings”</b>. Busquemos <b>“Maps & Modes”</b> y seleccionemos <b>“MainMap”</b> en ambos <b>“Editor Startup Map”</b> y <b>“Game Default Map”</b>.</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-Rnq-R-TO8vk/XErjYOWuwBI/AAAAAAAAFkQ/12G32fxAJw0ehkjD1rqn8W8KmTCg64ZgACLcBGAs/s1600/UE_Maps%2526Modes.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="636" data-original-width="947" height="214" src="https://4.bp.blogspot.com/-Rnq-R-TO8vk/XErjYOWuwBI/AAAAAAAAFkQ/12G32fxAJw0ehkjD1rqn8W8KmTCg64ZgACLcBGAs/s320/UE_Maps%2526Modes.jpg" width="320" /></a></div>
<div>
<br /></div>
<div>
Luego debemos bajar y seleccionar <b>“Engine --> Input”</b>. En la sección <b>“Mobile”</b> debemos establecer la <b>Default Touch Interface</b> como <b>None</b>.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-YujtCNe6OuY/XErjnp9TP2I/AAAAAAAAFkU/opkh8vP0bkkX9nJqGqswfarqKx9mQJ_fwCLcBGAs/s1600/UE_TouchInterface.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="186" data-original-width="568" height="104" src="https://3.bp.blogspot.com/-YujtCNe6OuY/XErjnp9TP2I/AAAAAAAAFkU/opkh8vP0bkkX9nJqGqswfarqKx9mQJ_fwCLcBGAs/s320/UE_TouchInterface.jpg" width="320" /></a></div>
<div>
<br /></div>
<div>
<div>
<div>
<div>
Nos movemos más abajo hasta <b>“Platforms”</b> y seleccionamos <b>“Android”</b>. Debemos hacer click en <b>“Configure Now”</b>. Luego nos movemos a <b>“Android”</b>. Y establecemos el minimum y target SDK version a <b>“19”</b>.</div>
<div>
<br /></div>
<div>
También debemos hacer click en <b>“Enable Fullscreen Immersive on KitKat and above devices”</b> para activarlo.</div>
<div>
<br /></div>
<div>
Buscamos <b>“Configure the AndroidManifest for Deployment to Oculus”</b> y lo activamos también.</div>
<div>
<br /></div>
<div>
Ahora, debemos hacer click en <b>“Android SDK”</b> y revisar la configuración. Si no tenemos las Variables del Sistema configuradas, entonces simplemente asignamos las rutas de las carpetas.</div>
<div>
<br /></div>
<div>
Finalmente, nos vamos a <b>“Engine --> Rendering”</b> y nos asegurámos de que <b>“Mobile HDR”</b> no esté seleccionado.</div>
<div>
<br /></div>
<div>
Si algo no está claro, simplemente vayan a este <a href="https://developer.oculus.com/documentation/unreal/latest/concepts/unreal-quick-start-guide-go/" target="_blank">enlace</a> 😉 </div>
<div>
<br /></div>
<div>
Listo, ahora finalmente podemos continuar 😊<br />
<br /></div>
</div>
<h2>
Creándo un Material Dinámico</h2>
<div>
<div class="MsoNormal">
<span style="font-size: 12pt; line-height: 17.12px;"><br /></span>Hacemos click en “<b>Add New --> Material</b>” y lo llamamos “<b>Dynamic_Mat</b>”. Una vez dentro del editor de materiales, hacemos click derecho en un espacio vacio y buscamos “<b>TextureSampleParameter2D</b>”.</div>
<div class="MsoNormal">
</div>
</div>
<br />
<br /></div>
</div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-lYwpgCK_3F0/XErkZ-kPFqI/AAAAAAAAFkg/Vn6nwMTMucQ6b_-b_virM8PL1gQZ_0EBQCLcBGAs/s1600/TextureSample.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="170" data-original-width="241" src="https://3.bp.blogspot.com/-lYwpgCK_3F0/XErkZ-kPFqI/AAAAAAAAFkg/Vn6nwMTMucQ6b_-b_virM8PL1gQZ_0EBQCLcBGAs/s1600/TextureSample.png" /></a></div>
<div align="center" class="MsoNormal" style="text-align: center;">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Una vez creado, lo llamaremos “<b>Texture_Sample</b>”. Viene con una textura por defecto que puedes cambiar si así lo quieres (Pero al final no importa si la cambias o no). Simplemente conecta el primer output al “<b>Base Color</b>” del nodo de “<b>Dynamic_Mat</b>”.</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-JK5KvI4w7hM/XEromCftZmI/AAAAAAAAFks/gbiupiFX5Rw6KXzDP_vmvIJWbwThPUjNwCLcBGAs/s1600/DynamicMaterial.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="508" data-original-width="475" height="320" src="https://3.bp.blogspot.com/-JK5KvI4w7hM/XEromCftZmI/AAAAAAAAFks/gbiupiFX5Rw6KXzDP_vmvIJWbwThPUjNwCLcBGAs/s320/DynamicMaterial.jpg" width="299" /></a></div>
<div>
<br /></div>
<div>
Grabalo y será aplicado automáticamente. Lo mejor de esta configuraciín es que Param2D es dinámico 😉 </div>
<div>
<h2>
</h2>
<h2>
Creándo nuestro primer Blueprint</h2>
<div>
<br />
Debemos crear una nueva carpeta llamada <b>“Blueprints”</b>. Aquí vamos a crear la pantalla donde las imágenes que vienen desde Vector van a ser mostradas.</div>
<div>
<br /></div>
<div>
Presionamos <b>“Add New --> Blueprint Class”</b>.</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-tWRlfU01-no/XErpA3BLQLI/AAAAAAAAFk0/74fV344YN4k8h8YNXUlR9i7OcInUDovkACLcBGAs/s1600/UE_BlueprintClass.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="667" data-original-width="218" height="320" src="https://3.bp.blogspot.com/-tWRlfU01-no/XErpA3BLQLI/AAAAAAAAFk0/74fV344YN4k8h8YNXUlR9i7OcInUDovkACLcBGAs/s320/UE_BlueprintClass.jpg" width="104" /></a></div>
<div>
<br /></div>
<div>
En vez de escoger <b>“Actor”</b> como la clase padre…vamos hacia abajo a <b>“All Classes”</b> y buscamos <b>“Image Parser”</b> y lo seleccionamos como clase padre.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-EF6yfUr33s0/XErpQ8vgd1I/AAAAAAAAFk4/KDaSfn6LOYkICCbFnmxfCO6l0VXE6OREACLcBGAs/s1600/UE_ImageParserClass.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="554" data-original-width="542" height="320" src="https://3.bp.blogspot.com/-EF6yfUr33s0/XErpQ8vgd1I/AAAAAAAAFk4/KDaSfn6LOYkICCbFnmxfCO6l0VXE6OREACLcBGAs/s320/UE_ImageParserClass.jpg" width="313" /></a></div>
<div>
<br /></div>
<div>
<div>
Lo llamamos <b>“ImageRenderer”</b>.</div>
<div>
<br /></div>
<div>
Una vez creada, nos vamos a la pestaña Viewport y presionamos <b>“Add Component --> Cube”</b>. Simplemente le cambiamos la escala a <b>“0.01, 1.0, 1.0”</b>.</div>
</div>
<div>
<br /></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-VkhUMj-BTNs/XErpgGiZJ7I/AAAAAAAAFlA/Ls6oI9wJXTkUASBzdlurGgSgrp-UVdXxgCLcBGAs/s1600/ViewportRenderer.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="520" data-original-width="703" height="236" src="https://4.bp.blogspot.com/-VkhUMj-BTNs/XErpgGiZJ7I/AAAAAAAAFlA/Ls6oI9wJXTkUASBzdlurGgSgrp-UVdXxgCLcBGAs/s320/ViewportRenderer.jpg" width="320" /></a></div>
<div>
<br /></div>
<div>
<div>
Luego nos vamos a la pestaña <b>“Event Graph”</b>. Aquí es donde vamos a construir nuestros Blueprints.</div>
<div>
<br /></div>
<div>
Pero primero, debemos crear un par de variables.</div>
<div>
<br /></div>
<div>
<b>CubeMaterial --> Material Instance (Object Reference)</b></div>
</div>
<div>
<b><br /></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-3tyIzV2M_dw/XErp1znuhfI/AAAAAAAAFlM/9fXh80OS4swsRnWlo_WCGBwnsc2ITwHvACLcBGAs/s1600/CubeMaterial.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="321" data-original-width="490" height="209" src="https://3.bp.blogspot.com/-3tyIzV2M_dw/XErp1znuhfI/AAAAAAAAFlM/9fXh80OS4swsRnWlo_WCGBwnsc2ITwHvACLcBGAs/s320/CubeMaterial.png" width="320" /></a></div>
<div>
<b><br /></b></div>
<div>
<div>
Este va a ser el material del cubo que hemos creado.</div>
<div>
<b><br /></b></div>
<div>
<b>TempImg --> Texture 2D (Object Reference)</b></div>
<div>
<b><br /></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-ScTo_jJYcUM/XErqCHqojqI/AAAAAAAAFlQ/AfvbDceqo5w28OH9W1YA-ccfO1T4c4wKgCLcBGAs/s1600/TempImg.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="319" data-original-width="491" height="207" src="https://3.bp.blogspot.com/-ScTo_jJYcUM/XErqCHqojqI/AAAAAAAAFlQ/AfvbDceqo5w28OH9W1YA-ccfO1T4c4wKgCLcBGAs/s320/TempImg.png" width="320" /></a></div>
<div>
<div>
<b><br /></b></div>
<div>
Aquí es donde vamos a almacenar la imagen luego de haber convertido de Base 64.</div>
<div>
<br /></div>
<div>
<b>TempMat --> Material Instance (Object Reference)</b></div>
<div>
<br /></div>
<div>
Este es el material dinámico que vamos a asignar a nuestro cubo.</div>
<div>
<br /></div>
<div>
<b>ImageJSON --> String</b></div>
<div>
<br /></div>
<div>
Este es el resultado de llamar al API…el String en Base 64.</div>
<div>
<br /></div>
<div>
Con las variables preparadas, podemos empezar creándo la primera parte del Blueprint.</div>
<div style="font-weight: bold;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-eAqLGJ2DXXA/XErqY06gDDI/AAAAAAAAFlg/jQvH9khrfAQvQiY06BXy3PoIiKqW4svfQCLcBGAs/s1600/ImageRenderer_BP_01.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="291" data-original-width="529" height="176" src="https://4.bp.blogspot.com/-eAqLGJ2DXXA/XErqY06gDDI/AAAAAAAAFlg/jQvH9khrfAQvQiY06BXy3PoIiKqW4svfQCLcBGAs/s320/ImageRenderer_BP_01.jpg" width="320" /></a></div>
<div style="font-weight: bold;">
<br /></div>
<div>
Aquí, estamos diciendo que una vez que nuestra aplicación inicie (<b>Event BeginPlay</b>) vamos a llamar a una función llamada <b>“Set Timer by Function Name”</b>. Esta función va a llamar a otra función cada 2.0 segundos (puesto que hemos marcado Looping value). La función que llamaremos será <b>“MyEvent”</b>.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-G-hvOaf5Lqs/XErqpTFOMEI/AAAAAAAAFls/wKllPdtTHcYBN8xVraHNZRtqRtcTvqfugCLcBGAs/s1600/RedVectorEyeTable.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="521" data-original-width="985" height="169" src="https://3.bp.blogspot.com/-G-hvOaf5Lqs/XErqpTFOMEI/AAAAAAAAFls/wKllPdtTHcYBN8xVraHNZRtqRtcTvqfugCLcBGAs/s320/RedVectorEyeTable.jpg" width="320" /></a></div>
<div>
<br /></div>
</div>
<div style="font-weight: bold;">
<br /></div>
</div>
<div>
Aquí, estamos llamándo a la función <b>“MyEvent”</b>, la cual llamará a <b>“Get JSON Request”</b> pasándole el URL y el Auth. Esto estará enlazado al evento <b>“OnGetResult”</b>. El resultado de la llamada al JSON será extraída utilizándo <b>Get Object Field</b>, <b>Get Object Array Field</b>, un <b>For Loop</b> y finalmente un <b>Get String Field</b> para poder obtener la imagen en Base 64 y guardarla en la variable <b>ImageJSON</b>.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-PnraAtJXkGU/XErq-YLHrYI/AAAAAAAAFl0/-qlReFAeZ1kuunD56NzdcXdyEb9ekcvEwCLcBGAs/s1600/DeleteTableVectorEye.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="273" data-original-width="686" height="127" src="https://3.bp.blogspot.com/-PnraAtJXkGU/XErq-YLHrYI/AAAAAAAAFl0/-qlReFAeZ1kuunD56NzdcXdyEb9ekcvEwCLcBGAs/s320/DeleteTableVectorEye.jpg" width="320" /></a></div>
<div>
<br /></div>
<div>
Luego de establecer la variable ImageJSON, llamamos a la API para borrar la tabla. Luego de esto…las cosas se ponen interesántes…</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/--8ca4CQTy84/XErrFDvCjkI/AAAAAAAAFl4/ectguhYx3BwLHfprFTMy8Y8BeuWQW_PcwCLcBGAs/s1600/LoadDynamicImage.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="292" data-original-width="1117" height="83" src="https://4.bp.blogspot.com/--8ca4CQTy84/XErrFDvCjkI/AAAAAAAAFl4/ectguhYx3BwLHfprFTMy8Y8BeuWQW_PcwCLcBGAs/s320/LoadDynamicImage.jpg" width="320" /></a></div>
<div>
<br /></div>
<div>
<div>
Aquí estamos llamándo a nuestra Clase en C++ <b>“Parse Image”</b> como si fuera otro elemento de Blueprints. Obtenemos el valor almacenado en <b>ImageJSON</b> para que sea decodificado como imagen. El resultado de decodificar en la cadena en Base 64 va a ir hacia <b>“Import Buffer as Texture 2D”</b>, el cual va a ir a la variable <b>TempImg</b>. Luego de esto <b>“Create Dynamic Material Instance”</b> va a aplicar un material dinámico a nuestro material <b>Dynamic_Mat</b> y asignarlo a <b>TempMat</b> que va a ser pásado como el destino de <b>Set Texture Parameter Value</b>, mientras que <b>TempImg</b> va a ser pasado como el valor de parámetro. Finalmente, un nodo <b>Set Material</b> va a establecer el material <b>TempMat</b> al cubo.</div>
<div>
<br /></div>
<div>
Para hacerlo más simple…tomamos la cadena en Base 64…la convertimos en imagen…creámos un material dinámico, lo utilizamos como valor para nuestro material dinámico y finalmente lo asignamos a nuestro cubo. Cada vez que obtengamos un nuevo valor en Base 64, obtendremos una nueva imagen y nuestro cubo podrá mostrárlo 😉</div>
</div>
<div>
<h2>
</h2>
<h2>
Importándo nuestro Modelo de Blender</h2>
<div>
<br />
Ahora, necesitamos simular que estamos dentro Vector…por lo tanto…debemos importar nuestro de Blender en formato .FBX 😉</div>
<div>
<br /></div>
<div>
Simplemente presionamos Import y seleccionamos el archivo .FBX que pueden obtener en este <a href="http://blagarts.com/files/InsideCozmo.fbx" target="_blank">enlace</a>. Presionamos Import All y lo tendremos en pantalla.</div>
<div>
<br /></div>
<div>
Cambiemos los siguientes parámetros…</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-oBthTzlxnVs/XErsmzysFyI/AAAAAAAAFmE/Kt_veG1olo4H_3rtgWddlpOiocmuDUjAgCLcBGAs/s1600/BlenderTransform.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="133" data-original-width="376" height="113" src="https://3.bp.blogspot.com/-oBthTzlxnVs/XErsmzysFyI/AAAAAAAAFmE/Kt_veG1olo4H_3rtgWddlpOiocmuDUjAgCLcBGAs/s320/BlenderTransform.png" width="320" /></a></div>
<div>
<br /></div>
<div>
Ahora, agregamos un Point Light con los siguientes parámetros…</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-S9C8G0LPmxo/XErssyu1a1I/AAAAAAAAFmI/VL_H0q4IOqsGOG9MSKzmoamH4MDMMSZiACLcBGAs/s1600/PointLightTransform.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="192" data-original-width="375" height="163" src="https://4.bp.blogspot.com/-S9C8G0LPmxo/XErssyu1a1I/AAAAAAAAFmI/VL_H0q4IOqsGOG9MSKzmoamH4MDMMSZiACLcBGAs/s320/PointLightTransform.png" width="320" /></a></div>
<div>
<br /></div>
<div>
Luego, selecionamos el Blueprint <b>“ImageRenderer”</b> y lo arrastramos a la pantalla. Debemos cambiar los parámetros así…</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-8EBjrtiLfqQ/XErs2fSG0jI/AAAAAAAAFmM/Y2yBfTQ0XCkF3yRClG3WVzGdvwdWjGyoACLcBGAs/s1600/ImageRenderedTransform.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="106" data-original-width="373" height="90" src="https://1.bp.blogspot.com/-8EBjrtiLfqQ/XErs2fSG0jI/AAAAAAAAFmM/Y2yBfTQ0XCkF3yRClG3WVzGdvwdWjGyoACLcBGAs/s320/ImageRenderedTransform.png" width="320" /></a></div>
<div>
<br /></div>
<div>
Luego, presionamos <b>“Build”</b> y esperamos a que todo (incluídas las luces) sean construídas.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-TP_u_NNFmkQ/XErs8Y_tA_I/AAAAAAAAFmU/Faoyd1CKgZgPy40FrAXPxYvTShlYNt1ogCLcBGAs/s1600/Build.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="80" data-original-width="882" height="29" src="https://3.bp.blogspot.com/-TP_u_NNFmkQ/XErs8Y_tA_I/AAAAAAAAFmU/Faoyd1CKgZgPy40FrAXPxYvTShlYNt1ogCLcBGAs/s320/Build.png" width="320" /></a></div>
<div>
<br /></div>
<div>
Una vez que termina…tendremos esto…</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-ncHRfveKd8M/XErtI-ka-9I/AAAAAAAAFmg/e1Vq_gPYLWgK2b_Np8EYNwutg2AckJ0lQCLcBGAs/s1600/BlenderModelUnreal.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="646" data-original-width="1218" height="169" src="https://1.bp.blogspot.com/-ncHRfveKd8M/XErtI-ka-9I/AAAAAAAAFmg/e1Vq_gPYLWgK2b_Np8EYNwutg2AckJ0lQCLcBGAs/s320/BlenderModelUnreal.png" width="320" /></a></div>
<div>
<br /></div>
<div>
Impresionante! Todo está comenzándo a tomar forma 😊 Pero ahora…debemos añadir el verdadero soporte para Oculus Go 😉</div>
<div>
<h2>
</h2>
<h2>
Añadiéndo soporte para Oculus Go</h2>
<div>
<br />
Así que, configuramos nuestro proyecto para que trabaje en el Oculus Go…pero eso no es suficiente 😉 Debemos hacer un par de cosas extras…y por supuesto…y más importánte…debemos añadir una forma de controlar a Vector utilizándo el control del Oculus Go 😊</div>
<div>
<br /></div>
<div>
Creamos una nueva carpeta y la llamamos <b>“Modes”</b>. Luego creamos una nueve <b>“Blueprint Class”</b> pero esta vez escogemos <b>“Pawn”</b> y lo llamamos <b>“Pawn Blueprint”</b> (Ingenioso, no?).</div>
<div>
<br /></div>
<div>
Cuando carga, debemos ir a la sección de la izquierda y seleccionar <b>“DefaultSceneRoot”</b>, luego hacer click en <b>“Add Component”</b> y seleccionar <b>“Scene”</b> y cambiarle el nombre a <b>“VRCameraRoot”</b>.</div>
<div>
<br /></div>
<div>
Seleccionamos <b>“VRCameraRoot”</b> y agregamos un componente de <b>“Camera”</b>, y lo llamamos <b>“VRCamera”</b>.</div>
<div>
<br /></div>
<div>
Seleccionamos <b>“VRCameraRoot”</b> y agregamos un componente "<b>Motion Controller</b>", y lo llamamos <b>“OculusGoController”</b>.</div>
<div>
<br /></div>
<div>
Seleccionamos <b>“OculusGoController”</b> y agregamos un componente "<b>Static Mesh</b>", y lo llamamos <b>“OculusGoMesh”</b>.</div>
<div>
<br /></div>
<div>
Para hacer las cosas claras…aquí les dejo una imagen 😊</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-l1kYbuX6q3I/XEruAUua4qI/AAAAAAAAFmw/1zfTOL20_gU_j2ryf_QWOGpivPtpneguQCLcBGAs/s1600/OculusController.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="195" data-original-width="280" src="https://3.bp.blogspot.com/-l1kYbuX6q3I/XEruAUua4qI/AAAAAAAAFmw/1zfTOL20_gU_j2ryf_QWOGpivPtpneguQCLcBGAs/s1600/OculusController.png" /></a></div>
<div>
<br /></div>
<div>
Con el <b>“OculusGoMesh”</b> seleccionado, vamos a sus propiedades y en la Static Mesh, escogemos el mesh <b>“OculusGoController”</b>.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-soQwu5MywhE/XEruIM1MDvI/AAAAAAAAFm0/GDfBQugxNowSRdA8hSpItbeFBV02dP8tACLcBGAs/s1600/OculusMesh.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="133" data-original-width="471" height="90" src="https://4.bp.blogspot.com/-soQwu5MywhE/XEruIM1MDvI/AAAAAAAAFm0/GDfBQugxNowSRdA8hSpItbeFBV02dP8tACLcBGAs/s320/OculusMesh.png" width="320" /></a></div>
<div>
<br /></div>
<div>
Luego de esto, debemos crear algunas variables…la primera se llamará <b>“CameraHeight”</b> y será un <b>“Vector”</b> editable.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-y6vicT7V51Q/XEruahTNDfI/AAAAAAAAFnA/_vhj4a-2dt8n6iGCGmiPie94L8L4eVenwCLcBGAs/s1600/CameraHeight.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="160" data-original-width="474" height="108" src="https://1.bp.blogspot.com/-y6vicT7V51Q/XEruahTNDfI/AAAAAAAAFnA/_vhj4a-2dt8n6iGCGmiPie94L8L4eVenwCLcBGAs/s320/CameraHeight.png" width="320" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<div>
La segunda será llamada <b>“request”</b> y será un <b>Json Field Data (Object Reference)</b>.<br />
<br /></div>
<div>
Finalmente, creamos una llamada <b>“Lift”</b> de tipo <b>Boolean</b> y una variable <b>String</b> llamada <b>“Var”</b>.</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-YAj9R02Xy1c/XEruf1HFFDI/AAAAAAAAFnE/klkQSnhLwWw1IRQZnoaauiE0yJU1j_DpwCLcBGAs/s1600/Variables.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="128" data-original-width="287" src="https://1.bp.blogspot.com/-YAj9R02Xy1c/XEruf1HFFDI/AAAAAAAAFnE/klkQSnhLwWw1IRQZnoaauiE0yJU1j_DpwCLcBGAs/s1600/Variables.png" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<div>
Si están preguntándo por el ojo al lado de <b>“CameraHeight”</b>, eso simplemente significa que es <b>“Pública”</b>, y puedes cambiarlo simplemente haciendo click en el ícono.</div>
<div>
<br /></div>
<div>
Ahora, podemos continuar con la pestaña <b>“Event Graph”</b>.</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-fsxBV04SA8U/XEruy_f3CSI/AAAAAAAAFnQ/OrrrIelWkNgI2FF7sBejcROxlz6g1e6yQCLcBGAs/s1600/SetTrackOrigin.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="300" data-original-width="776" height="123" src="https://3.bp.blogspot.com/-fsxBV04SA8U/XEruy_f3CSI/AAAAAAAAFnQ/OrrrIelWkNgI2FF7sBejcROxlz6g1e6yQCLcBGAs/s320/SetTrackOrigin.jpg" width="320" /></a></div>
<div>
<br /></div>
<div>
Aquí, queremos que cuando la aplicación empiece (<b>Evento BeginPlay</b>) el tracking origin se posicione al nivel de nuestros ojos. El nodo <b>SetRelativeLocation</b> será llamado donde el destino será <b>VRCameraRoot</b> y la nueva ubicación será asignada en <b>CameraHeight</b>. En otras palabras, lo que vamos a ver va a estar al nivel de nuestros ojos.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-Knxe3jZFIDY/XErvCRiBbBI/AAAAAAAAFnU/QOM30b4XMCEpKRJgrVr8gCnRr9FVULk1wCLcBGAs/s1600/SendMovementCommand.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="585" data-original-width="851" height="219" src="https://2.bp.blogspot.com/-Knxe3jZFIDY/XErvCRiBbBI/AAAAAAAAFnU/QOM30b4XMCEpKRJgrVr8gCnRr9FVULk1wCLcBGAs/s320/SendMovementCommand.jpg" width="320" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Cuando presionamos el Thumbstick Arriba o Adelánte, Izquierda o Derecha, asignamos el resultado a nuestra variable Var, luego llamamos a la función Get JSON Request. El URL va a ser la direeción del API más el valor de la variable Var.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-iKL1GW5gkak/XErvM0j0ZkI/AAAAAAAAFnc/GXoOl5YOpTA8zL-Uy9TEvMd4o4krUC6fQCLcBGAs/s1600/SendLiftCommand.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="462" data-original-width="1006" height="146" src="https://1.bp.blogspot.com/-iKL1GW5gkak/XErvM0j0ZkI/AAAAAAAAFnc/GXoOl5YOpTA8zL-Uy9TEvMd4o4krUC6fQCLcBGAs/s320/SendLiftCommand.jpg" width="320" /></a></div>
<div>
<br /></div>
<div>
<div>
Aquí, queremos presionar el boton <b>“Back”</b> del Control del Oculus. La primera vez que lo presionamos, la variable <b>“Lift”</b> va a ser "<b>Falsa</b>", así que la hacemos <b>“Verdadera”</b>. Si es <b>“Verdadera”</b> entonces enviamos el comando <b>“up”</b>. Si presionamos nuevamente, lo volvemos <b>“Falsa”</b> y pasamos el comando <b>“down”</b>. De esta manera podemos controlar como Vector mueve su brazo.</div>
<div>
<br /></div>
<div>
Listo, compilamos, salvamos y listo 😊 Simplemente debemos agregarlo a nuestra escena. Así que, arrástrenlo y cambiemos estos parámetros.</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-Ed6ivpukaZE/XErvnvGuUOI/AAAAAAAAFno/iyxNtT_RA_4HKahuB9uHVJtxdza581UoQCLcBGAs/s1600/OculusTransform.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="105" data-original-width="369" height="91" src="https://2.bp.blogspot.com/-Ed6ivpukaZE/XErvnvGuUOI/AAAAAAAAFno/iyxNtT_RA_4HKahuB9uHVJtxdza581UoQCLcBGAs/s320/OculusTransform.png" width="320" /></a></div>
<div>
<br /></div>
<div>
Además, esto es muy importánte…</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-jZqMKc8njEo/XErvtm_hXvI/AAAAAAAAFns/pg4iwVBQ4UIYUcx2KZ5uoWMAdazb05GjQCLcBGAs/s1600/Pawn.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="210" data-original-width="382" height="175" src="https://3.bp.blogspot.com/-jZqMKc8njEo/XErvtm_hXvI/AAAAAAAAFns/pg4iwVBQ4UIYUcx2KZ5uoWMAdazb05GjQCLcBGAs/s320/Pawn.png" width="320" /></a></div>
<div>
<br /></div>
<div>
<div>
<b>Auto Possess Player</b> debería de ser Player <b>0</b>.</div>
<div>
<br /></div>
<div>
Ahora, presionamos “<b>Build</b>” y esperámos a que todo (incluídas las luces) se compilen.</div>
<div>
<br /></div>
<div>
Luego presionamos Play…y veremos esto…</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-i9gS3JaST4s/XEsFG758RRI/AAAAAAAAFn8/yKgW5tID6JohCS27A0E9IleiunM__alLgCLcBGAs/s1600/EmptyScreen.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="638" data-original-width="1206" height="169" src="https://3.bp.blogspot.com/-i9gS3JaST4s/XEsFG758RRI/AAAAAAAAFn8/yKgW5tID6JohCS27A0E9IleiunM__alLgCLcBGAs/s320/EmptyScreen.png" width="320" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<div>
Por supuesto, si tratan de mover su mouse…nada va a pasar…así que necesitamos enviarlo al Oculus Go 😉</div>
<div>
<br /></div>
<div>
Para hacer esto, simplemente anda a Launch y selecciona tu dispositivo…va a tomar mucho tiempo la primera vez porque todos los shaders, Blueprints y demás, necesitan ser compilados…pero luego de eso, podrás ponerte tu headset y dar un vistazo 😊 Aunque, sin embargo…no va a poder ver nada en la pantalla porque aún necesitamos tener a Vector listo y funcionándo 😉</div>
<h2>
</h2>
<h2>
Instalándo el SDK de Vector</h2>
<div>
<br />
Primero, debemos asegurárnos de que Vector está conectado a Internet utilizándo la aplicación de Vector en nuestro celular…aquí hay un <a href="https://support.anki.com/hc/en-us/articles/360010124633--VIDEO-Vector-and-Connection-How-to-set-Vector-up" target="_blank">video</a> muy interesánte de como hacerlo…</div>
<div>
<br /></div>
<div>
Una vez que lo haz revisado, corta la aplicación en tu celular…puesto que podría interferir con tu aplicación, la cual va a solicitar el control de Vector…</div>
<div>
<br /></div>
<div>
Puedes instalar el SDK escribiéndo lo siguiente</div>
<div>
<br /></div>
<div>
<b><i>python3 -m pip install --user anki_vector</i></b></div>
<div>
<br /></div>
<div>
Luego…auntenticar tu Vector escribiéndo…</div>
<div>
<br /></div>
<div>
<b><i>python3 -m anki_vector.configure</i></b></div>
<div>
<br /></div>
<div>
Se te pedirá el nombre de Vector’s, su dirección IP y número de serie. Además, se te pedirán tus credenciales del Anki Cloud.</div>
<div>
<br /></div>
<div>
Para obtener esta información, simplemente coloca a Vector en su cargador…y presiona su cabeza dos veces. Esto te dará su nombre, luego sube y baja su brazo para poder obtener el IP. El número de serie está en la parte de abajo de Vector’s.</div>
<h2>
</h2>
<h2>
Creándo el Script de Vector</h2>
<div>
<br />
Este script es la última parte de nuestra aventura 😊 Simplemente debemos crear un nuevo archivo llamado VectorOculusGo.py</div>
</div>
<div>
<br /></div>
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">VectorOculusGo.py</th>
</tr>
<tr>
<td><pre>import anki_vector #Control Vector
import requests #Use REST APIs
import json #Consume JSON
import time #Manage time
from anki_vector.util import degrees, distance_mm, speed_mmps
import base64 #Encode/Decode images
import datetime #To get time and data
#URLs to manage upload of Base 64 images and to control Vector using the
#Oculus Go controller
urlAddEye = "https://YourHANA.ondemand.com/VectorEyes/AddVectorEye.xsjs"
urlGetCommand = "https:// YourHANA.ondemand.com/VectorEyes/</pre>
<pre> GetVectorCommand.xsodata/vectorcommand"
urlDeleteCommand = "https:// YourHANA.ondemand.com/VectorEyes/</pre>
<pre> DeleteVectorCommand.xsjs"
def main():
#We stablish a connection with Vector and enable his camara
robot = anki_vector.Robot(enable_camera_feed=True)
#We connect to Vector
robot.connect()
i = 0
#We want this to loop forever…until we close the program
while i == 0:
#We instruct Vector to take a pictures
image = robot.camera.latest_image
#And save it
image.save("./img/Temp.png")
#Once saved, we open it
with open("./img/Temp.png", "rb") as imageFile:
#We get the time and create a timestamp
ts = time.time()
timestamp = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
#We enconde the picture as an Base 64 string
strImg = base64.b64encode(imageFile.read())
#The payload is the parameters that we are sending to the REST API
payload = "{\"timestamp\":\"" + timestamp + "\",\"vectoreye\":\"" + </pre>
<pre> strImg.decode('ascii') + "\"}"
#In the headers, we pass the authentication for the REST API
headers = {
'Content-Type': "application/x-www-form-urlencoded",
'Authorization': "YourAuthorization",
}
#We upload the Base 64 string of the image to the DB
response = requests.request("POST", urlAddEye, data=payload, headers=headers)
#We put the application to sleep for 2 seconds just not to overload the DB
time.sleep(2)
querystring = {"$format":"json"}
#Right after uploading the Base 64 string,
#we want to get any commands coming through
response = requests.request("GET", urlGetCommand, headers=headers, </pre>
<pre> params=querystring)
#We convert the response to JSON
json_response = json.loads(response.text)
#We need to check if there’s any information first and then extract the command
try:
json_text = json_response['d']['results'][0]['COMMAND']
except:
json_text = ""
#Depending on the command, we make Vector move forward, backward or
#lift his handle. If the lift was already up, we put it down first…
if (json_text == 'forward'):
robot.behavior.drive_straight(distance_mm(50), speed_mmps(50))
elif (json_text == 'backward'):
robot.behavior.drive_straight(distance_mm(-50), speed_mmps(50))
elif(json_text == 'right'):
robot.behavior.turn_in_place(degrees(-90))
elif(json_text == 'left'):
robot.behavior.turn_in_place(degrees(90))
elif(json_text == 'up'):
robot.behavior.set_lift_height(0.0)
robot.behavior.set_lift_height(1.0)
elif(json_text == 'down'):
robot.behavior.set_lift_height(0.0)
#After receiving the command, we simply delete it from the DB
response = requests.request("GET", urlDeleteCommand, headers=headers)
if __name__ == '__main__':
main()
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Excelente, el código es bastante sencillo…pero aún así…veamos que sucede con esta aplicación…<br />
<br />
Queremos que Vector tome una foto cada 2 segundos…una vez que la foto es tomada, queremos convertirla a una cadena de Base 64 y luego junto con un Timestamp (que es un día con horas, minutos y segundos) enviarlo a la Base de Datos. Una vez que eso está hecho…descanzamos por 2 segundos y revisamos si hay algún comando disponible. Si hay alguno, hacemos que Vector actué de acuerdo al comando…y simplemente para evitar estar repitiéndo el mismo comado…simplemente lo borramos de la Database, y así podemos tranquilamente enviar un nuevo comando.<br />
<h2>
</h2>
<h2>
Juntando todo</h2>
<br />
Perfecto! Ahora tenemos nuestra aplicación ejecutándose en el Oculus Go y nuestro Vector listo para ejecutar nuestro script.<br />
<br />
Entonces…ten listo un CMD o Terminal y escribe lo siguiente…<br />
<br />
<b><i>python3 VectorOculusGo.py</i></b><br />
<br />
Colocate tu lentes Oculus Go, agarra tu control y presiona “<b>Enter</b>” en tu teclado. Nuestro script comenzará a ejecutarse y verás lo que Vector está mirándo…algo como esto…<br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-fQ1-KLLlkuo/XEsha4U5QeI/AAAAAAAAFoI/jV04SV6itq4uvccuOy0LrGpmZ1NdSliMACLcBGAs/s1600/OculusRunning.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="891" data-original-width="1600" height="178" src="https://1.bp.blogspot.com/-fQ1-KLLlkuo/XEsha4U5QeI/AAAAAAAAFoI/jV04SV6itq4uvccuOy0LrGpmZ1NdSliMACLcBGAs/s320/OculusRunning.jpg" width="320" /></a></div>
<div>
<br /></div>
<div>
Yo lo sé…eso está funcionándo en Unreal Engine y no en el Oculus…pero para eso es el video 😉</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/U5fu9C21DSs/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/U5fu9C21DSs?feature=player_embedded" width="320"></iframe></div>
<div>
<br /></div>
<div>
Espero que le haya gustado este blog y disfruten controlar a Vector desde el interior! -:D</div>
<div>
<br /></div>
<div>
Saludos,</div>
<div>
<br /></div>
<div>
Blag.</div>
<div>
SAP Labs Network.</div>
</div>
Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-87326193734262397052018-12-15T04:18:00.001-08:002018-12-15T04:18:51.092-08:00Hey Vector! A quién me parezco?<style type="text/css">
@page { size: 8.5in 11in; margin: 0.79in }
p { margin-bottom: 0.1in; line-height: 115%; background: transparent }
a:link { color: #000080; so-language: zxx; text-decoration: underline }
</style>
<br />
<div align="justify" style="line-height: 100%; margin-bottom: 0in;">
He jugado con <a href="https://www.anki.com/en-us/cozmo/cozmo-learn?gclid=EAIaIQobChMI_97EvLaa3wIVSYGzCh2i8A-LEAAYASAAEgImZ_D_BwE)%20in%20the%20past,%20so%20when%20Vector%20(https://www.anki.com/en-ca/vector" target="_blank">Cozmo</a> en el pasado...Así que cuando <a href="https://www.anki.com/en-ca/vector" target="_blank">Vector</a> salió al mercado, sabía que tenía que hacer algo con el ;)<br />
<br />
Así que...que es Vector?<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-Gyzs-rfGTCc/XBR3zSGfqHI/AAAAAAAAFgs/0PM-RzWuMrEfAvgB4NsrZ9MU9Hb_WA04wCEwYBhgL/s1600/AnkiVector.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="550" data-original-width="550" height="320" src="https://1.bp.blogspot.com/-Gyzs-rfGTCc/XBR3zSGfqHI/AAAAAAAAFgs/0PM-RzWuMrEfAvgB4NsrZ9MU9Hb_WA04wCEwYBhgL/s320/AnkiVector.jpg" width="320" /></a></div>
<br />
Más que nada un Cozmo de color negro? Bueno...si y no :) Vector tiene un mejor procesador, 4 cores, un microfono, casi el doble de partes, y una mejor y colorida camara.<br />
<br />
Como saben...Soy un verdadero fanático de los <a href="https://api.sap.com/package/SAPLeonardoMLFunctionalServices?section=Artifacts" target="_blank">APIs de Machine Learning de SAP Leonardo</a>...puesto que te permiten facilmente consumir servicios de Machine Learning.<br />
<br />
Para este blog quería hacer algo que siempre me ha gustado...tomar la foto de alguien y luego comparárla con fotos de actores y/o actrices famosas y ver a quien se parece más la persona ;)<br />
<br />
Así que, empecemos :D<br />
<br />
<h3>
<b>Instalándo el SDK de Vector</b></h3>
Asegurate de que Vector está conectado a Internet utilizándo la aplicación de Vector app en IPhone o Android. Aquí hay un muy buen video de como hacer <a href="https://support.anki.com/hc/en-us/articles/360010124633--VIDEO-Vector-and-Connection-How-to-set-Vector-up" target="_blank">eso</a>.<br />
<br />
Una vez que Vector está conectado a Internet...asegurate de terminar la aplicación de Vector en tu celular.<br />
<br />
El SDK de Vector solo estaba disponible para las personas que apoyaron a Anki en su <a href="https://www.kickstarter.com/projects/anki/vector-by-anki-a-giant-roll-forward-for-robot-kind" target="_blank">Campaña de Kickstarter</a>...pero desde el 11 de Noviembre, el SDK está en Alpha público! :D Lo cual significa...que finalmente podemos ensuciarnos las manos con el ;)<br />
<br />
Si por alguna razón ya teniamos el SDK instalado...hay que eliminarlo antes de continuar…<br />
<br />
<b>python3 -m pip uninstall anki_vector</b><br />
<br />
Luego, simplemente hay que hacer esto…<br />
<br />
<b>python3 -m pip install --user anki_vector</b><br />
<br />
Luego, tenemos que autenticar a nuestro Vector…<br />
<br />
<b>python3 -m anki_vector.configure</b><br />
<br />
Se te preguntará por el nombre de Vector, dirección IP y número de serie. También necesitaremos nuestras credenciales del Anki Cloud.<br />
<br />
Para obtener esta información, simplemente pon a Vector en su cargador...y presiona dos veces en un ¨espalda. Esto te dará su nombre, luego sube y baja su brazo para obtener el IP. El número de serie está en la parte de abajo de Vector’s.<br />
<br />
<h3>
<b>La fase de aprendizaje</b></h3>
<div>
<b><br /></b></div>
Primero, lo primero...necesitamos varias fotos de gente famosa...y para eso utilicé la página web the <a href="https://www.themoviedb.org/person?language=en-US&page=1" target="_blank">The Movie DB</a>...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-FbknOdzI3a4/XBR4Uhpy9YI/AAAAAAAAFgw/yU8wgF57yYIPJW5kdQyKQFpiwrL2TB8sACLcBGAs/s1600/TheMovieDB.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="644" data-original-width="1035" height="248" src="https://3.bp.blogspot.com/-FbknOdzI3a4/XBR4Uhpy9YI/AAAAAAAAFgw/yU8wgF57yYIPJW5kdQyKQFpiwrL2TB8sACLcBGAs/s400/TheMovieDB.png" width="400" /></a></div>
<br />
Ahí me descargué 100 imagenes casi aleatorias tanto de hombre como mujeres. No entré a cada perfil sino que simplemente descargué las fotos tal como se ven.<br />
<br />
Ahora, hay un API de SAP Leonardo que se llama <a href="https://api.sap.com/api/face_feature_extraction_api/resource" target="_blank">“Inference Service for Face Feature Extraction”</a> el cual basicamente toma una imagen, determina si hay o no un rostro y luego extrae sus características...como el color de los ojos, forma de la boca, cabello y demás...y esta información es retornada en un bonito aunque casi imposible de decifrar Vector de Características. Es decir...se ven solo como números...y pueden significar cualquier cosa :P<br />
<br />
En fín...Creé una carpeta llamada “People” y le pasé las 100 imagenes. Así que, el suguiente paso es por supuesto obtener todas la características de todas las imagenes...y manualmente es obviamente no solo dificil, pero también sin sentido...es mucho mejor optimizar el proceso ;)<br />
<br />
Un lenguaje de programación que he llegado a adorar es <a href="https://crystal-lang.org/" target="_blank">Crystal</a>...<b>Rápido como C, elegante como Ruby</b>, sí...un mucho mejor manera de hacer Ruby :)<br />
<br />
La instalación es muy sencilla y podemos encontrar información <a href="https://crystal-lang.org/docs/installation/" target="_blank">aquí</a> pero yo estoy utilizándo Ubuntu en VMWare, así que aquí están sus instrucciones…<br />
<br />
En una ventana de terminal copia y pega este código…<br />
<br />
<b>curl -sSL https://dist.crystal-lang.org/apt/setup.sh | sudo bash</b><br />
<br />
Luego, simplemente hacemos esto…<br />
<br />
<b>sudo apt-get update</b><br />
<b><br /></b>
<b>sudo apt install crystal</b><br />
<br />
La instalación de los siguiente módulos es opcional pero recomendada…<br />
<br />
<b>sudo apt install libssl-dev # for using OpenSSL</b><br />
<b>sudo apt install libxml2-dev # for using XML</b><br />
<b>sudo apt install libyaml-dev # for using YAML</b><br />
<b>sudo apt install libgmp-dev # for using Big numbers</b><br />
<b>sudo apt install libreadline-dev # for using Readline</b><br />
<br />
Una vez que hemos terminado...es hora de escribir nuestra aplicación…primero debemos crear una carpeta llamada “Features”.<br />
<br />
Llamaremos a nuestro script <b>PeopleGenerator.cr</b> y copiaremos y pegaremos el suguiente código…<br />
<br /></div>
<style type="text/css">
@page { size: 8.5in 11in; margin: 0.79in }
p { margin-bottom: 0.1in; line-height: 115%; background: transparent }
a:link { color: #000080; so-language: zxx; text-decoration: underline }
</style><style type="text/css">
@page { size: 8.5in 11in; margin: 0.79in }
p { margin-bottom: 0.1in; line-height: 115%; background: transparent }
a:link { color: #000080; so-language: zxx; text-decoration: underline }
</style>
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">PeopleGenerator.cr</th>
</tr>
<tr>
<td><pre>require "http"
require "json"
class FaceFeature
JSON.mapping({
face_feature: Array(Float64)
})
end
class Predictions
JSON.mapping({
faces: Array(FaceFeature)
})
end
class Person
JSON.mapping({
id: String,
predictions: Array(Predictions)
})
end
folder = Dir.new("#{__DIR__}/People")
while photo = folder.read
if photo != "." && photo != ".." && photo != "Features"
io = IO::Memory.new
builder = HTTP::FormData::Builder.new(io)
File.open("#{__DIR__}/People/" + photo) do |file|
metadata = HTTP::FormData::FileMetadata.new(filename: photo)
headers = HTTP::Headers{"Content-Type" => "image/jpg"}
builder.file("files", file, metadata, headers)
end
builder.finish
headers = HTTP::Headers{"Content-Type" => builder.content_type,
"APIKey" => "YourAPIKey",
"Accept" => "application/json"}
response = HTTP::Client.post("https://sandbox.api.sap.com/ml/
facefeatureextraction/
face-feature-extraction", body: io.to_s ,
headers: headers)
feature_name = "#{__DIR__}/Features/" + File.basename(photo, ".jpg") + ".txt"
puts photo
File.write(feature_name, Person.from_json(response.body).predictions[0].
faces[0].face_feature)
sleep 2.second
end
end
command = "zip -r -j features.zip #{__DIR__}/Features"
Process.run("sh", {"-c", command})
puts "Done."
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Expliquemos el código un momento, antes de ver los resultados…<br />
<br />
<b>require "http"</b><br />
<b>require "json"</b><br />
<br />
Necesitamos esta dos librerías para poder llamar al API de SAP Leonardo pero también para poder leer y extraer los resultados…<br />
<br />
<b>class FaceFeature</b><br />
<b> JSON.mapping({</b><br />
<b> face_feature: Array(Float64)</b><br />
<b> })</b><br />
<b>end</b><br />
<b><br /></b>
<b>class Predictions</b><br />
<b> JSON.mapping({</b><br />
<b><span style="white-space: pre;"> </span>faces: Array(FaceFeature)</b><br />
<b> })</b><br />
<b>end</b><br />
<b><br /></b>
<b>class Person</b><br />
<b> JSON.mapping({</b><br />
<b><span style="white-space: pre;"> </span>id: String,</b><br />
<b><span style="white-space: pre;"> </span>predictions: Array(Predictions)</b><br />
<b> })</b><br />
<b>end</b><br />
<br />
Este es el mapeo de JSON que necesitamos para extraer la información que regresa del API..<br />
<br />
<b>folder = Dir.new("#{__DIR__}/People")</b><br />
<b>while photo = folder.read</b><br />
<b> if photo != "." && photo != ".." && photo != "Features"</b><br />
<b><span style="white-space: pre;"> </span>io = IO::Memory.new</b><br />
<b><span style="white-space: pre;"> </span>builder = HTTP::FormData::Builder.new(io)</b><br />
<b><br /></b>
<b><span style="white-space: pre;"> </span>File.open("#{__DIR__}/People/" + photo) do |file|</b><br />
<b><span style="white-space: pre;"> </span>metadata = HTTP::FormData::FileMetadata.new(filename: photo)</b><br />
<b><span style="white-space: pre;"> </span>headers = HTTP::Headers{"Content-Type" => "image/jpg"}</b><br />
<b><span style="white-space: pre;"> </span>builder.file("files", file, metadata, headers)</b><br />
<b><span style="white-space: pre;"> </span>end</b><br />
<b><span style="white-space: pre;"> </span>builder.finish</b><br />
<b><br /></b>
<b><span style="white-space: pre;"> </span>headers = HTTP::Headers{"Content-Type" => builder.content_type, "APIKey" => "YourAPIKey,"Accept" => "application/json"}</b><br />
<b><span style="white-space: pre;"> </span>response = HTTP::Client.post("https://sandbox.api.sap.com/ml/facefeatureextraction/face-feature-extraction", body: io.to_s , headers: headers)</b><br />
<span style="white-space: pre;"><b> </b></span><br />
<b><span style="white-space: pre;"> </span>feature_name = "#{__DIR__}/Features/" + File.basename(photo, ".jpg") + ".txt"</b><br />
<span style="white-space: pre;"><b> </b></span><br />
<b><span style="white-space: pre;"> </span>puts photo<span style="white-space: pre;"> </span></b><br />
<span style="white-space: pre;"><b> </b></span><br />
<b><span style="white-space: pre;"> </span>File.write(feature_name, Person.from_json(response.body).predictions[0].faces[0].face_feature)</b><br />
<b><span style="white-space: pre;"> </span>sleep 2.second</b><br />
<b> end</b><br />
<b>end</b><br />
<b><br /></b>
<b>command = "zip -r -j features.zip #{__DIR__}"</b><br />
<b>Process.run("sh", {"-c", command})</b><br />
<b><br /></b>
<b>puts "Done."</b><br />
<b><br /></b>
Esta sección es más larga, primero especificamos la carpeta de la cuál serán leidas las imagenes. Luego por cada imagen determinaremos si es una imagen o una estructura de archivo...por supuesto nosotros solo queremos imagenes…<br />
<br />
Luego, creamos un FormData builder para poder evitar tener que codificar las imagenes en base64...ponerlas en un JSON payload...etc...esta manera es mucho más sencilla y nativa…<br />
<br />
Abrimos cada imagen y alimentamos el metadada y cabeceras del FormData.<br />
<br />
También, necesitamos para las “cabeceras” extras requeridas por SAP Leonardo.<br />
<br />
Una vez que esto está listo, podemos simplemente llamar al REST API, y luego crear un “Nombre de Característica” el cual va a ser el nombre del archivo generado...basicamente la imagen con una extensión “.txt”.<br />
<br />
Para cada archivo vamos a extraer el vector de características de la respuesta del JSON, escribir el archivo y darle 2 segundos de descanzo entre llamadas para no sobregargar a llamada del API…<br />
<br />
Una vez que está todo listo, simplemente llamamos un comando “zip” del terminal y los zipeamos…<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-sh_krR9O2SY/XBR8zgj3qNI/AAAAAAAAFg8/4qAzcARvN1Awmu8_eq-_O_OxJVyXWuxvACLcBGAs/s1600/FeaturesFiles.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="342" data-original-width="456" height="300" src="https://1.bp.blogspot.com/-sh_krR9O2SY/XBR8zgj3qNI/AAAAAAAAFg8/4qAzcARvN1Awmu8_eq-_O_OxJVyXWuxvACLcBGAs/s400/FeaturesFiles.png" width="400" /></a></div>
<br />
Ahora, el archivo zip contiene 100 archivos...cada uno con las características de cada una de las fotos que tenemos almacenadas en la carpeta “People”.<br />
<br />
Simply as that...we have trained our application ;)<br />
<br />
<h3>
<b>La fase de pruebas y execución</b></h3>
<br />
Yo sé que normalmente primero haces pruebas para validar tu modelo...pero por esta vez...podemos hacer las dos cosas al mismo tiempo ;)<br />
<br />
Vamos a crear un script en Python que va a hacer que se nos tome una foto...llamaremos al API de características en esa foto y luego llamaremos a otro API para determinar a quien nos parecemos…<br />
<br />
Creemos un script llamado <b>GuessWho.py</b><br />
<b><br /></b>
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">GuessWho.py</th>
</tr>
<tr>
<td><pre>import anki_vector
import threading
import requests
import os
import json
import time
import subprocess
import re
import math
from PIL import Image
from anki_vector.events import Events
from anki_vector.util import degrees
event_done = False
said_text = False
new_width = 184
new_height = 96
def main():
args = anki_vector.util.parse_command_args()
with anki_vector.Robot(args.serial, enable_face_detection=True,
enable_camera_feed=True) as robot:
evt = threading.Event()
def on_robot_observed_face(event_type, event):
global said_text
if not said_text
said_text = True
robot.say_text("Taking Picture!")
image = robot.camera.latest_image
image.save("Temp.png")
robot.say_text("Picture Taken!")
evt.set()
robot.behavior.set_head_angle(degrees(45.0))
robot.behavior.set_lift_height(0.0)
robot.events.subscribe(on_robot_observed_face, Events.robot_observed_face)
try:
if not evt.wait(timeout=10):
print("---------------------------------")
except KeyboardInterrupt:
pass
def guess_who():
args = anki_vector.util.parse_command_args()
with anki_vector.Robot(args.serial) as robot:
url = "https://sandbox.api.sap.com/ml/facefeatureextraction/
face-feature-extraction"
img_path = "Temp.png"
files = {'files': open (img_path, 'rb')}
headers = {
'APIKey': "YourAPIKey",
'Accept': "application/json",
}
response = requests.post(url, files=files, headers=headers)
robot.say_text("I'm processing your picture!")
json_response = json.loads(response.text)
json_text = json_response['predictions'][0]['faces'][0]['face_feature']
f = open("myfile.txt", "w")
f.write(str(json_text))
f.close()
time.sleep(1)
p = subprocess.Popen('zip -u features.zip myfile.txt', shell=True)
time.sleep(1)
url = "https://sandbox.api.sap.com/ml/similarityscoring/similarity-scoring"
files = {'files': ("features.zip", open ("features.zip", 'rb'),
'application/zip')}
params = {'options': '{"numSimilarVectors":100}'}
response = requests.post(url, data=params, files=files, headers=headers)
json_response = json.loads(response.text)
robot.say_text("I'm comparing your picture with one hundred other pictures!")
for x in range(len(json_response['predictions'])):
if json_response['predictions'][x]['id'] == "myfile.txt":
name, _ = os.path.splitext(json_response['predictions'][x]
['similarVectors'][0]['id'])
name = re.findall('[A-Z][^A-Z]*', name)
full_name = " ".join(name)
pic_name = "People/" + "".join(name) + ".jpg"
avg = json_response['predictions'][x]['similarVectors'][0]['score']
robot.say_text("You look like " + full_name +
" with a confidence of " +
str(math.floor(avg * 100)) + " percent")
image_file = Image.open(pic_name)
image_file = image_file.resize((new_width, new_height),
Image.ANTIALIAS)
screen_data = anki_vector.screen.convert_image_to_screen_data(
image_file)
robot.behavior.set_head_angle(degrees(45.0))
robot.conn.release_control()
time.sleep(1)
robot.conn.request_control()
robot.screen.set_screen_with_image_data(screen_data, 0.0)
robot.screen.set_screen_with_image_data(screen_data, 25.0)
print(full_name)
print(str(math.floor(avg * 100)) + " percent")
time.sleep(5)
if __name__ == '__main__':
main()
guess_who()
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Este script es mucho más largo...así que tratemos se asegurárnos de que entendemos todo…<br />
<br />
<b>import anki_vector</b><br />
<b>import threading</b><br />
<b>import requests</b><br />
<b>import os</b><br />
<b>import json</b><br />
<b>import time</b><br />
<b>import subprocess</b><br />
<b>import re</b><br />
<b>import math</b><br />
<b>from PIL import Image</b><br />
<b>from anki_vector.events import Events</b><br />
<b>from anki_vector.util import degrees</b><br />
<br />
Son muchas librerías :) La primera es bastante obvia...es como nos conectamos a Vector ;)<br />
<br />
La segunda es para manejar “threads” (hilos) puesto que necesitamos hacer alguna cosas de forma asíncrona.<br />
<br />
La tercera es para manejar la llamadas a los APIs.<br />
<br />
La cuarta es para manejar el acceso a carpetas.<br />
<br />
La quinta es para manejar las respuestas JSON que regresar de las llamadas a los APIs.<br />
<br />
La sexta es para que podamos retrazar la ejecución de nuestra aplicación.<br />
<br />
La séptima es para poder llamar comandos del terminal.<br />
<br />
La octava es para utilizar Expresiones Regulares.<br />
<br />
La novena es para majenar funciones matemáticas.<br />
<br />
La décima es para manejar manipulación de imágnes.<br />
<br />
La onceava es para manejar eventos puesto que queremos que Vector trate de detectar nuestro rostro.<br />
<br />
La última la utilizamos para poder mover la cabeza de Vector.<br />
<br />
<b>def main():</b><br />
<b> args = anki_vector.util.parse_command_args()</b><br />
<b> with anki_vector.Robot(args.serial, enable_face_detection=True, enable_camera_feed=True) as robot:</b><br />
<b> evt = threading.Event()</b><br />
<b><br /></b>
<b> def on_robot_observed_face(event_type, event):</b><br />
<b><br /></b>
<b> global said_text</b><br />
<b> if not said_text:</b><br />
<b> said_text = True</b><br />
<b> robot.say_text("Taking Picture!")</b><br />
<b> image = robot.camera.latest_image</b><br />
<b> image.save("Temp.png")</b><br />
<b> robot.say_text("Picture Taken!")</b><br />
<b> evt.set()</b><br />
<b><br /></b>
<b> robot.behavior.set_head_angle(degrees(45.0))</b><br />
<b> robot.behavior.set_lift_height(0.0)</b><br />
<b><br /></b>
<b> robot.events.subscribe(on_robot_observed_face, Events.robot_observed_face)</b><br />
<b><br /></b>
<b> try:</b><br />
<b> if not evt.wait(timeout=5):</b><br />
<b> print("---------------------------------")</b><br />
<b> except KeyboardInterrupt:</b><br />
<b> pass</b><br />
<b><br /></b>
Esté es de hecho...nuestro evento principal :) Aquí vamos a abrir una conexión con Vector, y puesto que podemos tener múltiples Vectors...necesitamos el número de serie para especificar cual de todos queremos utilizar...tambien debemos activar la detección de rostros y la camara.<br />
<br />
Vamos a iniciar un hilo puesto que debemos iniciar el evento donde Vector intenta detectar nuestro rostro. Si nos puede ver, entonces nos dirá “Taking Picture!”...tomará la imagen, la grabará y luego dirá “Picture Taken!”. Después de esto, nuestro evento está terminado...pero...pero mientras esto sucede podemos subir y bajar su cabeza, así como bajar su brazos para que Vector pueda vernos mejor.<br />
<br />
Como podemos ver estamos subscritos a dos eventos, uno que observa nuestro rostro y otro cuando nuestro rostro ha sido identificado…<br />
<br />
<b>def guess_who():</b><br />
<b> args = anki_vector.util.parse_command_args()</b><br />
<b> with anki_vector.Robot(args.serial) as robot:<span style="white-space: pre;"> </span></b><br />
<b> url = "https://sandbox.api.sap.com/ml/facefeatureextraction/face-feature-extraction"</b><br />
<b> </b><br />
<b> img_path = "Temp.png"</b><br />
<b> files = {'files': open (img_path, 'rb')}</b><br />
<b><br /></b>
<b> headers = {</b><br />
<b> 'APIKey': "YourAPIKey",</b><br />
<b> 'Accept': "application/json",</b><br />
<b> }</b><br />
<span style="white-space: pre;"><b> </b></span><br />
<b> response = requests.post(url, files=files, headers=headers)</b><br />
<b><br /></b>
<b> robot.say_text("I'm processing your picture!")</b><br />
<span style="white-space: pre;"><b> </b></span><br />
<b> json_response = json.loads(response.text)</b><br />
<b> json_text = json_response['predictions'][0]['faces'][0]['face_feature']</b><br />
<span style="white-space: pre;"><b> </b></span><br />
<b> f = open("myfile.txt", "w")</b><br />
<b> f.write(str(json_text))</b><br />
<b> f.close()</b><br />
<span style="white-space: pre;"><b> </b></span><br />
<b> time.sleep(1)</b><br />
<span style="white-space: pre;"><b> </b></span><br />
<b> p = subprocess.Popen('zip -u features.zip myfile.txt', shell=True)</b><br />
<span style="white-space: pre;"><b> </b></span><br />
<b> time.sleep(1)</b><br />
<span style="white-space: pre;"><b> </b></span><br />
<b> url = "https://sandbox.api.sap.com/ml/similarityscoring/similarity-scoring"</b><br />
<span style="white-space: pre;"><b> </b></span><br />
<b> files = {'files': ("features.zip", open ("features.zip", 'rb'), 'application/zip')}</b><br />
<b> params = {'options': '{"numSimilarVectors":100}'}</b><br />
<span style="white-space: pre;"><b> </b></span><br />
<b> response = requests.post(url, data=params, files=files, headers=headers)</b><br />
<b> json_response = json.loads(response.text)</b><br />
<b><br /></b>
<b> robot.say_text("I'm comparing your picture with one hundred other pictures!")</b><br />
<b><br /></b>
<b> for x in range(len(json_response['predictions'])):</b><br />
<b> if json_response['predictions'][x]['id'] == "myfile.txt":</b><br />
<b> name, _ = os.path.splitext(json_response['predictions'][x]['similarVectors'][0]['id']) </b><br />
<b> name = re.findall('[A-Z][^A-Z]*', name)</b><br />
<b> full_name = " ".join(name)</b><br />
<b> pic_name = "People/" + "".join(name) + ".jpg"</b><br />
<b> avg = json_response['predictions'][x]['similarVectors'][0]['score']</b><br />
<b> robot.say_text("You look like " + full_name + " with a confidence of " + str(math.floor(avg * 100)) + " percent")</b><br />
<b> image_file = Image.open(pic_name)</b><br />
<b> image_file = image_file.resize((new_width, new_height), Image.ANTIALIAS) </b><br />
<b> screen_data = anki_vector.screen.convert_image_to_screen_data(image_file)</b><br />
<b> robot.behavior.set_head_angle(degrees(45.0))</b><br />
<b> robot.conn.release_control()</b><br />
<b> time.sleep(1)</b><br />
<b> robot.conn.request_control() </b><br />
<b> robot.screen.set_screen_with_image_data(screen_data, 0.0)</b><br />
<b> robot.screen.set_screen_with_image_data(screen_data, 25.0)</b><br />
<b> </b><br />
<b> print(full_name)</b><br />
<b> print(str(math.floor(avg * 100)) + " percent")</b><br />
<b><br /></b>
<b> time.sleep(5)</b><br />
<br />
Este método maneja las partes complicadas de la aplicación…<br />
<br />
Nos conectamos a Vector nuevamente...aunque esta vez no debemos activar nada puesto que la imagen ya ha sido tomada.<br />
<br />
Pasamos el URL para el API de características.<br />
<br />
Luego abrimos nuestro archivo “Temp.png”, el cual es la foto que Vector nos tomó previamente.<br />
<br />
Debemos pasar la cabecera extra al API de SAP Leonardo.<br />
<br />
Llamamos al API y obtenemos el JSON de respuesta.<br />
<br />
Nuevamente, debemos extraer la información de las características de la respuesta JSON. Esta vez sin embargo, vamos a crear un archivo llamado “myfile.txt”. Vamos a hacer que la aplicación duerma por un segundo y luego llamar a un proceso en el terminal para poder agregar “myfile.txt” a nuestro archivo Features.zip…<br />
<br />
Luego, lo hacemos dormir por un segundo más...y esto es solo para nos sobrecargar de llamadas al API…<br />
<br />
Aquí, vamos a llamar a un API diferente, el cual se llama <a href="https://sandbox.api.sap.com/ml/similarityscoring/similarity-scoring" target="_blank">Inference Service for Similarity Scoring</a><br />
<br />
Esta API leerá los 101 archivos de características y determinará el coseno de la distancia (-1 a 1) para archivo comparado el uno con el otro. De esta manera, puede determinar que archivos están mas cercanos los unos de lo otros y por lo tanto a quien nos parecemos más...brindándonos además un porcentaje de confidencia.<br />
<br />
Esta llamada es un poco más complicada que las anteriores puesto que debemos cargar el archivo zip…<br />
<br />
<b> files = {'files': ("features.zip", open ("features.zip", 'rb'), 'application/zip')}</b><br />
<b> params = {'options': '{"numSimilarVectors":100}'}</b><br />
<span style="white-space: pre;"><b> </b></span><br />
<b> response = requests.post(url, data=params, files=files, headers=headers)</b><br />
<b> json_response = json.loads(response.text)</b><br />
<br />
Tengamos en cuenta de que aunque tenemos 101 archivos...debemos comparar 1 contra los 100 restántes...así que pasamos 100 al parámetro “numSimilarVectors”.<br />
<br />
Una vez que hemos hecho esto, debemos leer cada sección de la respuesta JSON hasta que encontremos un ïd"con el valor “myfile.txt”. Una vez que tenemos eso, uilizamos Expresiones Regulares para extraer solo el nombre sin la extensiòn. Además, necesitamos tener el nombre de la imagen...así que al fín y al cabo, debemos tener algo como esto…<br />
<br />
<b>full_name = “Nicolas Cage”</b><br />
<b>pic_name = “People/NicolasCage.jpg”</b><br />
<br />
También debemos extraer el porcentaje de confianza…<br />
<br />
<b>avg = json_response['predictions'][x]['similarVectors'][0]['score'] </b><br />
<br />
Así que, podemos tener a Vector diciéndo “You look like Nicolas Cage with a confidence of 75 percent”.<br />
<br />
Ahora...aquí viene la parte divertida ;) Ya sabemos a quien nos parecemos...pero digamos...que no nos acordamos realmente como es la cara de Nicolas Cage...así que tomamos ventaja de la elegante pantalla de Vector para mostrar una imagen ahí ;) Por cierto...debemos perder el control, tomarlo nuevamente y mostrar la imagen por cero segundos y luego volver a mostrarla...esto es basicamente por que si no lo hacemos, los ojos de Vector tapan toda la pantalla...y esta es una forma de corregir ese comportamiento;)<br />
<br />
<b> <span style="white-space: pre;"> </span> image_file = Image.open(pic_name)</b><br />
<b> image_file = image_file.resize((new_width, new_height), Image.ANTIALIAS) </b><br />
<b> screen_data = anki_vector.screen.convert_image_to_screen_data(image_file)</b><br />
<b> robot.behavior.set_head_angle(degrees(45.0))</b><br />
<b> robot.conn.release_control()</b><br />
<b> time.sleep(1)</b><br />
<b> robot.conn.request_control() </b><br />
<b> robot.screen.set_screen_with_image_data(screen_data, 0.0)</b><br />
<b> robot.screen.set_screen_with_image_data(screen_data, 25.0)</b><br />
<br />
Primero abrimos la imagen, luego le cambiamos el tamaño para que entre en la pantalla, luego los convertimos a formato de Vector y finalmente podemos mostrárla por pantalla, especificándo por cuanto tiempo queremos que permanence ahí…<br />
<br />
<b> print(full_name)</b><br />
<b> print(str(math.floor(avg * 100)) + " percent")</b><br />
<b><br /></b>
<b> time.sleep(5)</b><br />
<br />
Mostramos un poco de información en la pantalla y dejamos que duerma por 5 segundos para que la imagen no se desaparezca demasiado rápido ;)<br />
<br />
Finalmente! La parte más importante de todo el script...llamar a las funciones :P<br />
<br />
<b>if __name__ == '__main__':</b><br />
<b> main()</b><br />
<b> guess_who()</b><br />
<br />
Y basicamente eso es todo :) Abrimos un ventana de Terminar y escribimos…<br />
<br />
<b>python3 GuessWho.py</b><br />
<br />
Vector va a tratar de mirárnos y detectar nuesto rostro...tomará una foto...las APIs de SAP Leonardo serán llamadas...escucharemos y veremos a quien nos parecemos ;)<br />
<br />
Espero que disfruten de este blog...Yo obviamente lo hice :D<br />
<br />
Y solo para cerrar el tema de forma elegánte...aquí les dejo un pequeño video…<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/QcV1FT0SS8s/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/QcV1FT0SS8s?feature=player_embedded" width="320"></iframe></div>
<br />
Porsiacaso...aquí está la foto que Vector me tomó...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-kgymLbf31Y0/XBR_tT2gkWI/AAAAAAAAFhI/t2kXoBAlOc47QkxAQyoywrVC5ENtj4ATACLcBGAs/s1600/Temp.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="360" data-original-width="640" height="180" src="https://2.bp.blogspot.com/-kgymLbf31Y0/XBR_tT2gkWI/AAAAAAAAFhI/t2kXoBAlOc47QkxAQyoywrVC5ENtj4ATACLcBGAs/s320/Temp.png" width="320" /></a></div>
<br />
Saludos,<br />
<br />
Blag.<br />
SAP Labs Network.Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-69807455184406867752018-09-19T11:05:00.000-07:002018-09-19T11:05:08.741-07:00API's de SAP Leonardo Machine Learning en el Go<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-WOspSm12WX4/W18k-wNguLI/AAAAAAAAFZk/LQlUWdzh8vAbiI67d30lXTXFPq1dfFX3gCPcBGAYYCw/s1600/d_shop_blog_logoToronto.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="173" data-original-width="493" height="140" src="https://4.bp.blogspot.com/-WOspSm12WX4/W18k-wNguLI/AAAAAAAAFZk/LQlUWdzh8vAbiI67d30lXTXFPq1dfFX3gCPcBGAYYCw/s400/d_shop_blog_logoToronto.png" width="400" /></a></div>
<br />
<div style="text-align: justify;">
Trabajar en el d-shop, primero en el Silicon Valley y ahora en Toronto, me permite usar mi creatividad y jugar con cualquier nuevo gadget que llega al mercado.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Esta vez, fué el turno del <a href="https://www.oculus.com/go/" target="_blank">Oculus Go</a> 😉 y qué es el Oculus Go? Bueno, es un headset de VR independiente, lo cual significa basicamente…no hay cables que se puedan enredar 😉</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Para este proyecto tenía la oportunidad de trabajar ya sea con <a href="https://unity3d.com/" target="_blank">Unity</a> o <a href="https://www.unrealengine.com/en-US/what-is-unreal-engine-4" target="_blank">Unreal Engine</a>…Ya había utilizado Unity muchas veces para desarrollar aplicaciones para el <a href="https://www.oculus.com/" target="_blank">Oculus Rift</a> y <a href="https://www.microsoft.com/en-CA/hololens" target="_blank">Microsoft HoloLens</a>…así que pensé que Unreal Engine sería una mejor opción esta vez…aunque nunca lo había utilizado antes para un proyecto grande…especialmente porque nada le gana a Unreal cuando se trata de gráficos…</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Con Unreal selecionado…tenía que tomar otra decisión…C++ o Blueprints…bueno…aunque he utilizado C++ en el pasado para un par de aplicaciones en <a href="https://libcinder.org/" target="_blank">Cinder</a>…Blueprints se veía mejor puesto que quería desarrollar más rápidamente y sin demasiadas complicaciones…y bueno…esa es solo la mitad de la verdad…algunas veces los Blueprints pueden convertirse en algo muy confuso 😊</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Solo para que lo sepan, utilicé Unreal Engine 4.20.2 y cree una aplicación Blueprints.</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-jvH4y7zzpyA/W6JsSF-BqCI/AAAAAAAAFcQ/I-LLZfTzX2kTgWtblNBVnyUDvNKdRHdUgCLcBGAs/s1600/Unreal4_20_2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="163" data-original-width="244" src="https://4.bp.blogspot.com/-jvH4y7zzpyA/W6JsSF-BqCI/AAAAAAAAFcQ/I-LLZfTzX2kTgWtblNBVnyUDvNKdRHdUgCLcBGAs/s1600/Unreal4_20_2.jpg" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<div class="MsoNormal">
Desde el inicio sabía que quería utilizar las <a href="https://api.sap.com/package/SAPLeonardoMLFunctionalServices?section=Artifacts" target="_blank">API´s de Machine Learning de SAP Leonardo</a>…puesto que ya las había utilizado antes para mi blog “<a href="http://atejada.blogspot.com/2018/07/cozmo-leeme.html" target="_blank">Cozmo, leeme</a>” donde utilicé al robot <a href="https://www.anki.com/en-ca/cozmo" target="_blank">Cozmo</a>, OpenCV y el API de OCR de SAP Leonardo para leer una pizarra con un mensaje escrito a mano y hacer que Cozmo le leyera en voz alta.<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<b><span style="font-size: large;">La idea</span></b></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Esta vez, quería demostrar el uso de más de una API…así que tenía que escoger cuales…afortunadamente eso no fué muy dificil…la mayoría de las APIs son más orientadas al uso “Empresarial”…Así que eso me dejaba con “Image Classification, OCR y Language Translation” …</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Con todo decidido…aún debía pensar como utilizar las API’s…es decir…el Oculus Go es Realidad Virtual…así que no había la posibilidad de mirar a algo, tomarle una foto y enviarla al API…</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Así que, pensé…por qué no utilizo <a href="https://www.blender.org/" target="_blank">Blender</a> (que es una herramienta Open Source para el desarollo de gráficos generados por computadora) y hacer algunos models…luego podría hacer un render de los models…tomar una foto y enviarlas al API…y tener los model significa…que podía convertirlos a archivos “.fbx” y cargarlos a Unreal para una mejor experiencia…</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Con las API's de OCR…era diferente…puesto que necesitaba images con texto…así que decidí utilizar <a href="https://inkscape.org/en/" target="_blank">InkScape</a> (que es un software Open Source para editar imágenes vectoriales).</div>
<div>
<br /></div>
<div>
<div>
<b><span style="font-size: large;">La implementación</span></b></div>
<div>
<br /></div>
<div>
Cuando comencé a trabajar en este proyecto…sabía que tenía que ir paso a paso…así que primero hice una version Windows de la aplicación…luego la convertí a Android (Lo cual fué muy sencillo dicho sea de paso) y finalmente una aplicación para el Oculus Go (Lo cual fué un poco doloroso…)</div>
<div>
<br /></div>
<div>
Así qué, lástimosamente no voy a poder poner nada de código fuente en este blog…simplemente porque utilicé Blueprints…y no estoy muy seguro que si quieran reproducirlos a mano ☹ Se darán cuenta de lo que hablo más adelante en el blog…</div>
<div>
<br /></div>
<div>
En fín…continuemos 😊</div>
<div>
<br /></div>
<div>
Cuando pensé en este proyecto, lo primero que me vino a la mente fué…quiero tener un espacio de d-shop…con algunos escritorios…un letrero para cada API…algunas luces no estarían nada mal…</div>
</div>
<div>
<br /></div>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-2iBsN4PT3PY/W6J5BVW8zBI/AAAAAAAAFcc/cfn9KdE-HF8ewpFcXxsbi73BSrqr0IFJQCLcBGAs/s1600/SAPLeonardoGo_ShowFloor.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="579" data-original-width="1209" height="191" src="https://4.bp.blogspot.com/-2iBsN4PT3PY/W6J5BVW8zBI/AAAAAAAAFcc/cfn9KdE-HF8ewpFcXxsbi73BSrqr0IFJQCLcBGAs/s400/SAPLeonardoGo_ShowFloor.jpg" width="400" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<div>
Entonces, no se vé nada mal, no?</div>
<div>
<br /></div>
<div>
Luego, quería trabajar en el API de “Image Classification”…así que quería que fuera bastante parecido…pero con solo un escritorio en el medio…que luego se convirtío en un pedestal…con los objectos 3D rotándo encima…también debería haber un espacio listo para mostrar el resultado de llamar a la API…también…flechas para permitir al usuario cambiar el modelo 3D…y el ícono de una casa para permitir al usario regresar al “Showfloor”…</div>
<div>
<br /></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-aNllgAakoVQ/W6J5LbotrTI/AAAAAAAAFcg/Wv9A1ybiiUs9igb33DAsb6Jlsa4ZLrtegCLcBGAs/s1600/ImageClassificationEditor.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="572" data-original-width="1204" height="190" src="https://1.bp.blogspot.com/-aNllgAakoVQ/W6J5LbotrTI/AAAAAAAAFcg/Wv9A1ybiiUs9igb33DAsb6Jlsa4ZLrtegCLcBGAs/s400/ImageClassificationEditor.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
<div>
Se darán cuenta de dos cosas muy rápidamente…primero…qué se supone que esa esfera? Bueno…es solo un contenedor que será reemplazado con los objectos 3D 😊 También…pueden ver un poster negro que dice “SAP Leonardo Output”…eso está escondido y solo se vuelve visible cuando lanzamos la aplicación…</div>
<div>
<br /></div>
<div>
Para las escenas de “Optical Character Recognition” y “Language Translation”…es practicamente lo mismo aunque el último no tiene flechas 😊</div>
</div>
<div>
<br /></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-961s4KDCGmg/W6J5aBg-J5I/AAAAAAAAFco/UTzfgyfKJqYl5M3lpLs49ZaXHN2lh63NwCLcBGAs/s1600/LanguageTranslationEditor.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="573" data-original-width="1206" height="190" src="https://3.bp.blogspot.com/-961s4KDCGmg/W6J5aBg-J5I/AAAAAAAAFco/UTzfgyfKJqYl5M3lpLs49ZaXHN2lh63NwCLcBGAs/s400/LanguageTranslationEditor.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
<div>
<b><span style="font-size: large;">Los Problemas</span></b></div>
<div>
<br /></div>
<div>
Así que asi es basicamente como las escenas están conectadas…pero por supuesto…me encontré con el primer problema muy rápidamente…como llamar a las API’s utilizándo Blueprints? Busqué online y la mayoría de los plugins son pagados…pero afortunadamente encontré uno gratuito que realmente me sorprendió…<a href="https://github.com/RVillani/UnrealJSONQuery" target="_blank">UnrealJSONQuery</a> funciona perfectamente y no es tan complicado de utilizar…pero por supuesto…tuve que cambiar algunas cosas en el código fuente (como agregar la cabecera para la llave y cambiar los parámetros para poder subir las imágenes). Luego simplemente recompilé y voila! Tenía JSON en mi aplicación 😉</div>
<div>
<br /></div>
<div>
Pero quieren saber que es lo que cambié, no? Claro 😊Simplemente descomprimí el archivoy me fuí a JSONQuery --> Source --> JSONQuery --> Private y abrí JsonFieldData.cpp</div>
<div>
<br /></div>
<div>
Aquí agregué una nueva cabecera con (“APIKey”, “MySAPLeonardoAPIKey”) y luego busqué el PostRequestWithFile y cambié el parámetro “file” a “files”…</div>
<div>
<br /></div>
<div>
Para compilar el código fuente, simplemente creé una aplicación C++, luego cree una carpeta “plugins” en la carpeta de mi proyecto y puse todos los archivos del zip que descargué…abrí el proyecto…lo dejé compilar y luego recreé todo lo del proyecto anterior…una vez que hice eso…todo comenzó a funcionar perfectamente…</div>
<div>
<br /></div>
<div>
Entonces, veamos parte del Blueprint que utilicé para llamar al API…</div>
</div>
<div>
<br /></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-IEUDs13oywQ/W6J50C73rwI/AAAAAAAAFc0/JLRhsq9LOAMjRxupnD0sayBplozzCcj6QCLcBGAs/s1600/LanguageTranslationBlueprint.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="735" data-original-width="1243" height="236" src="https://4.bp.blogspot.com/-IEUDs13oywQ/W6J50C73rwI/AAAAAAAAFc0/JLRhsq9LOAMjRxupnD0sayBplozzCcj6QCLcBGAs/s400/LanguageTranslationBlueprint.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both;">
Basicamente, necesitamos crear el JSON, llamar al API y después leer el resultado y extraer la información.</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
Todo estaba yendo de las mil maravillas…hasta que me di cuenta de que necesitaba empaquetar las images 3D generadas por Blender…y no tenía idea de como hacerlo…así que afortunadamente…el <a href="https://github.com/EverNewJoy/VictoryPlugin" target="_blank">Victory Plugin</a> vino al rescate 😉 Victory tiene algunos nodos que nos permiten leer muchos de los directorios que se encuentran dentro de una aplicación compilada…así que finalmente estaba todo listo 😊</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
Así es como se vé el Victory plugin cuando lo llamamos en un Blueprint…</div>
<div class="separator" style="clear: both;">
<br /></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-ThI08lv4Qg4/W6J6KGsKxrI/AAAAAAAAFc8/Ax7bK3lsGfM-Tn-p-wFjVetq_7UIJ0WMACLcBGAs/s1600/VictoryBlueprint.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="557" data-original-width="781" height="285" src="https://3.bp.blogspot.com/-ThI08lv4Qg4/W6J6KGsKxrI/AAAAAAAAFc8/Ax7bK3lsGfM-Tn-p-wFjVetq_7UIJ0WMACLcBGAs/s400/VictoryBlueprint.jpg" width="400" /></a></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both;">
<b><span style="font-size: large;">Los Modelos</span></b></div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
Para los models 3D como ya lo dije…utilicé Blender…los modelé utilizándo el “Cycles Render”, hice un bake a los materiales y luego hice el render de las imágenes utilizándo el “Blender Render” para poder generar los archivos .fbx…</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-AcfnKUSWQAw/W6J6YrHCKXI/AAAAAAAAFdE/2WoR65GB5OottWBaWj22yrJ71OZ8bS8rQCLcBGAs/s1600/BlenderApples.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="890" data-original-width="1449" height="245" src="https://2.bp.blogspot.com/-AcfnKUSWQAw/W6J6YrHCKXI/AAAAAAAAFdE/2WoR65GB5OottWBaWj22yrJ71OZ8bS8rQCLcBGAs/s400/BlenderApples.jpg" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-CMK-yZfIgdg/W6J6Ylh8gOI/AAAAAAAAFdA/k9-QAy4EVYMuQx-C8Uj37qNeR3z8a9qyQCLcBGAs/s1600/Bowl_Apples_BlenderRender.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="682" data-original-width="1210" height="225" src="https://3.bp.blogspot.com/-CMK-yZfIgdg/W6J6Ylh8gOI/AAAAAAAAFdA/k9-QAy4EVYMuQx-C8Uj37qNeR3z8a9qyQCLcBGAs/s400/Bowl_Apples_BlenderRender.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div>
<div>
Si las manzanas se ven como si fueran de metal o de cera…culpen a mis pobres habilidades para definir la iluminación ☹</div>
<div>
<br /></div>
<div>
Cuando se carga en Unreal…los modelos se ven realmente muy bien…</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-_PWkpBCs5r8/W6J6lxQVUsI/AAAAAAAAFdI/_4tsc9KAZ2Y2IlUV9RxEvXmF7kusLe90QCLcBGAs/s1600/DeskModel.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="703" data-original-width="1600" height="175" src="https://1.bp.blogspot.com/-_PWkpBCs5r8/W6J6lxQVUsI/AAAAAAAAFdI/_4tsc9KAZ2Y2IlUV9RxEvXmF7kusLe90QCLcBGAs/s400/DeskModel.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Ahora…estoy seguro que quieren ver como ve una pantalla completa de Blueprint…está es para los modelos 3D en la escena de Image Classification…</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-ZuXw-It263I/W6J6tYURoGI/AAAAAAAAFdU/nZDjN6bH01oMY6_KFL3cjcmiR_tGhStTwCLcBGAs/s1600/3DModel_Blueprint_Full.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="742" data-original-width="1143" height="258" src="https://2.bp.blogspot.com/-ZuXw-It263I/W6J6tYURoGI/AAAAAAAAFdU/nZDjN6bH01oMY6_KFL3cjcmiR_tGhStTwCLcBGAs/s400/3DModel_Blueprint_Full.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both;">
Complicado? Bueno...un poco…usualmente los Blueprints son así…pero son realmente muy poderosos…</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
Aquí hay otro…esta vez para la "Flecha a la Derecha" que nos permite cambiar los modelos…</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-wajFoPRGGtk/W6J63R6_KbI/AAAAAAAAFdc/kXCocz295HkjUFmLC0QBo9_NQNP1sCFkACLcBGAs/s1600/ImageClassificationRightArrow.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="738" data-original-width="1143" height="257" src="https://2.bp.blogspot.com/-wajFoPRGGtk/W6J63R6_KbI/AAAAAAAAFdc/kXCocz295HkjUFmLC0QBo9_NQNP1sCFkACLcBGAs/s400/ImageClassificationRightArrow.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Se ve extraño y complejo…pero funciona muy bien 😉</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-6RnXDXHYKDg/W6J7DZljI3I/AAAAAAAAFdk/fS_apD4kXoMN95nvCTKB8o-u-w7vRDVHQCLcBGAs/s1600/ImageClassificationModel.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="578" data-original-width="1207" height="191" src="https://2.bp.blogspot.com/-6RnXDXHYKDg/W6J7DZljI3I/AAAAAAAAFdk/fS_apD4kXoMN95nvCTKB8o-u-w7vRDVHQCLcBGAs/s400/ImageClassificationModel.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both;">
Se darán cuenta de que “Image Classification” y “OCR” tienen ambos flechas a la izquierda y derecha…así que tenía que reutilizar algunas variables y estas debían ser compartidas entre Blueprints…así que…para esto creé un “Game Instance” donde simplemente creé unas cuantas variables públicas que podían ser compartidas y actualizadas.</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
Si se preguntan para que utilicé Inkscape? Bueno…quería tener una especie de anunción en luces de neon y una imagen escrita a mano…</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-cGdapZd8VSM/W6J7PyBe5lI/AAAAAAAAFds/ryMpcEs65akX1P8VG7s5-u1DF58BDrpuACLcBGAs/s1600/InkscapeNeon.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1039" data-original-width="1225" height="338" src="https://1.bp.blogspot.com/-cGdapZd8VSM/W6J7PyBe5lI/AAAAAAAAFds/ryMpcEs65akX1P8VG7s5-u1DF58BDrpuACLcBGAs/s400/InkscapeNeon.jpg" width="400" /></a></div>
<div>
<br /></div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
<b><span style="font-size: large;">De Android al Oculus Go</span></b></div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
Se preguntarán…por qué cambia de Android al Oculus Go? No están ambos basados en Android? Bueno…sí…pero aún así…gracias a mi experiencia personal…ahora se que las cosas cambian mucho…</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
Primero…en Android…creé las escenas…y todo estaba perfecto…en el Oculus Go…ninguna escena nueva estaba siendo cargada…cuando hacía click en alguna escena…la primera escena se cargaba a si misma… ☹ Por qué? Porque necesitaba agregarlas al array de escenas que debían ser empaquetadas…</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
Y lo gracioso es que la carpeta por defecto para proyecto de Unreal es“Documentss”…así que cuanod trataba de agregar las escenas, Unreal se quejaba de que la ruta era demasiado larga…así que tuve que clonar el proyecto y moverlo a una carpeta dentro de C:\</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
También…cuando cambié de Windows a Android…fué algo tan simple como cambiar el “Click” por “Touch”…pero para el Oculus Go…bueno…necesité crear un “Pawn”…donde puse una camara, un motion controller, y un puntero (actuándo como un puntero laser)…aquí cambie el “Touch” por un “Motion Controller Thumbstick”…y desde ahí tuve que controlar todos los aspectos de la navegación…bastante confuso…</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
Otra cosas que cambió completamente fué el “SAP Leonardo Output”…veamos como se veía en Android…</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-3_C8H3vd02A/W6J7hu5gliI/AAAAAAAAFd0/Bo_UCGlN1f8x-QUKYNgwO61mdDivYSS6wCLcBGAs/s1600/SAPLeonardoMobile_04.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="800" data-original-width="1280" height="250" src="https://4.bp.blogspot.com/-3_C8H3vd02A/W6J7hu5gliI/AAAAAAAAFd0/Bo_UCGlN1f8x-QUKYNgwO61mdDivYSS6wCLcBGAs/s400/SAPLeonardoMobile_04.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both;">
Aquí pueden ver que utilicé un “HUD”…así que sin importar a donde miremos…el HUD se mueve con nosotros…</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
En el Oculus Go…esto no funcionaba para nada…primero tuve que utilizar una imagen negra como fondo…</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
Luego tuve que crear un actor y luego agrgar el HUD dentro…para convertirlo en un HUD 3D…</div>
<div class="separator" style="clear: both;">
<br /></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-PJ3wNak393k/W6J7suyuIsI/AAAAAAAAFd4/Te7vKSn-wD48Y4vvg8fbL_f_jdxnd9AwACLcBGAs/s1600/ImageClassificationModelMsg.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="578" data-original-width="1209" height="190" src="https://4.bp.blogspot.com/-PJ3wNak393k/W6J7suyuIsI/AAAAAAAAFd4/Te7vKSn-wD48Y4vvg8fbL_f_jdxnd9AwACLcBGAs/s400/ImageClassificationModelMsg.jpg" width="400" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<div>
<b><span style="font-size: large;">El producto final</span></b></div>
<div>
<br /></div>
<div>
Cuando todo estaba listo…simplemente empaqueté mi aplicación y la cargué en el Oculus Go…y utilizándo <a href="https://www.vysor.io/" target="_blank">Vysor</a> pude grabar una simple sesión para que puedan ver como se vé en la vida real 😉 Por supuesto…el problema (Primero que nada porque…soy muy flojo como para seguir investigándo y segundo porque parece ser mucho trabajo) es que necesitas correr la aplicación desde la sección “Unknown Sources” en el Oculus Go…pero…está ahí y funciona y eso es lo único que importa 😉</div>
<div>
<br /></div>
<div>
Aquí esta el video para que puedan tener una mejor idea de que lo que se trata el proyecto 😊</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/5JonbfqF90g/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/5JonbfqF90g?feature=player_embedded" width="320"></iframe></div>
<br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Espero que les haya gustado 😉</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Saludos,</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Blag.</div>
<div class="separator" style="clear: both; text-align: justify;">
SAP Labs Network.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div>
<br /></div>
</div>
Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-91612631803132408352018-07-30T10:27:00.000-07:002018-07-30T10:27:38.316-07:00Cozmo, leeme<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-WOspSm12WX4/W18k-wNguLI/AAAAAAAAFZc/SC3gB5A9wZgc1Tom3eRR0-FkBHRdEvtCACLcBGAs/s1600/d_shop_blog_logoToronto.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="173" data-original-width="493" height="112" src="https://1.bp.blogspot.com/-WOspSm12WX4/W18k-wNguLI/AAAAAAAAFZc/SC3gB5A9wZgc1Tom3eRR0-FkBHRdEvtCACLcBGAs/s320/d_shop_blog_logoToronto.png" width="320" /></a></div>
<br />
Conoces a <a href="https://www.anki.com/en-ca/cozmo" target="_blank">Cozmo</a>? El robot amigable de <a href="https://www.anki.com/en-ca" target="_blank">Anki</a>? Bueno...aquí está...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-WjL3DptPJSQ/W18lJu7wb9I/AAAAAAAAFZg/U85QsTtU40M0bkXknsnquljmPPd9sBiaACLcBGAs/s1600/Cozmo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="425" data-original-width="425" height="200" src="https://3.bp.blogspot.com/-WjL3DptPJSQ/W18lJu7wb9I/AAAAAAAAFZg/U85QsTtU40M0bkXknsnquljmPPd9sBiaACLcBGAs/s200/Cozmo.jpg" width="200" /></a></div>
Cozmo es un robot programable que tiene muchas características...y una de ellas incluye una camera...así que puedes hacer que Cozmo tome una foto de algo...y luego hacer algo con esa foto...<br />
<br />
Para programar a Cozmo necesitamos utilizar <a href="https://www.python.org/" target="_blank">Python</a>...en realidad...Python 3 -;)<br />
<br />
Para este blog, vamos a necesitar un par de cosas...así que vamos a instalarlas...<br />
<br />
<table align="center" border="1" style="width: 600px;">
<tbody>
<tr>
<td><pre>pip3 install ‘cozmo[camera]’</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Esto va a instalar el SDK de Cozmo...y vamos a necesitar también instalar el app de Cozmo app en nuestro teléfono celular también...<br />
<br />
Si ya tienen el SDK instalado, quizás deberían actualizarlo porque si no tienen la última versión esto no va a funcionar...<br />
<div>
<br /></div>
<table align="center" border="1" style="width: 600px;">
<tbody>
<tr>
<td><pre>pip3 install --upgrade cozmo</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
<div>
Ahora, necesitamos algunas cosas adicionales...</div>
<div>
<br /></div>
<table align="center" border="1" style="width: 600px;">
<tbody>
<tr>
<td><pre>sudo apt-get install python-pygame
pip3 install pillow
pip3 install numpy
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
<div>
<strong>pygame</strong> es un framework para juegos<br />
<strong>pillow</strong> es un wrapper para la librería PIL que se usa para manipular images.<br />
<strong>numpy</strong> nos permite manejar números complejos en Python.<br />
<br />
Esta fué la parte sencilla...puesto que ahora debemos instalar <a href="https://opencv.org/" target="_blank">OpenCV</a>...el cual nos permite manipular imágenes y video...<br />
<br />
Esta parte es un poco complicada, así que si se quedan atracados...busquen en Google o simplemente escribanme un mensaje...<br />
<br />
Primero, debemos asegurárnos de que OpenCV no está instalado...desinstalándolo...a menos que estén seguros de que funciona correctamente...<br />
<br /></div>
<table align="center" border="1" style="width: 600px;">
<tbody>
<tr>
<td><pre>sudo apt-get uninstall opencv
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Luego, debemos instalar los siguientes pre-requisitos...<br />
<br />
<table align="center" border="1" style="width: 600px;">
<tbody>
<tr>
<td><pre>sudo apt-get install build-essential cmake pkg-config yasm python-numpy
sudo apt-get install libjpeg-dev libjpeg8-dev libtiff5-dev libjasper-dev
libpng12-dev
sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev
libv4l-dev libdc1394-22-dev
sudo apt-get install libxvidcore-dev libx264-dev libxine-dev libfaac-dev
sudo apt-get install libgtk-3-dev libtbb-dev libqt4-dev libmp3lame-dev
sudo apt-get install libatlas-base-dev gfortran
sudo apt-get install libopencore-amrnb-dev libopencore-amrwb-dev
libtheora-dev libxvidcore-dev x264 v4l-utils
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
<div>
Si por alguna razón, algo no está disponible en su sistema, simplemente remuevánlo de la lista e intenten otra vez...a menos que sean como yo y quieran pasar horas tratándo de conseguir todo...<br />
<br />
Ahora, debemos descargar el código fuente de OpenCV para que podamos construírlo...desde el código fuente...<br />
<br /></div>
<table align="center" border="1" style="width: 600px;">
<tbody>
<tr>
<td><pre>wget https://github.com/opencv/opencv/archive/3.4.0.zip
unzip opencv-3.4.0.zip //This should produce the folder opencv-3.4.0
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Luego, debemos descargar las contribuciones porque hay cosas que no vienen incluídas con OpenCV por defecto...y quizás quieran utilizarlas en algún otro proyecto...
<br />
<br />
<table align="center" border="1" style="width: 600px;">
<tbody>
<tr>
<td><pre>wget https://github.com/opencv/opencv_contrib/archive/3.4.0.zip
unzip opencv-contrib-3.4.0.zip
//This should produce the folder opencv_contrib-3.4.0
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Como tenemos las dos carpetas, podemos comenzar a compilar...
<br />
<br />
<table align="center" border="1" style="width: 600px;">
<tbody>
<tr>
<td><pre>cd opencv-3.4.0
mkdir build
cd build
cmake -D CMAKE_BUILD_TYPE=RELEASE
-D CMAKE_INSTALL_PREFIX=/usr/local
-D INSTALL_PYTHON_EXAMPLES=OFF
-D CMAKE_CXX_COMPILER=/usr/bin/g++
-D INSTALL_C_EXAMPLES=OFF
-D OPENCV_EXTRA_MODULES_PATH=/YourPath/opencv_contrib-3.4.0/modules
-D PYTHON_EXECUTABLE=/usr/bin/python3.6
-D WITH_FFMPEG=OFF
-D BUILD_OPENCV_APPS=OFF
-D BUILD_OPENCD_TS=OFF
-D WITH_LIBV4L=OFF
-D WITH_CUDA=OFF
-D WITH_V4L=ON
-D WITH_QT=ON
-D WITH_LAPACK=OFF
-D WITH_OPENCV_BIOINSPIRED=OFF
-D WITH_XFEATURES2D=ON
-D WITH_OPENCL=OFF
-D WITH_FACE=ON
-D ENABLE_PRECOMPILED_HEADERS=ON
-D WITH_OPENCL=OFF
-D WITH_OPENCL_SVM=OFF
-D WITH_OPENCLAMDFFT=OFF
-D WITH_OPENCLAMDBLAS=OFF
-D WITH_OPENCV_DNN=OFF
-D BUILD_OPENCV_APPS=ON
-D BUILD_EXAMPLES=OFF ..
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
<div>
Tengan mucho cuidado de pasar la ruta correcta para la carpeta <strong>opencv_contrib</strong>...así que es mejor pasar la ruta completa para evitar cualquier error de compilación...<br />
<br />
Y sí...ese es un comándo bastante extenso para un build...y me tomó mucho tiempo hacerlo funcionar...puesto que hay que asegurárse de que todos los parámetros son correctos...<br />
<br />
Una vez que hemos terminado, necesitamos compilarlo...puesto que <strong>cmake</strong> va a preparar la "receta"...<br />
<br /></div>
<table align="center" border="1" style="width: 600px;">
<tbody>
<tr>
<td><pre>make -j2
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Si hay algún error, simplemente hagan esto...y comiencen otra vez...<br />
<br />
<table align="center" border="1" style="width: 600px;">
<tbody>
<tr>
<td><pre>make clean
make
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Luego, podemos finalmente instalar OpenCV con estos comandos...
<br />
<br />
<table align="center" border="1" style="width: 600px;">
<tbody>
<tr>
<td><pre>sudo make install
sudo ldconfig
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Para pobrar que está funcionando correctamente...simplemente debemos hacer esto...
<br />
<br />
<table align="center" border="1" style="width: 600px;">
<tbody>
<tr>
<td><pre>python3
>>>import cv2
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-J9AF7S8m3qU/W18oT83A4RI/AAAAAAAAFZw/DjSt6i0_gNs_TEab_SxI0MkgKxw3Smy1gCLcBGAs/s1600/OpenCV2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="224" data-original-width="736" height="97" src="https://4.bp.blogspot.com/-J9AF7S8m3qU/W18oT83A4RI/AAAAAAAAFZw/DjSt6i0_gNs_TEab_SxI0MkgKxw3Smy1gCLcBGAs/s320/OpenCV2.png" width="320" /></a></div>
<br />
Si no tienen ningún error...entonces estamos listos para continuar -;)<br />
<br />
Eso fué bastante trabajo...en fín...necesitamos una herramienta adicional para asegurárnos de que nuestra images es procesada de la mejor manera...<br />
<br />
Debemos descargar <a href="http://www.fmwconcepts.com/imagemagick/downloadcounter.php?scriptname=textcleaner&dirname=textcleaner" target="_blank">textcleaner</a> y ponerlo en la misma carpeta que nuestro script de Python...<br />
<br />
Y...en caso de que se estén preguntándo...sí...vamos a hacer que Cozmo tome una foto...vamos a procesar la imagen...usar SAP Leonardo's OCR API y luego hacer que Cozmo nos lea el mensaje...emocionánte, no?<br />
<a href="https://api.sap.com/api/ocr_api" target="_blank">SAP Leonardo's OCR API</a> aún está en la version 2Alpha1...pero fuera de eso...funciona extremadamente bien -;)<br />
<br />
Aunque tomen en cuenta que si el resultado no es siempre el más acertado es porque la luz, la posición de la imagen, tu escritura y por el hecho de que el OCR API aún está en Alpha...<br />
<br />
Ok...así que primero lo primero...necesitamos una pequeña pizarra...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-gZuYDHZuFPw/W18oiq3xrsI/AAAAAAAAFZ0/hgk_BCR8COcAw-bwc97YSLUy_TWTB4jqACLcBGAs/s1600/Whiteboard.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="480" data-original-width="640" height="240" src="https://4.bp.blogspot.com/-gZuYDHZuFPw/W18oiq3xrsI/AAAAAAAAFZ0/hgk_BCR8COcAw-bwc97YSLUy_TWTB4jqACLcBGAs/s320/Whiteboard.JPG" width="320" /></a></div>
<br />
Y claro...mi escritura a mano no es la mejor del mundo... -:(<br />
<br />
Ahora, saltemos al código fuente...<br />
<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">CozmoOCR.py</th>
</tr>
<tr>
<td><pre>import cozmo
from cozmo.util import degrees
import PIL
import cv2
import numpy as np
import os
import requests
import json
import re
import time
import pygame
import _thread
def input_thread(L):
input()
L.append(None)
def process_image(image_name):
image = cv2.imread(image_name)
img = cv2.resize(image, (600, 600))
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(img, (5, 5), 0)
denoise = cv2.fastNlMeansDenoising(blur)
thresh = cv2.adaptiveThreshold(denoise, 255, </pre>
<pre> cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)
blur1 = cv2.GaussianBlur(thresh, (5, 5), 0)
dst = cv2.GaussianBlur(blur1, (5, 5), 0)
cv2.imwrite('imggray.png', dst)
cmd = './textcleaner -g -e normalize -o 12 -t 5 -u imggray.png out.png'
os.system(cmd)
def ocr():
url = "https://sandbox.api.sap.com/ml/ocr/ocr"
img_path = "out.png"
files = {'files': open (img_path, 'rb')}
headers = {
'APIKey': "APIKey",
'Accept': "application/json",
}
response = requests.post(url, files=files, headers=headers)
json_response = json.loads(response.text)
json_text = json_response['predictions'][0]
json_text = re.sub('\n',' ',json_text)
json_text = re.sub('3','z',json_text)
json_text = re.sub('0|O','o',json_text)
return json_text
def cozmo_program(robot: cozmo.robot.Robot):
robot.camera.color_image_enabled = False
L = []
_thread.start_new_thread(input_thread, (L,))
robot.set_head_angle(degrees(20.0)).wait_for_completed()
while True:
if L:
filename = "Message" + ".png"
pic_filename = filename
latest_image = robot.world.latest_image.raw_image
latest_image.convert('L').save(pic_filename)
robot.say_text("Picture taken!").wait_for_completed()
process_image(filename)
message = ocr()
print(message)
robot.say_text(message, use_cozmo_voice=True, </pre>
<pre> duration_scalar=0.5).wait_for_completed()
break
pygame.init()
cozmo.run_program(cozmo_program, use_viewer=True, force_viewer_on_top=True)
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
<div>
Analicemos un poco el código fuente...<br />
<br />
Vamos a utilizar threads (hilos), puesto que necesitamos tener una ventana donde podamos ver lo que Cozmo está viendo y otra con Pygame donde podamos presionar "Enter" como un comando para hacer que Cozmo tome la foto.<br />
<br />
Basicamente, cuando ejecutamos la aplicación, Cozmo va a mover su cabeza y entrar en modo de imagen...luego, si presionamos "Enter" (En el terminal) va a tomar una foto y enviarla a nuestra función de procesamiento de OpenCV.<br />
<br />
Esta función va a simplemente tomar la imagen, cambiar su tamaño, convertirla a escala de grises, hacer un GaussianBlur para distorcionar la imagen y remover el ruido y reducir el detalle. Luego vamos a aplicar denoising para deshacernos del polvo y el brillo...aplicar un threshold para separar los pixels blancos y negros, y aplicar un par más de distorciones...<br />
<br />
Finalmente vamos a llamar a <strong>textcleaner</strong> para remover aún más ruido y hacer que la imagen sea más clara...<br />
<br />
Así que, aquí está la foto original tomada por Cozmo...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-dEjBgNJtd5Y/W18pCaqx1bI/AAAAAAAAFaA/yPlUQoaLKfQ91kwwRJjj4dupAOK1u5B7ACLcBGAs/s1600/Message.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="240" data-original-width="320" src="https://3.bp.blogspot.com/-dEjBgNJtd5Y/W18pCaqx1bI/AAAAAAAAFaA/yPlUQoaLKfQ91kwwRJjj4dupAOK1u5B7ACLcBGAs/s1600/Message.png" /></a></div>
<br />
Esta es la imagen luego de nuestro post-procesamiento en OpenCV...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-pow53IDz25M/W18pJkCA9jI/AAAAAAAAFaE/192jkCXHnLIq-1ZL09Pt7zikiJSEPwcrwCLcBGAs/s1600/imggray.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="600" data-original-width="600" height="320" src="https://4.bp.blogspot.com/-pow53IDz25M/W18pJkCA9jI/AAAAAAAAFaE/192jkCXHnLIq-1ZL09Pt7zikiJSEPwcrwCLcBGAs/s320/imggray.png" width="320" /></a></div>
<br />
Y finalmente, esta es nuestra imagen luego de utilizae <strong>textcleaner</strong>...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-YrFAqK1i6Yk/W18pSTvq8aI/AAAAAAAAFaM/5BjBgmP8IiQ18VqOJga1ohZd7b-qKExaACLcBGAs/s1600/out.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="633" data-original-width="633" height="320" src="https://4.bp.blogspot.com/-YrFAqK1i6Yk/W18pSTvq8aI/AAAAAAAAFaM/5BjBgmP8IiQ18VqOJga1ohZd7b-qKExaACLcBGAs/s320/out.png" width="320" /></a></div>
Finalmente, una vez que tenemos la imagen tal como la queremos, podemos llamar al OCR API lo cual es bastante secillo y directo...<br />
<br />
Para obtener el API Key, simplemente vamos a <a href="https://api.sap.com/api/ocr_api/overview" target="_blank">https://api.sap.com/api/ocr_api/overview</a> y nos legeamos...<br />
<br />
Una que obtenemos una respuesta del API, podemos hacer una pequeña limpieza con Regular Expressions solo para asegurárnos de que no hay ninguna letra que ha sido incorrectamente identificada...<br />
<br />
Finalmente, podemos hacer que Cozmo nos lea el mensaje en voz alta -;) Y solo para propósitos de demostración...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-Cqw-UJ6cLL8/W18pa0eXHsI/AAAAAAAAFaU/qPujNnMvceojgtqf756cA7zCKRT8hDXNwCLcBGAs/s1600/CozmoWelcomeDShop.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="386" data-original-width="737" height="208" src="https://2.bp.blogspot.com/-Cqw-UJ6cLL8/W18pa0eXHsI/AAAAAAAAFaU/qPujNnMvceojgtqf756cA7zCKRT8hDXNwCLcBGAs/s400/CozmoWelcomeDShop.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Aquí, tuve la suerte de que la luz y todo lo demás estaban perfectos...así que obtuve una respuesta muy limpia...las pruebas sucesivas fueron bastante malas -:( Pero, nuevamente...es importante tener buena iluminación...<br />
<br />
Por supuesto...quieren ver un video del proceso en acción, no? Bueno...aunque suene gracioso...mi primer intento fué perfecto! Aún mejor que este...pero no tome el video -:( Las prubeas sucesivas fueron bastante malas hasta que pude conseguir also razonable...y eso es lo que van a ver...el sol entrándo por la ventana no me ayudo mucho...pero de todos es bastante bueno...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/UpVEC_unCyQ/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/UpVEC_unCyQ?feature=player_embedded" width="320"></iframe></div>
<br />
Espero que les haya gustado este blog-:)<br />
<br />
Saludos,<br />
<br />
Blag.<br />
SAP Labs Network.<br />
<br /></div>
Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-22090696443596612782018-05-21T06:43:00.001-07:002018-05-21T06:43:44.969-07:00The Blagchain<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-qdr94ZhkR48/WwKnfi4kYeI/AAAAAAAAFXY/APXk9-3AZPkghsmBJ0Qp21R3pRWqwD1ZwCLcBGAs/s1600/Blagcoin.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="541" data-original-width="961" height="225" src="https://1.bp.blogspot.com/-qdr94ZhkR48/WwKnfi4kYeI/AAAAAAAAFXY/APXk9-3AZPkghsmBJ0Qp21R3pRWqwD1ZwCLcBGAs/s400/Blagcoin.jpg" width="400" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Últimamente, he estado aprendiendo sobre <a href="https://en.wikipedia.org/wiki/Blockchain" target="_blank">Blockchain</a> y <a href="https://www.ethereum.org/" target="_blank">Ethereum</a>. Dos temas bastante interesántes y actuales...pero como dicen...la mejor manera de aprender es haciendo...así que me puse a trabajar en el <b><i>Blagchain</i></b>.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Entonces, que es Blagchain? Basicamente, es una pequeña aplicación Blockchain que tomas algunas cosas de Blockchain y otras cosas de Ethereum y fué creado como algo educacional...en el Blagchain puedes obtener un usuario, crear o un producto o comprarlo y todo será almacenado en una estructura en cadena...</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Antes de ir a las imagenes...vamos a hablar un poco sobre la tecnología que elegí para este proyecto...</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Existen muchas tecnologías disponibles...así que escoger la correcta siempre es complicado...a la mitad del proyecto, puedes darte cuenta de que no...esa no fué la decición más acertada...algún otro lenguaje puede ayudarte a hacer las cosas más rápido y mejor...o quizás aquella característica en especial no está disponible y no lo sabías porque nunca antes la habías necesitado...</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Cuando comencé a aprender acerca de Blockchain y Ethereum...sabía que quería escribir Blagchain utilizándo una interface web...así que los primeros lenguages que vinieron a mi mente estaban fueran de la discusión...basicamente porque no tienen interface web o porque sería realmente complicado crear una aplicación web utilizándolos...además, también quería un lenguaje con pocas dependencias y de instalación y extensión sencillas...quería un lenguaje sencillo pero potente y veloz...y fué así...casi de inmediato sabía cual era el lenguaje que tenía que utilizar...</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<a href="https://crystal-lang.org/" target="_blank">Crystal</a> es similar a Ruby pero más veloz...y elegante -;) Además...tiene a <a href="http://kemalcr.com/" target="_blank">Kemal</a>, un framework web parecido a <a href="http://sinatrarb.com/" target="_blank">Sinatra</a>.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Cuando descubrí Crystal estaba impresionado por lo bien que está diseñado...especialmente porque...aún está en Alpha! Como un lenguaje tan joven puede ser tan bueno? No lo entiendo...pero Crystal es realmente impresionante...</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
En fín...veamos como trabaja Blagchain...</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Por supuesto...no es una dapp...pero eso está bien porque solo vamos a usarlo de manera local...utiliza dos aplicaciones web que funcionan en puertos diferentes...uno trabajando como servidor y el otro trabajando como cliente...</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-1OfINKxkR70/WwKmb-QDNKI/AAAAAAAAFXE/ZXqIrw56PxoZWKNktB6BU0mhyitT_B2vwCLcBGAs/s1600/Blagchain_001.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="359" data-original-width="1600" height="140" src="https://2.bp.blogspot.com/-1OfINKxkR70/WwKmb-QDNKI/AAAAAAAAFXE/ZXqIrw56PxoZWKNktB6BU0mhyitT_B2vwCLcBGAs/s640/Blagchain_001.png" width="640" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Puedes agregar un nuevo producto...</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-ldSzeyimT3A/WwKnI9OJsBI/AAAAAAAAFXM/vroNqzaHYpYKwYEoyYmIRk8KwmWt6CdQACLcBGAs/s1600/Blagchain_003.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="355" data-original-width="1600" height="140" src="https://3.bp.blogspot.com/-ldSzeyimT3A/WwKnI9OJsBI/AAAAAAAAFXM/vroNqzaHYpYKwYEoyYmIRk8KwmWt6CdQACLcBGAs/s640/Blagchain_003.png" width="640" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Pueden ver aquí que tenemos un Genesis Block, un nuevo block porque hemos posteado un producto (Y estan conectados via el Previous Hash) y también puede ver que cada transacción cuesta 0.1 Blagcoin...</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-mbwfaCMSV7E/WwKnUO8T9JI/AAAAAAAAFXQ/UBR-Bd4S1ToLgx2_9dcwscWwZ1ZKDGwOQCLcBGAs/s1600/Blagchain_004.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="530" data-original-width="1600" height="210" src="https://4.bp.blogspot.com/-mbwfaCMSV7E/WwKnUO8T9JI/AAAAAAAAFXQ/UBR-Bd4S1ToLgx2_9dcwscWwZ1ZKDGwOQCLcBGAs/s640/Blagchain_004.png" width="640" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Ahora, podemos utilizar otro browser para crear un nuevo usuario...</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-VaBVGpoKpXo/WwKoFAOUp1I/AAAAAAAAFXk/ukBlyCFXcEwN7MDaAbvlb-lQZgl7pjHPACLcBGAs/s1600/Blagchain_005.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="554" data-original-width="1600" height="220" src="https://4.bp.blogspot.com/-VaBVGpoKpXo/WwKoFAOUp1I/AAAAAAAAFXk/ukBlyCFXcEwN7MDaAbvlb-lQZgl7pjHPACLcBGAs/s640/Blagchain_005.png" width="640" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Como este usuario no creo el producto el/ella...puede comprarlo...y agregar una nueva transacción a la cadena...</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-NTKUZEzzyv8/WwKoXUeOYyI/AAAAAAAAFXs/Faffi77EcakLrIWTecUnmUENV_Igeye3gCLcBGAs/s1600/Blagchain_009.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="505" data-original-width="1600" height="200" src="https://4.bp.blogspot.com/-NTKUZEzzyv8/WwKoXUeOYyI/AAAAAAAAFXs/Faffi77EcakLrIWTecUnmUENV_Igeye3gCLcBGAs/s640/Blagchain_009.png" width="640" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
El dinero (Blagcoin) va de una cuenta a la otra. La cadena crece y todo queda registrado...</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-Nne6fanmZ50/WwKoj9Q88_I/AAAAAAAAFXw/pGeF7AegqEYsr-Eg4g3CvAfP4z6DkVnRwCLcBGAs/s1600/Blagchain_011.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="640" data-original-width="1600" height="256" src="https://2.bp.blogspot.com/-Nne6fanmZ50/WwKoj9Q88_I/AAAAAAAAFXw/pGeF7AegqEYsr-Eg4g3CvAfP4z6DkVnRwCLcBGAs/s640/Blagchain_011.png" width="640" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Que pasa cuando no tenemos suficiente Blagcoin para comprar algo?</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-E1tEGE74ntw/WwKo8W6KknI/AAAAAAAAFX8/zJ-MmU6olg0tMvPKk28yLR27XS-AhhNWwCLcBGAs/s1600/Blagchain_013.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="460" data-original-width="1600" height="184" src="https://3.bp.blogspot.com/-E1tEGE74ntw/WwKo8W6KknI/AAAAAAAAFX8/zJ-MmU6olg0tMvPKk28yLR27XS-AhhNWwCLcBGAs/s640/Blagchain_013.png" width="640" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Bueno...si te gustan este tipo de cosas...aquí están la cantidad de líneas que me tomo hacer la aplicación...</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Blagchain.cr (Parte Servidor) --> 129 lineas</div>
<div style="text-align: justify;">
BlagchainClient.cr (Parte Cliente) --> 125 lineas</div>
<div style="text-align: justify;">
index.ecr (HTML, Bootstrap y JQuery) --> 219 lineas</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Así que ni siquiera 500 lineas de código para toda la aplicación...impresionante, no? -;)</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Y claro...se que aunque sea quieren ver un poco del código fuente, no? Bueno...por qué no? -:)<br />
<br /></div>
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">BlagchainClient.cr</th>
</tr>
<tr>
<td><pre>post "/sellArticle" do |env|
user = env.params.body["user"]
article = env.params.body["article"]
description = env.params.body["description"]
price = env.params.body["price"]
amount = (env.session.float("amount") - 0.1).round(2)
env.session.float("amount", amount)
HTTP::Client.post("http://localhost:3000/addTransaction", form: "user=" + user +
"&article=" + article + "&description=" + description + "&price=" + price)
env.session.bool("flag", true)
env.redirect "/Blagchain"
end
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Saludos,</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Blag.</div>
<div style="text-align: justify;">
SAP Labs Network.</div>
Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-81790759678231348322018-01-17T12:16:00.002-08:002018-01-17T12:16:56.280-08:00Wooden Puzzle - Mi primer juego en Amazon SumerianSi leyeron mi post anterior blog <a href="http://atejada.blogspot.com/2017/12/amazon-sumerian-primer-impresiones.html" target="_blank">Amazon Sumerian - Primeras impresiones</a> sabrán que no iba a parar ahí -;)<br />
<br />
He podido jugar bastante con Sumerian y lo más importante...aprender mucho...los <a href="https://docs.sumerian.amazonaws.com/" target="_blank">tutorials</a> son muy buenos así que deberían leerlos así no tengan acceso a Sumerian aún...<br />
<br />
Una cosa que simpre quize hacer...era animar mi modelo de Cozmo...que hice en <a href="http://blender.org/" target="_blank">Blender</a>...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-mfjQb74i2xE/Wl-lw8Ivr4I/AAAAAAAAFVA/CTX6-23nBngaaQiaNhU6zkAq5LQqxnyvQCLcBGAs/s1600/CozmoMirror.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="544" data-original-width="962" height="225" src="https://2.bp.blogspot.com/-mfjQb74i2xE/Wl-lw8Ivr4I/AAAAAAAAFVA/CTX6-23nBngaaQiaNhU6zkAq5LQqxnyvQCLcBGAs/s400/CozmoMirror.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Inténte hacerlo en Blender (con rigging y haciendo la animación pero estaba funcionando de manera extraña puesto que funcionaba bien en Blender pero no en Sumerian...y ahora se porque...pero en su momento me frustré bastante) pero fallé...así que pensé hacerlo en Sumerian utilizándo sus herramientas...</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Debo admitirlo...al inicio no funcionó...pero luego seguí explorándo y me di cuenta de que el Timeline era mi amigo...después de muchas pruebas e intentos...logré hacerlo funcionar -;)</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Así es como se vé...</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-Ogtv3xOYuH0/Wl-mpqdIoCI/AAAAAAAAFVI/jyMGemdvoC0rKYDmpUR2cR2x9UYmwAv_ACLcBGAs/s1600/CozmoSumerian.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="501" data-original-width="1600" height="125" src="https://3.bp.blogspot.com/-Ogtv3xOYuH0/Wl-mpqdIoCI/AAAAAAAAFVI/jyMGemdvoC0rKYDmpUR2cR2x9UYmwAv_ACLcBGAs/s400/CozmoSumerian.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Así que vayan a <a href="https://d2fzo86an1i3q6.cloudfront.net/5f116204d3aa4c6c8561cbb22972726d.scene/" target="_blank">Cozmo</a> y hagan click en el robot para comenzar la animación y luego hagan click otra vez para reiniciar la animación...</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Simple pero muy alucinante -:)</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Luego de eso...comencé a pensar en hacer algo más...algo más interesánte y esta vez utilizándo algo de programación...la cual es en realidad JavaScript y no NodeJS como pensé en un inicio -:(</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
En fín...intenté hacer eso una vez en <a href="https://unity3d.com/" target="_blank">Unity</a> y otra en <a href="http://www.flare3d.com/" target="_blank">Flare3D</a> pero no tuve mucha suerte...aunque para ser sinceros...en esas épocas no conocía Blender...así que puse a trabajar en esto...</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-6yZrJO65C44/Wl-nlp0OqTI/AAAAAAAAFVQ/JZWT3WCN4RgEXlRsyC-fJ4hBZkp9xTtwACLcBGAs/s1600/WoodenPuzzleBlender.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="844" data-original-width="1600" height="210" src="https://4.bp.blogspot.com/-6yZrJO65C44/Wl-nlp0OqTI/AAAAAAAAFVQ/JZWT3WCN4RgEXlRsyC-fJ4hBZkp9xTtwACLcBGAs/s400/WoodenPuzzleBlender.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Diseñé un tablero de Wooden Puzzle utilizándo Blender y luego lo importe a Sumerian y le apliqué un Mesh Collision...de esa manera...la pelota puede avanzar por el tablero y caerse si es que hay un hueco...</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Así es como se vé...</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-YiCJRKupTQ8/Wl-n4-lBlLI/AAAAAAAAFVU/tSkNwtH-iM4BCliNv9UKmFGHPARfjyNRwCLcBGAs/s1600/WoodenPuzzle_01.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="805" data-original-width="1600" height="201" src="https://3.bp.blogspot.com/-YiCJRKupTQ8/Wl-n4-lBlLI/AAAAAAAAFVU/tSkNwtH-iM4BCliNv9UKmFGHPARfjyNRwCLcBGAs/s400/WoodenPuzzle_01.jpg" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-Sj3dyVnZivY/Wl-n4zARgqI/AAAAAAAAFVc/ng1WD_DYtsEKAQ3DoXXLoQVJ9gNA5O13QCLcBGAs/s1600/WoodenPuzzle_02.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="805" data-original-width="1600" height="201" src="https://1.bp.blogspot.com/-Sj3dyVnZivY/Wl-n4zARgqI/AAAAAAAAFVc/ng1WD_DYtsEKAQ3DoXXLoQVJ9gNA5O13QCLcBGAs/s400/WoodenPuzzle_02.jpg" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-6bkBBKWecF0/Wl-n4-Y5MzI/AAAAAAAAFVY/UWFpSm3GRHsEm_PycsIkKmrxQMA1t8iVACLcBGAs/s1600/WoodenPuzzle_03.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="809" data-original-width="1600" height="201" src="https://4.bp.blogspot.com/-6bkBBKWecF0/Wl-n4-Y5MzI/AAAAAAAAFVY/UWFpSm3GRHsEm_PycsIkKmrxQMA1t8iVACLcBGAs/s400/WoodenPuzzle_03.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Para jugar...simplemente utiliza los cursores del teclado para mover el tablero y guiar a la pelota desde "Start" hasta "Finish". Presionando "r" reiniciamos el juego.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Aquí está el enlace para poder jugarlo "<a href="https://d2fzo86an1i3q6.cloudfront.net/864f526bc1ef433fbdafbcde7bc8b93c.scene/" target="_blank">Wooden Puzzle</a>"...</div>
<br />
Fué difícil de hacer? No realmente -:) Sumerian es impresionante y bastante potente...encima de todo eso...el equipo de Sumerian es muy bueno y siempre están deseosos y dispuestos a ayudar...<br />
<br />
Hasta ahora...mi experiencia con Sumerian no ha sido más que pura alegría y satisfacción...así que ya veo haciendo más y más proyectos...<br />
<br />
Por supuesto...Ya estoy trabajándo en un par más -;) Especialmente uno que involucra al <a href="https://www.oculus.com/" target="_blank">Oculus Rift</a>...pero eso de hecho me va a tomar más tiempo....puesto que necesito hacer mucho trabajo con Blender..<br />
<br />
Ya has probado Sumerian? Aún no? Por qué no vas y solicitas <a href="https://aws.amazon.com/sumerian/" target="_blank">acceso</a>?<br />
<br />
Saludos,<br />
<br />
Blag.<br />
Development Culture.Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-66016754817370765302017-12-22T08:51:00.001-08:002017-12-22T10:04:26.881-08:00Amazon Sumerian - Primer impresionesPara aquellos que me conocen y para aquellos que no...como trabajo como Developer Evangelist...mi trabajo principal es aprender, explorar y evangelizar nuevas tecnologías y lenguajes de programación...y por supuesto...AR/VR han estado en mi bandeja desde hace un buen tiempo...<br />
<br />
He jugado con <a href="https://unity3d.com/" target="_blank">Unity3D</a> y <a href="https://www.unrealengine.com/en-US/what-is-unreal-engine-4" target="_blank">Unreal Engine</a>...y por supuesto he desarrollado parael <a href="https://developers.google.com/glass/" target="_blank">Google Glass</a>, <a href="https://www.microsoft.com/en-us/hololens" target="_blank">Microsoft HoloLens</a> y <a href="https://www.oculus.com/rift/" target="_blank">Oculus Rift</a>...<br />
<br />
Cuando los buenos muchachos de Amazon anunciaron el <a href="https://aws.amazon.com/sumerian/" target="_blank">Amazon Sumerian</a> podrán suponer que estaba totalmente emocionado -:D<br />
<br />
Así que ayer, finalmente fuí aceptado dentro del Beta Programa, así que como era de esperárse comencé con un par de tutoriales para poder conocer la herramienta -;)<br />
<br />
Por favor tomen en cuenta que recién estoy empezándo...así que no he probado o inténtado de todo...quiero ir paso a paso siguiendo los tutoriales y tratándo de entender todo de la forma más positiva posible...<br />
<br />
He mencionado ya que Sumerian funciona en el browser? Qué tan loco es eso? No hay que instalar nada...solo lanzar tu browser y empezar a construir experiencias AR/VR...<br />
<br />
Cuando lo lanzas por primera vez, vas a ver la siguiente pantalla...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-pqIBeeydVms/Wj0tFO983AI/AAAAAAAAFTk/lOd_m0QIXgg475KOjWaQu4OuABgKVGrpwCLcBGAs/s1600/AmazonSumerian_001.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="737" data-original-width="1600" height="183" src="https://2.bp.blogspot.com/-pqIBeeydVms/Wj0tFO983AI/AAAAAAAAFTk/lOd_m0QIXgg475KOjWaQu4OuABgKVGrpwCLcBGAs/s400/AmazonSumerian_001.jpg" width="400" /></a></div>
<br />
<br />
Donde puedes crear una nueva pantalla o simplemente utilizar una plantilla.<br />
<br />
Sumerian provee muchos tutoriales, y hasta el momento solo he seguido los primeros 3...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-NEre8TgoWE0/Wj0tn-gkq0I/AAAAAAAAFTs/rBp2qezpROkw4ym1jDoeDqdF-55v5YyyQCLcBGAs/s1600/AmazonSumerian_Tutorials.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="524" data-original-width="1233" height="168" src="https://4.bp.blogspot.com/-NEre8TgoWE0/Wj0tn-gkq0I/AAAAAAAAFTs/rBp2qezpROkw4ym1jDoeDqdF-55v5YyyQCLcBGAs/s400/AmazonSumerian_Tutorials.jpg" width="400" /></a></div>
<br />
Así que, así es como se ve mi cuarto de TV...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-Zn8O-Iab7aM/Wj0tu9h3ZQI/AAAAAAAAFTw/V7aKBFE7sc0AGto9q284mqp1hsgBjebxwCLcBGAs/s1600/AmazonSumerian_002.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="755" data-original-width="1600" height="188" src="https://2.bp.blogspot.com/-Zn8O-Iab7aM/Wj0tu9h3ZQI/AAAAAAAAFTw/V7aKBFE7sc0AGto9q284mqp1hsgBjebxwCLcBGAs/s400/AmazonSumerian_002.jpg" width="400" /></a></div>
<br />
Como pueden ver...Sumerian es un editor completo y complejo que provee todas las herramientas que puedes encontrar en otros editores...además de muchas más que yo pienso que son completamente nuevas y excitantes...<br />
<br />
Por supuesto, puedes echar un vistazo para ver como va tu aplicación...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-4oVjEuj3rUk/Wj0uHIvfgbI/AAAAAAAAFT4/SRKr_OHvuUsSdb4hiTRFtlQa9pkYzVD3wCLcBGAs/s1600/AmazonSumerian_003.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="756" data-original-width="1600" height="188" src="https://3.bp.blogspot.com/-4oVjEuj3rUk/Wj0uHIvfgbI/AAAAAAAAFT4/SRKr_OHvuUsSdb4hiTRFtlQa9pkYzVD3wCLcBGAs/s400/AmazonSumerian_003.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Con el tutorial del cuarto de TV...la idea es que debajo de la pantalla de televisión, hay un Amazon Echo, así que puedes hacerle click para cambiar los vidos que son presentados en la pantalla. Para esto necesitas utilizar un State Machine y también crear un script que va a controlar los diferentes videos. Para la parte de scripting debemos utilizar NodeJS...lo cual es muy bueno puesto que es el lenguage que uso principalmente cuando tengo que desarrollar aplicaciones para Alexa...</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-yImny3uvESY/Wj0ujnFOpoI/AAAAAAAAFUA/ZICn4o0It4Yce63vyHibtXrYDELYJUFUQCLcBGAs/s1600/AmazonSumerian_004.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="970" data-original-width="1595" height="242" src="https://3.bp.blogspot.com/-yImny3uvESY/Wj0ujnFOpoI/AAAAAAAAFUA/ZICn4o0It4Yce63vyHibtXrYDELYJUFUQCLcBGAs/s400/AmazonSumerian_004.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
Así es como se ve mi cuarto de TV cuando está mostrándo un video en Render Mode -:)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-e65A0jyy4Gg/Wj0y8wiRGmI/AAAAAAAAFUM/Um6jwH9-2lozrUALY1z4MzY5ZL64_LTiwCLcBGAs/s1600/AmazonSumerian_005.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="973" data-original-width="1595" height="243" src="https://4.bp.blogspot.com/-e65A0jyy4Gg/Wj0y8wiRGmI/AAAAAAAAFUM/Um6jwH9-2lozrUALY1z4MzY5ZL64_LTiwCLcBGAs/s400/AmazonSumerian_005.jpg" width="400" /></a></div>
<br />
Antes de seguir aprendiendo Sumerian...debo decir que el sistema de navegación no parece ser muy bueno aún...puedes utilizar los botones del mouse, Tab y Shift...pero las direccionales del cursor o AWSD no parecen funcionar como se esperaría en Unity3D o Unreal Engine...les he enviado esta pregunta al equipo de Sumerian Team en Slack...Así que voy a actualizar este post apenas tenga una respuesta -:)<br />
<br />
<b>*UPDATE* </b>Siguiendo con el tutorial de "Lights and Camera" descubrí que mientras que la camara por defecto no permite una navegación fina...el FlyCam si lo hace! -:D Todo bien en el vecindario -;)<br />
<br />
Hasta la próxima,<br />
<br />
Blag.<br />
Development Culture.Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-37664803262638592942017-05-02T08:56:00.002-07:002017-05-02T08:56:19.869-07:00Blender Arte en Lego para el HoloLens<h2 style="background-color: #333333; color: #cccccc; font-family: arial, tahoma, helvetica, freesans, sans-serif; font-size: 22px; margin: 0px; position: relative; text-align: center;">
<b>Este blog fue posteado originalmente en <a href="https://blogs.sap.com/2017/05/02/blender-lego-art-for-hololens/" target="_blank">Blender Lego Art for HoloLens</a>.</b></h2>
<div>
<b><br /></b></div>
<div>
<div>
A quién no le gustan los Legos? Y si han usado Blender antes…a quién no le gusta Blender? -:)</div>
<div>
<br /></div>
<div>
Combinar ambos parecía una excelente idea, así que es fué lo que hice…Utilizándo Blender cree unas simples piezas de Lego que pueden ser utilizadas para construir modelos simples y complejos.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-p9hzfJVX8KQ/WQinEqWbTbI/AAAAAAAAFKo/NcAoCdlkGGA0zY8C9xFTNE-hacNdiGy7QCLcB/s1600/Lego_Set.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://1.bp.blogspot.com/-p9hzfJVX8KQ/WQinEqWbTbI/AAAAAAAAFKo/NcAoCdlkGGA0zY8C9xFTNE-hacNdiGy7QCLcB/s400/Lego_Set.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Una piece simple mide 0.25 por 0.25 y esta hecha con un simple vertice. En la imagen, los colores se usan simplemente para demostrar que las piezas son distintas.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
El punto principal es simplemente crear un nuevo archivo en Blender, agregar las diferentes piezas y comenzas a construir.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Al inicio es un poco complicado puesto que tienes que tratar con coordenadasX, Y y Z…pero una vez que te acostumbras…se vuelve un poco adictivo -:)</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-FlBqCxnm-F8/WQinaopbEAI/AAAAAAAAFKs/E9k-L9RHl-kM_lQU2tysTqWBDnZPm4t-wCLcB/s1600/Lego_House_Front.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="223" src="https://3.bp.blogspot.com/-FlBqCxnm-F8/WQinaopbEAI/AAAAAAAAFKs/E9k-L9RHl-kM_lQU2tysTqWBDnZPm4t-wCLcB/s400/Lego_House_Front.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Ahora, el nombre del blog es Arte en Lego, no? Entonces…si en vez de eso buscas en internet Pixel Art, vas a encontrar muchas imágenes interesántes…como esta…</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-Yarb3WVPzmo/WQinjY9D9DI/AAAAAAAAFKw/0z6M2Pz_SfIaBHiMJQVBUeDTCtSxNbm7ACLcB/s1600/MarioPixel.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://3.bp.blogspot.com/-Yarb3WVPzmo/WQinjY9D9DI/AAAAAAAAFKw/0z6M2Pz_SfIaBHiMJQVBUeDTCtSxNbm7ACLcB/s400/MarioPixel.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
El candidato perfecto para ser construído en Lego! Poniendo las piesas en orden y simplemente asignándoles el material correcto…podemos obtener esto…</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-wVkPnEVhEmk/WQinrSy3mfI/AAAAAAAAFK0/bPfrlC_MJJkHv4CEDTcr9V3q2cWTX2H-ACLcB/s1600/MarioBlog.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="https://4.bp.blogspot.com/-wVkPnEVhEmk/WQinrSy3mfI/AAAAAAAAFK0/bPfrlC_MJJkHv4CEDTcr9V3q2cWTX2H-ACLcB/s400/MarioBlog.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Y con un poco más de tiempo y dedicación…podemos hacer esto…</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-4eYPCbYsM7s/WQin0-2VTaI/AAAAAAAAFK4/yoIUUXn7Bh0JtFIMruUbSLzPOs342PiowCLcB/s1600/MegaMan.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="223" src="https://3.bp.blogspot.com/-4eYPCbYsM7s/WQin0-2VTaI/AAAAAAAAFK4/yoIUUXn7Bh0JtFIMruUbSLzPOs342PiowCLcB/s400/MegaMan.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-d6eQacNaBSI/WQin4imqpUI/AAAAAAAAFK8/ik0ajzzf5IEDKKkpumRFcd6d4sA_1vBrACLcB/s1600/Pikachu.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="223" src="https://3.bp.blogspot.com/-d6eQacNaBSI/WQin4imqpUI/AAAAAAAAFK8/ik0ajzzf5IEDKKkpumRFcd6d4sA_1vBrACLcB/s400/Pikachu.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
En fín…con una colección de modelos…podemos pensar un poco y utilizárlos en una aplicación para el Microsoft HoloLens -;)</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
La aplicación en si es bastante sencilla…comienzas viendo a los modelos en una repisa…puedes seleccionar uno, hacerlo más pequeño, grande, girárlo a la derecha o izquiera o simplemente regresar a la vista de la repisa.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Aquí hay un video para que vea más o menos de que se trata.</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/FBZGJC5mlmo/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/FBZGJC5mlmo?feature=player_embedded" width="320"></iframe></div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Saludos,</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Blag.</div>
<div class="separator" style="clear: both; text-align: justify;">
Development Culture.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div>
<br /></div>
<div style="font-weight: bold;">
<br /></div>
</div>
Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-88919328080071156552017-03-29T09:25:00.001-07:002017-03-29T09:25:28.845-07:00Room with a View – HoloLens, Unity3D y Blender<br />
<h2 style="background-color: #333333; color: #cccccc; font-family: arial, tahoma, helvetica, freesans, sans-serif; font-size: 22px; margin: 0px; position: relative; text-align: center;">
<b>Este blog fue posteado originalmente en <a href="https://blogs.sap.com/2017/03/29/room-with-a-view-hololens-unity3d-and-blender/" target="_blank">Room with a View – HoloLens, Unity3D and Blender</a>.</b></h2>
<span style="font-family: "calibri" , sans-serif; font-size: 12pt; line-height: 17.12px;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-NDPJAvVqxXM/VfBx6-s4OuI/AAAAAAAAEfU/Qxdi3npzcq4W2xwkH47Y-hw-c0yH2kkSwCPcB/s1600/d_shop_blog_logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="112" src="https://4.bp.blogspot.com/-NDPJAvVqxXM/VfBx6-s4OuI/AAAAAAAAEfU/Qxdi3npzcq4W2xwkH47Y-hw-c0yH2kkSwCPcB/s320/d_shop_blog_logo.jpg" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Desde el mes pasado más o menos he estado iluminándome con un impresionante <a href="https://www.udemy.com/blendertutorial/" target="_blank">curso de Blender</a>…y aunque tenía algo de experiencia adquirida tras haber leído varios libros…nada podía traerme al punto en el que estoy como este curso…así que…sin nada más que decir…este curso es altamente recomendado 🙂</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Entonces…uno de los retos was crear algo utilizándo solamente primitivos…esto significa…cubos, esferas, conos y demás…nada complicado…no modificadores…no experiencia previa…así que finalmente hice esto…</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-u1gf-aFGkmo/WNvXpszKcFI/AAAAAAAAFJ0/Q2Vh3R0f26wtoiO11JIo9SP0EqCow76ZACLcB/s1600/Desk_NoRender.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="226" src="https://3.bp.blogspot.com/-u1gf-aFGkmo/WNvXpszKcFI/AAAAAAAAFJ0/Q2Vh3R0f26wtoiO11JIo9SP0EqCow76ZACLcB/s400/Desk_NoRender.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Era un buen comienzo…pero sabía que necesitaba algo más…además de utilizar texturas claro está 🙂 Se veía bastante simple…así que el siguiente paso era hacerlo más interesánte visualmente…y terminé haciendo esto…</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-oZqMs0VVrm0/WNvXz1v64cI/AAAAAAAAFJ4/E7nWRHK4wP4JNweiqPwRVnvGb9OA0KUfgCLcB/s1600/Desk_Rendered.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="132" src="https://4.bp.blogspot.com/-oZqMs0VVrm0/WNvXz1v64cI/AAAAAAAAFJ4/E7nWRHK4wP4JNweiqPwRVnvGb9OA0KUfgCLcB/s400/Desk_Rendered.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Ahora…se ve bastante interesánte, no? Todo creado a mano en Blender -;)</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Así que…el siguiente paso era determinar que hacer con esto…se veía muy bonito como para simplemente dejarlo descanzándo en mi disco duro…así que empecé a pensar y luego recordé que la Holographic Academy tiene un interesánte demo llamado <a href="https://developer.microsoft.com/en-us/windows/mixed-reality/holograms_101e" target="_blank">Origami</a>...en este demo tienes que seleccionar una pelota de origami que se va a resbalar por un avión de papel y luego chocar con un ventilador que va a explotar y este a la vez va a abrir un hueco en el piso donde un impresionante mundo subterráneo puede ser visto…realmente impresionante si me lo preguntan…entonces…tenía una idea 🙂 Por que no tener un cuadro del d-shop que explota cuando es seleccionado y luego abre un hueco en la pared donde el cuarto que diseñe en Blender puede ser visto?…eso para mi sonaba como una demo impresionánte…así que eso es exactamente lo que hice 😉</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Aquí está el video para que lo disfruten…espero que les guste 🙂</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/H952alwpxIU/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/H952alwpxIU?feature=player_embedded" width="320"></iframe></div>
<div style="text-align: center;">
<br /></div>
<br />
<div style="text-align: justify;">
Al inicio pensé que iba a ser mucho trabajo…pero no lo fué tanto realmente…aquí hay algunos puntos importantes…</div>
<div style="text-align: justify;">
<div>
<br /></div>
<div>
* Encerré el cuarto en una caja negra con un agujero para que pueda ser visto por adentro…</div>
<div>
<br /></div>
<div>
* En Unity3D simplemente utilizé un Unlit shader…como el color negro es procesado como invisible en HoloLens, tener el modelo en una caja negra que no esta iluminada da la impresión de que es invisible…por lo tanto…se ve como un hueco en la pared…</div>
<div>
<br /></div>
<div>
* Utilicé spatial mapping para poder ajustar el cuadro y el cuarto en la pared…para que no aparezca volando en el espacio pero más bien como si hubiera un cuarto dentro de la pared…</div>
<div>
<br /></div>
<div>
* Cuando importé el modelo de Blender a Unity3D la primera vez…ninguna de mis texturas estaba disponible…lo cual me pareció muy extraño…puesto que estaban ahí y estaban realmente asignadas…resulta que necesitaba escoger cada parte del modelo y hacer un…para que el shader sepa como implementar la textura…</div>
<div>
<br /></div>
<div>
* Al principio hice pruebas con el emulador…el cual es muy bueno…pero realmente no implementa la misma unidad de medida que se utiliza en el dispositivo físico…así que mientras que se vería bien en el emulador…se veía muy lejos en el HoloLens…así que necesité hacer muchas pruebas para poder tenerlo en la posición correcta…</div>
<div>
<br /></div>
<div>
* Para filmar el video, al inicio intenté utilizándo Camtasia…pero por supuesto…el rendering no era lo que yo estaba esperándo…una cosa es verlo por el HoloLens y otra cosa es verlo por la laptop…así que en vez de eso hice una grabación del Live Streaming en el mismo HoloLens…y ese era el truco…</div>
<div>
<br /></div>
<div>
Como pueden imaginarse…esto trae un nuevo mundo de posibilidades y demos…</div>
<div>
<br /></div>
<div>
Ahora es su turno de mostrárnos…que es lo que ustedes pueden hacer 😉</div>
<div>
<br /></div>
<div>
<div style="text-align: start;">
Saludos,</div>
<div style="text-align: start;">
<br /></div>
<div style="text-align: start;">
Blag.</div>
<div style="text-align: start;">
Development Culture.</div>
</div>
</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-61380747348235681182017-02-15T13:05:00.000-08:002017-02-15T13:05:12.714-08:00SAP d-shop’s Virtual House – Un viaje de los físico a lo Virtual<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"></span><br />
<h2 style="background-color: #333333; color: #cccccc; font-family: arial, tahoma, helvetica, freesans, sans-serif; font-size: 22px; margin: 0px; position: relative; text-align: center;">
<b>Este post fué posteado originalmente en <a href="https://blogs.sap.com/2017/02/15/sap-d-shops-virtual-house-a-journey-from-physical-to-virtual/#" target="_blank">SAP d-shop’s Virtual House – A journey from Physical to Virtual</a>.</b></h2>
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-NDPJAvVqxXM/VfBx6-s4OuI/AAAAAAAAEfU/Qxdi3npzcq4W2xwkH47Y-hw-c0yH2kkSwCPcB/s1600/d_shop_blog_logo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="112" src="https://4.bp.blogspot.com/-NDPJAvVqxXM/VfBx6-s4OuI/AAAAAAAAEfU/Qxdi3npzcq4W2xwkH47Y-hw-c0yH2kkSwCPcB/s320/d_shop_blog_logo.jpg" width="320" /></a></div>
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"><br /></span>
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"><br /></span>
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;">Hace algún tiempo…nuestro buenos amigos del SAP d-shop Newtown Square (Llamados John Astill y companía) construyeron una Casa IoT para SAP Insurance. Esta pequeña casa (Por cierto, hecha y pintada a mano) utiliza un Arduino Nano, varios sensores y luces LED…y…lo cual es impresionante dicho sea de paso…una lavadora impresa en 3D con un sensor de agua…y por supuesto…estaba y está…habilitada para IoT.</span><br />
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-W8kFYeixcGw/WKSf2_we5EI/AAAAAAAAFIo/GsREZwiRmHQ_Z5V0A8w2e1wJaIptTCbiACLcB/s1600/IotHouse.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://3.bp.blogspot.com/-W8kFYeixcGw/WKSf2_we5EI/AAAAAAAAFIo/GsREZwiRmHQ_Z5V0A8w2e1wJaIptTCbiACLcB/s320/IotHouse.jpg" width="320" /></a></div>
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"><br /></span>
<br />
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;">Pensamos que era impresionate…así que tenemos una en nuestro propio SAP
d-shop en Silicon Valley y se ha convertido en pieza clave de todos nuestros tours por el d-shop.<o:p></o:p></span></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;"><br /></span></div>
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;">
<span style="font-size: 12pt; line-height: 107%;">Luego…un tiempo después, nuestro amigos de HCP
Marketing (Llamados Joe Binkley y compañía) e Intel construyeron un Edificio Inteligente. Un edificio muy bonito…controlado por Amazon Alexa que utiliza un Intel Galileo,
algunos Arduinos así como también servos (motores), luces, un panel solar y hasta un ventilador…nuevamente todo esto…habilitado para IoT…pero como se deben haber imaginado ya…controlado por voz…así que puedes enviar el elevador de arriba para abaja…abrir o cerrar las puertas e inclusive enviar todo el edificio en modo de emergencia…afortunadamente…lo hemos mantenido en el d-shop por bastante tiempo y eso otro de nuestros demos con factor “wow” cada vez que alguien viene a visitarnos…</span></span><br />
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"><span style="font-size: 12pt; line-height: 107%;"><br /></span></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-99xuih23JEY/WKSf-DqHIzI/AAAAAAAAFIs/Pb8hKfRp9eMKY-hu5zv21F9tPcCKFqp8gCLcB/s1600/SmartBuilding.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://4.bp.blogspot.com/-99xuih23JEY/WKSf-DqHIzI/AAAAAAAAFIs/Pb8hKfRp9eMKY-hu5zv21F9tPcCKFqp8gCLcB/s320/SmartBuilding.jpg" width="311" /></a></div>
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"><span style="font-size: 12pt; line-height: 107%;"><br /></span></span>
<br />
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;">Tener ambos disponibles…poco a poco se fué enciendo la llama de la innovación y la creatividad…por que no construir una Casa Virtual que pueda ser utilizada con el Oculus Rift y que es controlada por Alexa?<o:p></o:p></span><br />
<span style="font-size: 12.0pt; line-height: 107%;"><br /></span></div>
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;">
</span><br />
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;">No es un trabajo sencillo…pero de hecho que amamos los retos…y gracias a nuestra experiencia previa con <a href="https://blogs.sap.com/2016/12/01/unity3d-and-alexa-working-together/" target="_blank">Unity3D and Alexa working together</a> </span><span style="font-size: 12.0pt; line-height: 107%;">ya sabiamos por donde empezar…<o:p></o:p></span></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;"><br /></span></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-qsiZ3CzPXDU/WKSgKCH-btI/AAAAAAAAFIw/PSx2JuDzuDA2xJPZk0xZcXuueeJMIcaugCLcB/s1600/Unity_Diagram.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://4.bp.blogspot.com/-qsiZ3CzPXDU/WKSgKCH-btI/AAAAAAAAFIw/PSx2JuDzuDA2xJPZk0xZcXuueeJMIcaugCLcB/s320/Unity_Diagram.jpg" width="304" /></a></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;"><br /></span></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;">La arquitectura es bastante simple… El servidor Heroku no es más que un servidor de Eco, así que va a repetir todo lo que le pasemos como una respuesta JSON.
Nuestra aplicación en Unity constantemente revisa el servidor Heroku para determinar si algún mensaje al que deba responder. Por supuesto, para que este trabaje como se quiere, necesitamos crear un skill en Amazon Alexa solo para poder actualizar el servidor. Así que, cuando decimos “open
door”, entonces Alexa va a enviar un comando al servidor Heroku y este en respuesta va a generar un mensaje “open door” en JSON. Nuestra aplicación en Unity va a leer la respuesta generada por el servidor Heroku, y actuar correctamente abriendo la puerta…por supuesto, no queremos que esto siga pasándo una vez que ya se ejecutó el comando…así que luego de que Unity executa la acción va a enviar un mensaje nulo al servidor Heroku, así que la próxima vez el mensaje JSON va a ser también nulo y Unity simplemente va a esperar por el próximo comando válido.<o:p></o:p></span></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;"><br /></span></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;">
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;">Si quieren echar un pequeño vistazo a como se ve la Casa Virtual…aquí hay un par de imagenes…pero no se olviden de ver el video, </span><span style="font-family: "wingdings"; font-size: 12.0pt; line-height: 107%;">J</span><span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"> Así tendrán la experiencia completa -;)</span></span></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;"><span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"><br /></span></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-w2sr06i0iVo/WKSgYas0xlI/AAAAAAAAFI8/m_0Zn15T7uMNVhHvDJAWfnMvCCSkiHYdgCLcB/s1600/FirstFloor_Coffee_Corner.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="150" src="https://1.bp.blogspot.com/-w2sr06i0iVo/WKSgYas0xlI/AAAAAAAAFI8/m_0Zn15T7uMNVhHvDJAWfnMvCCSkiHYdgCLcB/s320/FirstFloor_Coffee_Corner.jpg" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-oCTI0Lr2fUg/WKSgYa-uXbI/AAAAAAAAFI0/DObtFAKl73AasGjMndVPMNm-6gY20lmvQCLcB/s1600/FirstFloor_Disclaimer.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="150" src="https://3.bp.blogspot.com/-oCTI0Lr2fUg/WKSgYa-uXbI/AAAAAAAAFI0/DObtFAKl73AasGjMndVPMNm-6gY20lmvQCLcB/s320/FirstFloor_Disclaimer.jpg" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-NC3KT5jj3bY/WKSgYSCc47I/AAAAAAAAFI4/7MIvPHuVFrktu0g4O8x4zoBoty6iywPkgCLcB/s1600/ThirdFloor_DShop_Lights.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="154" src="https://4.bp.blogspot.com/-NC3KT5jj3bY/WKSgYSCc47I/AAAAAAAAFI4/7MIvPHuVFrktu0g4O8x4zoBoty6iywPkgCLcB/s320/ThirdFloor_DShop_Lights.jpg" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-Mh37qfEdlTA/WKSgYtmolmI/AAAAAAAAFJA/2rV8TtrNZ9kjxy-qVH1iaSb2biaQaefTgCLcB/s1600/ThirdFloor_DShop_Twiiter_No_Messages.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="150" src="https://2.bp.blogspot.com/-Mh37qfEdlTA/WKSgYtmolmI/AAAAAAAAFJA/2rV8TtrNZ9kjxy-qVH1iaSb2biaQaefTgCLcB/s320/ThirdFloor_DShop_Twiiter_No_Messages.jpg" width="320" /></a></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;"><span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"><br /></span></span></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;"><span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"></span></span></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;">Ahora…este proyecto comenzó como un “Project in a Box” (para uso interno solamente…disculpen por eso) lo cual significa…que todo el código fuente y la explicación de como construirlo desde cero debería ser proporcionada…pero…pero por razones obvias…eso no pasó </span><span style="font-family: "wingdings"; font-size: 12.0pt; line-height: 107%;">L</span><span style="font-size: 12.0pt; line-height: 107%;"> Así que en vez de eso…lo convertimos en un “Product in a Box” lo cual significa que (Disculpen nuevamente…solo empleados) pueden descargar la aplicación compilada y simplemente modificar el archivo de configuración para tenerlo funcionando por su cuenta </span><span style="font-family: "wingdings"; font-size: 12.0pt; line-height: 107%;">J</span><span style="font-size: 12.0pt; line-height: 107%;"> El código fuente no está siendo proporcionado pero obviamente con un simpático email pueden conseguirlo -;)<o:p></o:p></span></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;"><br /></span></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;"></span></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;">Consigánlo <a href="https://jam4.sapjam.com/blogs/show/jviRpc0UjVhdvmCBO3uibC" target="_blank">aquí</a><o:p></o:p></span></div>
<div class="MsoNormal" style="text-align: justify;">
<br /></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;">Ahora…que tengo toda su atención…por favor vean el video </span><span style="font-family: "wingdings"; font-size: 12.0pt; line-height: 107%;">J</span><span style="font-size: 12.0pt; line-height: 107%;"> Es un bonito viaje que parte la Casa IoT a la Casa Virtual pasándo por el Edificio Inteligente…<o:p></o:p></span></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/0mps7dR-ZQ8/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/0mps7dR-ZQ8?feature=player_embedded" width="320"></iframe></div>
<div class="MsoNormal" style="text-align: justify;">
<br /></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;"><br /></span></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-size: 12.0pt; line-height: 107%;">Ahora…se preguntarán por los modelos 3D Models utilizados para la Casa Virtual…como pueden ver en una de las images, la mayoría de ellos fueron descargados pero algunos de ellos fueron diseñados por nosotros </span><span style="font-family: "wingdings"; font-size: 12.0pt; line-height: 107%;">J</span><span style="font-size: 12.0pt; line-height: 107%;"> utilizándo Blender…Como el Amazon Echo,
el Robot impreso en 3D y los llaveros con nombre, el Amazon Echo y obviamente la casa en sí </span><span style="font-family: "wingdings"; font-size: 12.0pt; line-height: 107%;">J</span><span style="font-size: 12.0pt; line-height: 107%;"> Para algunas otras cosas como las mesas y las plantas…esos fueron importados a Blender y “pintados a mano” puesto que la texturas no estaban disponibles.<o:p></o:p></span><br />
<span style="font-size: 12.0pt; line-height: 107%;"><br /></span></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;">Ahora…algo que pensamos que es realmente importante…es listar todos los problemas y lecciones que aprendimos mientras desarrollamos esta aplicación…</span></div>
<div class="MsoNormal" style="text-align: justify;">
<span style="font-family: "calibri" , sans-serif; font-size: 12.0pt; line-height: 107%;"><br /></span></div>
<div class="MsoNormal" style="text-align: justify;">
<b><span style="font-size: 18.0pt; line-height: 107%;">Problemas y Lecciones Aprendidas:<o:p></o:p></span></b></div>
<div class="MsoNormal" style="text-align: justify;">
<b><span style="font-size: 18.0pt; line-height: 107%;"><br /></span></b></div>
<div class="MsoNormal" style="text-align: justify;">
</div>
<ul>
<li>Como este es un Product in a Box y no un Project in a Box,
no vamos a incluir el código fuente de la aplicación, pero lo que vamos a hacer en vez de eso es contarles los problemas y lecciones que aprendimos del desarrollo de este proyecto.</li>
<li><span style="font-size: 12pt; line-height: 107%; text-indent: -0.25in;">Unity
utiliza el .NET Framework 3.5, el cual ya está deprecado por el .NET 4.0 así que muchas cosas no van a funcionar simplemente porque no han sido implementadas…y por qué es esto? Bueno…Unity utiliza Mono (que es .NET para Linux) y yo pienso que lo utilizán para mantener uniformidad entre todas las plataformas. Mientras Mono continue en .NET 3.5, lo más seguro es que Unity tampoco va a actualizase.</span></li>
<li>Cuando cargamos escenas, la iluminación puede quedar hecha un desastre…así que empiezas en el nivel uno…te mueves al nivel dos y de pronto parece que fuera de noche…la solución es simple…escogemos “Window <span style="font-family: "wingdings"; font-size: 12pt; line-height: 107%; text-indent: -0.25in;">à</span><span style="font-size: 12pt; line-height: 107%; text-indent: -0.25in;"> Lighting </span><span style="font-family: "wingdings"; font-size: 12pt; line-height: 107%; text-indent: -0.25in;">à</span><span style="font-size: 12pt; line-height: 107%; text-indent: -0.25in;"> Lightmaps”, desmarcamos el “Auto” checkbox y presionamos “Build” para hacer un bake a luz nuevamente.</span></li>
<li>Los Coroutines
son simplemente asombrosos. Normalmente, no puedes hacer que tu aplicación duerma o espere…pero utilizándo Coroutines de hecho que puedes…Coroutines son como threads o hilos.</li>
<li>Cuando utlizamos luces, debemos asegurarnos de apagarlas cuando el personaje no está en el cuarto,
porque esto nos va a ahorrar un poco de procesamiento gráfico y porque aunque sea virtualmente…tenemos que preocuparnos por el medio ambiente…</li>
<li>Unity
no tiene una función o propiedad de wrap (salto de línea automático) para textos en 3D…lo cual es problemático especialmente si queremos hacer un muro de Twitter…así que la única opción es programar uno desde cero…aunque no es tan complicado…simplemente tomamos el texto, lo dividimos por espacio en un array…concatenamos cada palabra revisándo primero si la longitud de la cadena es menor que nuestro tope (que puede ser el máximo número de caracteres que entran en nuestro muro), si la cadena es mayor que el tope, simplemente agregamos un retorno de carro (“\n”) antes de hacer la concatenación.</li>
<li>A medida que la aplicación crece puedes tener la necesidad de duplicar assets, lo cual es perfectamente normal y no agrega mucho poder extra de procesamiento (Especialmente si crean un Prefab y utilizan ese prefab), pero no se olviden de asignar un nombre único,
de otra manera van a tener un dolor de cabeza si la aplicación necesita interactuar con esos assets.</li>
<li>Algunas veces vamos a tener que descargar modelos 3D de internet…otras veces vamos a crearlos utilizándo Blender…pero no hay que olvidar que muchas veces una simple esfera, cubo o algún otro objeto primitivo de Unity puede funcionar perfectamente utilizándo simplemente una imagen como textura.</li>
<li>Cuando creamos nuestro skill con Alexa…debemos asegurárnos de no cometer ningún error ortográfico…de otra manera se van a golpear la cabeza pensándo porque Alexa no está haciendo lo que le hemos pedido que haga…</li>
<li>Cuando probamos nuestra aplicación Debug.log() y Print() se volverán tus mejores amigos…nada mejor que un valor impreso o un mensaje para darse cuenta que es lo está funcionando mal.</li>
<li>Cuando movemos un objeto, debemos asegurárnos de guardar la posición original y luego agregar el nuevo valor al valor que tenemos guardado. De lo contrario, algo puede provocar que los valores resulten errados…teniendo guardados los valores originales, podemos evitar tener que calcular los valores originales nuevamente y simplemente retornar al valor original para que todo encaje un su lugar.</li>
<li>Cuando utilizámos Texo en 3D nos daremos cuenta de que aún cuando coloquemos un objeto en frente del textp…este se mantendrá siempre visible…lo cual obviamente no es muy bonito…así que temos dos opciones…crear un shader para ocultarlo…o la manera más simple…hacer el objeto que está en frente...transparente. Esto no es algo perfecto para cualquier solución pero por lo menos funcionará.</li>
<li>El mayor problema al que nos podemos enfrentar cuando hacemos que Unity y Alexa conversen, es que cuando le digamos a Alexa
que prenda las luces, ella responderá “La luz está prendida”…pero si le decimos lo mismo una segunda vez su respuesta debería ser “La luz ya está prendida” …para hacer esto…deberíamos utilizar una Base de Datos o algo parecido para almacenar la información…y cuando cerramos la aplicación, deberíamos limpiar los estados…si bien esto se puede hacer…es bastante trabajo, y que pasa si la aplicación colapsa de improviso? Deberíamos resetear los estados manualmente? No es lo ideal…</li>
<li>Esto nos lleva al punto de utilizar el elevador…podemos abrir la puerta o enviarlo a cualquiera de los pisos…en línes generales…eso es bastante sencillo…cada piso es una escena, así que necesitamos estar en el primer piso para poder enviar el elevador al segundo o tercer piso…pero…que pasa si estamos afuera del elevador? Estamos en el primer piso…pedimos el tercer piso…y luego abrimos la puerta…a medida que el personaje se mueve junto con el elevador…cuando abrimos la puerta todo se va a ver mal…solución? Simplemente utilizamos un cubo que no tenga un mesh renderer, así que es invisible…asignámos un collider con “is trigger” habilitado…y validamos que el personaje está interactuándo con el cubo para poder hacer que el elevador se mueva…de esa manera, así aunque pidamos el tercer piso y
Alexa confirme que el elevador se está moviendo hacia arriba…nada va a pasar…cuando abrimos la puerta…podemos asumir que el elevador subió o bajó hasta tu piso para que podamos entrar…solo una ilusión…pero funciona…</li>
<li>Alexa
no tiene una opción para retrazar el re-prompt (cuando pregunta si queremos hacer algo más), así que cuando exploramos la Casa Virtual ella nos preguntará “What else can I do for you?” y si no le respondemos nada, el skill simplemente se irá a dormir…así que tendremos que despertarla nuevamente…eso es algo un poco triste debido a la naturaleza de la aplicación…pero no hay nada que podamos hacer a menos que Amazon proponga una solución para que el re-prompt espere más tiempo…</li>
<li>Como todo el Alexa-Unity3D depende de Heroku…podemos esperar algunos retrazos o comandos dados por Alexa que no van a ser replicados necesariamente en el mundo virtual…puede ser un problema con la conexión a Internet o simplemente un problema con Heroku …</li>
</ul>
<div>
<br /></div>
Como mencioné al inicio…el entorno se ve afectado por el clima…si está soleado…veremos un cielo claro y soleado…si está lloviendo veremos cielo oscuro y húmedo…y esto involucra utilizar un Skybox…aunque no un Skybox común y corriendo…y a propósito de esto, que es un Skybox? Bueno…simplemente…es un cubo que cubre todo el entorno y tiene diferentes images para simular el cielo…el problema es que un Skybox regular solo nos permite asignar 6 lados…lo cual por supuesto no es óptimo…necesitamos un Skybox de 12 lados…para que así podamos asignar images de cielo soleado e imágenes de cielo oscuro…de esa manera cuando revisamos el clime podemos modificar la luminosidad y esto va a afectar al Skybox puesto que va a utilizar uno u otro brindándonos el efecto de que está reflejando el clima que hace afuera…<br /><ul>
</ul>
<div>
Greetings,</div>
<div>
<br /></div>
<div>
Blag.</div>
<div>
Development Culture.</div>
Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-43529605523034408322017-02-14T15:20:00.000-08:002017-02-14T15:20:13.660-08:00LED es mi nuevo Hello World - Tiempo de PrologComo lo prometí...aquí está mi aplicación de Números LED escrita en Prolog...me tomó bastante tiempo...bastante investigación...muchos dolores de cabeza...así que espero que les guste...aun soy un completo novato en Prolog...así que...no hay ningún tipo de garantía -;)<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">LEDNumbers.pl</th>
</tr>
<tr>
<td><pre></pre>
<pre>number(0,[[' _ '],['| | '],['|_| ']]).
number(1,[[' '],['| '],['| ']]).
number(2,[[' _ '],[' _| '],['|_ ']]).
number(3,[['_ '],['_| '],['_| ']]).
number(4,[[' '],['|_| '],[' | ']]).
number(5,[[' _ '],['|_ '],[' _| ']]).
number(6,[[' _ '],['|_ '],['|_| ']]).
number(7,[['_ '],[' | '],[' | ']]).
number(8,[[' _ '],['|_| '],['|_| ']]).
number(9,[[' _ '],['|_| '],[' _| ']]).
digits(0,[]).
digits(X,[H|T]) :- (X/10 > 0 -> H1 is floor(X/10), H is X mod 10, digits(H1,T)), !.
accRev([],A,A).
accRev([H|T],A,R) :- accRev(T,[H|A],R).
getDigits(L,R) :- digits(L,Y), accRev(Y, [], R).
show_records([]).
show_records([A|B]) :-
print_records(A), nl,
show_records(B).
print_records([]).
print_records([A|B]) :-
format('~w',A),
print_records(B).
merge([L], L).
merge([H1,H2|T], R) :- maplist(append, H1, H2, H),
merge([H|T], R), !.
listnum([],[]).
listnum([H1|T1],[R|Y]) :- number(H1,R), listnum(T1,Y).
led(X) :- getDigits(X,Y), listnum(Y,Z), merge(Z,R), show_records(R).
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Quieren verlo en acción? Yo también -;)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-e-lCM8ud_Sc/WKOPj92K_sI/AAAAAAAAFII/aZVOQ4sbI8oxcoYmi_m0-prS6tQbpXyqQCLcB/s1600/LEDNumbers_Prolog.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://1.bp.blogspot.com/-e-lCM8ud_Sc/WKOPj92K_sI/AAAAAAAAFII/aZVOQ4sbI8oxcoYmi_m0-prS6tQbpXyqQCLcB/s400/LEDNumbers_Prolog.jpg" width="400" /></a></div>
<br />
A seguir aprendiendo -;)<br />
<br />
Saludos,<br />
<br />
Blag.<br />
Development Culture.<br />
<br />Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-25164502782905928152017-02-14T15:09:00.003-08:002017-02-14T15:09:58.936-08:00Mi primer post en PrologComo siempre...estaba buscando el próximo lenguaje de programación que quieo aprender...y de alguna manera...<a href="https://en.wikipedia.org/wiki/Prolog" target="_blank">Prolog</a> se metió en el camino...<br />
<br />
Ya había jugado con Programación Lógica en el pasado cuando aprendí <a href="https://mercurylang.org/" target="_blank">Mercury</a>...pero realmente...cuando se trata de lógica...Prolog se gana el premio...<br />
<br />
Ustedes sabían que el primer compilador de <a href="https://www.erlang.org/" target="_blank">Erlang</a> se construyó en Prolog? Yo tampoco -:P<br />
<br />
Para aprender...Estoy utilizándo <a href="http://www.swi-prolog.org/" target="_blank">SWI-Prolog</a> que parece ser el mejor y más usado...y debo admitirlo...me encanta -;)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-gUJwn-T-BHA/WKOKBQ0eOGI/AAAAAAAAFH8/bAPqRSGpCOUfHm7JwA6WOlsNJoAwJHNzwCLcB/s1600/Prolog_Logo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://3.bp.blogspot.com/-gUJwn-T-BHA/WKOKBQ0eOGI/AAAAAAAAFH8/bAPqRSGpCOUfHm7JwA6WOlsNJoAwJHNzwCLcB/s200/Prolog_Logo.png" width="200" /></a></div>
<br />
Así que...a simple vista...Prolog me recuerda a Mercury obviamente...pero también un poco a <a href="https://www.forth.com/forth/" target="_blank">Forth</a>...y extrañamente a <a href="https://www.haskell.org/" target="_blank">Haskell</a> en el sentido de que la recursividad es un elemente muy importante...<br />
<br />
Como sucede generalmente cuando aprendo un nuevo lenguaje de programación...Empecé con mi aplicación de lista de números Fibonacci...así que aquí está...<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">fibonacci.pl</th>
</tr>
<tr>
<td><pre>fibo(NUM,A,B,[H|T]) :- (NUM > 1 -> H is A + B, X is NUM - 1,
(A =:= 0 -> fibo(X,H,B,T); fibo(X,H,A,T))).
fibo(_,_,_,[]).
fibonacci(NUM,R) :- fibo(NUM,0,1,X), !, append([0,1], X, R).
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
extensión<b> .pl</b>? Sip...la misma que <a href="https://www.perl.org/" target="_blank">Perl</a>...pero como pueden ver...no tiene nada que ver como Perl -;)<br />
<br />
En fín...aquí está el resultado...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-UkAPQkxxrtY/WKOLoiU9jQI/AAAAAAAAFIE/5qkIcE4VONEBhKEfDEjtwH1UxgvMrZKvACLcB/s1600/Fibonacci_Prolog.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="238" src="https://4.bp.blogspot.com/-UkAPQkxxrtY/WKOLoiU9jQI/AAAAAAAAFIE/5qkIcE4VONEBhKEfDEjtwH1UxgvMrZKvACLcB/s400/Fibonacci_Prolog.jpg" width="400" /></a></div>
<br />
Mi aplicación de Números LED está felizmente terminada y van a poder verla justo después de este blog -;)<br />
<br />
Saludos,<br />
<br />
Blag.<br />
Development Culture.Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-59078443086981105822016-12-05T10:28:00.000-08:002016-12-05T10:28:13.084-08:00LED es mi nuevo Hello World - Tiempo de RustComo actualmente estoy aprendiendo <a href="https://www.rust-lang.org/en-US/" target="_blank">Rust</a>, tenía que publica mi aplicación de números LED otra vez -;)<br />
<br />
Por favor tomen en cuenta...que..."Estoy aprendiendo Rust"...así que código puede ser flojo, redundante y nada idiomático...pero...suficiente para mostrar el lenguaje y permitirme aprender más -;)<br />
<br />
Aquí está el código...
<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">led_numbers.rs</th>
</tr>
<tr>
<td><pre>use std::io;
use std::collections::HashMap;
fn main(){
let mut leds:HashMap<&str, &str> = HashMap::new();
leds.insert("0", " _ ,| | ,|_| ");
leds.insert("1", " ,| ,| ");
leds.insert("2", " _ , _| ,|_ ");
leds.insert("3", "_ ,_| ,_| ");
leds.insert("4", " ,|_| , | ");
leds.insert("5", " _ ,|_ , _| ");
leds.insert("6", " _ ,|_ ,|_| ");
leds.insert("7", "_ , | , | ");
leds.insert("8", " _ ,|_| ,|_| ");
leds.insert("9", " _ ,|_| , _| ");
println!("Enter a number : ");
let mut input_text = String::new();
io::stdin().read_line(&mut input_text)
.expect("failed to read");
let split = input_text.split("");
let vec: Vec<&str> = split.collect();
let count = &vec.len() - 2;
for i in 0..3{
for j in 0..count{
match leds.get(&vec[j]){
Some(led_line) => {
let line = led_line.split(",");
let vec_line: Vec<&str> = line.collect();
print!("{}",&vec_line[i]);
},
None => println!("")
}
}
print!("");
}
println!("");
}
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Y aquí está el resultado...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-NLkBXMRoaQo/WEWwZrKl33I/AAAAAAAAFEY/lzNka92yyNQ5IaUFDNX3xjNYQfzBh-TOgCLcB/s1600/LedNumbers_Rust.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="178" src="https://1.bp.blogspot.com/-NLkBXMRoaQo/WEWwZrKl33I/AAAAAAAAFEY/lzNka92yyNQ5IaUFDNX3xjNYQfzBh-TOgCLcB/s400/LedNumbers_Rust.jpg" width="400" /></a></div>
<br />
Espero que les guste y si pueden mostrarme una manera más Rusty de hacer las cosas...por favor me avisan -:D<br />
<br />
Saludos,<br />
<br />
Blag.<br />
Development Culture.Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-9535872658753511232016-12-05T10:12:00.002-08:002016-12-05T10:12:44.207-08:00Mi primer post en RustNuevamente...estoy aprendiendo un nuevo lenguaje de programación...y esta vez le tocó el turno a <a href="https://www.rust-lang.org/en-US/" target="_blank">Rust</a>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-XL5zVchURM8/WEWqXkGxSfI/AAAAAAAAFEA/Sndk1NdeQEclbvrWQ1vCsqgXTxj7vvCnACLcB/s1600/Rust_Logo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://4.bp.blogspot.com/-XL5zVchURM8/WEWqXkGxSfI/AAAAAAAAFEA/Sndk1NdeQEclbvrWQ1vCsqgXTxj7vvCnACLcB/s1600/Rust_Logo.png" /></a></div>
<br />
Rust es muy bueno y tiene características muy interesántes como ownership y borrowing...y la sintáxis me recuerda bastante a OCaml...lo cual me gusta bastante...<br />
<br />
Por ahora estoy leyendo la documentación oficial, que está realmente muy bien hecha...así que por supuesto hice mi aplicación de números fibonacci...<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">fibonacci.rs</th>
</tr>
<tr>
<td><pre>use std::io;
fn fib(num: i64, a: i64, b:i64) -> String{
let mut result: String = "".to_string();
let sum: i64 = a + b;
let sum_str: &str = &sum.to_string();
let a_str: &str = &a.to_string();
let b_str: &str = &b.to_string();
if a > 0 && num > 1 {
result = result + sum_str + " " + &fib((num - 1), (a + b), a);
}else if a == 0{
result = "".to_string() + a_str + " " + b_str + " " +
sum_str + " " + &fib((num - 1), (a + b), b);
}
result
}
fn main(){
println!("Enter a number : ");
let mut input_num = String::new();
io::stdin().read_line(&mut input_num)
.expect("failed to read");
let trimmed = input_num.trim();
match trimmed.parse::<i64>() {
Ok(i) => { let result: String = fib(i, 0, 1); print!("{}", result);}
Err(..) => println!("Please enter an interger, not {}", trimmed)
};
}
</i64></pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
El código es un poco extenso para mi gusto...pero puede ser simplemente porque no he aprendido lo suficiente de Rust...o porque su sistema de ownership/borrowing sacrifica longitud para agregar seguridad...lo cual es algo realmente muy bueno...<br />
<br />
Aquí está el resultado<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-RBrak6iWYP8/WEWt8ifTrFI/AAAAAAAAFEM/2j3oCe3k_MUkPbjbbznO2O0HUj8YfP7TgCLcB/s1600/Fibonacci_Rust.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="133" src="https://2.bp.blogspot.com/-RBrak6iWYP8/WEWt8ifTrFI/AAAAAAAAFEM/2j3oCe3k_MUkPbjbbznO2O0HUj8YfP7TgCLcB/s400/Fibonacci_Rust.jpg" width="400" /></a></div>
<br />
<br />
Por supuesto, mi aplicación de Números LED ya está lista...y está viniendo justo después de este post -;)<br />
<br />
Saludos,<br />
<br />
Blag.<br />
Development Culture.Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-74019620261726986032016-12-02T13:22:00.000-08:002016-12-06T11:24:32.816-08:00Unity3D y Alexa trabajándo juntos<div style="text-align: justify;">
<h2 style="background-color: #333333; color: #cccccc; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 22px; margin: 0px; position: relative; text-align: center;">
<b>Este post fué posteado originalmente en <a href="https://blogs.sap.com/2016/12/01/unity3d-and-alexa-working-together/" target="_blank">Unity3D and Alexa working together</a>.</b></h2>
</div>
<br />
Desde hace tiempo ...Tuve la idea de hacer que Unity3D y Alexa trabajen juntos...sin embargo...otros proyectos me mantuvieron ocupado y me quitaron tiempo para poder trabajar en esto...así que...hace unos días...una conversación con un amigo me hizo recordar que tenía muchas ganas de hacer esto...así que lo hice :)<br />
<br />
Al inicio...no estaba exactamente seguro...pero lentamente las ideas se comenzaron a formar en mi mente...que pasaría si Unity leyera un webservice que es actualizado por Alexa? Cuando el comando correcto es interpretado, entonces Unity creará el objeto y problema resuelto...parece sencillo, no? Bueno...en realidad lo es...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-STGov1tFNSc/WEcQaD2Ez9I/AAAAAAAAFEw/0Pc-YBxIeEMSp0JcDDD7HfUrq6WCsqO1gCLcB/s1600/Unity_Diagram.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://3.bp.blogspot.com/-STGov1tFNSc/WEcQaD2Ez9I/AAAAAAAAFEw/0Pc-YBxIeEMSp0JcDDD7HfUrq6WCsqO1gCLcB/s320/Unity_Diagram.jpg" width="304" /></a></div>
<br />
Primero lo primero...tenemos que crear un pequeñp servidor web en NodeJS con <a href="https://www.hekoru.com/" target="_blank">Heroku</a>...luego...necesitamos instalar <a href="https://toolbelt.heroku.com/" target="_blank">Heroku Toolbelt</a>...<br />
<br />
Ahora...creamos una carpeta llamada <b>node_alexa</b> y dentro creamos los siguientes archivos...<br />
<div>
<br /></div>
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">package.json</th>
</tr>
<tr>
<td><pre>{
"dependencies": {
"express": "4.13.3"
},
"engines": {
"node": "0.12.7"
}
}
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">procfile</th>
</tr>
<tr>
<td><pre>web: node index.js
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">index.js</th>
</tr>
<tr>
<td><pre>var express = require('express')
,app = express()
,last_value;
app.set('port', (process.env.PORT || 5000));
app.get('/', function (req, res) {
if(req.query.command == ""){
res.send("{ \"command\":\"" + last_value + "\"}");
}else{
if(req.query.command == "empty"){
last_value = "";
res.send("{}");
}else{
res.send("{ \"command\":\"" + req.query.command + "\"}");
last_value = req.query.command;
}
}
})
app.listen(app.get('port'), function () {
console.log("Node app is running on port', app.get('port')");
})
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Una vez que tenemos esto...nos logeamos en Heroku Toolbelt y escribimos lo siguiente...<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">Heroku Toolbelt</th>
</tr>
<tr>
<td><pre>cd node_alexa
git init .
git add .
git commit -m "Init"
heroku apps:create "yourappname"
git push heroku master
heroku ps:scale = web 0
heroku ps:scale = web 1
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
El WebService está listo para rockear -:) Deberías poder encontrárlo si vas a "http://yourappname.herokuapp.com/"<br />
<br />
Ahora...este simple webservice potenciado por NodeJS nos servirá como un pequeño servidor de Eco...lo cual significa...cualquier cosa que escribamos nos devolverá una respuesta json...por supuesto...si escribimos "empty" entonces la respuesta será un json vacío...así que la idea principal es que podamos guardar el último valor ingresado...si pasamos un comando, este va a ser llamado cuando volvamos a llamar el servicio sin pasar ningún tipo de comando...así que si lo llamamos una vez...podemos llamarlo multiples veces sin interferir con su valor...<br />
<br />
Lo siguiente en la lista...será crear nuestra aplicación en Unity...<br />
<br />
Creamos una nueva aplicación y la llamamos "WebService" o algo parecido...el nombre del proyecto no importa mucho...<br />
<br />
En la ventana de Hierarchy seleccionamos "Main Camera" y cambiamos los detalles del "Tranform" de esta forma...<br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-ooor6WjhtW4/WECF7vdrM8I/AAAAAAAAFCI/ElqXzHbevTovAa5G8Q5S3e2D4rdgqCTLQCLcB/s1600/Main_Camera.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="143" src="https://4.bp.blogspot.com/-ooor6WjhtW4/WECF7vdrM8I/AAAAAAAAFCI/ElqXzHbevTovAa5G8Q5S3e2D4rdgqCTLQCLcB/s400/Main_Camera.jpg" width="400" /></a></div>
<div>
<br /></div>
<div>
Ahora, creamos un nuevo "3D Object" -> "Cube" y lo llamamos "Platform" con los siguientes detalles en el "Transform"...</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-oq8yaHRfnLA/WECGBztnYjI/AAAAAAAAFCM/5xD5BbqcEKIdRiN5JP3gyPVkROBP8NAVQCLcB/s1600/Platform.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="140" src="https://1.bp.blogspot.com/-oq8yaHRfnLA/WECGBztnYjI/AAAAAAAAFCM/5xD5BbqcEKIdRiN5JP3gyPVkROBP8NAVQCLcB/s400/Platform.jpg" width="400" /></a></div>
<div>
<br /></div>
<div>
Luego de eso, debemos crear cuatro paredes que van a ir alrededor de la plataforma...así que creamos 4 "3D Object" -> "Cube" y los llamamos "Wall 1", "Wall 2", "Wall 3" y "Wall 4"...</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-6tY1Om9fwTU/WECGIpkEHOI/AAAAAAAAFCQ/LLA93Td6ikEs9wF8KpHs5eex1NiRojPEgCLcB/s1600/Wall1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="138" src="https://1.bp.blogspot.com/-6tY1Om9fwTU/WECGIpkEHOI/AAAAAAAAFCQ/LLA93Td6ikEs9wF8KpHs5eex1NiRojPEgCLcB/s400/Wall1.jpg" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-1fnmKQupUUQ/WECGIn_zLOI/AAAAAAAAFCY/5iyWgfgKMKcZCTn9Urn1QQ5MRrqusOLGwCLcB/s1600/Wall2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="145" src="https://3.bp.blogspot.com/-1fnmKQupUUQ/WECGIn_zLOI/AAAAAAAAFCY/5iyWgfgKMKcZCTn9Urn1QQ5MRrqusOLGwCLcB/s400/Wall2.jpg" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-_otC0gSDGzo/WECGIivFH0I/AAAAAAAAFCU/l8yqC_jnWa0ifW37vmXGmNHv5nBvS5RcACLcB/s1600/Wall3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="140" src="https://2.bp.blogspot.com/-_otC0gSDGzo/WECGIivFH0I/AAAAAAAAFCU/l8yqC_jnWa0ifW37vmXGmNHv5nBvS5RcACLcB/s400/Wall3.jpg" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-7xyDPZHClW0/WECGI2UWGJI/AAAAAAAAFCc/h6jlZCic6fUyBACNBvVZpJg4llUBUZrnwCLcB/s1600/Wall4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="141" src="https://3.bp.blogspot.com/-7xyDPZHClW0/WECGI2UWGJI/AAAAAAAAFCc/h6jlZCic6fUyBACNBvVZpJg4llUBUZrnwCLcB/s400/Wall4.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Cuando todo está listo, nuestro workspace debería verse así...</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-VuwbQciQ4fY/WECGbELj7DI/AAAAAAAAFCg/yyn3Q3s8acgC-E1aY5A9Gn5WITHKmsnUwCLcB/s1600/Workspace.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="210" src="https://1.bp.blogspot.com/-VuwbQciQ4fY/WECGbELj7DI/AAAAAAAAFCg/yyn3Q3s8acgC-E1aY5A9Gn5WITHKmsnUwCLcB/s400/Workspace.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Vamos al tab project y creamos una carpeta llamada "plugins" y después creamos un nuevo archivo C# llamado "SimpleJSON"...dentro copiamos el código fuente que esta <a href="http://wiki.unity3d.com/index.php/SimpleJSON" target="_blank">aquí</a>...esto nos va a pemitir utlizar SimpleJSON para interpretar el JSON...</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Ahora...creamos otra carpeta llamada "Script" y dentro creamos un nuevo archivo C# llamado "MetaCoding"...o cualquier otra cosa...</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div>
<br /></div>
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">MetaCoding.cs</th>
</tr>
<tr>
<td><pre>using UnityEngine;
using System.Collections;
using System.Net;
using System.IO;
using SimpleJSON;
public class MetaCoding : MonoBehaviour {
int counter = 1;
IEnumerator DownloadWebService()
{
while (true) {
WWW w = new WWW("http://yourapp.herokuapp.com/?command");
yield return w;
print("Waiting for webservice\n");
yield return new WaitForSeconds(1f);
print("Received webservice\n");
ExtractCommand(w.text);
print("Extracted information");
WWW y = new WWW("http://yourapp.herokuapp.com/?command=empty");
yield return y;
print("Cleaned webservice");
yield return new WaitForSeconds(5);
}
}
void ExtractCommand(string json)
{
var jsonstring = JSON.Parse(json);
string command = jsonstring["command"];
print(command);
if (command == null) { return; }
string[] commands_array = command.Split(" "[0]);
if(commands_array.Length < 3)
{
return;
}
if (commands_array[0] == "create")
{
CreateObject(commands_array[1], commands_array[2]);
}
}
void CreateObject(string color, string shape)
{
string name = "NewObject_" + counter;
counter += 1;
GameObject NewObject = new GameObject(name);
switch (shape)
{
case "cube":
NewObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
break;
case "sphere":
NewObject = GameObject.CreatePrimitive(PrimitiveType.Sphere);
break;
case "cylinder":
NewObject = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
break;
case "capsule":
NewObject = GameObject.CreatePrimitive(PrimitiveType.Capsule);
break;
}
NewObject.transform.position = new Vector3(0, 5, 0);
NewObject.AddComponent<rigidbody>();
switch (color)
{
case "red":
NewObject.GetComponent<renderer>().material.color = Color.red;
break;
case "yellow":
NewObject.GetComponent<renderer>().material.color = Color.yellow;
break;
case "green":
NewObject.GetComponent<renderer>().material.color = Color.green;
break;
case "blue":
NewObject.GetComponent<renderer>().material.color = Color.blue;
break;
case "black":
NewObject.GetComponent<renderer>().material.color = Color.black;
break;
case "white":
NewObject.GetComponent<renderer>().material.color = Color.white;
break;
}
}
// Use this for initialization
void Start () {
print("Started webservice import...\n");
StartCoroutine(DownloadWebService());
}
// Update is called once per frame
void Update () {
}
}
</renderer></renderer></renderer></renderer></renderer></renderer></rigidbody></pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Una vez que tenemos el código...simplemente debemos adjuntarlo al Main Camera...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-jP_f3zIMoxA/WECHAp1zkbI/AAAAAAAAFCs/Hji57gNYj5QPWpe1W5EOtvlSQ_IxHPF3ACLcB/s1600/Main_Camera_Script.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="162" src="https://3.bp.blogspot.com/-jP_f3zIMoxA/WECHAp1zkbI/AAAAAAAAFCs/Hji57gNYj5QPWpe1W5EOtvlSQ_IxHPF3ACLcB/s400/Main_Camera_Script.jpg" width="400" /></a></div>
<br />
El concepto básico para este script es bastante simple...Estamos creándo "DownloadWebService" como un método IEnumerator para poder llamarlo como un Coroutine...y eso nos permite poder poner la función a dormir puesto que necesitamos darle un poco de tiempo entre llamadas a la función...<br />
<br />
Este método va a llamar a nuestro WebService en Heroku buscándo un comando "create"...una vez que lo tiene...va a interpretar la respuesta JSON y dividirla en 3...para que podamos tener..."create", "blue" y "sphere"...esto va a llmar a CreateObject que luego va a crear la esfera azul...luego de que ha hecho eso...el coroutine va a continuar y simplemente va a enviar un nuevo comando a nuestro WebService para limpiar el resultado...para hacer que esto funcione de manera correcta...queremos darle un tiempo de 5 segundos después de haber el limpiado el webservice antes de poder llamar a otros comando "create"...<br />
<br />
Y esta llamada va a ser hecha por nuestro skill en Alexa...así que basicamente cuando decimos "create blue sphere" en Alexa...ella va a enviar el comando al WebService...va a actualizar el mensaje y nuestra aplicación en Unity va a tomar el mensaje...hacer su trabajo...y luego limpiar el Webservice...luego esperar a que Alexa le provea el siguiente comando...<br />
<br />
Así que...para ir finalizándo...necesitamos crear nuestro skill en Alexa...<br />
<br />
Primera, vamos a crear una función Lambda...así que nos logeamos <a href="https://www.amazon.com/ap/signin?openid.assoc_handle=aws&openid.return_to=https%3A%2F%2Fsignin.aws.amazon.com%2Foauth%3Fresponse_type%3Dcode%26client_id%3Darn%253Aaws%253Aiam%253A%253A015428540659%253Auser%252Flambda%26redirect_uri%3Dhttps%253A%252F%252Fconsole.aws.amazon.com%252Flambda%252Fhome%253Fregion%253Dus-east-1%2526state%253DhashArgs%252523%25252Ffunctions%2526isauthcode%253Dtrue%26noAuthCookie%3Dtrue&openid.mode=checkid_setup&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&action=&disableCorpSignUp=&clientContext=&marketPlaceId=&poolName=&authCookies=&pageId=aws.ssop&siteState=registered%2Cen_US&accountStatusPolicy=P1&sso=&openid.pape.preferred_auth_policies=MultifactorPhysical&openid.pape.max_auth_age=120&openid.ns.pape=http%3A%2F%2Fspecs.openid.net%2Fextensions%2Fpape%2F1.0&server=%2Fap%2Fsignin%3Fie%3DUTF8&accountPoolAlias=&forceMobileApp=0&language=en_US&forceMobileLayout=0" target="_blank">aquí</a>...<br />
<br />
Por supuesto...yo ya tengo todo configurado...así que vamos a crear una función dummy solamente para mostrárles los pasos...<br />
<br />
Debemos hacer click en "Create Lambda Function" y veremos la siguiente pantalla...<br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-DQev0_ELrJE/WECHPp2jtVI/AAAAAAAAFCw/JLfetp2OnRkQXBYDnV7qHJkzofx_ftdzACLcB/s1600/Blueprint.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="150" src="https://2.bp.blogspot.com/-DQev0_ELrJE/WECHPp2jtVI/AAAAAAAAFCw/JLfetp2OnRkQXBYDnV7qHJkzofx_ftdzACLcB/s400/Blueprint.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Por supuesto hay bastantes...así que escribimos "Color" en el filtro...</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-A6LRwBuOMOo/WECHbswzvFI/AAAAAAAAFC0/5iMotxe2SHgGwACbGlO-gDe-0QQncfz4wCLcB/s1600/Alexa_Color.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="158" src="https://1.bp.blogspot.com/-A6LRwBuOMOo/WECHbswzvFI/AAAAAAAAFC0/5iMotxe2SHgGwACbGlO-gDe-0QQncfz4wCLcB/s400/Alexa_Color.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Escogemos "alexa-skills-kit-color-expert"</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-4P-y7T3iZzA/WECHnxqbFQI/AAAAAAAAFC4/0wL0S1sEwxosgX3iIkCOTensBSuw4BYkACLcB/s1600/Trigger.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="147" src="https://1.bp.blogspot.com/-4P-y7T3iZzA/WECHnxqbFQI/AAAAAAAAFC4/0wL0S1sEwxosgX3iIkCOTensBSuw4BYkACLcB/s400/Trigger.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Dejamos todo como está y presionamos "Next"</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-y0o4FtMHVfE/WECH3MMnOhI/AAAAAAAAFC8/nii6-HnOFYMe71i2p2GHDsHpLnGu0H7zgCLcB/s1600/Lambda_Name.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="140" src="https://1.bp.blogspot.com/-y0o4FtMHVfE/WECH3MMnOhI/AAAAAAAAFC8/nii6-HnOFYMe71i2p2GHDsHpLnGu0H7zgCLcB/s400/Lambda_Name.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Escogemos un nombre y descripción...</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-npwydpGoEFI/WECH9ALETfI/AAAAAAAAFDA/MwzGeY1KehwrkZgI29atKbsSL9RuCX32ACLcB/s1600/Lambda_Roles.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="193" src="https://2.bp.blogspot.com/-npwydpGoEFI/WECH9ALETfI/AAAAAAAAFDA/MwzGeY1KehwrkZgI29atKbsSL9RuCX32ACLcB/s400/Lambda_Roles.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Escogemos un rol existente si es que tenemos alguno creado anteriormente...de otra forma creamos un nuevo lambda_basic_execution...luego aumentamos el Timeout a 10 segundos y dejamos todo lo demás como está...presionamos "Next"...una ventana de confirmación va a aparecer...así que simplemente presionamos "Create function"...</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Vamos a ser presentados con una ventana en donde podemos subir nuestro código fuente (lo cual vamos a hacer un poco más adelánte) y un número ARN...el cual necesitamos para un paso más adelánte...</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-xZlMcWEsxpE/WECIHMSvexI/AAAAAAAAFDE/RMp_iuUcBYsIuuX86huKfZReGexm0nBZQCLcB/s1600/Lambda_ARN.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="25" src="https://3.bp.blogspot.com/-xZlMcWEsxpE/WECIHMSvexI/AAAAAAAAFDE/RMp_iuUcBYsIuuX86huKfZReGexm0nBZQCLcB/s400/Lambda_ARN.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Esta sección explica como crear el skill en Alexa...así que favor sigan mis pasos...y logeense <a href="https://www.amazon.com/ap/signin?openid.return_to=https%3A%2F%2Fdeveloper.amazon.com%2Fap_login.html&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.assoc_handle=mas_dev_portal&openid.mode=checkid_setup&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&pageId=mas_dev_portal2&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&language=en_US&openid.assoc_handle=mas_dev_portal&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.mode=logout&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.return_to=https%3A%2F%2Fwww.amazon.com%2Fap%2Fsignin%3Fopenid.return_to%3Dhttps%253A%252F%252Fdeveloper.amazon.com%252Fap_login.html%26openid.identity%3Dhttp%253A%252F%252Fspecs.openid.net%252Fauth%252F2.0%252Fidentifier_select%26openid.assoc_handle%3Dmas_dev_portal%26openid.mode%3Dcheckid_setup%26openid.claimed_id%3Dhttp%253A%252F%252Fspecs.openid.net%252Fauth%252F2.0%252Fidentifier_select%26pageId%3Dmas_dev_portal2%26openid.ns%3Dhttp%253A%252F%252Fspecs.openid.net%252Fauth%252F2.0%26language%3Den_US&#/skills" target="_blank">aquí</a>...</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-1gn5VE86-ho/WECIZzvoLKI/AAAAAAAAFDI/nQhlXjrmxSQbvLTQtSB2J3PxwghDts1VQCLcB/s1600/Alexa_Skills_Kit.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="261" src="https://4.bp.blogspot.com/-1gn5VE86-ho/WECIZzvoLKI/AAAAAAAAFDI/nQhlXjrmxSQbvLTQtSB2J3PxwghDts1VQCLcB/s400/Alexa_Skills_Kit.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Escogemos "Alexa Skills Kit"...y creamos un nuevo skill...</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-73FXQjhEZIM/WECI-2i0A1I/AAAAAAAAFDU/pD3-bEdDSBI168PWyJMbwXBU6OTtMuSBgCLcB/s1600/Alexa_Skill_Information.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="205" src="https://1.bp.blogspot.com/-73FXQjhEZIM/WECI-2i0A1I/AAAAAAAAFDU/pD3-bEdDSBI168PWyJMbwXBU6OTtMuSBgCLcB/s400/Alexa_Skill_Information.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Escogemos un nombre para nuestro skill y lo más importante...escogemos un "Invocation Name"...que es lo que le vamos a decir a Alexa para que abra nuestra aplicación..algo como..."Alexa, open Sandbox"...presionamos click...</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
En el tab de Interaction Model tenemos dos ventanas...llenamos esto en "Intent Schema"...</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">Intent Schema</th>
</tr>
<tr>
<td><pre>{
"intents": [
{
"intent": "GetUnityIntent",
"slots": [
{
"name": "color",
"type": "LITERAL"
},
{
"name": "shape",
"type": "LITERAL"
}
]
},
{
"intent": "HelpIntent",
"slots": []
}
]
}
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Estos son basicamente parámetros que podemos usar cuando estamos preguntándole algo a Alexa...<br />
<br />
Y debemos llenar esto en "Sample Utterances"...<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">Sample Utterances</th>
</tr>
<tr>
<td><pre>GetUnityIntent create {red|color} {sphere|shape}
GetUnityIntent create {yellow|color} {sphere|shape}
GetUnityIntent create {green|color} {sphere|shape}
GetUnityIntent create {blue|color} {sphere|shape}
GetUnityIntent create {black|color} {sphere|shape}
GetUnityIntent create {white|color} {sphere|shape}
GetUnityIntent create {red|color} {cube|shape}
GetUnityIntent create {yellow|color} {cube|shape}
GetUnityIntent create {green|color} {cube|shape}
GetUnityIntent create {blue|color} {cube|shape}
GetUnityIntent create {black|color} {cube|shape}
GetUnityIntent create {white|color} {cube|shape}
GetUnityIntent create {red|color} {cylinder|shape}
GetUnityIntent create {yellow|color} {cylinder|shape}
GetUnityIntent create {green|color} {cylinder|shape}
GetUnityIntent create {blue|color} {cylinder|shape}
GetUnityIntent create {black|color} {cylinder|shape}
GetUnityIntent create {white|color} {cylinder|shape}
GetUnityIntent create {red|color} {capsule|shape}
GetUnityIntent create {yellow|color} {capsule|shape}
GetUnityIntent create {green|color} {capsule|shape}
GetUnityIntent create {blue|color} {capsule|shape}
GetUnityIntent create {black|color} {capsule|shape}
GetUnityIntent create {white|color} {capsule|shape}
GetUnityIntent {thank you|color}
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Estos son todos los comandos que Alexa puede entender...y sí...podriamos haber utilizado "Custom Slot Types" para hacer el código más corto...pero...he tenido problemas donde no funciona muy bien cuando hay mas de un slot...simplemente presionamos next...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-bcw6gv51Q0s/WECJzdYjItI/AAAAAAAAFDg/pUOyogsTGbwJFnygjW3MlSKg_cQHZVPtQCLcB/s1600/Alexa_Global_Fields.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="235" src="https://2.bp.blogspot.com/-bcw6gv51Q0s/WECJzdYjItI/AAAAAAAAFDg/pUOyogsTGbwJFnygjW3MlSKg_cQHZVPtQCLcB/s400/Alexa_Global_Fields.jpg" width="400" /></a></div>
<br />
Aquí, escogemos AWS Lambda ARN...y escogemos North America o Europe dependiendo de nuestra locación física...luego en la caja de texto...simplemente copiamos y pegamos el número ARN que recibimos de nuestra función Lambda...<br />
<br />
Esto nos va a enviar al tab de "Test"...pero en realidad no queremos eso e inclusive no podemos usarlo...así que debemos regresar al tab "Skill Information" y encontraremos que un nuevo campo ha aparecido...<br />
<br />
Este campo será "Application Id"...copiamos este número y continuamos hacia la parte final...<br />
<br />
Creamos una carpeta llamada "Unity" y dentro una carpeta llamada "src"...dentro de esa carpeta copiamos el archivo "<a href="https://github.com/dustincoates/alexa-bus-schedule/blob/master/src/index.js" target="_blank">AlexaSkills.js</a>"<br />
<br />
Vamos a utilizar el módulo "request" de NodeJS...así que lo instalamos localmente en la carpeta Unity...<br />
<br />
<div style="text-align: center;">
<b><i>sudo npm install --prefix=~/Unity/src request </i></b></div>
<div style="text-align: center;">
<b><i><br /></i></b></div>
<div style="text-align: justify;">
Esto va a crear una carpeta llamada node_module conteniendo al módulo request...</div>
<div style="text-align: justify;">
</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Luego, creamos un nuevo archivo llamado "index.js"</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">index.js</th>
</tr>
<tr>
<td><pre>var request = require("request")
, AlexaSkill = require('./AlexaSkill')
, APP_ID = 'yourappid';
var error = function (err, response, body) {
console.log('ERROR [%s]', err);
};
var getJsonFromUnity = function(color, shape, callback){
var command = "create " + color + " " + shape;
if(color == "thank you"){
callback("thank you");
}
else{
var options = { method: 'GET',
url: 'http://yourapp.herokuapp.com/',
qs: { command: command },
headers:
{ 'postman-token': '230914f7-c478-4f13-32fd-e6593d8db4d1',
'cache-control': 'no-cache' } };
var error_log = "";
request(options, function (error, response, body) {
if (!error) {
error_log = color + " " + shape;
}else{
error_log = "There was a mistake";
}
callback(error_log);
});
}
}
var handleUnityRequest = function(intent, session, response){
getJsonFromUnity(intent.slots.color.value,intent.slots.shape.value, function(data){
if(data != "thank you"){
var text = 'The ' + data + ' has been created';
var reprompt = 'Which shape would you like?';
response.ask(text, reprompt);
}else{
response.tell("You're welcome");
}
});
};
var Unity = function(){
AlexaSkill.call(this, APP_ID);
};
Unity.prototype = Object.create(AlexaSkill.prototype);
Unity.prototype.constructor = Unity;
Unity.prototype.eventHandlers.onSessionStarted = function(sessionStartedRequest, session){
console.log("onSessionStarted requestId: " + sessionStartedRequest.requestId
+ ", sessionId: " + session.sessionId);
};
Unity.prototype.eventHandlers.onLaunch = function(launchRequest, session, response){
// This is when they launch the skill but don't specify what they want.
var output = 'Welcome to Unity. Create any color shape by saying create and providing a color and a shape';
var reprompt = 'Which shape would you like?';
response.ask(output, reprompt);
console.log("onLaunch requestId: " + launchRequest.requestId
+ ", sessionId: " + session.sessionId);
};
Unity.prototype.intentHandlers = {
GetUnityIntent: function(intent, session, response){
handleUnityRequest(intent, session, response);
},
HelpIntent: function(intent, session, response){
var speechOutput = 'Create a new colored shape. Which shape would you like?';
response.ask(speechOutput);
}
};
exports.handler = function(event, context) {
var skill = new Unity();
skill.execute(event, context);
};
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
El código es bastante simple...principalmente porque la mayoría es un template...simplemente lo copia...cambias un par de cosas y estás listo para continuar...<br />
<br />
Basicamente cuando decimos "Alexa, open Unity"...ella va a escuchar nuestros pedidos...así que podemos decir "create green cube"...y va a llamar a nuesto WebService en Heroku y luego esperar por otro comando...si no le hablas nuevamente...te va a invitar a decir algo...si le dices "Thank you" ella va a desactivarse a si misma de una manera muy educada...<br />
<br />
Y eso es basicamente todo...una vez que Alexa envia el comando al WebServer...nuestra aplicación en Unity va a leerla y actuar como corresponde...creándo cualquier forma y color que le hemos pedido...interesánte, huh?<br />
<br />
Pero por supuesto...no me creen, no? No puede ser tan simple...bueno...si y no...es simple...pero saqué todos los puntos complicados para poder proveerles una serie de instruciones claras y consisas...<br />
<br />
Así que...así es como se ve cuando ejecutamos la aplicación en Unity...<br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-69mPc37Nt9w/WECKqGIaCoI/AAAAAAAAFDo/_KlJXg2FZkYFgww2dyhbK0ZmZwD0KvcUQCLcB/s1600/IMG_1563%2B%2528Copy%2529.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://2.bp.blogspot.com/-69mPc37Nt9w/WECKqGIaCoI/AAAAAAAAFDo/_KlJXg2FZkYFgww2dyhbK0ZmZwD0KvcUQCLcB/s400/IMG_1563%2B%2528Copy%2529.JPG" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-XoQ8xc-zGkw/WECKqLh2vQI/AAAAAAAAFDk/iHLhIpt2ryIfou1JLELFAVVjuMUc-CJQACLcB/s1600/IMG_1564%2B%2528Copy%2529.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://4.bp.blogspot.com/-XoQ8xc-zGkw/WECKqLh2vQI/AAAAAAAAFDk/iHLhIpt2ryIfou1JLELFAVVjuMUc-CJQACLcB/s400/IMG_1564%2B%2528Copy%2529.JPG" width="400" /></a></div>
<div>
<br /></div>
<div>
Y aquí está el video en acción...</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/bWEfDRIIIRY/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/bWEfDRIIIRY?feature=player_embedded" width="320"></iframe></div>
<div>
<br /></div>
<div>
Espero que les guste...y mantenganse alertas...porque para mi esto solo fué una prueba de concepto...mi próximo proyecto va a llevar esto al siguiente nivel...</div>
<div>
<br /></div>
<div>
Saludos,</div>
<div>
<br /></div>
<div>
Blag.</div>
<div>
Development Culture.</div>
Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-71268336300054821402016-12-01T07:52:00.001-08:002016-12-01T07:52:24.116-08:00LED es mi nuevo Hello World - Tiempo de Swift (para Linux)Me tomó un poco de tiempo escribir este blog...principalmente porque estoy aprendiendo <a href="https://www.rust-lang.org/en-US/" target="_blank">Rust</a> y también porque acabo de teminar mi último demo...cuyo blog publicaré más tarde -;)<br />
<br />
Esta versión de mi applicación de Números LED se convierte en la versión de 25 lenguajes de programación...así que...obviamente...es una gran hito para mi -:D Quién sabe? Quizás haga algo interesánte si alcanzo los 50 lenguajes de programación -:D<br />
<br />
En fín...como me encanta decir..."Basta de hablar...muestrame el Código Fuente" -;)<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">LedNumbers.swift</th>
</tr>
<tr>
<td><pre>let leds: [Character:String] = [
"0" : " _ ,| | ,|_| ",
"1" : " ,| ,| ",
"2" : " _ , _| ,|_ ",
"3" : "_ ,_| ,_| ",
"4" : " ,|_| , | ",
"5" : " _ ,|_ , _| ",
"6" : " _ ,|_ ,|_| ",
"7" : "_ , | , | ",
"8" : " _ ,|_| ,|_| ",
"9" : " _ ,|_| , _| "
];
print("Enter a number: ",terminator:"");
let num = readLine(strippingNewline: true);
var line = [String]();
var led = "";
for i in 0...2{
for character in num!.characters{
line = String(leds[character]!)!.
characters.split(separator: ",").map(String.init);
print(line[i], terminator:"");
}
print("");
}
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Y aquí está la imagen del programa haciendo su magia -:)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-SfGFArKMdv0/WEBF-IkZU0I/AAAAAAAAFBw/gI3LU0s6L4sJl0h93XIkjuyyeG81PGRygCLcB/s1600/LedNumbers_Swift.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="138" src="https://2.bp.blogspot.com/-SfGFArKMdv0/WEBF-IkZU0I/AAAAAAAAFBw/gI3LU0s6L4sJl0h93XIkjuyyeG81PGRygCLcB/s400/LedNumbers_Swift.jpg" width="400" /></a></div>
<br />
Saludos,<br />
<br />
Blag.<br />
Development Culture.Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-40454506038864867762016-11-28T15:06:00.001-08:002016-11-28T15:06:41.878-08:00Mi primer post en Swift (para Linux)<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-6vmU6de7tOU/WDygZJC1m3I/AAAAAAAAFBI/Rqg3WLg7YicZopVAZr7wYrnCwCBu0hFigCLcB/s1600/swiftlinux-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="276" src="https://3.bp.blogspot.com/-6vmU6de7tOU/WDygZJC1m3I/AAAAAAAAFBI/Rqg3WLg7YicZopVAZr7wYrnCwCBu0hFigCLcB/s400/swiftlinux-1.png" width="400" /></a></div>
<br />
Puesto que Apple liberó de forma muy amable <a href="https://swift.org/" target="_blank">Swift for Linux</a>...Tenía que aprenderlo-:)<br />
<br />
Por supuesto...no está totalmente implementado...así que la mayoría de cosas que hacen a Swift impresionante en IOS no están presenten aún...pero aún así...es impresionante! -:D<br />
<br />
Swift es medio functional...así que puedes ver muchas cosas de Haskell y Erlang...pero también imperativo y orientado a objects...así que eso lo convierte en un lenguaje muy interesánte...<br />
<br />
Como siempre...aquí está mi pequeña aplicación de números Fibonacci...<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">fibonacci.swift</th>
</tr>
<tr>
<td><pre>func fib(num:Int,a:Int,b:Int) -> String{
var result: String = "";
if a > 0 && num > 1{
result = result + String(a + b) + " " +
fib(num: (num - 1), a: (a + b), b: a);
}else if a == 0{
result = String(a) + " " + String(b) + " " +
String(a + b) + " " +
fib(num: (num - 1), a: (a + b), b: b);
}
return result;
}
print("Enter a number: ",terminator:"");
let number = Int(readLine(strippingNewline: true)!);
print(fib(num: number!, a: 0, b: 1));
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Y aquí está el resultado....<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-X4fRZPlMROY/WDyh_uqZIxI/AAAAAAAAFBQ/8Eype9haEXggFNL2SqYRBmwFjrgafmmkACLcB/s1600/Fibonacci_Swift.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="131" src="https://3.bp.blogspot.com/-X4fRZPlMROY/WDyh_uqZIxI/AAAAAAAAFBQ/8Eype9haEXggFNL2SqYRBmwFjrgafmmkACLcB/s400/Fibonacci_Swift.jpg" width="400" /></a></div>
<br />
Ya tengo lista mi aplicación de Números LED...así que esperen -;)<br />
<br />
Saludos,<br />
<br />
Blag.<br />
Development Culture.Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-26227219837460197802016-11-15T10:11:00.003-08:002016-11-15T10:11:38.295-08:00LED es mi nuevo Hello World - Tiempo de PerlComo lo prometí...acá está mi LED Numbers a la <a href="https://www.perl.org/" target="_blank">Perl</a>...y como siempre...por favor tengan en cuenta de que soy un novato en Perl...Yo se que hay maneras más eficientes, cortas y consisas de hacer esta aplicación...pero...que tan bueno es un código introductorio que usa código oscuro y arcano? Yo no quiero que la gente se asuste con Perl...Quiero que la gente diga "Hey...eso no se ve tan dificil...Yo quiero aprender Perl"...<br />
<br />
Así que...aquí está...<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">LedNumbers.pl</th>
</tr>
<tr>
<td><pre>#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
my %leds = (
0 => ' _ ,| | ,|_| ',
1 => ' ,| ,| ',
2 => ' _ , _| ,|_ ',
3 => '_ ,_| ,_| ',
4 => ' ,|_| , | ',
5 => ' _ ,|_ , _| ',
6 => ' _ ,|_ ,|_| ',
7 => '_ , | , | ',
8 => ' _ ,|_| ,|_| ',
9 => ' _ ,|_| , _| '
);
print "Enter a number: ";
my $num = <>;
my @numbers = ( $num =~ /\d/g );
for my $i (0 .. 2){
for my $j (0 .. scalar(@numbers) - 1){
my @line = split /\,/,$leds{$numbers[$j]};
print $line[$i];
}
print "\n";
}
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Y acá está el resultado...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-OjxDqVF1AU0/WCtKjvHXGUI/AAAAAAAAFAs/o7AE03rRvlUuVCRg3bE4ZtS9miB8WGDdACLcB/s1600/LedNumbers_Perl.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="143" src="https://3.bp.blogspot.com/-OjxDqVF1AU0/WCtKjvHXGUI/AAAAAAAAFAs/o7AE03rRvlUuVCRg3bE4ZtS9miB8WGDdACLcB/s400/LedNumbers_Perl.jpg" width="400" /></a></div>
<br />
Y para que lo sepan...esta es mi versión numero 23 del código...sip...he escrito mi aplicación de LED Numbers en 24 lenguages hasta el momento -;) Cuál va a ser mi punto de quiebre? Quien sabe...la programación es el límite -;)<br />
<br />
Saludos,<br />
<br />
Blag.<br />
Development Culture.Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-58024284030768505662016-11-15T10:06:00.000-08:002016-11-15T10:06:01.329-08:00Mi primer post en Perl<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-eHvp3rwAg_o/WCtG-gGywmI/AAAAAAAAFAY/eU5PHll9CWwTAQ93cxFPANCOEKNRr4pTQCLcB/s1600/Perl.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="201" src="https://1.bp.blogspot.com/-eHvp3rwAg_o/WCtG-gGywmI/AAAAAAAAFAY/eU5PHll9CWwTAQ93cxFPANCOEKNRr4pTQCLcB/s400/Perl.jpg" width="400" /></a></div>
<br />
<br />
Así que...he comenzado a aprender <a href="https://www.perl.org/" target="_blank">Perl</a>...porque? 3 simples razones...<br />
<ol>
<li>Amo la programación.</li>
<li>Para mí...Perl pertenece a la Santa Trinidad de los Lenguajes de Programación junto con Ruby y Python (Disculpa PHP...pero no llegas a la lista)</li>
<li>Porque...es Perl! Vamos!</li>
</ol>
<div>
<br /></div>
<div>
Entonces...he estado leyendo <a href="https://smile.amazon.com/Beginning-Perl-Curtis-Poe/dp/1118013840" target="_blank">Beginning Perl</a>...que dicho sea de paso es un libro impresionante...</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://smile.amazon.com/Beginning-Perl-Curtis-Poe/dp/1118013840" target="_blank"><img border="0" height="400" src="https://1.bp.blogspot.com/-6EUXubQHbIU/WCtG5Ci_7ZI/AAAAAAAAFAU/FxIKAwLq0nsibQTkftK0IIsgAlVR27ziQCLcB/s400/Beginning_Perl.jpg" width="315" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Si estás utilizándo algun tipo de Linux o Mac...ya deberías tener Perl instalado por defecto...si estás usando Windows...bueno...siempre puedes descargarlo e instalarlo -:)</div>
<div>
<br /></div>
<div>
Hasta el momento...Amo Perl...es impresionante...y ahora puedo porque las personas dicen que tanto Python como Ruby piden prestado bastante de Perl...y claro...PHP lo hace también...</div>
<div>
<br /></div>
<div>
Por supuesto, no tengo mucha experiencia...pero como siempre...Comienzo haciendo un simple y pequeño programa para posicionarme en el camino correcto...así que aquí está mi applicación de lista de números Fibonacci...</div>
<div>
<br /></div>
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">fibonacci.pl</th>
</tr>
<tr>
<td><pre>#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
sub fib {
my ($num,$a,$b) = @_;
my $result = '';
if ($a>0 && $num>1){
$result = $result . ($a+$b) . " " . fib($num-1,$a+$b,$a)
}elsif($a == 0){
$result = $a . " " . $b . " " . ($a+$b) . " " . fib($num-1,$a+$b,$b)
}
return $result
}
print "Enter a number: ";
my $num = <>;
print(fib($num,0,1));
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Y aquí esta el resultado...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-3AOIhO6uCGY/WCtI50HNdpI/AAAAAAAAFAk/tXDEoyEC3vQwKuDw8YEXxasYaGDpEPiFACLcB/s1600/Fibonnaci_Perl.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="107" src="https://3.bp.blogspot.com/-3AOIhO6uCGY/WCtI50HNdpI/AAAAAAAAFAk/tXDEoyEC3vQwKuDw8YEXxasYaGDpEPiFACLcB/s400/Fibonnaci_Perl.jpg" width="400" /></a></div>
<br />
Claro...ya tengo m clásica aplicación "LED Numbers" lista...pero eso va en otro post -;)<br />
<br />
Saludos,<br />
<br />
Blag.<br />
Development Culture.Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-69998978864214640562016-08-09T11:23:00.000-07:002016-08-09T11:23:18.559-07:00Microsoft HoloLens en el SAP’s d-shop<h2 style="background-color: #333333; color: #cccccc; font-family: arial, tahoma, helvetica, freesans, sans-serif; font-size: 22px; margin: 0px; position: relative; text-align: center;">
<b>Este post fué posteado originalmente en <a href="http://scn.sap.com/community/innovation-management/blog/2016/08/09/microsoft-hololens-on-sap-s-d-shop" target="_blank">Microsoft HoloLens on SAP’s d-shop</a>.</b></h2>
<div>
<b><br /></b></div>
<div>
<div>
Una de las metas principales del SAP’s d-shop es recolectar, aprender y compartir conocimientos en las últimas tecnologías y el <a href="https://www.microsoft.com/microsoft-hololens/en-us" target="_blank">Microsoft HoloLens</a> no ha estado fuera de nuestro radar -;)</div>
<div>
<br /></div>
<div>
Pero…que es HoloLens?</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-4C_aaui6gyo/V6oWDUNh_pI/AAAAAAAAE9M/WWCwsOjmkXc3ZvGeJ1E39JguaxCZxketQCLcB/s1600/HoloLens.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="266" src="https://3.bp.blogspot.com/-4C_aaui6gyo/V6oWDUNh_pI/AAAAAAAAE9M/WWCwsOjmkXc3ZvGeJ1E39JguaxCZxketQCLcB/s400/HoloLens.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
HoloLens es un dispositivo de Mixed Reality…que es basicamente una mezcla de Virtual (Oculus Rift) y Augmented (Google Glass) Realities.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Una de las capacidades más importantes del Mixed Reality es que te permite crear Hologramas que pueden ser mezclados con el mundo real e interactuar con el.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
HoloLens hace un mapeo de tu cuarto y puede reconocer que tienes una mesa de café, un sofá, una lámpara, y demás…cuando posicionas tus Hologramas en la mesa o en el sofá, el Holograma no va pasar a través de el, más bien va a "sentarse" encima…y esto te permite hacer cosas asombrosas como crear una Holograma de una lampara que va a ser posicionada al costado del sofá para ver como se vería. Y este es solo uno de los múltiples casos de uso que el HoloLens puede proporcionar.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
En el d-shop Silicon Valley, creemos que utilizar y programar el HoloLens es una experiencia asombrosa.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Así que…que necesitas para comenzar a programar y crear experiencias asombrosas?</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
En esta <a href="https://developer.microsoft.com/en-us/windows/holographic/install_the_tools" target="_blank">página</a> encontrarás todas las herramientas que necesitas, las cuales incluyen Visual Studio, una versión a la medida de Unity3D y el emulador del HoloLens.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Además, también sería excelente para ti echarle un vistazo a los videos del <a href="https://developer.microsoft.com/en-us/windows/holographic/academy" target="_blank">Holographic Academy</a>…los cuales te guía a través de los conceptos básicos de la programación en HoloLens…</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Pero…con este blog queremos mostrarte un video con algunos de los demos/proyectos que hemos creado en el d-shop Silicon Valley.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<b><i>Word Clock</i></b> Con esta aplicación, puedes literalmente leer la hora -;) Esto demuestra el de texo en Unity y fué adaptado de una aplicación escrita en Python.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<b><i>d-shop Robot</i></b> Esta aplicación utiliza un model del robot del d-shop diseñado utilizándo <a href="https://www.blender.org/" target="_blank">Blender</a>. Esto demuestra el uso de Spatial Mapping y comandos simples de voz.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<b><i>SAP Puzzle Game</i></b> Esta aplicación muestra un juego de rompecabeza deslizable que tiene dos modos…fácil y dificil…demuestra como recortar una imagen y distribuirla en multiples cubos, también demuestra el uso de comandos de voz simples y física.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<b><i>HoloHouse</i></b> Esta aplicación es un proyecto diseñado para un banco en Dubai para ser utilizado como aplicación de Bienes Raices. Los modelos fueron diseñados utilizándo <a href="http://www.autodesk.com/products/3ds-max/overview" target="_blank">3ds Max</a> por una compañía externa. Esta aplicación demuestra el uso del Scripting en Unity para rotar las casas, decolorar las luces, levantar el techo y los pisos de las casas y mostrar señalizaciones.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/J_Fmv_MdDVg/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/J_Fmv_MdDVg?feature=player_embedded" width="320"></iframe></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
<br /></div>
<div>
Ahora que has visto lo que se puede hacer en un par de semanas…por que no te unes a nosotros en el d-shop y nos muestras que es lo tú puedes hacer! -:D</div>
<div style="font-weight: bold;">
<br /></div>
</div>
<div>
Saludos,</div>
<div style="font-weight: bold;">
<br /></div>
<div>
Blag.</div>
<div>
Development Culture.</div>
Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-86800824535841137952016-08-07T05:53:00.004-07:002016-08-07T05:53:57.765-07:00El primer Hackaton de Hololens del d-shop<h2 style="background-color: #333333; color: #cccccc; font-family: arial, tahoma, helvetica, freesans, sans-serif; font-size: 22px; margin: 0px; position: relative; text-align: center;">
<b>Este post fué posteado originalmente en <a href="http://scn.sap.com/community/innovation-management/blog/2016/08/05/d-shop-s-very-first-hololens-hackaton" target="_blank">d-shop's very first Hololens Hackaton</a>.</b></h2>
<div>
<b><br /></b></div>
<div>
El Miércoles 3 de Agosto, tuvimos el primer Hackaton de Hololens en el d-shop’s de SAP. Personas que nunca antes habían utilizado Unity3D y que nunca antes habían programado para el Hololens se reunieron para tener una divertiva y emocionante experiencia de aprendizaje.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-X-gUrtrxUjo/V6cnfgiMqcI/AAAAAAAAE7U/SpK4LGZUmeY6Q4pUEr6FWjaGJkLQQNF-gCLcB/s1600/IMG_1138.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://1.bp.blogspot.com/-X-gUrtrxUjo/V6cnfgiMqcI/AAAAAAAAE7U/SpK4LGZUmeY6Q4pUEr6FWjaGJkLQQNF-gCLcB/s400/IMG_1138.JPG" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Gracias al trabajo duro y esfuerzos de organización de mis compañeros de equipo <a href="https://scn.sap.com/people/aaron.williams2" target="_blank">Aaron Williams</a> y <a href="http://scn.sap.com/people/julia.satsuta" target="_blank">Julia Satsuta</a> tuvimos 3 personas asombrozas de Microsoft, Vlad Kolesnikov, Petri Wihelmsen y Jaime Rodriguez.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-XA_C5528_PQ/V6cnzCwEXkI/AAAAAAAAE7Y/cBBoCAEJX_I3H9ryq7f6QMXK27WQEmFdACLcB/s1600/IMG_1143.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://3.bp.blogspot.com/-XA_C5528_PQ/V6cnzCwEXkI/AAAAAAAAE7Y/cBBoCAEJX_I3H9ryq7f6QMXK27WQEmFdACLcB/s400/IMG_1143.JPG" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
El primer día hubo explicaciones de que es el Hololens…una rápida introducción a Unity3D y una parte de Design Thinking/Brainstorming donde los equipos pudieron planear sus ideas.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
El segundo día arrancó con programación en Hololens y apoyo por nuestro lado. La personas estaban completamente emocionadas y con energía…intentándo cosas en el editor de Unity3D…en el emulador de Hololens y en el mismo dispositivo.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Como una manera de incluir a más personas y dejar que los equipos pudieran trabajar sin distracciones, tuvimos una sesión sobre Hololens en el Edificio 7…abierto para todos lo que querían asistir…</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-NF2lhBxaHck/V6cn_6GSeTI/AAAAAAAAE7c/nm2-bidwdLsx0v0OhFH4y6kxcI4pkPplwCLcB/s1600/IMG_1151.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://1.bp.blogspot.com/-NF2lhBxaHck/V6cn_6GSeTI/AAAAAAAAE7c/nm2-bidwdLsx0v0OhFH4y6kxcI4pkPplwCLcB/s400/IMG_1151.JPG" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-0iJPYSci5CM/V6coNUfbG0I/AAAAAAAAE7k/owjUhBDDOtEzlTk7bLVERpHPOqzQ4oUhwCLcB/s1600/IMG_1154.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://2.bp.blogspot.com/-0iJPYSci5CM/V6coNUfbG0I/AAAAAAAAE7k/owjUhBDDOtEzlTk7bLVERpHPOqzQ4oUhwCLcB/s400/IMG_1154.JPG" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-XyRkit-iV5w/V6coRSSnK2I/AAAAAAAAE7o/5vk433KHDRotpBleBzVgTinJePU9ULpcwCLcB/s1600/IMG_1163%2B-%2BCopy.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="142" src="https://3.bp.blogspot.com/-XyRkit-iV5w/V6coRSSnK2I/AAAAAAAAE7o/5vk433KHDRotpBleBzVgTinJePU9ULpcwCLcB/s400/IMG_1163%2B-%2BCopy.JPG" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Luego de eso, regresamos para poder continuar ayudándo y apoyándos a los equipos. Ellos necesitaban estar totalmente listos a las 3:30pm puesto que teniamos la presentación de demos a las 4:00pm.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
La presentación de demos estuvo localizada en la cafetería del Edificio 8.</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-0kQeTBl43xQ/V6coh99cxfI/AAAAAAAAE7w/iOENe57kZBExI5XPt8Pt6CL43Wb9fhvBQCLcB/s1600/IMG_1167.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://3.bp.blogspot.com/-0kQeTBl43xQ/V6coh99cxfI/AAAAAAAAE7w/iOENe57kZBExI5XPt8Pt6CL43Wb9fhvBQCLcB/s400/IMG_1167.JPG" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-7Mqcs0z-ND8/V6comXWMInI/AAAAAAAAE70/Bxowmzhoz5QB8bnpQT0qFR9mUWc6FDh9ACLcB/s1600/IMG_1168%2B-%2BCopy.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="112" src="https://3.bp.blogspot.com/-7Mqcs0z-ND8/V6comXWMInI/AAAAAAAAE70/Bxowmzhoz5QB8bnpQT0qFR9mUWc6FDh9ACLcB/s400/IMG_1168%2B-%2BCopy.JPG" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Tuvimos a 5 equipos presentándo su impresionante trabajo…</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<b>Holoterior</b></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Diseña el cuarto de tus sueños</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-KUqJI8C1udk/V6coyYYzvUI/AAAAAAAAE78/TwISBIZKUAcuEobghKPavvovBYsEmndjACLcB/s1600/IMG_1174.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://1.bp.blogspot.com/-KUqJI8C1udk/V6coyYYzvUI/AAAAAAAAE78/TwISBIZKUAcuEobghKPavvovBYsEmndjACLcB/s400/IMG_1174.JPG" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-wZJFbtkb1ps/V6coye9uXNI/AAAAAAAAE8A/qurmYIBR9yYPw77vwu4TBecwR6b5CRVJACLcB/s1600/IMG_1177.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://1.bp.blogspot.com/-wZJFbtkb1ps/V6coye9uXNI/AAAAAAAAE8A/qurmYIBR9yYPw77vwu4TBecwR6b5CRVJACLcB/s400/IMG_1177.JPG" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-F30frqo2phg/V6coyNFUbzI/AAAAAAAAE74/jZ7JD4ZbcbYcaydPL9gnG55H3Y0FyIvrQCLcB/s1600/IMG_1180.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://2.bp.blogspot.com/-F30frqo2phg/V6coyNFUbzI/AAAAAAAAE74/jZ7JD4ZbcbYcaydPL9gnG55H3Y0FyIvrQCLcB/s400/IMG_1180.JPG" width="400" /></a></div>
<div>
<br /></div>
<div>
<div>
<b>Mechannotate</b></div>
<div>
</div>
<div>
Ordena parte de repuesto a medida que las necesitas...no pierdas el tiempo llenándo formularios</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-sdRB0TmrAFM/V6co_Ee83UI/AAAAAAAAE8Q/UG3BIuz7AVQgzAsyrh9oR8MZ1Q78sQGgACLcB/s1600/IMG_1183.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://3.bp.blogspot.com/-sdRB0TmrAFM/V6co_Ee83UI/AAAAAAAAE8Q/UG3BIuz7AVQgzAsyrh9oR8MZ1Q78sQGgACLcB/s400/IMG_1183.JPG" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-7beIbDThCV0/V6co_EoLhFI/AAAAAAAAE8M/-ctyrEBZrJsItqWDV8F7wo_c6msT1Wf7QCLcB/s1600/IMG_1184.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://4.bp.blogspot.com/-7beIbDThCV0/V6co_EoLhFI/AAAAAAAAE8M/-ctyrEBZrJsItqWDV8F7wo_c6msT1Wf7QCLcB/s400/IMG_1184.JPG" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-J6oiMfzv27s/V6co_OHUK7I/AAAAAAAAE8I/CaSDTMLSqR43GhdChso-rTwX0XNP6eLkACLcB/s1600/IMG_1188.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://4.bp.blogspot.com/-J6oiMfzv27s/V6co_OHUK7I/AAAAAAAAE8I/CaSDTMLSqR43GhdChso-rTwX0XNP6eLkACLcB/s400/IMG_1188.JPG" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<b>Next Talent</b></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Motiva a tus nuevos empleados ofreciéndoles una inmersión en tu historia y cultura</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-WwXd2gCacwQ/V6cpLn52JyI/AAAAAAAAE8U/Ys7Ocqf20nAwkSorjYYr4SZ97c-xHhksACLcB/s1600/IMG_1189.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://1.bp.blogspot.com/-WwXd2gCacwQ/V6cpLn52JyI/AAAAAAAAE8U/Ys7Ocqf20nAwkSorjYYr4SZ97c-xHhksACLcB/s400/IMG_1189.JPG" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-7Ew_gspJ7iQ/V6cpLhS7cgI/AAAAAAAAE8c/Aw-HB0Cx4qMpqbM9R9Lozj9MapkJ9mIBACLcB/s1600/IMG_1196.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://2.bp.blogspot.com/-7Ew_gspJ7iQ/V6cpLhS7cgI/AAAAAAAAE8c/Aw-HB0Cx4qMpqbM9R9Lozj9MapkJ9mIBACLcB/s400/IMG_1196.JPG" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://3.bp.blogspot.com/-E_SbCkzlWbc/V6cpLoXK51I/AAAAAAAAE8Y/hExi_EURzRM-2lWQhmWkJaz7Ki13ruGdwCLcB/s1600/IMG_1197.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://3.bp.blogspot.com/-E_SbCkzlWbc/V6cpLoXK51I/AAAAAAAAE8Y/hExi_EURzRM-2lWQhmWkJaz7Ki13ruGdwCLcB/s400/IMG_1197.JPG" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<b>TripIp</b></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Planeando tu próximo viaje? Obtén el asiento que es más comfortable para ti</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-WsYwR3KwfVE/V6cpanGnZLI/AAAAAAAAE8o/_waEumpw3443rONRk5y7wEpGkFL2JXHvACLcB/s1600/IMG_1201.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://2.bp.blogspot.com/-WsYwR3KwfVE/V6cpanGnZLI/AAAAAAAAE8o/_waEumpw3443rONRk5y7wEpGkFL2JXHvACLcB/s400/IMG_1201.JPG" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-rQQ6fbqbrU4/V6cpahr-CQI/AAAAAAAAE8s/_BJ3bfmv4bY_3ON4SYrsImrugxQzyyVywCLcB/s1600/IMG_1202.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://1.bp.blogspot.com/-rQQ6fbqbrU4/V6cpahr-CQI/AAAAAAAAE8s/_BJ3bfmv4bY_3ON4SYrsImrugxQzyyVywCLcB/s400/IMG_1202.JPG" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-JDvDk845anw/V6cparh0aVI/AAAAAAAAE8k/AbivoSfFSr4QIAJVV_Rza2GKK7jbPdzywCLcB/s1600/IMG_1204.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://2.bp.blogspot.com/-JDvDk845anw/V6cparh0aVI/AAAAAAAAE8k/AbivoSfFSr4QIAJVV_Rza2GKK7jbPdzywCLcB/s400/IMG_1204.JPG" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<b>The Wesley Crushers</b></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Se rompio una de tus cañerías? Revisala...mídela y pide un repuesto</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://2.bp.blogspot.com/-J4JO2hSt78c/V6cpoEVnGbI/AAAAAAAAE84/oBB9I9ZpAyMkRjotr_1HgzsjRmLkXQbQgCLcB/s1600/IMG_1206.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://2.bp.blogspot.com/-J4JO2hSt78c/V6cpoEVnGbI/AAAAAAAAE84/oBB9I9ZpAyMkRjotr_1HgzsjRmLkXQbQgCLcB/s400/IMG_1206.JPG" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://4.bp.blogspot.com/-dOBUcewzYAc/V6cpoJpv_TI/AAAAAAAAE80/ZUmZ_XaFoBgqAbqECgHHR7uzwbgc4xf1QCLcB/s1600/IMG_1208.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://4.bp.blogspot.com/-dOBUcewzYAc/V6cpoJpv_TI/AAAAAAAAE80/ZUmZ_XaFoBgqAbqECgHHR7uzwbgc4xf1QCLcB/s400/IMG_1208.JPG" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-4zZ6YRwTSE4/V6cpoNj6yPI/AAAAAAAAE8w/e2xHQjlSHr4K1vEXU6JuaTi-v6Iex2U7ACLcB/s1600/IMG_1212.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://1.bp.blogspot.com/-4zZ6YRwTSE4/V6cpoNj6yPI/AAAAAAAAE8w/e2xHQjlSHr4K1vEXU6JuaTi-v6Iex2U7ACLcB/s400/IMG_1212.JPG" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Como pueden ver…todos los equipos pusieron todo su esfuerzo y crearon demos realmente alucinantes…pero…siempre debe haber solo un ganador…así que el último equipo en presentarse fué también el equipo de ganó…así que…felicidades al equipo “<b>The Wesley Crushers</b>”!</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Luego de esta emocionante experiencia…estoy seguro de que vamos a regresar por más…Hololens es aún una tecnología muy nueva, así que aún hay mucho por aprender, mucho por hackear y muchisimas posibilidades.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Si estás en Palo Alto…ven y visítianos! Recuerden que el d-shop está localizado en el primer piso del Edificio 9 en Deer Creek Road -;)</div>
<div>
<br /></div>
<div>
Saludos,</div>
<div>
<br /></div>
<div>
Blag.</div>
<div>
Development Culture.</div>
Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-69428311254603996722015-12-07T11:48:00.003-08:002015-12-07T11:48:36.687-08:00LED es mi nuevo Hello World - Tiempo de ElmAsí que...como lo prometí...aquí está mi aplicación de Números LED escrita en Elm -:)<br />
<br />
Ahora...esto me tomó más tiempo del esperado, por un par de detalle...<br />
<br />
a) A Elm le gusta manejar valores Just y Maybe...Haskell también lo hace...pero Haskell provee funciones alternativas que manejan el Just y el Maybe...Elm no...así que tuve que crear algunas funciones adicionales para manejarlo...<br />
<br />
b) Elm no provee una funcion para obtener elementos de una lista...así que tuve que crear la función...<br />
<br />
c) Todavía soy un novato en Elm<br />
<br />
Poniéndo eso de lado...Elm sigue siendo asombroso y me encanta -;)<br />
<br />
En fín...hay que ponernos a trabajar...<br />
<br />
Debemos crear una carpeta llamada LED_Numbers y escribir esto en el terminal...<br />
<br />
<b>elm package install evancz/elm-html</b><br />
<b><br /></b>
<b>elm package install evancz/start-app</b><br />
<b><br /></b>
Luego, abre tu editor favorito y copia y pega este código...<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">LED_Numbers.elm</th>
</tr>
<tr>
<td><pre>module LED_Numbers where
import Html exposing (..)
import Html.Events exposing (..)
import Html.Attributes exposing (..)
import StartApp.Simple as StartApp
import String exposing (toInt,toList)
import Dict
--MODEL
type alias Model =
{
number: String,
leds: Dict.Dict Char (List String),
line: String
}
initialModel: Model
initialModel =
{
number = "",
leds = Dict.fromList[('0',[" _ ","| | ","|_| "]),('1',[" ","| ","| "]),
('2',[" _ "," _| ","|_ "]),('3',["_ ","_| ","_| "]),
('4',[" ","|_| "," | "]),('5',[" _ ","|_ "," _| "]),
('6',[" _ ","|_ ","|_| "]),('7',["_ "," | "," | "]),
('8',[" _ ","|_| ","|_| "]),('9',[" _ ","|_| "," _| "])],
line = ""
}
--UPDATE
fromJust : Maybe a -> a
fromJust x = case x of
Just y -> y
Nothing -> Debug.crash ""
fromMaybe : Maybe (List String) -> List String
fromMaybe x = case x of
Just y -> y
Nothing -> Debug.crash ""
fromMaybeChar : Maybe Char -> Char
fromMaybeChar x = case x of
Just y -> y
Nothing -> Debug.crash ""
get_elem : List a -> Int -> Maybe a
get_elem lst n =
List.head (List.drop n lst)
fromMaybeListChar : Maybe (List Char) -> List Char
fromMaybeListChar x = case x of
Just y -> y
Nothing -> Debug.crash ""
get_list: String -> List Char
get_list str =
String.toList str
type Action = NoOp | Submit | UpdateNumber String
update: Action -> Model -> Model
update action model =
case action of
NoOp ->
model
UpdateNumber contents ->
{ model | number = contents }
Submit ->
{ model |
line = get_led (get_list model.number) 0 model,
number = ""
}
get_led: List Char -> Int -> Model -> String
get_led lst n model =
if List.length lst > 0
then let h = fromMaybeChar(List.head lst)
t = fromMaybeListChar(List.tail lst)
leds = model.leds
line = Dict.get h leds
in fromJust(get_elem (fromMaybe line) n) ++ get_led t n model
else if n < 2
then "" ++ "\n" ++ get_led (get_list model.number) (n+1) model
else if n == 2
then "" ++ "\n"
else ""
--VIEW
buttonStyle: Attribute
buttonStyle =
style
[ ("outline", "none"),
("border", "none"),
("border-radius","4px"),
("margin-right","5px"),
("padding","4px 10px"),
("background","#61a1bc"),
("color","#fff")
]
divStyle: Attribute
divStyle =
style
[ ("margin", "50px auto"),
("padding", "0px 50px"),
("text-align", "center")
]
pStyle: Attribute
pStyle =
style
[ ("font-size", "30px") ]
pageHeader : Html
pageHeader =
h1 [ ] [text "LED Numbers"]
view: Signal.Address Action -> Model -> Html
view address model =
div [ divStyle ]
[ pageHeader,
input
[
type' "text",
name "number",
placeholder "Enter a number",
value model.number,
on "input" targetValue (\v -> Signal.message address (UpdateNumber v))
]
[ ],
button [ buttonStyle, onClick address Submit ] [ text "Submit" ],
pre [ pStyle ]
[ text (model.line) ]
]
main: Signal Html
main =
StartApp.start { model = initialModel, view = view, update = update }
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Anda al terminal y escribe esto...<br />
<br />
<b>elm make LED_Numbers.elm --output LED_Numbers.html</b><br />
<b><br /></b>
Abre el archivo en tu browser y ejecutalo...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-jGXuH38sH4Y/VmXg7oDTEnI/AAAAAAAAE1s/pD4kzuusRxE/s1600/LED_Elm_01.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="137" src="http://4.bp.blogspot.com/-jGXuH38sH4Y/VmXg7oDTEnI/AAAAAAAAE1s/pD4kzuusRxE/s400/LED_Elm_01.jpg" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-Wu-pag-pD5g/VmXg7h3-UnI/AAAAAAAAE1o/9e90zSk8hAY/s1600/LED_Elm_02.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="140" src="http://1.bp.blogspot.com/-Wu-pag-pD5g/VmXg7h3-UnI/AAAAAAAAE1o/9e90zSk8hAY/s400/LED_Elm_02.jpg" width="400" /></a></div>
<br />
Espero que les guste -;)<br />
<br />
Saludos,<br />
<br />
Blag.<br />
Development Culture.Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-66236612345129049792015-12-03T11:01:00.001-08:002015-12-03T11:01:29.968-08:00Mi primer post en ElmMirándo en mi lista de nuevos lenguajes a aprender...me encontré con <a href="http://elm-lang.org/" target="_blank">Elm</a>...programación funcional y reactiva en el browser...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/--UqWvMc_79Q/VmCDcItkEyI/AAAAAAAAE0U/YHZT99bundI/s1600/Elm_Logo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/--UqWvMc_79Q/VmCDcItkEyI/AAAAAAAAE0U/YHZT99bundI/s1600/Elm_Logo.png" /></a></div>
<br />
No es eso impresionante? Por supuesto que lo es -:)<br />
<br />
Bueno...Elm comenzó a existir a mediados del 2012...así que es relativamente nuevo...lo cual significa que no hay muchos tutoriales y que yo sepa ningún libro...pero...no dejes que eso te asuste...existen un par de asombrosos cursos dictados por <a href="https://pragmaticstudio.com/" target="_blank">The Pragmatic Studio</a> que te ayudarán a estar listo y preparado -;)<br />
<br />
<a href="https://pragmaticstudio.com/elm" target="_blank">Elm: Building Reactive Web Apps</a><br />
<br />
<a href="https://pragmaticstudio.com/elm-signals" target="_blank">Elm: Signals, Mailboxes & Ports</a><br />
<br />
Ahora...para instalar Elm...si ya tienes instalado <a href="https://nodejs.org/en/" target="_blank">NodeJS</a> entonces simplemente puedes utilizar npm que es yo pienso la manera más rápida y sencilla...<br />
<br />
<b>sudo npm install elm</b><br />
<b><br /></b>
De lo contrario puedes ir a la sección de <a href="http://elm-lang.org/install" target="_blank">download</a> para los instaladores o para el código fuente para compilarlo...pero debes tener en cuenta que para compilarlo vas a necesitar tener <a href="https://www.haskell.org/" target="_blank">Haskell</a> instalado también...<br />
<br />
En todo caso...vamos a ver como crear una aplicación de List Fibonacci en Elm... -:)<br />
<br />
Crea una carpeta y llamala "Fibonacci_App" y entra en la carpeta desde el terminal...<br />
<br />
<b>elm package install evancz/elm-html</b><br />
<b><br /></b>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-gbMoQ6KoNoM/VmCIN3yL6QI/AAAAAAAAE0o/SacLJh86_tc/s1600/Elm_Html.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="http://1.bp.blogspot.com/-gbMoQ6KoNoM/VmCIN3yL6QI/AAAAAAAAE0o/SacLJh86_tc/s400/Elm_Html.jpg" width="400" /></a></div>
<b><br /></b>
<b>elm package install evancz/start-app</b><br />
<b><br /></b>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-weWz6QV-3XI/VmCIfR7DcQI/AAAAAAAAE0w/KwrhHm0lk1A/s1600/Elm_Start.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="210" src="http://4.bp.blogspot.com/-weWz6QV-3XI/VmCIfR7DcQI/AAAAAAAAE0w/KwrhHm0lk1A/s400/Elm_Start.jpg" width="400" /></a></div>
<b><br /></b>
El primer paquete nos va a permitir generar código HTML y el segundo va a hacer nuestras vidas más sencillas...<br />
<br />
Ahora...abre tu editor favorito y copia y pega este código...<br />
<br />
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">Fibonacci.elm</th>
</tr>
<tr>
<td><pre>module Fibonacci where
import Html exposing (..)
import Html.Events exposing (..)
import Html.Attributes exposing (..)
import StartApp.Simple as StartApp
import String exposing (toInt)
--MODEL
type alias Model =
{
number: String,
fibonacci: String
}
initialModel: Model
initialModel =
{
number = "",
fibonacci = ""
}
--UPDATE
parseInt : String -> Int
parseInt string =
case String.toInt string of
Ok value ->
value
Err error ->
0
type Action = NoOp | Submit | UpdateNumber String
update: Action -> Model -> Model
update action model =
case action of
NoOp ->
model
UpdateNumber contents ->
{ model | number = contents }
Submit ->
{ model |
fibonacci = fib (parseInt model.number) 0 1,
number = ""
}
fib: Int -> Int -> Int -> String
fib num a b =
if a > 0 && num > 1 then toString(a+b) ++ " " ++ fib (num-1) (a+b) a
else if a == 0 then toString a ++ " " ++ toString b ++ " "
++ toString(a+b) ++ " " ++ fib(num-1) (a+b) b
else ""
--VIEW
buttonStyle: Attribute
buttonStyle =
style
[ ("outline", "none"),
("border", "none"),
("border-radius","4px"),
("margin-right","5px"),
("padding","4px 10px"),
("background","#61a1bc"),
("color","#fff")
]
divStyle: Attribute
divStyle =
style
[ ("margin", "50px auto"),
("padding", "0px 50px"),
("text-align", "center")
]
pStyle: Attribute
pStyle =
style
[ ("font-size", "30px") ]
pageHeader : Html
pageHeader =
h1 [ ] [text "Fibonacci List"]
view: Signal.Address Action -> Model -> Html
view address model =
div [ divStyle ]
[ pageHeader,
input
[
type' "text",
name "number",
placeholder "Enter a number",
value model.number,
on "input" targetValue (\v -> Signal.message address (UpdateNumber v))
]
[ ],
button [ buttonStyle, onClick address Submit ] [ text "Submit" ],
p [ pStyle ]
[ text (model.fibonacci) ]
]
main: Signal Html
main =
StartApp.start { model = initialModel, view = view, update = update }
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
Luego, solo debemos compilar nuestra aplicación -;)<br />
<br />
<b>elm make Fibonacci.elm --output Fibonaccci.html</b><br />
<b><br /></b>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-P1-gktbRzOI/VmCNQE7bb4I/AAAAAAAAE1A/PYD6C0c4TDM/s1600/Fibonacci_Elm.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="77" src="http://1.bp.blogspot.com/-P1-gktbRzOI/VmCNQE7bb4I/AAAAAAAAE1A/PYD6C0c4TDM/s400/Fibonacci_Elm.jpg" width="400" /></a></div>
<b><br /></b>
Ahora, podemos abrir el archivo Fibonacci.html y probar nuestra aplicación -:D<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-lvD19eoO05Q/VmCOtUFa2jI/AAAAAAAAE1Q/OTiy38cTMGg/s1600/Fibonacci_App_01.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="112" src="http://1.bp.blogspot.com/-lvD19eoO05Q/VmCOtUFa2jI/AAAAAAAAE1Q/OTiy38cTMGg/s400/Fibonacci_App_01.jpg" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-RgqGKMpQHas/VmCOtaUKHyI/AAAAAAAAE1M/JNtMomRHcPc/s1600/Fibonacci_App_02.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="106" src="http://4.bp.blogspot.com/-RgqGKMpQHas/VmCOtaUKHyI/AAAAAAAAE1M/JNtMomRHcPc/s400/Fibonacci_App_02.jpg" width="400" /></a></div>
<br />
Espero que le haya gustado -:) Y voy a tratar de tener mi aplicación de Números LED terminada pronto -;)<br />
<br />
Saludos,<br />
<br />
Blag.<br />
Development Culture.Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0tag:blogger.com,1999:blog-13698376.post-63119820732094730972015-11-23T13:17:00.001-08:002015-11-23T13:17:11.147-08:00SAP HANA en la luna (con Lua)<h2 style="text-align: center;">
<b>Este post fué posteado originalmente en <a href="http://scn.sap.com/community/innovation-management/blog/2015/11/23/sap-hana-on-the-moon-with-lua" target="_blank">SAP HANA on the moon (with Lua)</a>.</b></h2>
<div>
<b><br /></b></div>
<div>
<div>
<a href="http://www.lua.org/" target="_blank">Lua</a> es un lenguaje de programación ligero diseñado como un lenguaje script con semántica extensible como primera regla. Lua es multi plataforma puesto que está escrito en ANSI C, y tiene una API de C relativamente simple.</div>
<div>
<br /></div>
<div>
Si están preguntándo acerca del título…bueno…Lua significa Luna en Portugués -;)</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-2NhFapgj97o/VlN_CxWRT9I/AAAAAAAAEzw/MQ-UExJWTdQ/s1600/Lua_Logo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="http://4.bp.blogspot.com/-2NhFapgj97o/VlN_CxWRT9I/AAAAAAAAEzw/MQ-UExJWTdQ/s200/Lua_Logo.png" width="200" /></a></div>
<div>
<br /></div>
<div>
<div>
Lua es bastante potente pero sencillo de aprender y usar. Si ya saben Python o Ruby, no tendrán muchos problemas…<br />
<br /></div>
<div>
</div>
<div>
Antes de continuar…quiero compartir una pequeña broma…que no es especificamente única para Lua…pero aún así es graciosa -;)</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-MnwMSrh4n4Y/VlN_KA8reaI/AAAAAAAAEz4/AMb-SYG-wTY/s1600/LuaArrays.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="212" src="http://2.bp.blogspot.com/-MnwMSrh4n4Y/VlN_KA8reaI/AAAAAAAAEz4/AMb-SYG-wTY/s320/LuaArrays.jpg" width="320" /></a></div>
<div>
<br /></div>
<div>
<div>
<div>
<div class="separator" style="clear: both; text-align: justify;">
Así que…ninguna demostración estaría completa si la enlazaramos con SAP HANA, so? Así que…vamos a hacerlo -;)</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div>
<div style="text-align: justify;">
Primero, debemos crear un objeto Join y asociar la tabla por MANDT y CARRID. De ahí, seleccionar los siguientes campos como output MANDT, CARRID, CARRNAME, PRICE y CURRENCY.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Luego crear un objeto Aggregation seleccionándo los campos CARRNAME, PRICE (Como columnas agregadas) y CURRENCY. Debemos filtrar el campo CURRENCY por ‘USD’.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Luego, debemos crear un objeto Projection y seleccionar solo PRICE y CARRNAME.<br />
<br />
En el objeto Semantics asegúrense de marcar “CROSS CLIENT” como cliente por defecto.</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-RadWDRVzXJM/VfsH9v38WWI/AAAAAAAAEnc/_0pdHy9cOtA/s1600/Calculation_View.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="http://3.bp.blogspot.com/-RadWDRVzXJM/VfsH9v38WWI/AAAAAAAAEnc/_0pdHy9cOtA/s320/Calculation_View.jpg" width="172" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Ahora, cambiemos a la vista SAP HANA Development y creemos un nuevo repositorio. Lo llamaremos“Flights”.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Creamos un proyecto “XS Engine” y también lo llamamos “Flights”. Lo enlazamos con el repositorio “Flights”.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Creamos un archivo vacio llamado “.xsapp”.</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Creamos un archivo llamado “.xsaccess” con el siguente código.</div>
<div style="text-align: justify;">
<br /></div>
<table align="center" style="width: 600px;"><tbody>
<tr><th bgcolor="#6690BC">.xsaccess</th></tr>
<tr><td><pre>{
"exposed" : true,
"authentication" : [ { "method" : "Basic" } ]
}
</pre>
</td></tr>
<tr></tr>
</tbody></table>
<br />
<span style="text-align: justify;">Finalmente creamos un archivo llamado “flights.xsodata” con el siguiente código</span><br />
<br />
<table align="center" style="width: 600px;"><tbody>
<tr><th bgcolor="#6690BC">flights.xsodata</th></tr>
<tr><td><pre>service {
"Blag/FLIGHTS_BY_CARRIER.calculationview" as "FLIGHTS" keys
generate local "Id";
}
</pre>
</td></tr>
<tr></tr>
</tbody></table>
<br />
<span style="text-align: justify;">Activamos el proyecto y lanzamos nuestro browser...deberíamos ver algo como esto…</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-hgyYGKg3vjs/VfsJHWVPulI/AAAAAAAAEnk/91PjeWsjUIA/s1600/XSProject.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="100" src="http://1.bp.blogspot.com/-hgyYGKg3vjs/VfsJHWVPulI/AAAAAAAAEnk/91PjeWsjUIA/s400/XSProject.jpg" width="400" /></a></div>
</div>
<div style="text-align: justify;">
<br /></div>
<div>
<div>
</div>
</div>
<div class="separator" style="clear: both; text-align: justify;">
La parte de SAP HANA está lista…así que ahora podemos pasar a la parte de Lua…</div>
</div>
<div>
</div>
<br />
Antes de continuar…debo de recomendar que instalen un par de cosas…si usan Linux…instalen esto…<br />
<div>
<br />
<b>sudo apt-get install lua5.2</b><br />
<br />
Y luego sigan las instrucciones para instalar <a href="https://luarocks.org/#quick-start" target="_blank">LuaRocks</a> un manejador de paquetes para Lua…<br />
<br />
Instalen los siguientes paquetes…<br />
<br />
<b>sudo luarocks install luasec</b><br />
<b>sudo luarocks install luajson</b><br />
<b>sudo luarocks install luasocket</b><br />
<br />
Para programar en Lua, pueden utilizar cualquier editor que les guste…copien y peguen el siguiente código…</div>
</div>
<div>
<br /></div>
<div>
<div>
</div>
</div>
</div>
<table align="center" style="width: 600px;">
<tbody>
<tr><th bgcolor="#6690BC">Lua_HANA.lua</th>
</tr>
<tr>
<td><pre>http = require("socket.http")
mime = require("mime")
ltn12 = require("ltn12")
json = require("json")
h = {Authorization = "Basic " .. (mime.b64("SYSTEM:YourPassword")),
["Content-Type"] = "application/json", ["content-length"] = jsonsize }
local resp ={}
ok, code, headers = http.request{url = "http://YourServer:8000/Flights/flights.xsodata/FLIGHTS?$format=json",
redirect = true, method = "GET", headers = h, source = source,
sink = ltn12.sink.table(resp)}
local dec = json.decode(resp[1])
local d = dec["d"]["results"]
for i = 1, #d do
io.write(d[i]["CARRNAME"]," : ",d[i]["PRICE"], "\n")
end
print("")
</pre>
</td>
</tr>
<tr>
</tr>
</tbody></table>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-QW7_1RPywj8/VlN_0W5Ct0I/AAAAAAAAE0A/F5KvzDHnkEo/s1600/Lua_HANA.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="154" src="http://3.bp.blogspot.com/-QW7_1RPywj8/VlN_0W5Ct0I/AAAAAAAAE0A/F5KvzDHnkEo/s320/Lua_HANA.jpg" width="320" /></a></div>
<br />
Hacer esta integración con Lua fué rápido y sencillo…Lua es un lenguaje muy interesánte con algunas características muy buenas que no se pueden encontrar en otros lenguajes...<br />
<br />
Saludos,<br />
<br />
Blag.<br />
Development Culture.Alvaro "Blag" Tejada Galindohttp://www.blogger.com/profile/12061593528820409779noreply@blogger.com0