<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-5595960729038325799</atom:id><lastBuildDate>Tue, 08 Oct 2024 21:37:31 +0000</lastBuildDate><category>videojuegos</category><category>MVC</category><category>Física</category><category>gráficos</category><category>voxels</category><category>Chrome</category><category>IA</category><category>Inteligencia Artificial</category><category>Joomla</category><category>automata celular</category><category>c#</category><category>json</category><category>juegos</category><category>unity3d</category><category>webgl</category><title>Ezequiel_Pozzo-&amp;gt;rand()</title><description></description><link>http://ezequielpozzo.blogspot.com/</link><managingEditor>noreply@blogger.com (2b)</managingEditor><generator>Blogger</generator><openSearch:totalResults>15</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5595960729038325799.post-5394231958758050463</guid><pubDate>Sat, 27 Oct 2012 20:24:00 +0000</pubDate><atom:updated>2012-10-28T08:12:20.125-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">gráficos</category><category domain="http://www.blogger.com/atom/ns#">videojuegos</category><category domain="http://www.blogger.com/atom/ns#">voxels</category><category domain="http://www.blogger.com/atom/ns#">webgl</category><title>Algoritmo Marching Squares</title><description>&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;script src=&quot;//webgl-voxel-renderer.googlecode.com/git/js/three.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;//webgl-voxel-renderer.googlecode.com/git/js/jquery.uniform.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;//webgl-voxel-renderer.googlecode.com/git/js/voxel2D.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;link charset=&quot;utf-8&quot; href=&quot;//webgl-voxel-renderer.googlecode.com/git/css/uniform.default.css&quot; media=&quot;screen&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot;&gt;&lt;/link&gt;
&lt;script type=&quot;text/javascript&quot;&gt;$(document).ready(function () {renderer.init(&#39;#v2dwidget&#39;, new VoxelField(6,6));});&lt;/script&gt;
&lt;style type=&quot;text/css&quot;&gt;
 #v2dwidget canvas
 {
  margin-left: auto;
  margin-right: auto;
  display: block;
 }
 #v2dwidget table
 {
  float:left;
 }
 #v2dwidget td
 {
  margin:0;
  padding:0;
 }
 #v2dwidget td:last-child
 {
  padding-right: 1px;
 }
 #v2dwidget td input
 {
  width: 2em;
  margin:0;
 }
 #v2dwidget table {
  border-spacing: 0;
  border-collapse:collapse;
 }
 #v2dwidget table td input::-webkit-outer-spin-button,
 #v2dwidget table td input::-webkit-inner-spin-button {
     -webkit-appearance: none;
     margin: 0; 
 }
&lt;/style&gt;
En el post anterior, comenté que no hay solamente una forma de&amp;nbsp;&lt;a href=&quot;http://rand.ezequielpozzo.com.ar/2012/10/introduccion-voxels.html&quot;&gt;representar voxels en pantalla&lt;/a&gt;. Mostré las formas mas simples y mencioné el&amp;nbsp;algoritmo&amp;nbsp;Marching Cube como una posibilidad para&amp;nbsp;suavizar&amp;nbsp;el renderizado de voxels.&lt;/div&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
En este post, en lugar de mostrar el&amp;nbsp;algoritmo&amp;nbsp;Marching Cubes voy a mostrar su versión 2D: Marching Squares. El algoritmo Marching Squares permite hacer una analogía directa con Marching Cubes y puede ser comprendido mas fácilmente.&lt;/div&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
La idea entonces, es que tenemos un campo de voxels 2D. Una posible representación de ese campo de voxels es un simple array bidimensional de&amp;nbsp;números&amp;nbsp;enteros:&lt;/div&gt;
&lt;br /&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;  v = [[0,     0,   0, 0],
       [30,  100,  30, 0],
       [100, 100, 100, 0],
       [ 30, 100,  30, 0]];
&lt;/pre&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Una forma de representar esto sería utilizando una escala de grises con 0 = blanco y 100 = negro. Pero para &amp;nbsp;evitar un resultado pixelado sería necesario un array de mayor dimensión.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Si lo que buscamos es usar un array de tamaño reducido para representar los voxels, pero a su vez un resultado no tan pixelado, podemos generar una &quot;curva de nivel&quot;. Esto es, un contorno continuo de este campo usando segmentos de lineas. Es ahi donde entra Marching Squares.&lt;/div&gt;
&lt;h3&gt;
Algoritmo Marching Squares&lt;/h3&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Este algoritmo permite obtener una &quot;curva de nivel&quot; de una función discreta (en este caso, los voxels). Los pasos a seguir son:&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Convertir los voxels a un campo de valores 0 o 1 según un isovalor predeterminado.&lt;/li&gt;
&lt;li&gt;Seleccionar un sub cuadrado 2x2 del campo de valores 0/1 y determinar un caso utilizando una tabla de casos (ver table mas adelante).&lt;/li&gt;
&lt;li&gt;Utilizar los valores del campo de voxels original junto con el caso determinado en el paso 2 para obtener una linea interpolada.&lt;/li&gt;
&lt;li&gt;Repetir desde el paso 2 para cada subcuadrado 2x2 del array original.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
Utilizando el ejemplo anterior sería:&lt;/div&gt;
&lt;h3&gt;
1. Convertir a campo de valores:&lt;/h3&gt;
&lt;div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
En este paso elijo un isovalor de 50 (punto medio entre 0 y 100) y creo un nuevo array que tiene 0 cuando el valor v[i][j] es menor o igual a 50 y 1 cuando v[i][j] es mayor a 50.&lt;/div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;  v_iso = [[0, 0, 0, 0],
           [0, 1, 0, 0],
           [1, 1, 1, 0],
           [0, 1, 0, 0]];&lt;/pre&gt;
&lt;div&gt;
&lt;h3&gt;
2. Determinar casos:&lt;/h3&gt;
Viendo cada subarray de 2x2 y comparando con la siguiente tabla de casos, determino el caso.&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;Casos de marching squares&quot; border=&quot;0&quot; height=&quot;376&quot; src=&quot;https://docs.google.com/drawings/pub?id=1s1hb5Kb3u7fOuIgmdNixJxq5cC-kXJ0kZFN7fEuiYoY&amp;amp;w=1140&amp;amp;h=1076&quot; title=&quot;&quot; width=&quot;400&quot; /&gt;&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Tabla de casos. Un circulo negro corresponde a un voxel con valor superior al isovalor, uno blanco corresponde a un valor inferior. Los extremos de la linea resultante tienen que ser interpolados utilizando los valores de los voxels&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
En nuestro ejemplo, volviendo a v_iso, el primer subarray de 2x2 es&lt;/div&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;  [[0, 0], 
   [0, 1]&lt;/pre&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Comparando con la imagen, esto corresponde al caso 2. Ese caso es una linea diagonal de abajo hacia la derecha.&lt;/div&gt;
&lt;h3&gt;
3. Interpolar usando voxels:&lt;/h3&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Si miramos en la tabla, cada extremo de las lineas celestes tiene una flecha que indica que ese extremo deberá ser ubicado teniendo en cuenta los valores de los extremos del lado. Tomemos el ejemplo encontrado en el paso anterior. Los valores de cada voxel eran 0, 0, 30 y 100, que resultaron en un Caso 2:&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://docs.google.com/drawings/pub?id=15g-5brjDCURQGOMBPLohilaNFu2lNXzdFjOkOgJUR6U&amp;amp;w=313&amp;amp;h=273&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://docs.google.com/drawings/pub?id=15g-5brjDCURQGOMBPLohilaNFu2lNXzdFjOkOgJUR6U&amp;amp;w=313&amp;amp;h=273&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
En este punto, hay que tener en cuenta que lo que estamos buscando es una linea correspondiente a un valor de 50. Si la distancia vertical y horizontal entre voxels es L y los valores en la posición de cada voxel son los valores dados por el voxel, entonces podemos interpolar las posiciones de cada extremo de la linea usando una proporción.&lt;/div&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;(100-30)/L =&amp;nbsp;(50-30)/x&lt;/pre&gt;
que implica que x es L*(50-30)/(100-30) = 0.28L&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
De la misma manera, para obtener la coordenada Y de la posición del otro extremo, podemos usar una proporción similar. En este caso, dado que los extremos corresponden a 0 y a 100, el valor de y es en el centro exacto de los voxels, o sea y=L*(50-0)/(100-0) = 0.5L&lt;/div&gt;
&lt;br /&gt;
&lt;h3&gt;
4. Repetir cada paso:&lt;/h3&gt;
Si repetimos los pasos anteriores para cada subarray de 2x2 obtenemos el siguiente gráfico:&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://docs.google.com/drawings/pub?id=12oJTkL9Z4fVjZRKflL5IJmAWw2baq2LuvZg-i9EYeig&amp;amp;w=472&amp;amp;h=452&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;307&quot; src=&quot;https://docs.google.com/drawings/pub?id=12oJTkL9Z4fVjZRKflL5IJmAWw2baq2LuvZg-i9EYeig&amp;amp;w=472&amp;amp;h=452&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
En el dibujo ya están las&amp;nbsp;posiciones&amp;nbsp;interpoladas de cada linea y un sombreado marcando el interior del&amp;nbsp;polígono&amp;nbsp;resultante. El interior del polígono se puede determinar fácilmente modificando la tabla de casos para mostrar cual es el interior en cada caso.&lt;/div&gt;
&lt;h3&gt;
Ejemplo interactivo&lt;/h3&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
La mejor forma de entender el&amp;nbsp;algoritmo&amp;nbsp;es utilizando un ejemplo interactivo y viendo el código. Hice un pequeño widget para poder experimentar. El ejemplo está realizado completamente en HTML5 usando webgl y javascript, por lo que es necesario un navegador moderno. El código fuente está disponible en &lt;a href=&quot;http://code.google.com/p/webgl-voxel-renderer/&quot;&gt;google code&lt;/a&gt;.&lt;/div&gt;
&lt;br /&gt;
Para usar el ejemplo solamente hay que asignar valores en la tabla. Se puede desactivar y activar el renderizado de voxels y de la isolinea.&lt;br /&gt;
&lt;br /&gt;
&lt;div id=&quot;v2dwidget&quot;&gt;
&lt;table&gt;
 &lt;/table&gt;
&lt;div id=&quot;controls&quot;&gt;
&lt;div&gt;
&lt;label for=&quot;voxel&quot;&gt;
     &lt;input checked=&quot;checked&quot; id=&quot;voxel&quot; type=&quot;checkbox&quot; /&gt;Mostrar Voxels&lt;/label&gt;
     &lt;label for=&quot;marching&quot;&gt;
     &lt;input checked=&quot;checked&quot; id=&quot;marching&quot; type=&quot;checkbox&quot; /&gt;Mostrar Isolinea&lt;/label&gt;&lt;/div&gt;
&lt;label for=&quot;isovalue&quot;&gt;Isovalue&lt;/label&gt;
  &lt;input id=&quot;isovalue&quot; max=&quot;100&quot; min=&quot;0&quot; step=&quot;10&quot; type=&quot;number&quot; value=&quot;50&quot; /&gt;

 &lt;/div&gt;
&lt;/div&gt;
&lt;br /&gt;
En el &amp;nbsp;próximo artículo voy a mostrar cómo se generaliza esta idea a un caso 3D con el algoritmo Marching Cubes.</description><link>http://ezequielpozzo.blogspot.com/2012/10/algoritmo-marching-squares.html</link><author>noreply@blogger.com (2b)</author><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5595960729038325799.post-2469671274206552660</guid><pubDate>Wed, 17 Oct 2012 21:57:00 +0000</pubDate><atom:updated>2012-10-17T15:03:23.731-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">gráficos</category><category domain="http://www.blogger.com/atom/ns#">videojuegos</category><category domain="http://www.blogger.com/atom/ns#">voxels</category><title>Introducción a Voxels</title><description>Una forma utilizada&amp;nbsp;frecuentemente&amp;nbsp;para representar objetos&amp;nbsp;tridimensionales&amp;nbsp;es mediante&amp;nbsp;triángulos&amp;nbsp;en un espacio&amp;nbsp;tridimensional. Esto es, conjuntos de vértices que forman&amp;nbsp;triángulos&amp;nbsp;en el espacio. Esta es la forma en que las&lt;i&gt;&lt;b&gt; placas de video actuales&lt;/b&gt;&lt;/i&gt; reciben los datos antes de representarlos en pantalla y por lo tanto es la forma en que se almacenan los polígonos que se utilizan en aplicaciones gráficas.&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://docs.google.com/drawings/pub?id=1FVKAR8Y8Gy5dLTJ658TRcXX-Vn4qu4I2jXBWAQgnIbQ&amp;amp;w=341&amp;amp;h=220&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;207&quot; src=&quot;https://docs.google.com/drawings/pub?id=1FVKAR8Y8Gy5dLTJ658TRcXX-Vn4qu4I2jXBWAQgnIbQ&amp;amp;w=341&amp;amp;h=220&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Un ortoedro representado mediante&amp;nbsp;triángulos. Representación gráfica y formulación matemática.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Una forma alternativa de almacenar información&amp;nbsp;tridimensional&amp;nbsp;es el uso de voxels. Así como un pixel es un elemento 2D que indica el estado (color) de un punto en una pantalla, un voxel es un elemento 3D que asocia un valor (o valores) a cada posición de un espacio tridimensional. El voxel puede contener información de &quot;cantidad&quot; de materia (0 a 1 con 0 = vacío y 1 = lleno)&amp;nbsp;así&amp;nbsp;como información mas compleja como tipo de material, textura, etc.&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://docs.google.com/drawings/pub?id=1hjwOF7Gaxm0OwDo2j7coA1n1g-_AGhFaG4_jCqt23GA&amp;amp;w=553&amp;amp;h=300&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;216&quot; src=&quot;https://docs.google.com/drawings/pub?id=1hjwOF7Gaxm0OwDo2j7coA1n1g-_AGhFaG4_jCqt23GA&amp;amp;w=553&amp;amp;h=300&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Representación gráfica y matemática de los voxels. Solo se muestran 4 voxels &quot;vacíos&quot; pero se supone que hay voxels vacios en todas las posiciones en que no hay voxels llenos.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
En la imagen se ve una representación de la figura de antes, pero ahora usando voxels. Así como antes se almacenaban&amp;nbsp;vértices&amp;nbsp;y uniones entre estos para formar&amp;nbsp;triángulos,&amp;nbsp;en la representación de voxels se asignan valores a los distintos puntos del espacio tridimensional. En la representación anterior se almacenaba información de la &lt;i&gt;&lt;b&gt;superficie &lt;/b&gt;&lt;/i&gt;de la figura, en la representación actual se almacena información del &lt;b&gt;&lt;i&gt;volumen&lt;/i&gt;&lt;/b&gt;.&lt;/div&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Esta representación es solo una forma de &lt;b&gt;&lt;i&gt;almacenar &lt;/i&gt;&lt;/b&gt;información. La ventaja de esta forma de representar la información es que permite editar el objeto representado simplemente agregando o removiendo voxels.&amp;nbsp;&lt;/div&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
En la figura de arriba se puede ver cada voxel como un cubo. Esta representación asume que un voxel solo puede tener el valor 0 o 1 (lleno/vacío), pero en general podríamos asignar &lt;i&gt;&lt;b&gt;cualquier valor&lt;/b&gt;&lt;/i&gt; entre 0 y 1 a cada voxel. Por otro lado, está el problema de convertir los voxels a un formato que pueda ser manejado por una placa de video (o sea, una serie de triángulos).&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h4&gt;
Formas de Representación&lt;/h4&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://docs.google.com/drawings/pub?id=11FkO3uqnYU8oiY1QVY_fLn-idv7XOuoppBo57au3RnA&amp;amp;w=769&amp;amp;h=364&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;188&quot; src=&quot;https://docs.google.com/drawings/pub?id=11FkO3uqnYU8oiY1QVY_fLn-idv7XOuoppBo57au3RnA&amp;amp;w=769&amp;amp;h=364&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Izquierda: alta densidad de voxels representados mediante cubos de colores.&lt;br /&gt;
Derecha: baja densidad de voxels con la misma representación. (fuente: Google Images)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Si uno se limita a reemplazar puntos por cubos de color (así&amp;nbsp;como en una imagen 2D se reemplazan puntos por rectángulos), el resultado es una imagen pixelada. Al igual que en una imagen 2D (pantalla o impresión) la calidad de la imagen depende de la &lt;b&gt;&lt;i&gt;densidad de pixels&lt;/i&gt;&lt;/b&gt;, la calidad de la representación 3D dependerá de la &lt;i&gt;&lt;b&gt;densidad de voxels&lt;/b&gt;&lt;/i&gt;. Es posible aumentar la resolución de voxels tanto como el hardware lo permita y obtener objetos fotorealistas. Esta forma de representar voxels no es eficiente para renderizado en tiempo real (por ejemplo un videojuego) si lo que se busca es fotorealismo ya que una alta densidad de voxels se traduce en una alta densidad de&amp;nbsp;vértices&amp;nbsp;incluso si evita renderizar las caras de los voxels que no son visibles por la cámara.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Una alternativa a lo anterior, para bajas densidades de voxels, es utilizar cubos con textura para reemplazar cada voxel. Esto permite darle una ambientación retro a un juego 3D, ver por ejemplo &lt;a href=&quot;https://minecraft.net/&quot;&gt;Minecraft&lt;/a&gt;.&lt;/div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://docs.google.com/drawings/pub?id=10_CYbmjWYqfVXYwynqdWoikwTn4wlZczhtAOIccyuDs&amp;amp;w=638&amp;amp;h=376&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;188&quot; src=&quot;https://docs.google.com/drawings/pub?id=10_CYbmjWYqfVXYwynqdWoikwTn4wlZczhtAOIccyuDs&amp;amp;w=638&amp;amp;h=376&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Mincraft renderiza voxels usando cubos texturados.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
Si lo que se busca es evitar formas &quot;pixeladas&quot; pero a la vez evitar usar altas densidades de voxels, hay que recurrir a algoritmos que utilicen el estado del voxel y el estado de sus vecinos para generar una superficie suave.&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;
En el próximo post voy a explicar el algoritmo de Marching Cubes usando un ejemplo 2D para simplificar la explicación.&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
</description><link>http://ezequielpozzo.blogspot.com/2012/10/introduccion-voxels.html</link><author>noreply@blogger.com (2b)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5595960729038325799.post-3545392112807726265</guid><pubDate>Thu, 01 Apr 2010 17:08:00 +0000</pubDate><atom:updated>2010-04-01T10:09:31.043-07:00</atom:updated><title></title><description>Se acerca el momento de desempolvar la Wii&lt;br /&gt;
&lt;br /&gt;
&lt;object  height=&quot;385&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://www.youtube.com/v/2bFhHkOnqYM&amp;hl=en_US&amp;fs=1&amp;&quot;&gt;&lt;/param&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;&gt;&lt;/param&gt;&lt;param name=&quot;allowscriptaccess&quot; value=&quot;always&quot;&gt;&lt;/param&gt;&lt;embed src=&quot;http://www.youtube.com/v/2bFhHkOnqYM&amp;hl=en_US&amp;fs=1&amp;&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; height=&quot;385&quot;&gt;&lt;/embed&gt;&lt;/object&gt;</description><link>http://ezequielpozzo.blogspot.com/2010/04/se-acerca-el-momento-de-desempolvar-la.html</link><author>noreply@blogger.com (2b)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5595960729038325799.post-8700003649263764717</guid><pubDate>Fri, 05 Mar 2010 23:58:00 +0000</pubDate><atom:updated>2010-03-05T18:07:20.183-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">c#</category><category domain="http://www.blogger.com/atom/ns#">json</category><category domain="http://www.blogger.com/atom/ns#">unity3d</category><title>Probando Unity3D</title><description>&lt;div style=&quot;text-align: justify;&quot;&gt;Estuve probando Unity3D como una forma de hacer juegos web. En el espíritu de &quot;dejar todo asentado&quot;, muestro y explico los resultados.&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;Después de implementar el &lt;a href=&quot;http://unity3d.com/support/resources/tutorials/3d-platform-game&quot;&gt;tutorial de plataformas&lt;/a&gt;, me largué a hacer un pequeño asset.&lt;/div&gt;&lt;br /&gt;
El desafío:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Hacerlo con scripts en C# (lenguaje que desconozco)&lt;/li&gt;
&lt;li&gt;Interactuar con algún servicio web, usando JSON como tecnología&lt;/li&gt;
&lt;/ul&gt;El resultado:&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://www.ezequielpozzo.com.ar/plataformas.html&quot;&gt;Lector de twitter en Unity3D&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;La primera impresión es que hacer scripts es extremadamente intuitivo. Si a alguien le interesa le puedo exportar el asset completo para que lo examinen en detalle.&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;Para comunicarme con Twitter, usé JSON. Existen muchos parsers JSON para C#, pero el que yo usé es &quot;LitJson&quot;. Usarlo es tan simple como hacer un drag and drop de todos los archivos .cs de la distribución al árbol del proyecto Unity.&lt;/div&gt;&lt;br /&gt;
Paso a explicar el script:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;prettyprint csh&quot;&gt;using UnityEngine;
using System.Collections;
using LitJson;

public class TwitterPlatform : MonoBehaviour {
 public Camera camera;
 public Transform player;
 public string queryString=&quot;pozzoeRand&quot;;
 private Transform messageBoard;
 private ParticleEmitter emitter;
 private string twitterText;
 private MeshRenderer meshRender;
 private bool active=false;
&lt;/pre&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;No hay mucho de decir. A diferencia de Unity+Javascript, todo nuestro código debe ser implementado en una clase hija de MonoBehaviour. El nombre de la clase debe ser igual al archivo donde se encuentra el script.&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;Gracias a la magia de Unity, todas las variables públicas de la clase, son puestas en el object Inspector de Unity y pueden ser configuradas como parámetros del script. Instalar un script en un objeto implica haber instanciado un objeto de la clase del script. Y cada objeto tiene su propia variable. Eso hace que uno pueda tener un &quot;query string&quot; distinto en cada lector Twitter.&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;La variables &quot;camera&quot; y &quot;player&quot; deben ser configuradas al momento de instanciar un objeto que hace uso de este script. La forma de configurarlas es simplemente arrastrar una cámara y un jugador al slot correspondiente en el inspector de objetos. La cámara y el jugador se usan para permitirle al script acomodar el mensaje de texto de modo tal que sea siempre visible por el jugador. &lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;El flag active indica si la plataforma está activada. El string twitterText es el texto a mostrar en el cartel de texto.&lt;/div&gt;&lt;br /&gt;
&lt;pre class=&quot;prettyprint csh&quot;&gt;void Start () {
  this.emitter=this.transform.Find(&quot;CommunicationEffect&quot;)
    .GetComponent&lt;particleemitter&gt;();
  this.messageBoard=this.transform.Find(&quot;TextRenderer&quot;);
  this.meshRender=this.messageBoard.GetComponent&lt;meshrenderer&gt;();
  this.meshRender.enabled=true;
  this.setDefaultText();
 }
&lt;/meshrenderer&gt;&lt;/particleemitter&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;La función Start es llamada automáticamente cuando se inicia la escena (el juego en este caso). En este script ajusto algunas variables privadas. Los detalles son mas evidentes al ver la estructura del objeto en si. El objeto donde está ubicado el script, tiene objetos hijos que se pueden acceder con la función Find. El objeto CommunicationEffect es un &quot;efecto especial&quot; que utiliza partículas para mostrar al usuario que está &quot;pasando algo&quot;. &lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;Por otro lado, el objeto &quot;TextRenderer&quot; es un cartel de texto que va a ser usado para mostrar el resultado de los queries al Api Twitter y también para mostrar el querystring antes que el jugador haya activado la plataforma.&lt;/div&gt;&lt;br /&gt;
&lt;pre class=&quot;prettyprint csh&quot;&gt;void Update () {
  this.meshRender.transform.forward = this.camera.transform.forward;
  if (this.active==false)
   return;

  this.meshRender.transform.position = 
  this.player.transform.position 
  + this.camera.transform.forward*20
  + this.camera.transform.up*8;
 }
&lt;/pre&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;La función update es llamada automáticamente en cada frame del juego. Lo primero que hace es  asegurarse que el cartel está paralelo al plano de la cámara, para que sea visto claramente.&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;Después, si la plataforma está desactivada (el jugador no ha pisado la plataforma), termina.&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;Si la plataforma está activada, la función update se asegura de que el cartel de texto esté puesto en un lugar de la plataforma que permita al jugador leer el texto. Las variables forward y up son vectores en las coordenadas locales del objeto (en este caso la cámara).&lt;/div&gt;&lt;br /&gt;
&lt;pre class=&quot;prettyprint csh&quot;&gt;void OnTriggerEnter(){
  this.emitter.Emit();
  StartCoroutine(this.readTwitter());
  this.messageBoard.GetComponent&lt;meshrenderer&gt;().transform.position=this.player.transform.position+this.camera.transform.forward*20+this.camera.transform.up*8;
  this.active=true;
 }
&lt;/meshrenderer&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;La función OnTriggerEnter es llamada cuando el engine detecta una colisión con el mesh del objeto en el que se encuentra el script. Mas adelante muestro dos funciones relacionadas.&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;La función Emit() es llamada sobre el emisor de partículas. Hace que se emita un chorro de partículas. Luego, se inicia una corrutina que es la que se encarga de interactuar con twitter. Una corrutina es una función que puede interrumpir su ejecución para luego resumir en el mismo punto. La función StartCoroutine, usada de esta forma, ejecuta la corrutina readTwitter en paralelo a la función update, y continua ejecutando la funcion update.&lt;/div&gt;&lt;br /&gt;
&lt;pre class=&quot;prettyprint csh&quot;&gt;IEnumerator readTwitter()
 {
  while (true) {
   string url=&quot;http://search.twitter.com/search.json?q=&quot;+this.queryString;
   this.twitterText=&quot;Loading&quot;;
   WWW tweets = new WWW (url); 
   
   int retries=0;
   bool  reload=false;
   while (!tweets.isDone){
    if (retries&amp;gt;20) {
     /*Will retry after trying 0.2*20 seconds*/
     this.twitterText=&quot;Loading&quot;;
     retries=0;
     reload=true;
     break;
    }
    this.twitterText+=&quot;.&quot;;
    yield return new WaitForSeconds(0.2f);
    retries++;
   }
   if (reload)
    continue;

&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Esta función es la que lee de Twitter. Recordemos que se está ejecutando en paralelo con el resto de las funciones. Y en particular, esta corrutina se está ejecutando en un loop infinito. &lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Lo que se hace es crear una petición al servidor de Twitter. Y después esperar hasta que Twitter responda. El problema es que no sabemos cuanto se puede demorar Twitter (con sus magnificos servers) en responder. Y aca es cuando entra la magia de las corrutinas. Cada &quot;yield&quot; es como un return, que devuelve la ejecución para que el engine sigua funcionando, con la particularidad de que al llamar la corrutina en el próximo frame la ejecución se continua en el yield, como si nunca se hubiera abandonado.&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;En particular se puede hacer otras cosas como yield return new WaitForSeconds(0.2f), hace que unity suspenda la ejecución de la corrutina y espere 0.2 segundos antes de continuar. De esta manera, en cada paso se le pregunta al objeto &quot;tweet&quot; si ya terminó de cargar todo lo que twitter le devolvió. Si no, se aumenta un contador y se espera 0.2 segundos antes de volver a preguntar. Si el server tarda mas de 4 segundos en responder, se vuelve a empezar desde el principio, o sea, se envía otro query (al menos es lo que teóricamente debería pasar, no lo probé, si alguien piensa lo contrario que hable).&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;En caso contrario, la corrutina continua su ejecución.&lt;/div&gt;&lt;br /&gt;
&lt;pre class=&quot;prettyprint csh&quot;&gt;JsonData queryResult=JsonMapper.ToObject(tweets.data);
   JsonData tweetList=queryResult[&quot;results&quot;];
   for (int i=0;i&amp;lt;tweetList.Count; i++)
   {
    this.messageReceived();
    this.twitterText=wrap((string)tweetList[i][&quot;text&quot;]);
    yield return new WaitForSeconds(4f);

   }

  } 
 }
&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Esta parte de la corrutina usa el parser de JSON para leer uno a uno los tweets. No hay mucho que sea especifico de Unity. Solo que nuevamente se espera 4 segundos entre tweet y tweet, para darle tiempo al jugador de que lea cada uno. &lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;Este lector de twitter es absolutamente precario e ineficiente. Lo ideal sería que se muestren los tweets de mas viejo a mas nuevo y luego se actualizen los nuevos tweets a medida que la gente los va publicando. Todo eso es posible, pero no tiene sentido perder mas tiempo en este script que es solo de prueba. &lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;La función MessageReceived simplemente activa el efecto del emisor de partículas. Se puede extender para que también reproduzca un sonido, por ejemplo.&lt;/div&gt;&lt;br /&gt;
&lt;pre class=&quot;prettyprint csh&quot;&gt;void OnTriggerStay() {
  this.messageBoard.GetComponent&lt;textmesh&gt;().text=this.twitterText;
  
 }
 void OnTriggerExit(){
  StopAllCoroutines();
  this.active=false;
  this.setDefaultText();
 }
&lt;/textmesh&gt;&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;La función OnTriggerStay es ejecutada una y otra vez mientras el jugador está en contacto con la plataforma. Lo único que hace es mantener actualizado el texto de la plataforma. Tranquilamente podría haberse hecho en la función Update, luego de comprobar que la plataforma se encuentra activa.&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;La función OnTriggerExit es ejecutada automáticamente cuando se abandona la plataforma. En este caso, se detiene la ejecución de todas las corrutinas &lt;b&gt;de este objeto&lt;/b&gt; y luego se vuelve a mostrar el texto con el querystring ubicado en el centro de la plataforma.&lt;/div&gt;&lt;br /&gt;
&lt;pre class=&quot;prettyprint csh&quot;&gt;void setDefaultText()
 {
  this.meshRender.transform.position=this.transform.position+this.meshRender.transform.up*1;
  this.messageBoard.GetComponent&lt;textmesh&gt;().text=this.queryString;
 }
 void messageReceived(){
  this.emitter.Emit();
 }
 string wrap(string text)
 {
  string newText=&quot;&quot;;
  text=text.PadRight(140);
  for (int i=0; i&amp;lt;text.Length-28; i+=28)
  {
   newText+= text.Substring(i, 28)+&quot;\n&quot;;
  }
  return newText;
 }
&lt;/textmesh&gt;&lt;/pre&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;Estas son funciones de ayuda. La única que no mencioné es wrap, que simplemente distribuye el mensaje en varias lineas.&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;Este script es incompleto. Sería adecuado considerar errores como que twitter no devuelva tweets, y probar algunos detalles del funcionamiento.&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;Pero esto no es nada mas que un proyecto descartable para probar algunas de las características de Unity.&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;La idea inicial era implementar el &lt;a href=&quot;http://ezequielpozzo.blogspot.com/2010/02/automatas-celulares-para-simulacion-de.html&quot;&gt;automata celular&lt;/a&gt; mostrado anteriormente en Unity. Pero no estoy seguro de que sea lo mejor la performance de los scripts para ese tipo usos. Lo ideal sería extender la funcionalidad de Unity usando C++, pero esa es una característica disponible solamente en la version Pro (i.e. paga).&lt;/div&gt;</description><link>http://ezequielpozzo.blogspot.com/2010/03/probando-unity3d_05.html</link><author>noreply@blogger.com (2b)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5595960729038325799.post-3180580960808762686</guid><pubDate>Fri, 12 Feb 2010 01:27:00 +0000</pubDate><atom:updated>2010-02-11T17:40:20.869-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">automata celular</category><category domain="http://www.blogger.com/atom/ns#">juegos</category><category domain="http://www.blogger.com/atom/ns#">videojuegos</category><title>Automatas Celulares para simulación de explosiones en juegos</title><description>Ayer me puse a implementar un Autómata Celular. El objetivo final es simular una explosión en tiempo real. Voy a mostrar unos resultados preliminares que obtuve con Python, pero primero explico la idea.&lt;br /&gt;
&lt;br /&gt;
En general, simular fenómenos físicos como explosiones, fluidos y fuego, en forma realista es demasiado costoso numéricamente como para que se pueda hacer en tiempo real en un videojuego. Sin embargo, hay una técnica que permite obtener aproximaciones visualmente plausibles utilizando los llamados Autómatas Celulares (AC).&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Autómatas Celulares&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Un Autómata Celular se puede pensar como una grilla cuyas celdas tienen asociado uno o varios valores. Cada celda tiene vecinos, generalmente 4. Al principio, se asignan valores iniciales, y luego se va haciendo evolucionar el AC siguiendo una regla matemática predefinida. Con este esquema general se pueden simular muchos fenómenos biológicos y físicos interesantes. Ver por ejemplo una &lt;a href=&quot;http://blog.soulwire.co.uk/flash/actionscript-3/2d-cellular-automata&quot;&gt;implementación en Flash&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Este concepto se puede generalizar mediante &lt;a href=&quot;http://en.wikipedia.org/wiki/Graph_dynamical_system&quot;&gt;grafos dinámicos&lt;/a&gt;. Un grafo es en conjunto de &lt;b&gt;Vértices&lt;/b&gt;, unidos mediante conexiones llamadas &lt;b&gt;Aristas&lt;/b&gt;. Tanto los Vértices como las Aristas pueden tener propiedades asignadas.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv1ox5xLeR0-1NuKJnwQy4O7MgqHghFUHxnyQhynvegsGJ-FKkgUdcrVa17Nb878fI2zefJCT_DhdGVf4MuboG50uSFKaOeWapq6vSUl2rvKEGCd_9gQ9u9OA11iSt1nZEEXdY1MxNvZE/s1600-h/TouchGraph.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv1ox5xLeR0-1NuKJnwQy4O7MgqHghFUHxnyQhynvegsGJ-FKkgUdcrVa17Nb878fI2zefJCT_DhdGVf4MuboG50uSFKaOeWapq6vSUl2rvKEGCd_9gQ9u9OA11iSt1nZEEXdY1MxNvZE/s320/TouchGraph.PNG&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;i&gt;Ejemplo de grafo: mis contactos de Facebook. Cada Vértice (círculos) es un amigo. Cada Arista (lineas) indica que mis contactos se tienen en sus respectivas listas de contactos. También hay propiedades asociadas: cada Arista tiene un tamaño dado por la actividad del contacto, una foto y un color que indica que el contacto pertenece a un &quot;cluster&quot;. Los lados tienen un numero relacionado que indica la cantidad de fotos en la que ambos aparecen juntos.&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: small;&quot;&gt;Usando ese concepto general, se puede generalizar un Autómata Celular&lt;/span&gt;. El ejemplo básico de una grilla 2D, se traslada a un grafo en que cada Vértice tiene una posición (lo que originalmente era una coordenada en la grilla) y cuatro Aristas que conectan al Vértice con sus vecinos.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj23Ymp_tkEwrVxrCTawGiI9y-ZT6vEN7-4H9I5XgY9bVG2BEORZrGfh1209KwVZEZcGsthquaVnj9nevV9m-d4Qnap-R_yx8N5iGs-NQoA17XPIfYejF-Bj_derSuxINIvUgCnXTpB4PM/s1600-h/exgrafo.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj23Ymp_tkEwrVxrCTawGiI9y-ZT6vEN7-4H9I5XgY9bVG2BEORZrGfh1209KwVZEZcGsthquaVnj9nevV9m-d4Qnap-R_yx8N5iGs-NQoA17XPIfYejF-Bj_derSuxINIvUgCnXTpB4PM/s320/exgrafo.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;Esquema básico de un grafo que reemplaza a una grilla. Cada vértice de las paredes está conectado con un vértice de la pared opuesta, para crear un &quot;mundo infinito&quot;. Todo lo que &quot;sale&quot; por la derecha &quot;entra&quot; por la izquierda.&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;Este esquema es mas general en varios sentidos. Primero, permite desacoplar fácilmente el espacio &quot;real&quot; de nuestro Autómata Celular, de esa forma ahora cada nodo del grafo puede estar en &lt;b&gt;posiciones arbitrarias &lt;/b&gt;del espacio y, en particular, ubicados en forma no uniforme. Se puede entonces simular con mas detalle la física en algunas partes que en otras. Solamente es cuestión de ser cuidadoso a la hora de crear nuestro algoritmo. Otra virtud que tiene este esquema es que se pueden generar &lt;b&gt;obstrucciones &lt;/b&gt;en el grafo para simular obstrucciones reales. Por ejemplo&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHsm65lBZeMW7l0oOgD_Q2ucR9frYaS36-fZHbXl1DtMoA1Liy1m57wxrhH9Pn3MeqYutq2vqJNVhCqor8AMHfMF-7FUu7cWAPW8Pt4xzepBpIAt21-1MhuAUzLhiEkbLKjzwxxYSJdXM/s1600-h/exgrafo-interrump.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHsm65lBZeMW7l0oOgD_Q2ucR9frYaS36-fZHbXl1DtMoA1Liy1m57wxrhH9Pn3MeqYutq2vqJNVhCqor8AMHfMF-7FUu7cWAPW8Pt4xzepBpIAt21-1MhuAUzLhiEkbLKjzwxxYSJdXM/s320/exgrafo-interrump.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;Simular obstrucciones es solo cuestión de romper Aristas o Vértices. La imagen muestra cómo se puede relacionar un grafo con un mapa de nuestro juego. En esquemas mas complicados se pueden mantener todos los Vértices y Aristas y asignar pesos a las Aristas. De esta forma se pueden simular distintos tipos de &quot;rigidez&quot; o incluso destrucción de paredes.&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;/div&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Simulando explosiones&lt;/span&gt; &lt;br /&gt;
&lt;br /&gt;
En este post explico como usar este esquema para simular explosiones plausibles en un juego, con &lt;b&gt;poco costo numérico&lt;/b&gt;. El primer código que uso es python puro, con fines demostrativos. Incluso con la lentitud de implementar un algoritmo que recorre nodos en python, es posible ejecutar en tiempo real una grilla de 30x30 que ya presenta resultados interesantes.&lt;br /&gt;
Para simular una explosión se necesita un grafo que tenga definida una presión en cada Vértice. En cada paso de tiempo, se mira Vértice por Vértice y se compara la presión del Vértice con la presión de sus vecinos. Luego se calcula un flujo de &quot;aire&quot; entre el Vértice y el vecino y se lo utiliza para modificar sus presiones.&lt;br /&gt;
El esquema es básico e intuitivo: El nodo que tiene mas presión le entrega un poco a cada uno de los vecinos que tienen menos. Y así, paso por paso, se genera una frente de presión.&lt;br /&gt;
&lt;br /&gt;
Voy a mostrar los resultados primero, y dejar el código comentado al final para quien quiera leerlo.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://www.ezequielpozzo.com.ar/img/explosion-pared.gif&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://www.ezequielpozzo.com.ar/img/explosion-pared.gif&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;i&gt;Explosión al lado de un obstáculo pequeño. Los tiempos son t=1, 4, 10, 20, 40, 80 y 200.&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://www.ezequielpozzo.com.ar/img/explosion-puerta.gif&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://www.ezequielpozzo.com.ar/img/explosion-puerta.gif&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;Explosión en una &quot;puerta&quot;. La linea marrón es una pared. La simulación es para tiempos t=1, 4, 8, 20, 100 y 200.&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Trasladando los efectos de la explosión al juego&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Supongamos que el objeto que nos interesa se encuentra cerca de la explosión. Podemos calcular el vector de flujo sumando los vectores de flujo entre un Vértice y sus vecinos. En el ejemplo de la imagen siguiente, el vector de flujo sobre el nodo en que se encuentra el personaje tiene dirección sur oeste. Ese flujo, multiplicado por algún valor que represente la sección de área del personaje, nos indica una &lt;b&gt;fuerza&lt;/b&gt; que actúa sobre el personaje.&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Es interesante notar que en una explosión,  todo sucede muy rápido en los primeros instantes de tiempo. En un juego  uno podría simular decenas de pasos de un CA por cada frame del juego y  utilizar un promedio en el tiempo del vector de flujo. Este promedio es el &lt;b&gt;impulso &lt;/b&gt;a transferir a  los objetos del juego en cada frame. De esta forma se puede simular el  efecto medio de la onda expansiva en los jugadores, objetos ubicados en el  mapa y en el mapa mismo.&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;También se puede sumar las magnitudes de flujo, para simular efectos como sordera momentanea, ruptura de vidrios, o daño general.&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnSl2PFUbfr9YdoOE6lvxQMPK0TGrq3PO8XdjwPR5FoDk2lkzvOBpwqUrs4Y5Ojs9hBlbvjFznnB0AAinjFm63ysZBDgX4FKwrJtr57HKDf7LkL4qP40jpLjGJuPmJwumnpEncAwTmEwA/s1600-h/exmegaexp.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnSl2PFUbfr9YdoOE6lvxQMPK0TGrq3PO8XdjwPR5FoDk2lkzvOBpwqUrs4Y5Ojs9hBlbvjFznnB0AAinjFm63ysZBDgX4FKwrJtr57HKDf7LkL4qP40jpLjGJuPmJwumnpEncAwTmEwA/s320/exmegaexp.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;i&gt;&amp;nbsp;Cómo trasladar el efecto de la onda expansiva al personaje: Las flechas azules son los vectores de flujo entre el nodo del personaje y sus vecinos. La flecha roja es la suma de los vectores azules, que multiplicadas por el cross-section del personaje dan la &lt;b&gt;fuerza &lt;/b&gt;que actúa sobre el personaje debido a la explosión. Promediando esta fuerza durante varios pasos del AC se obtiene el &lt;b&gt;impulso&lt;/b&gt; sobre el personaje.&lt;/i&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Código Python&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Este es el código de los dos ejemplos mostrados. El próximo paso es convertir el loop principal a código C y exponer funciones para calcular los impulsos transmitidos a objetos del mapa.&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;pre class=&quot;prettyprint&quot;&gt;def&amp;nbsp;iterate_fl(N=30,&amp;nbsp;steps=10):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;Automata&amp;nbsp;Celular&amp;nbsp;simple&amp;nbsp;para&amp;nbsp;calcular&amp;nbsp;una&amp;nbsp;explosión.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Implementación&amp;nbsp;de&amp;nbsp;algoritmo&amp;nbsp;de&amp;nbsp;Tom&amp;nbsp;Forsyth.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Mas&amp;nbsp;detalles:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;http://home.comcast.net/~tom_forsyth/papers/cellular_automata_for_physical_modelling.html&#39;&#39;&#39;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;from&amp;nbsp;itertools&amp;nbsp;import&amp;nbsp;product,&amp;nbsp;combinations
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;clamp(value,&amp;nbsp;max,&amp;nbsp;min):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Función&amp;nbsp;clamp,&amp;nbsp;devuelve&amp;nbsp;value&amp;nbsp;si&amp;nbsp;value&amp;nbsp;está&amp;nbsp;entre&amp;nbsp;min&amp;nbsp;y&amp;nbsp;max,&amp;nbsp;devuelve&amp;nbsp;min&amp;nbsp;o&amp;nbsp;max&amp;nbsp;si&amp;nbsp;no.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;value&amp;lt;min:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;min
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;value&amp;gt;max:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;max
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;value
&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;class&amp;nbsp;Node():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Decidí&amp;nbsp;usar&amp;nbsp;una&amp;nbsp;implementación&amp;nbsp;python&amp;nbsp;pura&amp;nbsp;para&amp;nbsp;las&amp;nbsp;pruebas&amp;nbsp;preliminares.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Este&amp;nbsp;modelo&amp;nbsp;permite&amp;nbsp;CA&amp;nbsp;en&amp;nbsp;3D&amp;nbsp;y&amp;nbsp;simular&amp;nbsp;paredes&amp;nbsp;u&amp;nbsp;objetos&amp;nbsp;inmoviles&amp;nbsp;mediante&amp;nbsp;desconexiones&amp;nbsp;de&amp;nbsp;nodos.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pressure=0.0
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new_pressure=0.0
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;t=0
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;__init__(self,&amp;nbsp;position):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.position=position
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;Los&amp;nbsp;grafos&amp;nbsp;tienen&amp;nbsp;un&amp;nbsp;conjunto&amp;nbsp;de&amp;nbsp;vecinos.&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.neighbours=set()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;__repr__(self):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;&#39;node&amp;nbsp;pressure:&amp;nbsp;%s&#39;%self.pressure
&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;Mantengo&amp;nbsp;un&amp;nbsp;diccionario&amp;nbsp;posicion-&amp;gt;nodo&amp;nbsp;para&amp;nbsp;poder&amp;nbsp;acceder&amp;nbsp;los&amp;nbsp;nodos&amp;nbsp;facilmente&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;nodes=dict([(position,Node(position))&amp;nbsp;for&amp;nbsp;position&amp;nbsp;in&amp;nbsp;product(range(N),range(N))])
&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;Cada&amp;nbsp;nodo&amp;nbsp;representa&amp;nbsp;una&amp;nbsp;celda&amp;nbsp;en&amp;nbsp;un&amp;nbsp;espacio&amp;nbsp;2D,&amp;nbsp;cuyo&amp;nbsp;vecino&amp;nbsp;es&amp;nbsp;el&amp;nbsp;vecino&amp;nbsp;físico&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;node&amp;nbsp;in&amp;nbsp;nodes.values():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;En&amp;nbsp;este&amp;nbsp;ejemplo&amp;nbsp;muestro&amp;nbsp;dos&amp;nbsp;tipos&amp;nbsp;de&amp;nbsp;condiciones&amp;nbsp;de&amp;nbsp;contorno&amp;nbsp;en&amp;nbsp;un&amp;nbsp;grafo:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;paredes&amp;nbsp;rígidas&amp;nbsp;y&amp;nbsp;condiciones&amp;nbsp;periodicas&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;node.position[0]+1&amp;lt;N-1:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;Hay&amp;nbsp;paredes&amp;nbsp;rigidas&amp;nbsp;a&amp;nbsp;la&amp;nbsp;derecha&amp;nbsp;del&amp;nbsp;mapa.&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;nodes[node.position].neighbours.add(nodes[(node.position[0]+1,&amp;nbsp;node.position[1])])
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;node.position[0]-1&amp;gt;0:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;Hay&amp;nbsp;paredes&amp;nbsp;rígidas&amp;nbsp;a&amp;nbsp;la&amp;nbsp;izquierda&amp;nbsp;del&amp;nbsp;mapa&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;nodes[node.position].neighbours.add(nodes[(node.position[0]-1,&amp;nbsp;node.position[1])])

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;La&amp;nbsp;funcion&amp;nbsp;mod&amp;nbsp;(%)&amp;nbsp;me&amp;nbsp;asegura&amp;nbsp;que&amp;nbsp;la&amp;nbsp;parte&amp;nbsp;superior&amp;nbsp;e&amp;nbsp;inferior&amp;nbsp;del&amp;nbsp;mapa&amp;nbsp;están
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conectadas.&amp;nbsp;Lo&amp;nbsp;que&amp;nbsp;sale&amp;nbsp;por&amp;nbsp;arriba,&amp;nbsp;aparece&amp;nbsp;por&amp;nbsp;abajo.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;nodes[node.position].neighbours.add(nodes[(node.position[0],(node.position[1]+1)%N)])
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;nodes[node.position].neighbours.add(nodes[(node.position[0],(node.position[1]-1)%N)])
&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;Crea&amp;nbsp;pared,&amp;nbsp;removiendo&amp;nbsp;nodos&amp;nbsp;del&amp;nbsp;grafo.&amp;nbsp;Se&amp;nbsp;pueden&amp;nbsp;hacer&amp;nbsp;paredes&amp;nbsp;removiendo&amp;nbsp;solamente&amp;nbsp;conexiones
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Tipos&amp;nbsp;de&amp;nbsp;pared:&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;wall=product((10,),range(0,15)+range(17,N))&amp;nbsp;#pared&amp;nbsp;que&amp;nbsp;cruza&amp;nbsp;el&amp;nbsp;mapa&amp;nbsp;en&amp;nbsp;x=10,&amp;nbsp;con&amp;nbsp;un&amp;nbsp;agujero
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#wall=product((10,),range(14,17))&amp;nbsp;#pared&amp;nbsp;de&amp;nbsp;tamaño&amp;nbsp;limitado&amp;nbsp;en&amp;nbsp;x=10
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;edg_pos&amp;nbsp;in&amp;nbsp;wall:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;node=nodes[edg_pos]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;neigh&amp;nbsp;in&amp;nbsp;node.neighbours:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;neigh.neighbours.remove(node)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;del&amp;nbsp;nodes[edg_pos]
&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;La&amp;nbsp;explosión&amp;nbsp;propiamente&amp;nbsp;dicha.&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#explosion=product(range(4,10),(5,15))&amp;nbsp;#&amp;nbsp;Un&amp;nbsp;rectangulo
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;explosion=((9,15),)&amp;nbsp;#Solo&amp;nbsp;un&amp;nbsp;punto
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;node&amp;nbsp;in&amp;nbsp;explosion:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;nodes[node].pressure=300.0&amp;nbsp;#La&amp;nbsp;explosion&amp;nbsp;son&amp;nbsp;puntos&amp;nbsp;de&amp;nbsp;alta&amp;nbsp;presion
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;nodes[node].new_pressure=300.0&amp;nbsp;#Este&amp;nbsp;es&amp;nbsp;un&amp;nbsp;detalle&amp;nbsp;del&amp;nbsp;algoritmo
&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;Ejecutar&amp;nbsp;CA&#39;&#39;&#39;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;i=0
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;i&amp;lt;steps:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;i=i+1
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;Algoritmo&amp;nbsp;CA&amp;nbsp;para&amp;nbsp;explosiones&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;node&amp;nbsp;in&amp;nbsp;nodes.values():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Recorremos&amp;nbsp;todo&amp;nbsp;el&amp;nbsp;grafo
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;node.t!=i:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;Detalle&amp;nbsp;de&amp;nbsp;implementación:&amp;nbsp;iteramos&amp;nbsp;por&amp;nbsp;todo&amp;nbsp;el&amp;nbsp;grafo&amp;nbsp;aplicando&amp;nbsp;las&amp;nbsp;reglas&amp;nbsp;del&amp;nbsp;CA,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;los&amp;nbsp;calculos&amp;nbsp;se&amp;nbsp;realizan&amp;nbsp;con&amp;nbsp;el&amp;nbsp;estado&amp;nbsp;actual&amp;nbsp;del&amp;nbsp;grafo&amp;nbsp;y&amp;nbsp;se&amp;nbsp;almacenan&amp;nbsp;los&amp;nbsp;resultados&amp;nbsp;en
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new_pressure,&amp;nbsp;en&amp;nbsp;el&amp;nbsp;siguiente&amp;nbsp;paso&amp;nbsp;de&amp;nbsp;tiempo,&amp;nbsp;se&amp;nbsp;actualizan&amp;nbsp;los&amp;nbsp;valores&amp;nbsp;de&amp;nbsp;la&amp;nbsp;presion&amp;nbsp;usando
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;los&amp;nbsp;valores&amp;nbsp;calculados&amp;nbsp;en&amp;nbsp;el&amp;nbsp;paso&amp;nbsp;anterior.&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;node.t=i
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;node.pressure=node.new_pressure
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;neight&amp;nbsp;in&amp;nbsp;node.neighbours:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;Para&amp;nbsp;cada&amp;nbsp;nodo,&amp;nbsp;se&amp;nbsp;miran&amp;nbsp;los&amp;nbsp;vecinos.&amp;nbsp;Este&amp;nbsp;ejemplo&amp;nbsp;es&amp;nbsp;2D,&amp;nbsp;pero&amp;nbsp;se&amp;nbsp;puede&amp;nbsp;hacer
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;3D.&amp;nbsp;En&amp;nbsp;2D&amp;nbsp;tenemos&amp;nbsp;4&amp;nbsp;vecinos&amp;nbsp;por&amp;nbsp;cada&amp;nbsp;posicion&amp;nbsp;del&amp;nbsp;espacio,&amp;nbsp;pero&amp;nbsp;puede&amp;nbsp;haber&amp;nbsp;menos
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;vecinos&amp;nbsp;si&amp;nbsp;hay&amp;nbsp;una&amp;nbsp;obstrucción.&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;neight.t!=&amp;nbsp;i:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;Nos&amp;nbsp;aseguramos&amp;nbsp;de&amp;nbsp;estar&amp;nbsp;usando&amp;nbsp;los&amp;nbsp;valores&amp;nbsp;de&amp;nbsp;presion
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;calculados&amp;nbsp;en&amp;nbsp;el&amp;nbsp;paso&amp;nbsp;anterior&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;neight.t=i
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;neight.pressure=neight.new_pressure
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;Se&amp;nbsp;calcula&amp;nbsp;la&amp;nbsp;dif.&amp;nbsp;de&amp;nbsp;presion&amp;nbsp;entre&amp;nbsp;el&amp;nbsp;nodo&amp;nbsp;actual&amp;nbsp;y&amp;nbsp;su&amp;nbsp;vecino&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dpres=node.pressure-neight.pressure
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&#39;&#39;&#39;Calculamos&amp;nbsp;el&amp;nbsp;flujo&amp;nbsp;como&amp;nbsp;una&amp;nbsp;fracción&amp;nbsp;de&amp;nbsp;la&amp;nbsp;diferencia&amp;nbsp;de&amp;nbsp;presion.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0.12&amp;nbsp;es&amp;nbsp;un&amp;nbsp;numero&amp;nbsp;magico&amp;nbsp;que&amp;nbsp;indica&amp;nbsp;la&amp;nbsp;fluidez&amp;nbsp;del&amp;nbsp;medio.&amp;nbsp;Un&amp;nbsp;valor&amp;nbsp;muy&amp;nbsp;chico
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;hace&amp;nbsp;que&amp;nbsp;la&amp;nbsp;presion&amp;nbsp;se&amp;nbsp;propague&amp;nbsp;mas&amp;nbsp;lentamente.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Usamos&amp;nbsp;la&amp;nbsp;funcion&amp;nbsp;clamp&amp;nbsp;para&amp;nbsp;evitar&amp;nbsp;valores&amp;nbsp;negativos&amp;nbsp;en&amp;nbsp;el&amp;nbsp;nodo.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Se&amp;nbsp;hace&amp;nbsp;que&amp;nbsp;el&amp;nbsp;flujo&amp;nbsp;no&amp;nbsp;produsca&amp;nbsp;una&amp;nbsp;presión&amp;nbsp;negativa&#39;&#39;&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;flow=clamp(0.12*dpres,&amp;nbsp;node.pressure/4.0,&amp;nbsp;-neight.pressure/4.0)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#Actualizamos&amp;nbsp;la&amp;nbsp;presion&amp;nbsp;(usando&amp;nbsp;new_pressure&amp;nbsp;en&amp;nbsp;lugar&amp;nbsp;de&amp;nbsp;pressure)&amp;nbsp;substrayendo
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#presion&amp;nbsp;del&amp;nbsp;nodo&amp;nbsp;actual&amp;nbsp;y&amp;nbsp;agregandola&amp;nbsp;a&amp;nbsp;su&amp;nbsp;vecino.&amp;nbsp;Un&amp;nbsp;flujo&amp;nbsp;negativo&amp;nbsp;implica
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#que&amp;nbsp;la&amp;nbsp;presion&amp;nbsp;del&amp;nbsp;nodo&amp;nbsp;actual&amp;nbsp;aumenta&amp;nbsp;y&amp;nbsp;la&amp;nbsp;del&amp;nbsp;vecino&amp;nbsp;disminuye.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;node.new_pressure&amp;nbsp;-=&amp;nbsp;flow
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;neight.new_pressure&amp;nbsp;+=&amp;nbsp;flow
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;nodes&lt;/pre&gt;</description><link>http://ezequielpozzo.blogspot.com/2010/02/automatas-celulares-para-simulacion-de.html</link><author>noreply@blogger.com (2b)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv1ox5xLeR0-1NuKJnwQy4O7MgqHghFUHxnyQhynvegsGJ-FKkgUdcrVa17Nb878fI2zefJCT_DhdGVf4MuboG50uSFKaOeWapq6vSUl2rvKEGCd_9gQ9u9OA11iSt1nZEEXdY1MxNvZE/s72-c/TouchGraph.PNG" height="72" width="72"/><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5595960729038325799.post-3511989670111276089</guid><pubDate>Mon, 01 Feb 2010 22:03:00 +0000</pubDate><atom:updated>2010-02-01T14:03:15.475-08:00</atom:updated><title>Instalando Django a la par de un sitio PHP en Apache2 Ubuntu</title><description>Estoy comenzando a portear el sitio www.cruzandoelsuquia.com.ar, &lt;a href=&quot;http://ezequielpozzo.blogspot.com/2010/01/creando-un-directorio-joomla-en-un-par.html&quot;&gt;hecho en Joomla&lt;/a&gt;, a Django. Para eso necesito instalar Django en mi server de desarrollo (Una instalación básica de Ubuntu 9.10).&lt;br /&gt;
&lt;br /&gt;
El objetivo es mantener la instalación actual del sitio Joomla, http:// localhost:80/ , y agregar una instalación de Django en el puerto 8000.&lt;br /&gt;
&lt;br /&gt;
Los siguientes pasos son triviales, pero los dejo asentados para cualquiera que le pueda interesar. &lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Paso 1 - Instalar los paquetes &lt;/span&gt;&lt;br /&gt;
Instalar los paquetes de django, python para apache2 y los paquetes para acceder a mysql:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;sudo apt get install libapache2-mod-python python-django python-mysqldb&lt;/div&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Paso 2 - La configuración de Apache2&lt;/span&gt;&lt;br /&gt;
Suponiendo que el sitio de django se encuentra en la carpeta /home/usuario/django/sitio, debemos agregar el siguiente archivo:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;prettyprint&quot;&gt;# /etc/apache2/sites-available/django&lt;br /&gt;
&amp;nbsp;MaxRequestsPerChild 1&lt;br /&gt;
&amp;lt;VirtualHost *:8000&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;DocumentRoot /home/guillermo/django/cruzandoelsuquiaDJ/&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;lt;Location &quot;/&quot;&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SetHandler python-program&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PythonHandler django.core.handlers.modpython&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SetEnv DJANGO_SETTINGS_MODULE cruzandoelsuquiaDJ.settings&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PythonDebug On&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;PythonPath &quot;[&#39;/home/guillermo/django&#39;] + sys.path&quot;&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;lt;/Location&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;lt;Location &quot;/media&quot;&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SetHandler None&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;lt;/Location&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;lt;Location &quot;/feincms_media&quot;&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SetHandler None&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;lt;/Location&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;lt;LocationMatch &quot;\.(jpg|gif|png)$&quot;&amp;gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SetHandler None&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;lt;/LocationMatch&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Luego, para activar este host virtual, es necesario agregar el sitio a la lista de sitios activados:&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;cd /etc/apache2/sites-enabled/&lt;/div&gt;&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;ln -s ../sites-available/django 001-django&lt;/div&gt;&lt;br /&gt;
En mi caso particular, tengo el sitio predeterminado 000-default, que es mi instalación de Joomla en el pueto standard 80.&lt;br /&gt;
&lt;br /&gt;
Paso 3 - Activar los puertos&lt;br /&gt;
&lt;br /&gt;
Agregar lo marcado en rojo en el archivo /etc/apache2/ports.conf&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;prettyprint&quot;&gt;# /etc/apache2/ports.conf&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: black;&quot;&gt;NameVirtualHost *:80&lt;/span&gt;&lt;br /&gt;
&lt;div style=&quot;color: black;&quot;&gt;&amp;nbsp;&amp;nbsp;Listen 80&lt;/div&gt;&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&lt;span style=&quot;color: red;&quot;&gt;NameVirtualHost *:8000&lt;/span&gt;&lt;br /&gt;
&lt;div style=&quot;color: red;&quot;&gt;&amp;nbsp;&amp;nbsp;Listen 8000&lt;/div&gt;...&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
Paso 4 - Reiniciar Apache &lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;cd /etc/init.d&lt;/div&gt;&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;sudo ./apache2 restart&lt;/div&gt;&lt;br /&gt;
Paso 5 - Servir los media del admin de django a travez de Apache&lt;br /&gt;
&lt;br /&gt;
Hay varias opciones, una es linkear la carpeta &quot;media&quot; de la instalacion de django, para tener los css e imagenes del backend:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;cd /home/usuario/sitio&lt;/div&gt;&lt;div style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;ln -s /var/lib/python-support/python2.6/django/contrib/admin/media media&lt;/div&gt;&lt;br /&gt;
Esto es una solución provisoria, la solución final es usar la carpeta admin_media, previamente configurada en settings.py.&lt;br /&gt;
&lt;br /&gt;
Espero que esto le sirva a alguien. A mi me hubiera servido.</description><link>http://ezequielpozzo.blogspot.com/2010/02/instalando-django-la-par-de-un-sitio.html</link><author>noreply@blogger.com (2b)</author><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5595960729038325799.post-7966035992787822651</guid><pubDate>Wed, 20 Jan 2010 20:20:00 +0000</pubDate><atom:updated>2010-01-20T12:20:27.150-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Joomla</category><category domain="http://www.blogger.com/atom/ns#">MVC</category><title>Creando un directorio joomla en un par de semanas</title><description>&lt;div style=&quot;text-align: justify;&quot;&gt;A veces uno comienza un proyecto que no es necesariamente divertido, o intelectualmente recompensante, sino que es necesario realizar por razones prácticas.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Tal es el caso de un proyecto al que me dediqué las ultimas semanas. La idea es simple y ya recorrida: un &lt;a href=&quot;http://www.cruzandoelsuquia.com.ar/&quot;&gt;directorio de locales comerciales&lt;/a&gt; (si, ya se, soy un SEO whore). La diferencia con otros directorios es que este solo tiene el modesto objetivo de contener la información de todos los locales comerciales del barrio general paz y barrios aledaños. Digo modesto porque no pretende ser un directorio a nivel pais y mucho menos mundial. Pero es un sitio que se me hace necesario (y supongo que a otras personas del barrio).&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Relevar un barrio no es solo un problema tecnológico, implica también caminar por el barrio y recolectar la información necesaria. Pretender que un 80% del barrio se abalance a mi página a cargar su local (y que lo hagan correctamente) es fantasioso. Pero este es un tema que no voy a tratar en este post.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;La razón de este post es explicar la &quot;tecnología&quot; detrás del sitio y la razón para usarla.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Como este no es una novela de suspenso, voy a empezar revelando el misterio.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Tecnología usada en el sitio&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;La solución es mas bien &quot;enlatada&quot; y consiste en:&lt;br /&gt;
&lt;/div&gt;&lt;ul style=&quot;text-align: justify;&quot;&gt;&lt;li&gt;El &lt;a href=&quot;http://www.joomlaspanish.org/&quot;&gt;sistema gestor de contenidos&lt;/a&gt; Joomla. &lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Un &lt;a href=&quot;http://www.sigsiu.net/sobi2.html&quot;&gt;componente de directorio comercial para Joomla&lt;/a&gt; (sitio en ingles) llamado SOBI2.&lt;/li&gt;
&lt;li&gt;Un &lt;a href=&quot;http://www.joomprod.com/&quot;&gt;componente de avisos clasificados&lt;/a&gt; (sitio en ingles) llamado AdsManager.&lt;/li&gt;
&lt;li&gt;Un &lt;a href=&quot;http://www.joomla.royy.net/&quot;&gt;componente de tags&lt;/a&gt; (sitio en ingles) integrable con los componentes anteriores.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://code.google.com/apis/maps/&quot;&gt;Google maps&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;Una herramienta útil para hacer &lt;a href=&quot;http://joomlapack.net/&quot;&gt;backups y deployments en Joomla&lt;/a&gt; llamada JoomlaPack.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Y algunas herramientas de SEO básicas para Joomla: shSEF404 para generar urls search-engine-friendly y un componente para generar sitemaps que sea integrable con SOBI2 (xmaps).&lt;/li&gt;
&lt;/ul&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Con estas partes pude cumplir el objetivo de hacer lo que podríamos llamar &quot;un bosquejo&quot; del sitio que me propuse. Esto en solo unas 3 semanas, utilizando tiempo libre.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;¿Por qué Joomla?&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Antes que nada, tengo que aclarar algo que mucha gente que no usa Joomla piensa: Joomla no es solamente un sistema gestor de contenidos, también tiene un framework PHP que permite programar cualquier funcionalidad en forma de nuevos componentes, módulos y plugins para Joomla, usando la &lt;a href=&quot;http://ezequielpozzo.blogspot.com/search/label/MVC&quot;&gt;filosofía MVC&lt;/a&gt;. Una vez aclarado esto, no es esta la razón por la que usé Joomla. La razón para usar Joomla es que quería obtener un producto funcionando en el menor tiempo posible.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;El sitio necesitaba tener un gestor de usuarios, de categorías, de locales comerciales, de tags. Y a la vez tener un nivel básico de seguridad.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Una vez instalados los componentes, módulos y plugins, solo me restó crear un template, o sea la parte visual del sitio. Todavía hay partes incompletas, y las partes completas fueron diseñadas por mi, con lo cual hay mucho que desear. Creé un template yo mismo para que sea lo mas simple posible.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;El resto del tiempo dedicado al proyecto fue en parte configuración de los componentes para que funciones entre ellos (por ejemplo, que el sitemap muestre las categorias y avisos del componente de directorio comercial, o que las busquedas devuelvan locales, o que los tags cambien según el contexto de la categoría de SOBI2 que el usuario está mirando, etc).&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Pero el mayor consumo de tiempo vino de pulir la parte visual de los componentes. Y esto nos lleva a los problemas de Joomla (o mas bien de sus componentes).&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Los problemas de Joomla&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;En principio, el framework PHP que provee Joomla permite utilizar el paradigma MVC de diseño. Con lo cual adaptar los componentes visualmente para un sitio propio debería ser tan simple como crear un view. El problema es que Joomla &lt;b&gt;no obliga&lt;/b&gt; al programador a usar MVC y me atrevería a decir que la mayor parte de los componentes de Joomla no están programados usando dicha filosofía. La razón principal, me imagino, es que muchos componentes fueron creado para versiones viejas de Joomla que no poseían herramientas para desarrollar en MVC y luego adaptado para nuevas versiones.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Uno de los puntos que me llevó mas tiempo fue unificar el componente de directorio comercial y el componente de avisos clasificados para que tuvieran una estructura html similar y que utilizaran las mismas hojas CSS (esto para unificar ambas partes visualmente y para disminuir el numero de hojas de estilo descargadas por el usuario).&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Por alguna razón que desconozco, ambos componentes utilizan tablas para posicionar las categorías y subcategorías. Pero en el caso de SOBI2 la situación fue especialmente odiosa, con las instrucciones que corresponderían al view &quot;desparramadas&quot; por varios archivos, entre queries sql y llamadas a funciones.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Básicamente tuve que hackear a travez del componente para cambiar lineas del estilo &quot;echo &#39;&#39;&quot;, incrustadas entre llamadas a la base de dato, a funciones de otros archivos e includes.... Y esto en múltiples lugares, ya que al no abstraer el view hay una repetición de código según el contexto en que se muestran los distintos elementos. Todavía se puede ver en el código fuente del sitio, vestigios de tablas que no sé en que momento son mostradas.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Por otro lado, varios queries SQL son impenetrables: JOINS muútiples concatenados, múltiples niveles de SELECT, etc. Lo cual me llevó a preguntarme: ¿No hay una abstracción de la base de datos en el framework Joomla? En general los frameworks modernos proveen algún tipo de abstracción, por ejemplo Rails y Turbogears permiten al usuario programar usando objetos, los cuales son mapeados en forma semiautomatica a tablas en la base de datos. Entonces, en lugar de tener que escribir un query sql que use JOINS entre tablas puedo manejarme con objetos, con los JOINS abstraidos convenientemente como una composición de objetos.&amp;nbsp; Probablemente Joomla tenga dicha abstracción, pero los programadores de SOBI2 no la hayan usado.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;En principio podría haber usado otro componente en lugar de SOBI2. Pero según lei en internet SOBI2 es &quot;el&quot; componente de directorios para Joomla. Y claramente, desde el punto de vista del usuario que no va a modificar el código del componente, SOBI2 es una excelente herramienta, altamente configurable.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Esto es un problema principalmente de SOBI2, no de Joomla. Es interesante comparar, por ejemplo, con el poco trabajo que tuve cuando quise adaptar el componente nativo de busqueda de Joomla para mostrar los resultados en un Google Map. En este caso solo fue cuestión de buscar el modelo, insertar código para sacar de la base de datos la información geográfica de los resultados de busqueda y luego buscar el view e insertar el código para mostrar dicha información en un mapa. En un rato ya tenía el problema de los mapas solucionado.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;A pesar de todos estos inconvenientes, es claro que la opción de programar el componente o el sitio yo mismo hubiera sido mucho mas costoso en tiempo. &lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Seguridad&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt; &lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Desconozco si existe algún tipo de agujero de seguridad en mi sitio. Dejo como tarea al lector tratar de hackear mi sitio. Todo se encuentra adecuadamente backupeado y listo para ser restaurado ante cualquier eventualidad. Solamente le pido que tenga en consideración describir la técnica usada en un comment de este blog. &lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;El sitio es tan fuerte como el mas débil de sus componentes. Y teniendo los componentes distintos orígenes (y habiendo visto el código fuente de alguno de ellos), no me extrañaría que exista alguna vulnerabilidad.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Conclusión&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Utilizar las herramientas que mencioné es una buena opción porque me permitió tener un sitio funcionando con poco tiempo de programación. Esto me permite empezar a dedicarme a promocionar el sitio y encontrar el set de funcionalidades óptimo para los posibles clientes.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Por otro lado, los cambios que tuve que hacer en los componentes hacen que se me haga imposible aplicar las actualizaciones oficiales sin romper mis cambios. Con esto en mente, es posible que en un futuro, cuando solucione o encamine el tema del &quot;Business model&quot; , se haga conveniente reimplementar el sitio desde 0 usando algún framework web. Hasta entonces, esta solución es adecuada.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Sería interesante saber si alguien conoce una mejor solución.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;</description><link>http://ezequielpozzo.blogspot.com/2010/01/creando-un-directorio-joomla-en-un-par.html</link><author>noreply@blogger.com (2b)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5595960729038325799.post-2278459705175059418</guid><pubDate>Wed, 09 Dec 2009 02:58:00 +0000</pubDate><atom:updated>2010-01-18T10:12:50.609-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Física</category><category domain="http://www.blogger.com/atom/ns#">MVC</category><category domain="http://www.blogger.com/atom/ns#">videojuegos</category><title>Un Modelo básico: Integrando ecuaciones de movimiento con el método de Euler</title><description>&lt;div style=&quot;text-align: justify;&quot;&gt;En los anteriores posts, expliqué las &lt;a href=&quot;http://ezequielpozzo.blogspot.com/2009/11/mvc-en-la-creacion-de-videojuegos.html&quot;&gt;razones para usar MVC&lt;/a&gt; y la &lt;a href=&quot;http://ezequielpozzo.blogspot.com/2009/12/mvc-en-la-creacion-de-videojuegos-ii.html&quot;&gt;estructura general&lt;/a&gt; de un programa simple usando este patrón de diseño. Ahora voy a explicar la versión mas básica de un modelo: un simulador de partículas. Bajo ninguna circunstancia este modelo es un modelo aplicable directamente para un juego, pero es una primer iteración para poder implementar algunas cosas básicas como Steering Behaviors (que voy a explicar en otro post).&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Este modelo considera partículas puntuales que se mueven libremente o bajo la acción de una fuerza, en un espacio de 2 dimensiones. Con este modelo simple se pueden implementar muchos tipos de juegos 2D: Juegos de plataforma, Arcanoid, Galaga, juegos de carrera o los juegos que sugirió &lt;a href=&quot;http://lavidasegunjuanpi.blogspot.com/&quot;&gt;Juan Pablo&lt;/a&gt;: &lt;a href=&quot;http://en.wikipedia.org/wiki/Desert_Strike_Advance&quot;&gt;Choplifter&lt;/a&gt;, &lt;a href=&quot;http://en.wikipedia.org/wiki/Desert_Strike_Advance&quot;&gt;Desert strike&lt;/a&gt; y &lt;a href=&quot;http://en.wikipedia.org/wiki/Cannon_Fodder&quot;&gt;Cannon Fodder&lt;/a&gt;. Que el Modelo sea 2D no implica usar gráficos 2D (el View), incluso se pueden usar gráficos 3D. Ejemplos clásicos de esto es el Wolfenstein 3D, donde en realidad el mapa es 2D, no existe la altura en el modelo, y sin embargo la representación es 3D. Un juego como Diablo, también tiene gráficos 3D siendo el modelo 2D. Con esto quiero aclarar que tener un modelo 2D no implica para nada utilizar gráficos 2D.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Derivadas&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Antes que nada voy a explicar muy básicamente lo que es una derivada. Se puede encontrar una &lt;a href=&quot;http://es.wikipedia.org/wiki/Derivada&quot;&gt;definición mas avanzada&lt;/a&gt; en la Wikipedia.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;La derivada de la posición respecto al tiempo es el cociente entre la variación de la posición y la variación de tiempo. Si un auto avanza 10 km en 1 hora, podemos &lt;b&gt;aproximar &lt;/b&gt;la derivada como 10km/1h=10km/h.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Pero la derivada es más que eso, la derivada implica que la variación en el tiempo sea muy pequeña. En nuestro caso, tendríamos que ver cuanto avanzó el auto en, por ejemplo, un segundo. Si el auto avanzó 3 metros en un segundo, entonces la derivada de la posición respecto al tiempo es &lt;b&gt;más precisamente &lt;/b&gt;3 m/1s=3m/s=10.8 m/s.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Se puede refinar el experimento, midiendo el desplazamiento para intervalos de tiempo aun mas pequeños. La derivada es el valor que obtenemos cuando el intervalo de tiempo es infinitamente pequeño:&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqws-jFQOdZCCyTDC6KKoZnhYx3OLcrW5edi3kytcrFyLjhkgMY336k3kdZ1szuvDRvGA2otL_xcomeHG-KvRePksbRmi8kkGURhnO3HA5br5ka9zkQwFDyv7LqXJgZiFJrQpe3hVweGI/s1600-h/derivativePosition.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqws-jFQOdZCCyTDC6KKoZnhYx3OLcrW5edi3kytcrFyLjhkgMY336k3kdZ1szuvDRvGA2otL_xcomeHG-KvRePksbRmi8kkGURhnO3HA5br5ka9zkQwFDyv7LqXJgZiFJrQpe3hVweGI/s320/derivativePosition.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;En esta ecuación el triángulo es una forma de escribir &quot;La variación de&quot;. O sea que la ecuación puede ser leída como &quot;La variación de la posición, dividido la variación del tiempo&quot;. Cuando la variación del tiempo es infinitamente pequeña, el cociente se llama derivada. El caso de la derivada de la posición respecto al tiempo,&amp;nbsp; es denominada &quot;la velocidad&quot;. Pero uno puede derivar la velocidad, o sea, dividir un cambio de velocidad por el intervalo de tiempo en que ocurre este cambio. Esta última derivada se llama &quot;aceleración&quot;.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Cuando derivamos dos veces, se suele decir que obtenemos &quot;la derivada segunda&quot;. Entonces, la derivada segunda de la posición es la aceleración.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Una forma de escribir la derivada es poniendo un punto sobre la variable que se deriva. Y la derivada segunda se escribe con dos puntos sobre la variable que se deriva. Pero el significado es el mismo: el cociente entre la variación de una magnitud y la variación de tiempo durante el cual ocurre el cambio.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Ecuaciones de movimiento&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Supongamos que tenemos un objeto que se mueve con una fuerza F&lt;span style=&quot;font-size: xx-small;&quot;&gt;X&lt;/span&gt;, F&lt;span style=&quot;font-size: xx-small;&quot;&gt;Y&lt;/span&gt; aplicada. La ecuación de movimiento mas general que se puede escribir es:&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJlkz6YUZfIhN3OWNNVn9EhVetbRrdgD7bPpGs3b1eZkWo3cxXxzjZdlClJBv_R5tP92GEXH68IURt4j1XdsBeP_vpeEH7WXsvenkWTFhb_sT7wwCeixyhi-5r08Q5zzeGSPSqD4GZLr8/s1600-h/forceEquation.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJlkz6YUZfIhN3OWNNVn9EhVetbRrdgD7bPpGs3b1eZkWo3cxXxzjZdlClJBv_R5tP92GEXH68IURt4j1XdsBeP_vpeEH7WXsvenkWTFhb_sT7wwCeixyhi-5r08Q5zzeGSPSqD4GZLr8/s320/forceEquation.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;Donde m es la masa del objeto. X e Y son las coordenadas de la partícula,&amp;nbsp; y los dos puntos sobre la variable X es la derivada segunda de la posición respecto al tiempo (o sea, derivar la posición dos veces respecto al tiempo). Como dijimos anteriormente, esta derivada segunda se conoce como &lt;b&gt;aceleración&lt;/b&gt;. &quot;Fuerza igual masa por aceleración&quot; es la llamada &lt;b&gt;segunda ley de Newton&lt;/b&gt;.&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;Esta fórmula debe ser resuelta para obtener ecuaciones de movimiento que podamos usar. En el caso de una &lt;b&gt;fuerza constante&lt;/b&gt;, las ecuaciones pueden ser resueltas en forma exacta:&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;&amp;nbsp;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9_iLdgmKPMiYpNhQ434m6AzUAbR7k1yalKoWApzb6qdwkdsmb6XSH0LdQ_cU2MJ5XFEnXd61eVZtXVk73yqv9MQHTzalig7P2HzfK26BDcWd96xoBl03oHYn_8AXKG5O4Fly2oBw_clM/s1600-h/motion_equations.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9_iLdgmKPMiYpNhQ434m6AzUAbR7k1yalKoWApzb6qdwkdsmb6XSH0LdQ_cU2MJ5XFEnXd61eVZtXVk73yqv9MQHTzalig7P2HzfK26BDcWd96xoBl03oHYn_8AXKG5O4Fly2oBw_clM/s320/motion_equations.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;Para quien sepa derivadas, es fácil ver que si derivamos la expresión de X dos veces, obtenemos la segunda ley de Newton con una fuerza constante (a0 es la fuerza dividida la masa).&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;Estas ecuaciones consideran solo la coordenada X. En las ecuaciones, el tiempo aparece en forma explicita como t, y los subindices 0 en la posición, velocidad y aceleración indican que se trata de la posición, la velocidad y la aceleración &lt;b&gt;iniciales&lt;/b&gt;. En esta ecuación, la aceleración es constante, por tratarse de una fuerza constante. La segunda ecuación es la velocidad en función del tiempo, que en algunas situaciones es útil.&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;Las ecuaciones para las coordenada en Y son idénticas, pero reemplazando X por Y y utilizando las velocidades en la dirección Y en lugar de usar las velocidades en X y la aceleración en Y en lugar de la aceleración en X.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Estas ecuaciones tienen la limitación de que solo son válidas mientras la fuerza se mantiene constante. De cambiar la fuerza, cambia la aceleración, y las ecuaciones dejan de ser válidas. Si en general la fuerza varía en forma complicada con el tiempo, se hace imposible resolver las ecuaciones de movimiento en forma exacta.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;El caso de fuerza que varía con el tiempo debe ser resuelto para cada caso particular. Las ecuaciones que resultan son en general mas complicadas. En un juego, necesitamos utilizar algún método general para resolver las ecuaciones independientemente de como varíe la fuerza. &lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Para hacer esto, lo mas simple es usar el llamado &lt;a href=&quot;http://es.wikipedia.org/wiki/M%C3%A9todo_de_Euler&quot;&gt;Método de Euler&lt;/a&gt;. El método de Euler es un método numérico para resolver ecuaciones diferenciales de &lt;b&gt;primer orden&lt;/b&gt;, o sea que solo tienen derivadas primeras. La primer ecuación que mostré es una ecuación de &lt;b&gt;segundo orden&lt;/b&gt;, pero eso puede ser resuelto fácilmente introduciendo la velocidad. La velocidad, como dijimos, es la primer derivada de la posición:&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaXIrAY0LVDQR2TAgpxscy4OHbgV8NM-Ttop0aiXk5V7Y9-ki35QPDjSCHQeu2xDMr2a_MZ5_2J3_puPSwDSKRzb0tB1_N7AJ99xr9v_z36XGJUelo7uclZQFStgPYCiEnoMi0p2XHNvY/s1600-h/velocity.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaXIrAY0LVDQR2TAgpxscy4OHbgV8NM-Ttop0aiXk5V7Y9-ki35QPDjSCHQeu2xDMr2a_MZ5_2J3_puPSwDSKRzb0tB1_N7AJ99xr9v_z36XGJUelo7uclZQFStgPYCiEnoMi0p2XHNvY/s320/velocity.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Utilizando esta definición, es posible convertir nuestra ecuación de movimiento en varias ecuaciones de primer orden, ya que la aceleracion es la primer derivada de la velocidad:&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgthe5IxqQbFdEkQmLpPJGrV7QF0QkQWXc8dVTG4YF_tppuftO9tn2PXEYt2GKzcxMGdA_y_u7OmuksxDNNTtBuXa6w6YiOqqa-tqLAVAsL2nTJE4tcORmANg-Hqvi5wjiROfnNTHaCLBs/s1600-h/firstOrderMotion.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgthe5IxqQbFdEkQmLpPJGrV7QF0QkQWXc8dVTG4YF_tppuftO9tn2PXEYt2GKzcxMGdA_y_u7OmuksxDNNTtBuXa6w6YiOqqa-tqLAVAsL2nTJE4tcORmANg-Hqvi5wjiROfnNTHaCLBs/s320/firstOrderMotion.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;Con estas cuatro ecuaciones, tenemos solo derivadas primeras, con lo cual podemos aplicar el método de Euler.&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;El método de Euler consiste simplemente en volver a la definición de la derivada, y &quot;olvidarse&quot; que la derivada requiere que la variación del tiempo sea infinitamente pequeña. En lugar de eso, se utiliza una variacion de tiempo finita.&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;Usando esa aproximación, las ecuaciones anteriores resultan ser:&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCJz2tTWwovWkXASDWT-9zDYMkKgKpGN7I6kqO7XI_teQd93_zi2USTfW68ouHeQbT8H_egG3HzyYHGolJ8mW3mp_fIEiHXkMoyHiIPVFao5pelDx0X5DhFM2IhRhvfaK2RHkZDN1ia8g/s1600-h/discreteEcuations.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCJz2tTWwovWkXASDWT-9zDYMkKgKpGN7I6kqO7XI_teQd93_zi2USTfW68ouHeQbT8H_egG3HzyYHGolJ8mW3mp_fIEiHXkMoyHiIPVFao5pelDx0X5DhFM2IhRhvfaK2RHkZDN1ia8g/s320/discreteEcuations.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;La ventaja de estas ecuaciones es que podemos resolverlas algebraicamente para obtener la posición de la partícula en un tiempo t, un instante de&amp;nbsp; tiempo anterior t- Dt. Simplifiquemos un poco la notación, para que sea mas evidente como resolverlas:&lt;br /&gt;
&lt;/div&gt;&lt;ul style=&quot;text-align: justify;&quot;&gt;&lt;li&gt;Llamemos a la posición a tiempo t utilizando un subíndice: Xt es la posición a tiempo t. &lt;/li&gt;
&lt;li&gt;Llamemos &lt;b&gt;h&lt;/b&gt; a la variación del tiempo (también llamado paso de tiempo). &lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;La variación de la posición no es mas que la diferencia entre dos posiciones. O sea:&lt;br /&gt;
&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHY0OPO8-e2behA8BeYtWU_Qm-1VbJIFMR7Z8jXtynFo5DA39OVjLO9gaNr8GWu7XSk6W7CmKSGsoEcvrXGLtBS3rjla-lEN4214peE2bgfi1EmdNBLiiuVJy0Ixtkkgmi4zgGx2wEPZ8/s320/differential.png&quot; /&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Lo mismo puede decirse de la velocidad. La velocidad es la diferencia de la velocidad en dos tiempos distintos:&lt;br /&gt;
&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6aZQLQXnWrhz9n9oaXMrYOpKAj66-3UtojnYfM5025Io53Y5c1wpUjcliaUFs0gVHWn8xFs_lFHHMXRnx8T0kd6vIypZMBIcXxerqAWhid6VxLIWiVlGuUa2XzrAuY5w8mnovnM3vf3c/s320/differentialVel.png&quot; /&gt;&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Teniendo eso en cuenta, resultan las siguientes ecuaciones (considerando solo las coordenadas en X):&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjD7PGmChNdjbLpQhTp9TYmmaEi7DbQMl3HWzAXo2ie6plLUdRzu2kHzQBUZUbujY47TW49cZy1Q179R54EzDdHRiW2TNm-fXLDzrHuerkKds3RqxA52bFJzDIsVbwXYdB5hOPSUDPMcQ8/s1600-h/integratedEuler.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjD7PGmChNdjbLpQhTp9TYmmaEi7DbQMl3HWzAXo2ie6plLUdRzu2kHzQBUZUbujY47TW49cZy1Q179R54EzDdHRiW2TNm-fXLDzrHuerkKds3RqxA52bFJzDIsVbwXYdB5hOPSUDPMcQ8/s320/integratedEuler.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Lo bueno de estas ecuaciones, es que solo requieren una multiplicación y una suma. Además, son absolutamente generales, pueden ser usadas para cualquier tipo de fuerza(aunque en algunas situaciones puede fallar).&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;La desventaja, es que es el método mas simple, y el mas impreciso. En general, la precisión de este método depende del tamaño de h (el intervalo de tiempo). Si usamos un h mas pequeño, el resultado va a ser mas preciso, pero van a ser necesarios mas cálculos para encontrar el valor de la posición.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;El algoritmo en Python&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Veamos como se traducen las ecuaciones anteriores para el caso de un modelo con una sola partícula, en Python.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;pre class=&quot;prettyprint&quot;&gt;class ParticulaSimple():
   def __init__(self, x0, y0, vx0, vy0, masa):
      self.x=x0
      self.y=y0
      self.vx=vx0
      self.vy=vy0 
      self.t=0
      self.masa=masa
      
   def get_force(self):
      &#39;&#39;&#39;
      Devuelve la fuerza a tiempo self.t.
      &#39;&#39;&#39;
      ...
      return force_x, force_y

   def update(self, h):
      t, x_prev, y_prev= self.t, self.x, self.y,
      vx_prev, vy_prev=self.vx, self.vy
      masa =  self.masa
      #Obtener la fuerza a tiempo t
      fuerza_x, fuerza_y=self.get_force()

      #Actualizar el tiempo, posición 
      #y velocidad usando el método de Euler.
      #Actualiza a tiempo t+h
      self.t = t+h
      self.x +=vx_prev*h
      self.y +=vy_prev*h
      self.vx += (fuerza_x*1.0/masa) *h
      self.vy += (fuerza_y*1.0/masa) *h

&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;El código es bastante simple. En caso de ser más de una partícula, se debe iterar sobre todas las partículas. Usando librerías como numpy todo esto puede hacerse en una sola instrucción (multiplicando matrices por vectores). Pero eso no importa ahora.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Una parte clave del algoritmo es la función get_force, que devuelve las fuerzas en la dirección x e y. Esta función puede depender del tiempo, y en situaciones mas complejas puede depender de la velocidad (por ejemplo, la fricción es proporcional a la velocidad) o también puede depender de la posición (por ejemplo, la fuerza que hace un resorte depende linealmente del estiramiento del resorte).&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Para ejemplificar muestro tres tipos de fuerzas:&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;b&gt;Gravedad cerca de la tierra&lt;/b&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Esta fuerza es la fuerza que experimentan todos los objetos cercanos a la superficie de la tierra. Es una fuerza constante, con dirección &quot;hacia abajo&quot;.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;pre class=&quot;prettyprint&quot;&gt;...
   get_force(self):
      #La constante gravitatoria es un valor 
      #que depende solamente del planeta
      #donde se está parado. 
      #Para la tierra es g=9.8 m/s^2
      #Para la luna es g=1.6 m/s^2
      
      g=9.8
      return 0, -self.masa*g  #La fuerza es vertical
      #, apuntando hacia abajo(y&amp;lt;0 es abajo)

   ...
&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;b&gt;Fuerza gravitatoria general&lt;/b&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Si el objeto en cuestión se encuentra alejado del planeta, la fuerza depende de la distancia al centro del planeta. La dirección de la fuerza es directamente apuntando al centro del planeta.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;pre class=&quot;prettyprint&quot;&gt;...
   get_force(self):
      #En este caso, la fuerza depende 
      #de la distancia al planeta y 
      #de la masa del mismo. 
      from math import sqrt
      #Un planeta en la coordenada (0,0)
      planetaX, planetaY= 0, 0 
      #Un planeta de la masa de la tierra
      k=3.98*10^14 

      distancia_X=planetaX-self.x
      distancia_Y=planetaY-self.y
      distancia_al_cuadrado=distancia_X*distancia_X\\
         +distancia_Y*distancia_Y

      F_magnitud=k*self.masa*1.0/distancia_al_cuadrado

      x_dir=distancia_X/sqrt(distancia_al_cuadrado)
      y_dir=distancia_Y/sqrt(distancia_al_cuadrado)

      return F_magnitud*x_dir, F_magnitud*Y_dir

   ...
&lt;/pre&gt;&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;b&gt;Fricción de aire&lt;/b&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Esta fuerza es una fuerza que depende de la velocidad. Para velocidades pequeñas la dependencia es lineal y puede programarse de la siguiente manera:&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;pre class=&quot;prettyprint&quot;&gt;...
   get_force(self):
      #El coefficiente de fricción 
      #es un indice de cuanta fricción hay
      # si es 0 no hay ninguna fricción 
      #(la fuerza de fricción es siempre 0)

      fric_coef=0.1
      return -fric_coef*self.vx, -fric_coef*self.vy
   ...
&lt;/pre&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Por razones que no voy a explicar ahora, esta fuerza no funciona muy bien con el método de Euler. Pero se puede usar si se mantiene el coeficiente de fricción muy chico o el paso de tiempo suficientemente pequeño. Lo mejor para un juego es probar distintos coeficientes hasta encontrar un valor que dé resultados plausibles.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Errores&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Este método, como cualquier otro método de integración numérica, tiene errores. Para mostrar la diferencia entre el método exacto y el método aproximado voy a usar el ejemplo de la gravedad cercana a la tierra, que tiene solución exacta. Supongamos que un personaje de un juego de plataformas viene corriendo a 7 metros por segundo, y salta adquiriendo la misma velocidad.&amp;nbsp; La velocidad inicial es 7 m/s. vertical y 7 m/s horizontal La trayectoria del personaje va a estar dada en forma exacta por:&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhr1IK7DjnT0FR19QelderbvfOR0vjQrxe9M2ZD9lknPOPJc3pco5V5V2JWVTqpWQbOlVeSq8sOx4oE2jLam5GxrtOY7QZVvMYZEXMGSo2N4akH66qp21MUHd132PqwnWH4V8fonC10pjY/s1600-h/jump.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhr1IK7DjnT0FR19QelderbvfOR0vjQrxe9M2ZD9lknPOPJc3pco5V5V2JWVTqpWQbOlVeSq8sOx4oE2jLam5GxrtOY7QZVvMYZEXMGSo2N4akH66qp21MUHd132PqwnWH4V8fonC10pjY/s320/jump.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Comparemos la solución exacta con la solución numérica usando h=0.05&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh93WX6gqIwveh-BAEXuVwNOg53TLxzcwWcZkztyv3qi1F7tpKCwEMpud0voqy3qIhwiliP0ceFiBu-ikinRAfQudX9LgjuVJ5j6XsqEee_Gs95L02MK8XLKy1JjBJwCEpkwcKdluHT6yk/s1600-h/jump.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh93WX6gqIwveh-BAEXuVwNOg53TLxzcwWcZkztyv3qi1F7tpKCwEMpud0voqy3qIhwiliP0ceFiBu-ikinRAfQudX9LgjuVJ5j6XsqEee_Gs95L02MK8XLKy1JjBJwCEpkwcKdluHT6yk/s320/jump.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Comparación de resultados para una fuerza constante en dirección vertical.&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Claramente hay un error. Generalmente, para juegos este tipo el resultado sea suficiente, ya que el movimiento es cualitativamente correcto y el algoritmo usado es rápido. &lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Si se usa un h mas chico, el error va a ser menor (pero van a ser necesarios más FPS del modelo). Existen otros algoritmos que resultan en valores mas precisos para iguales valores de h.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Aun más, en algunas aplicaciones el método de Euler no funciona adecuadamente. Para estas aplicaciones es necesario utilizar otros algoritmos como &lt;a href=&quot;http://en.wikipedia.org/wiki/Velocity_Verlet#Velocity_Verlet&quot;&gt;Verlet Velocidad&lt;/a&gt;.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Conclusiones&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Quizá la matemática de estos algoritmos sea un poco complicada. Pero programarlos es casi trivial. Existen algunas aplicaciones en las cuales el paso de tiempo h necesario para tener un resultado adecuado es demasiado pequeño, con lo cual se deben utilizar otro tipo de métodos de integración.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;En una aplicación normal, la fuerza y velocidad varían según las instrucciones del controlador. En cada paso de tiempo, se llama a la función update del modelo para actualizar las posiciones y al update de la vista para utilizar las posiciones para ubicar los sprites en pantalla. A pesar de que en el modelo utilizo unidades &quot;reales&quot; como metros, en la vista estas unidades se transforman en pixels de pantalla.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Por ahora, para explicar Steering Algorithms, es suficiente con este método de integración. En el próximo post voy a explicar como se crea un controller que utilize fuerzas para dirigir un vehiculo a su destino.&lt;br /&gt;
&lt;/div&gt;</description><link>http://ezequielpozzo.blogspot.com/2009/12/un-modelo-basico-integrando-ecuaciones.html</link><author>noreply@blogger.com (2b)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqws-jFQOdZCCyTDC6KKoZnhYx3OLcrW5edi3kytcrFyLjhkgMY336k3kdZ1szuvDRvGA2otL_xcomeHG-KvRePksbRmi8kkGURhnO3HA5br5ka9zkQwFDyv7LqXJgZiFJrQpe3hVweGI/s72-c/derivativePosition.png" height="72" width="72"/><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5595960729038325799.post-3822389183046070596</guid><pubDate>Fri, 04 Dec 2009 23:35:00 +0000</pubDate><atom:updated>2009-12-04T15:58:38.190-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Física</category><category domain="http://www.blogger.com/atom/ns#">IA</category><category domain="http://www.blogger.com/atom/ns#">Inteligencia Artificial</category><category domain="http://www.blogger.com/atom/ns#">MVC</category><category domain="http://www.blogger.com/atom/ns#">videojuegos</category><title>MVC en la creación de videojuegos II - Esquema general</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;En el post anterior expliqué la &lt;a href=&quot;http://ezequielpozzo.blogspot.com/2009/11/mvc-en-la-creacion-de-videojuegos.html&quot;&gt;razón que tuve para utilizar MVC&lt;/a&gt; en mi &lt;a href=&quot;http://code.google.com/p/steeringbehaviors/&quot;&gt;proyecto de inteligencia artificial&lt;/a&gt;. Pero si alguien leyó el post completo, seguramente el final fue anticlimático, por no explicarse nada concreto. En este post me encargo de dar una vista a ojo de pájaro de como implementé un sistema MVC. La idea me surgió gracias al &lt;a href=&quot;http://ezide.com/games/writing-games.html&quot;&gt;tutorial de sjbrown para escribir juegos en pygame&lt;/a&gt; (en ingles), por lo que hay varias similitudes. Sin embargo esa guia es un tutorial básico de pygame, que no implementa mucho mas que lo mínimo y necesario para entender el sistema MVC con Mediador. Como expliqué en el anterior post, la idea es hacer un sistema para implementar algoritmos de inteligencia artificial (IA), pero siempre con la creación de videojuegos en mente.&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;La primer versión de mi programa se pude representar esquemáticamente de la siguiente manera:&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBkcAZpDJdPi-n1R2TKewgPqUlIse5dIIrBeeGpYhCQ6TEw7VXAMzP3CJ8-uwqdmJozwFwvRDwko3XIt52ciB8NHB6UmvHH58zApboB4DldzCdiz-4VBYI0QdSqkTDhFNtgU4vpuO_tA0/s1600-h/MVC-videojuegos-ex.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBkcAZpDJdPi-n1R2TKewgPqUlIse5dIIrBeeGpYhCQ6TEw7VXAMzP3CJ8-uwqdmJozwFwvRDwko3XIt52ciB8NHB6UmvHH58zApboB4DldzCdiz-4VBYI0QdSqkTDhFNtgU4vpuO_tA0/s320/MVC-videojuegos-ex.png&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;background-color: white; color: #666666;&quot;&gt;Esquema MVC general para un sistema de videojuegos. (click para agrandar)&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;El Modelo&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;La parte mas importante de este programa es el modelo, por ser (junto a la IA) la razón de la existencia de todo el programa. Esencialmente es una simulación física. Dentro de lo que son las simulaciones físicas, este modelo es por el momento un simulador de dinámica molecular. Básicamente un conjunto de partículas puntuales que tienen posición, velocidad y fuerzas. Hay varias formas de implementar esto, y para muchas partículas es uno de los puntos críticos de eficiencia del sistema.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;La función del modelo es actualizar las posiciones y velocidades de todas las partículas en cada frame. También permite acceder agregar, quitar y acceder a las partículas y aplicarle fuerzas.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Por el momento, el modelo es 2D. Pero puede ser fácilmente adaptado a 3D.&lt;br /&gt;
Con este modelo se pueden implementar juegos de varios tipos: Shooters, Tower Defense, Pacman, FPSs a la antigua como el Wolfenstein y sidescrollers. Pero en general va a ser necesaria una abstracción del concepto de &quot;Mapa&quot; y mas adelante de &quot;cuerpo rígido&quot; que incluya la posibilidad de colisiones para realizar juegos con física mas avanzada.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;La Vista&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;La vista es una representación del modelo. Actualmente hay dos vistas, una vista gráfica y una vista que solamente almacena los valores del modelo en un archivo de texto. La primera es la pantalla gráfica que todos conocemos y la segunda es utilizada para hacer un análisis mas detallado del modelo y su correcto funcionamiento.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;En cada cuadro, la representación gráfica se encarga de obtener directamente del modelo la información necesaria para actualizar la pantalla. Por una razón de eficiencia, la vista accede al modelo directamente.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;La vista tiene también una &quot;cámara&quot; que abstrae las coordenadas del modelo de las coordenadas de la pantalla. La cámara es una simple transformación de rotación, translación y escala que convierte de las coordenadas del modelo a las coordenadas de la pantallas. La cámara puede ser manipulada en tiempo real para hacer zoom, rotaciones y translaciones.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;La vista actual es 2D, implementada en pygame, por ser lo mas simple. Manteniendo la misma interfaz, se puede realizar una implementación en Java o incluso una implementación 2D+1 (2D isometrica con una tercer dimensión falsa) utilizando pygame mismo o mejor aun &lt;a href=&quot;http://www.ogre3d.org/wiki/index.php/PyOgre&quot;&gt;python-ogre&lt;/a&gt; u otra librería 3D para python.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Actualmente, debido a que pygame posee una implementación, la colisión entre objetos se calcula en la vista. En el futuro esta funcionalidad se va a pasar al modelo, agregando el concepto de &quot;forma&quot; al mismo. La colisión solo se usa por el momento para funciones del mouse y otros controladores. No hay colisiones estrictas (o sea, las partículas pueden superponerse).&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;El Procesador de Eventos&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;El procesador de eventos es una implementación del &lt;a href=&quot;http://es.wikipedia.org/wiki/Mediator_%28patr%C3%B3n_de_dise%C3%B1o%29&quot;&gt;patrón de diseño Mediador&lt;/a&gt;. Este objeto es un mensajero que media en la comunicación entre todas las partes del sistema. Es muy importante para generar una aplicación modular y de comportamiento altamente configurable. Este patrón de diseño se suele utilizar por ejemplo para programar GUIs, donde uno crea widgets que generan eventos al ser activados.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Utilizando el procesador de eventos, se pueden registrar tipo de eventos, que son asociados a funciones que &quot;escuchan&quot; estos eventos. Durante la ejecución del programa, los distintos componentes del programa publican eventos en el procesador de eventos, el procesador de eventos se encarga de distribuir estos eventos a las distintas funciones registradas.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Por ejemplo, se puede asociar una función que termina el programa para que se ejecute cuando se publique un evento &quot;Tecla escape apretada&quot;, durante la ejecución del programa; cuando el usuario presione la tecla escape, el controlador de teclado publicará el evento en el procesador de eventos y el programa terminará. La ventaja de este esquema es que el teclado no tiene que conocer ningún detalle del resto de los componentes, ni quién escucha el componente. Por otro lado, se puede cambiar el comportamiento del programa fácilmente sin afectar los componentes involucrados, simplemente cambiando qué objeto escucha qué tipo de eventos.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;La &lt;a href=&quot;http://ezide.com/games/writing-games.html&quot;&gt;implementación básica&lt;/a&gt; del procesador de eventos no distingue entre tipos de eventos (todos los componentes escuchan todos los eventos). Mi implementación agrega &quot;tipos de eventos&quot;. En el futuro tengo planeado implementar el concepto de prioridad y orden de eventos (el orden en que se llama a las AI puede ser importante, como descubrí recientemente).&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Los controladores&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;span style=&quot;font-size: small;&quot;&gt;Los controladores son&lt;/span&gt; &lt;/span&gt;objetos que manipulan el modelo. En un diseño general del patrón MVC un controlador puede acceder a la vista y al modelo directamente. En la práctica depende del controlador. Yo intenté mantener los acoples directos entre MVC al mínimo necesario, utilizando el Procesador de Eventos donde fuera posible.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;b&gt;&lt;span style=&quot;font-size: small;&quot;&gt;El mouse y el teclado&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;Los controladores obvios son el mouse y el teclado. El mouse es por el momento el único objeto que requiere acceder a la vista en forma directa. Mas allá de eso, ambos controladores son completamente basados en eventos. En la sección de Procesador de Eventos explico las funciones.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;En cada update del mouse, el objeto lee la posición del mouse y el estado de los botones y de haber alguna actualización, envía un mensaje utilizando el Procesador de Eventos. El mouse recibe del sistema operativo, posiciones en coordenadas de la pantalla, por esta razón se necesita el View para convertir estas coordenadas a coordenadas del modelo.El mouse emite eventos cuando se presiona un botón, cuando se lo deja de presionar y cuando se mueve el mouse. Cada botón tiene un tipo de evento distinto, así hay eventos &quot;BUTTON_1_DOWN&quot;, &quot;BUTTON_1_UP&quot;, &quot;BUTTON_2_DOWN&quot;, etc.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;El teclado es puramente basado en eventos. Genera mensajes indicando si se ha presionado/soltado una tecla en particular del mouse. Se emiten mensajes cuando se presiona una tecla y cuando se la suelta. En el mismo mensaje se indica si hay teclas modificadoras (ctrl, shift, alt) presionadas. Cada combinación de teclas tiene un tipo de evento distinto.&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;La inteligencia artificial&lt;/b&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;Este objeto es una abstracción del &quot;cerebro&quot; de las entidades del modelo. No es obvia la razón por la que la IA es un controlador en lugar de ser parte del modelo. Se puede pensar que la IA es parte de las reglas del modelo. Así como en el modelo las fuerzas modifican en forma determinista las posiciones de las partículas, uno puede pensar que la inteligencia artificial es una regla más, que modifica el modelo en forma determinista. Esta forma de ver la IA es obvia en reglas simples como los Steering Algorithms (que voy a explicar en futuros posts). Pero a medida que uno usa técnicas de IA mas elaboradas (redes neuronales, partículas), esta forma de ver las cosas se torna menos obvia. Aun mas, separar la IA del modelo es atractivo por varias razones:&lt;br /&gt;
&lt;/div&gt;&lt;ol style=&quot;text-align: justify;&quot;&gt;&lt;li style=&quot;text-align: justify;&quot;&gt;Facilita simular el desconocimiento que tiene una IA del mundo, si la IA fuera parte del modelo sería muy tentador hacer IAs omniscientes.&amp;nbsp;&lt;/li&gt;
&lt;li style=&quot;text-align: justify;&quot;&gt;La IA se maneja en escalas temporales distintas al modelo. Por un lado, es útil que el modelo se actualice con la mayor frecuencia posible, por ejemplo 60 cuadros por segundo. Es fácil darse cuenta que una persona no tiene reacciones inmediatas, si un enemigo aparece en el rango de visión, la persona tardará en reaccionar (por ejemplo dispararle). Es sumamente atractivo transladar esta limitación a las IA, para ponerlas en igualdad de condiciones. En lugar de llamar al update de las IA 30 veces por segundo se puede llamar 10 veces por segundo, simulando un retardo en las reacciones de la IA similar al de un humano.&lt;/li&gt;
&lt;li style=&quot;text-align: justify;&quot;&gt;Una misma IA puede actuar con los mismos algoritmos sobre distintos modelos. Asi, si en el futuro hago un modelo 3D, las IA pueden permanecer (en muchos casos, pero no todos) sin cambio y aun ser útiles.&lt;/li&gt;
&lt;li style=&quot;text-align: justify;&quot;&gt;La IA no es tan prioritaria como el modelo. Se pueden aplicar técnicas avanzadas sobre las IA para balanceo de carga del CPU. &lt;a href=&quot;http://ezequielpozzo.blogspot.com/2009/12/mvc-en-la-creacion-de-videojuegos-ii.html#Referencias&quot; name=&quot;referenciasback&quot;&gt;[1]&lt;/a&gt; Ya que la evaluación de las IA no es prioritaria en ciertos contextos.&lt;br /&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Por una razón de eficiencia, la IA posee acceso directo al modelo.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Para explicar el esquema MVC mas fácilmente supuse que hay una sola IA en este post. En realidad hay muchos tipos de IA implementadas en mi proyecto (10 al momento de escribir esto), todas pertenecientes a la categoría &quot;Steering Algorithms&quot;. En un post futuro voy a explicar en detalle eso.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;b&gt;El reloj de sistema&lt;/b&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Este objeto es el &quot;corazón&quot; del programa. Es un objeto muy simple que solamente se encarga de generar eventos indicando un nuevo frame del programa. Es una abstracción ingeniosa del bucle principal del programa. En lugar de hacer un bucle infinito en el cual se llama a cada objeto del programa, se generan eventos a una frecuencia dada, que luego son distribuidos por el procesador de eventos entre los distintos componentes que requieren un update periódico. Para inicial el programa, se ejecuta el reloj de sistema. Para pausar el programa, se detiene el reloj. &lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;En la implementación actual del programa el reloj de sistema emite un mensaje &quot;TICK&quot; en cada frame del programa, a un ritmo predefinido. En próximas implementaciones, el reloj de sistema va a tener distintos tipos de TICK para distintas frecuencias, permitiendo actualizar distintos componentes del sistema a distintas frecuencias. Así será posible actualizar, por ejemplo, el modelo a 40 FPS, el view a 30FPS y los controllers a 10FPS.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-size: large;&quot;&gt;Conclusión&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Como dije al principio, esta es una vista general del programa que estamos programando. El código se puede descargar del repositorio &lt;a href=&quot;http://code.google.com/p/steeringbehaviors/source/checkout&quot;&gt;SVN público&lt;/a&gt;.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;En próximos posts voy a explicar mas concretamente varios puntos que no necesariamente están relacionados con el esquema MVC. Voy a explicar las bases de una simulación física de dinámica molecular, incluyendo algunos algoritmos concretos, voy a explicar también qué es un Steering Algorithm. Volviendo al tema MVC también voy a explicar algunos problemas que encontré en las implementaciones iniciales y como fuí solucionandolos.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;a href=&quot;http://www.blogger.com/post-edit.g?blogID=5595960729038325799&amp;amp;postID=3822389183046070596&quot; name=&quot;Referencias&quot;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;[1] &quot;An Efficient AI Architecture Using Prioritized Task Categories&quot; Alex W. McLean - AI Game Programming Wisdom 1. Capitulo 6-3 p290 &lt;a href=&quot;http://ezequielpozzo.blogspot.com/2009/12/mvc-en-la-creacion-de-videojuegos-ii.html#referenciasback&quot;&gt;[volver]&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;</description><link>http://ezequielpozzo.blogspot.com/2009/12/mvc-en-la-creacion-de-videojuegos-ii.html</link><author>noreply@blogger.com (2b)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBkcAZpDJdPi-n1R2TKewgPqUlIse5dIIrBeeGpYhCQ6TEw7VXAMzP3CJ8-uwqdmJozwFwvRDwko3XIt52ciB8NHB6UmvHH58zApboB4DldzCdiz-4VBYI0QdSqkTDhFNtgU4vpuO_tA0/s72-c/MVC-videojuegos-ex.png" height="72" width="72"/><thr:total>5</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5595960729038325799.post-2078372027907854277</guid><pubDate>Mon, 30 Nov 2009 18:56:00 +0000</pubDate><atom:updated>2009-12-04T15:38:42.303-08:00</atom:updated><title>MVC en la creación de videojuegos - Introducción</title><description>&lt;div style=&quot;text-align: justify;&quot;&gt;En el último tiempo, estuve muy interiorizado en la programación de aplicaciones web utilizando Frameworks como Ruby on Rails y Joomla Framework (me refiero al Framework PHP detrás del CMS). Ambos tienen una característica en común, que están diseñados siguiendo una filosofía Model-View-Controller (&lt;a href=&quot;http://es.wikipedia.org/wiki/Modelo_Vista_Controlador&quot;&gt;Modelo-Vista-Controlador&lt;/a&gt;).&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Este tipo de arquitectura se basa en separar una aplicación en tres módulos independientes. El Modelo son los datos que se manejan en la aplicación, las Vistas son las distintas formas de presentar los datos al usuario, el Controlador es la forma que tiene el usuario de manipular los datos. También se suele agregar un &lt;a href=&quot;http://es.wikipedia.org/wiki/Mediator_%28patr%C3%B3n_de_dise%C3%B1o%29&quot;&gt;Mediador &lt;/a&gt;que no es mas que un objeto que se encarga de coordinar la comunicación entre los componentes. De esta forma se evitan dependencias fuertes entre las interfases de cada componentes, y mas importante, se genera una reusabilidad sorprendente en las aplicaciones que programamos.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Hace poco, &lt;a href=&quot;http://ailab.ifi.uzh.ch/carbajal/&quot;&gt;con un amigo&lt;/a&gt; decidimos empezar un &lt;a href=&quot;http://code.google.com/p/steeringbehaviors/&quot;&gt;pequeño proyecto&lt;/a&gt; para probar distintas técnicas de inteligencia artificial en tiempo real. La idea es comenzar con los clásicos &lt;a href=&quot;http://www.red3d.com/cwr/steer/gdc99/&quot;&gt;Steering Behaviors&lt;/a&gt; de Craig Reynolds, extenderlo a Flocking y Squads y después implementar técnicas mas avanzadas (aunque tampoco totalmente novedosas) como Automatas Celulares, para simulación de fenómenos físicos como explosiones, y filtros de partículas para simular el conocimiento que tiene la AI del mundo que la rodea.&amp;nbsp; Todo esto, ejemplificado con pequeños juegos que muestren las técnicas. Voy a ir mostrando en este blog las distintas técnicas, ejemplificandolas con Applets cuando sea posible.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;El primer problema que hace años que no programo en Java y no tenía ganas de empezar inmediatamente a refrescar esos conocimientos. En lugar de eso me propuse utilizar Python, un lenguaje que permite programar en forma veloz, generalmente para realizar prototipos de programas que luego pueden ser reimplementados en lenguajes &quot;de gente seria&quot; o, mi preferida, optimizados implementando solamente partes críticas en lenguajes de mejor rendimiento.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Otra ventaja que tiene Python, es la posibilidad de crear Applets Java mediante la utilización de &lt;a href=&quot;http://es.wikipedia.org/wiki/Jython&quot;&gt;Jython&lt;/a&gt;. Utilizando esta implementación de Python en Java, es posible compilar código Python puro (que no depende de bibliotecas externas) e incluso utilizar bibliotecas externas de Java (imprescindible si se quiere mostrar gráficos y acceder al mouse).&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Mientras tanto, y hasta tener algo para mostrar en este blog, voy a utilizar Python para programar, &lt;a href=&quot;http://www.pygame.org/news.html&quot;&gt;Pygame&lt;/a&gt; para acceder al hardware (mouse, teclado, display y reloj del sistema) y &lt;a href=&quot;http://numpy.scipy.org/&quot;&gt;numpy&lt;/a&gt; para los cálculos que requieran mayor eficiencia.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Viendo ese spaghetti de requerimientos, necesitábamos asegurarnos una buena separación de lo que es Pygame y Numpy (librerías externas que no pueden ser llevadas a Java) de lo que es código Python puro. Y a la vez, necesito que sea fácil introducir Java y sus bibliotecas cuando decidamos implementar Applets.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Elegir una arquitectura MVC pareció la solución mas atinada. De esta forma, el View principal y los Controllers del mouse y el teclados serían implementados usando Pygame, heredando de una clase abstracta general. En un futuro se puede reimplementar la misma clase abstracta en Java. El modelo y el mediador, por otro lado, no tendría ninguna dependencia de este tipo de librerías.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Veamos ahora la estructura mas concreta y los &lt;a href=&quot;http://www.google.com/search?hl=es&amp;amp;q=MVC+in+games&amp;amp;sourceid=navclient-ff&amp;amp;rlz=1B3GGGL_esAR346AR348&amp;amp;ie=UTF-8&quot;&gt;dilemas típicos&lt;/a&gt; que surgen al utilizar MVC para juegos. Voy a utilizar como ejemplo la aplicación que tengo desarrollada hasta el momento. Por supuesto que iré refactorizando en el futuro y cuando sea necesario explicaré los cambios en este blog. El código al día se puede descargar en el &lt;a href=&quot;http://code.google.com/p/steeringbehaviors/source/checkout&quot;&gt;repositorio SVN&lt;/a&gt;, no soy ningún gurú de programación, con lo cual se aceptan sugerencias e ideas.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;En el siguiente post voy a explicar a grandes rasgos como diseñé la primer versión de la aplicación (Casi un esqueleto con capacidades mínimas en el momento de escribir este post) utilizando MVC.&lt;br /&gt;
&lt;br /&gt;
Ver &lt;a href=&quot;http://ezequielpozzo.blogspot.com/2009/12/mvc-en-la-creacion-de-videojuegos-ii.html&quot;&gt;siguiente post&lt;/a&gt;. &lt;br /&gt;
&lt;/div&gt;</description><link>http://ezequielpozzo.blogspot.com/2009/11/mvc-en-la-creacion-de-videojuegos.html</link><author>noreply@blogger.com (2b)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5595960729038325799.post-7843942390514242461</guid><pubDate>Sat, 07 Nov 2009 08:09:00 +0000</pubDate><atom:updated>2009-11-07T00:09:44.941-08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Chrome</category><title>Tarde pero seguro: Sidewiki para Google Chrome</title><description>Es raro esto de usar Google Chrome.&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;El navegador en sí es rápido, pero le faltan muchos de los agregados de Firefox. Seguramente ambas cosas estén relacionadas. Y encima uno se siente un ciudadano de segunda en el mismo mundo Google, ya que varias aplicaciones que google desarrolla tardan en estar disponibles en Chrome.&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;Ahora llegó &lt;a href=&quot;http://chrome.blogspot.com/2009/10/bringing-google-sidewiki-goodness-to.html&quot;&gt;Sidewiki para Google Chrome&lt;/a&gt;. No es lo mismo que en Firefox, donde el sidewiki es realmente un sidebar. Esta versión es un bookmarklet que se &lt;a href=&quot;http://www.google.com/support/toolbar/bin/answer.py?hl=en&amp;amp;answer=164493&quot;&gt;arrastra de la página de ayuda de Chrome&lt;/a&gt; hasta la barra de bookmarks (la barra se activa con &lt;b&gt;ctrl-b&lt;/b&gt; en Chrome). Una vez puesto el bookmarklet en la barra, uno puede activar un &quot;popupwiki&quot; donde se tiene las misma funcionalidad que en el sidewiki... algo es algo.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Para ver mis, pocos, comentarios en sidewiki:&amp;nbsp;&lt;a href=&quot;http://www.google.com/profiles/ezequiel.pozzo#sidewiki&quot;&gt;http://www.google.com/profiles/ezequiel.pozzo#sidewiki&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;</description><link>http://ezequielpozzo.blogspot.com/2009/11/tarde-pero-seguro-sidewiki-para-google.html</link><author>noreply@blogger.com (2b)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5595960729038325799.post-7918180472008697644</guid><pubDate>Sun, 27 Sep 2009 08:21:00 +0000</pubDate><atom:updated>2009-09-28T14:47:39.771-07:00</atom:updated><title>Lo concreto y lo etereo</title><description>&lt;div style=&quot;text-align: justify;&quot;&gt;Acabo de leer una nota de &lt;a href=&quot;http://www.pagina12.com.ar/diario/elpais/1-132503-2009-09-27.html&quot;&gt;Horacio Verbitsky&lt;/a&gt; y una de &lt;a href=&quot;http://www.perfil.com/contenidos/2009/09/26/noticia_0001.html&quot;&gt;Eliseo Verón&lt;/a&gt;. Veo por un lado que hay una repetición de las mismas consideraciones entre los artículos de opinión que se oponen a la ley. Y por el otro, entre los que están a favor, siempre encuentro nuevos argumentos y puntos de vista, en particular encuentro que se focalizan en puntos concretos y los analizan. &lt;br /&gt;
La nota de Verbitsky tiene una primer parte que gira alrededor de los argumentos legales que se le hace a la ley. Y luego los refuta en forma concreta y documentada. Luego sigue con una consideración sobre la influencia de los medios en la sociedad y el punto de inflexión entre la relación Clarín-gobierno. Y termina con una pequeña reseña histórica sobre el proceso mediante el cual, parafraseando a los genios de marketing de TN &quot;1800 cables y varios canales &#39;desaparecieron&#39;&quot;. Esta ultima parte me hizo recordar de mi tan querido &quot;Cablin&quot; y me acordé que Morgado era conductor de ese canal.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
La nota de Verón es mas académica. La cuestión de fondo es la que ya se ha dicho mil veces &quot;los K son malos, autoritarios y están atacando al grupo Clarín&quot;. Pero en el caso de Verón es justificable que haga este análisis ya que Verón es un semiologo, con lo cual es natural que haga un analisis de los significados.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Es importante recalcar, que un análisis de significado es en mayor o menor medida subjetivo. Y mas alla del enfoque académico, y la prosa que recuerda a un libro de filosofía, el contenido concreto no es nada que no se haya dicho y escrito miles de veces en forma mas chapucera (ver Joaquín Morales Solá o Silvina Walger en un &lt;a href=&quot;http://ezequielpozzo.blogspot.com/2009/09/2090.html&quot;&gt;post anterior&lt;/a&gt;)&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Algunos puntos para destacar:&lt;br /&gt;
&lt;/div&gt;&lt;ul style=&quot;text-align: justify;&quot;&gt;&lt;li&gt;[Darle el dinero a los pobres no ocurrió] y los 600 millones de pesos fueron usados para pagar a la AFA:&lt;/li&gt;

&lt;ul&gt;&lt;li&gt;Este es el viejo argumento que esconde los supuestos de que el fútbol es un negocio que da perdida. Yo no tendré una prosa tan refinada como la del semiólogo, pero se notar cuando se esgrime un argumento pedorro que ya fue criticado. Veremos que pasa en unos años, cuál es el saldo del negocio del fútbol cuando se hayan vendido las licencias, publicidad y demás. Por ahora, es un simple golpe de efecto.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;&amp;nbsp; a los componentes del tercio privado sin fines de lucro (comunidades, entidades no gubernamentales, sindicatos, etc.) difícilmente se los puede imaginar adoptando actitudes de crítica hacia un gobierno que les ha otorgado la explotación de ciertos medios, y la liberación de numerosas licencias permitirá colocar en el tercer tercio, comercial privado, a buen número de aliados actuales y potenciales del kirchnerismo.&lt;/li&gt;

&lt;ul&gt;&lt;li&gt;Acá el semiólogo abandona toda pretensión de explicación y simplemente tira el cliché de &quot;se van a quedar con los medios&quot; sin mas. No trata de analizar la autoridad de aplicación, o considerar los mecanismos precisos mediante los cuales un gobierno corrupto podría coercionar a los licenciatarios. El postula &quot;difícilmente se los puede imaginar&quot; y &quot;permitirá [beneficiar a muchos aliados] actuales y potenciales del NK&quot; y deja el resto a la imaginación del lector. Seguramente un lector convencido pueda llenar los blancos de este razonamiento con mil fantasías y expectativas, pero uno esperaría que el rol de un analista es analizar, y no estimular la imaginación.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Todo discurso es sintomático y el de Cristina Kirchner no escapa a esta regla.&lt;/li&gt;

&lt;ul&gt;&lt;li&gt;Supongo que el lector promedio del diario Perfil entiende perfectamente lo que esa frase significa.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Considerados en sí mismos, nadie puede estar en desacuerdo con estos principios, que son la base de las legislaciones que reglamentan el campo de los medios de comunicación en la mayoría de los países “centrales” y particularmente en los países de la Unión Europea, cuya legislación fue ampliamente utilizada por los redactores de la ley aprobada en la Cámara de Diputados. Pero es un grave error olvidar que las consecuencias de la aplicación de una ley no pueden ser evaluadas sin tomar en cuenta la cultura política del país en que se la discute y las metodologías que se ejercitan desde el Ejecutivo.&lt;/li&gt;

&lt;ul&gt;&lt;li&gt;El problema de esta frase es doble. Primero por ser una falacia de ataque ad-hominem, luego de este párrafo se procede a explicar que el gobierno tiene malos motivos para impulsar la ley. Yo digo que si bien el fin no justifica los medios, los medios tampoco refutan el fin. Podemos atacar todo lo que se quiera los motivos ulteriores de la presidenta y como hace las cosas, pero eso en ningún momento es una refutación de la ley en si. (mas allá de algún punto concreto que se pueda llevar a la justicia o se pueda demostrar violatorio de las reglas de juego de la democracia)&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;El segundo problema de esta frase es que se centra, básicamente, en argumentar que no somos un pais Europeo. No explica específicamente las diferencias entre la cultura política de Argentina y un pais Europeo (también lo deja a la imaginación del lector), pero algo es claro: los problemas de la cultura política de este pais no van a terminar con este gobierno ni con el siguiente, con lo cual es poco constructivo utilizar ese argumento para refutar los puntos objetivos de una ley.&amp;nbsp; Porque se desprende de allí que para tratar esta ley tendríamos que tener un sistema de políticos probos e interesados por el bien común, un acceso libre a la información y una visión clara y no parcial sobre el asunto.&amp;nbsp; O sea, no podríamos sancionar la ley nunca.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Lo que hay que entender es que estas tres lecturas se adicionan, y que el proceso político en curso (como todo proceso político-social) es multidimensional. Focalizarse en los contenidos ideológicos del proyecto, descuidando las otras dimensiones (todas las intervenciones de la señora presidenta han estado destinadas a empujar en esa dirección) es precisamente caer en la trampa de la metodología kirchnerista. Ese parece ser el caso de los socialistas –si hacemos de su actitud la evaluación más benévola posible, a saber: que han sido políticamente ingenuos.&lt;/li&gt;

&lt;ul&gt;&lt;li&gt;Acá el escritor parece olvidar que además de los contenidos ideológicos y los contenidos &quot;de otras dimensiones&quot; (o sea, consideraciones propias del escritor que apelan a millones de argumentos aledaneos), además de esos puntos, están los puntos concretos, objetivos y numéricos de la ley. El intelectual se pierde en sus propias consideraciones y llega a una conclusión que olvida lo fundamental en una acción: los resultados concretos y materiales que de ella se desprenden. Quizá sea esto una deformación profesional. No lo se.&lt;br /&gt;
&lt;/li&gt;
&lt;li&gt;Por lo tanto, hay una evaluación mas benévola que la que hace el escritor sobre el voto socialista. Esta evaluación es considerar que han incurrido en el pragmatismo de votar una ley que corresponde cuantitativamente (además de ideológicamente) con su propio parecer y con las leyes que ellos mismos han defendido en el pasado. Esto es todo lo contrario a ser ingenuo. Ser ingenuo sería, luego de varios intentos fallidos de promover una ley de la democracia, frenar el avance de esta ley argumentando que &quot;algo huele mal&quot;. Algo siempre va a oler mal en este país lamentablemente.&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;En la oposición, todos se han vuelto un poco semiólogos con este gobierno. Tratando de analizar los significados detrás de cada discurso, acción o gesto de la presidenta y el ex presidente. Pero no olvidemos que mas allá de los motivos ulteriores, están los hechos concretos.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Y en ese juego, muchos fallan en explicar de que manera concreta esta ley puede llegar a ser usada por el&amp;nbsp; gobierno, para en el plazo de 2 años tomar las riendas de las telecomunicaciones argentinas y utilizarlas a su favor. Fallan en explicar como un gobierno con una reputación en descenso puede lograr en 2 años crear y aceitar una maquinaria que le llevó al grupo Clarín 30 años de construcción, para luego llevar a cabo su supuesto plan de ser reelectos en las próximas elecciones, sin encontrar en el camino ninguna oposición de parte de diputados, senadores, jueces, partidos políticos opositores. En todo este juego etéreo de argumentaciones, retórica e &quot;hipnopedia&quot; (gracias Verbitsky) se olvidan dar dos pasos al costado y analizar lo concreto y material.&lt;br /&gt;
&lt;/div&gt;</description><link>http://ezequielpozzo.blogspot.com/2009/09/lo-concreto-y-lo-etereo.html</link><author>noreply@blogger.com (2b)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5595960729038325799.post-5895119154507629875</guid><pubDate>Tue, 22 Sep 2009 05:38:00 +0000</pubDate><atom:updated>2009-09-21T22:44:03.307-07:00</atom:updated><title>El espejo</title><description>&lt;div style=&quot;text-align: justify;&quot;&gt;Hoy leí un articulo en un &lt;a href=&quot;http://www.reloco.com.ar/columna/2007/06/propuestas-vs-ideas.html&quot;&gt;blog&lt;/a&gt; que me dejó pensando&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;A mi me parece interesante el concepto de que las propuestas son generalmente universales, porque corresponden a lo que el político cree que los votantes piden. La campaña de un político en tiempo de elecciones es una épica creada por un asesor de marketing. El político en tiempo de elecciones es un espejo de las esperanzas del electorado.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Es así, que votar por &quot;propuestas&quot; puede ser un acto de vanidad: Una persona puede cometer el error de votar al candidato que mejor refleja sus expectativas, sin tener en consideración lo razonable de sus expectativas o qué tan probable es que sean satisfechas. Por esto es mas importante ver el desempeño de los candidatos en actos concretos fuera de campaña que la campaña en si. &lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Me parece que hay algo que está perdido (si es que alguna vez lo hubo) en el proceso democrático, y es el uso de la información. No parece haber en la población una necesidad de datos concretos. Los programas políticos actuales, por ejemplo, son mas bien una serie de entrevistas y encuestas. Las notas periodísticas se enfocan mas en el lado humano de los hecho, lo subjetivo, que en el contexto cuantitativo. En esta situación, no es extraño que un candidato apoye su campaña en lo general y ambiguo mas que en lo concreto.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Internet, y estoy consciente de que esto es un cliché, es una herramienta que puede mejorar notablemente el acceso a la información. Las redes sociales y los blogs permiten desenterrar en forma eficiente los cadáveres que cada político con trayectoria tiene en su haber. Uno podría soñar con un sitio utópico de internet que permita obtener un CV &quot;no autorizado&quot; de cada político, o una serie de links para adquirir un contexto sobre una determinada situación. Es en parte esa idea que me llevó a escribir este post. &lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Pero hay una ingenuidad en ese concepto. Porque lamentablemente, en cierto modo no es tan importante la posibilidad de acceder a la información si no hay voluntad de utilizar esa información. La vanidad es la excesiva confianza en la propia capacidad. Y una persona que es vana intelectualmente, no siente la necesidad de buscar la información fuera de su propia persona. A una persona vana le basta con su ser y un espejo para poder tomar todas las decisiones.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;</description><link>http://ezequielpozzo.blogspot.com/2009/09/mi-columna-propuestas-vs-ideas.html</link><author>noreply@blogger.com (2b)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5595960729038325799.post-4581789864849769467</guid><pubDate>Sun, 20 Sep 2009 18:24:00 +0000</pubDate><atom:updated>2009-09-20T11:39:43.054-07:00</atom:updated><title>2090</title><description>&lt;div style=&quot;text-align: justify;&quot;&gt;¿Que significa el consenso? Si yo no puedo ceder a mi enemigo sin que sea puesta en duda mi integridad y se me acuse de estar cooptado. &lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: justify;&quot;&gt;¿Que significa consensuar? Si no puedo otorgar sin que esto sea visto como un signo de debilidad, una marcha atras, un mero &quot;juntar voto&quot;.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Por qué esa contradicción de gritar a cuatro vientos, y de forma espontánea, &quot;Alguien tiene que ceder&quot;, salir a la calle a clamar &quot;alguien tiene que ceder&quot;, pero nunca ceder.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Son épocas como estas en las que me siento desesperanzado. Veo como la opinión de personas que se supone que deben iluminar sobre la realidad se tiñe de una antiintelectualidad trágica, que tilda de&amp;nbsp;&lt;a href=&quot;http://www.criticadigital.com/index.php?secc=nota&amp;amp;nid=29885&quot;&gt;&quot;monos y progres&quot;&lt;/a&gt; a quien da su voto sincero. Un párrafo después de vanagloriarse de la deshonestidad de discutir las personas antes que las ideas. Sin siquiera detenerse a explicar ningún tipo de afirmación, por mas increíble que sea, justamente porque la credibilidad es hoy en día una cuestión meramente subjetiva.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Estamos en una época donde las leyes ya no se discuten por sus artículos, si no que se discuten con nombre y apellido y esgrimiendo consideraciones y argumentos infalsables. Los artículos de &lt;a href=&quot;http://www.lanacion.com.ar/nota.asp?nota_id=1176753&amp;amp;pid=7355455&amp;amp;toi=6259&quot;&gt;opinión periodística&lt;/a&gt; son un trabajo de construcción Ptolomeica, que apelan a lo que sobreentiende (pero nunca se demuestra), a lo que no se define (pero se repite hasta el hartazgo) y a lo que no puede ser refutado (y sin embargo se lo considera intelectualmente honesto, por alguna magia de la retórica). &lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Mi teoría es que estamos gestando una &lt;a href=&quot;http://es.wikipedia.org/wiki/Neolengua&quot;&gt;neolengua&lt;/a&gt;. Donde dialogar significa &quot;callar&quot;y ceder significa &quot;resistir&quot;. Donde la oposición solo puede oponerse, y el que acompaña solo puede ser cómplice. Donde la magnitud de la evidencia necesaria disminuye proporcionalmente a la magnitud de la afirmación esgrimida.&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;Un país donde no se admiten los principios, solamente las alineaciones.&lt;br /&gt;
&lt;/div&gt;</description><link>http://ezequielpozzo.blogspot.com/2009/09/2090.html</link><author>noreply@blogger.com (2b)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-5595960729038325799.post-6083209302287962305</guid><pubDate>Sun, 23 Aug 2009 08:20:00 +0000</pubDate><atom:updated>2009-08-23T03:19:12.795-07:00</atom:updated><title>La necesidad de OpenID</title><description>&lt;div style=&quot;text-align: justify;&quot;&gt;¿Cuantas veces uno llega a un sitio que podría resultar interesante, pero lo abandona en el momento de registrarse?&lt;br /&gt;&lt;br /&gt;En algunos sitios, el proceso de registrarse surge como una manera de garantizar &lt;span style=&quot;font-weight: bold;&quot;&gt;privacidad &lt;/span&gt;y permitir &lt;span style=&quot;font-weight: bold;&quot;&gt;personalización&lt;/span&gt;. Sin embargo en otros sitios, se exigen datos privados (email, nombre, dirección) sin que haya una necesidad verdadera. Un ejemplo de sitios donde es necesario identificarse puede ser un webmail, para mantener la privacidad del correo. Un ejemplo de un sitio donde es innecesario es un diario online, donde no hay razón para exigir al usuario identificarse antes de leer una noticia.&lt;br /&gt;&lt;br /&gt;Sea cual sea la razón, el proceso de registración implica varios puntos negativos para el usuario:&lt;br /&gt;&lt;/div&gt;&lt;ol style=&quot;text-align: justify;&quot;&gt;&lt;li&gt;Pérdida de &lt;span style=&quot;font-weight: bold;&quot;&gt;tiempo &lt;/span&gt;en el proceso de registrarse.&lt;/li&gt;&lt;li&gt;Entregar información privada a desconocidos.&lt;/li&gt;&lt;li&gt;Encontrarse ante la situación de tener que manejar una cantidad cada vez mayor de nombres de usuario y contraseñas o...&lt;/li&gt;&lt;li&gt;Tener que conformarse con recurrir a un numero reducido de contraseñas que son reutilizadas en distintos sitios.&lt;/li&gt;&lt;/ol&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;El primer punto es solo una perdida inmediata, pero los siguientes tres puntos pueden llevar a una &lt;span style=&quot;font-weight: bold;&quot;&gt;pérdida &lt;/span&gt;mayor de datos (hackeo de cuentas).&lt;br /&gt;&lt;br /&gt;Esto hace que uno se plantee muchas veces evitar conocer nuevos sitios. Personalmente, me encontré en la situación de descubrir 5 redes sociales sobre un mismo tema pero finalmente entrar solo a un par, para evitar el problema de tener que registrarme 5 veces.&lt;br /&gt;&lt;br /&gt;Es aquí donde entra OpenID. OpenID es una tecnología reciente, que permite a los usuarios obtener una identificación reutilizable. La idea es que uno registra su nombre de usuario y password en un sitio de confianza, y luego lo utiliza para ingresar en cualquier sitio que soporte esta forma de identificación.&lt;br /&gt;&lt;br /&gt;Asi, por ejemplo, uno puede crear un OpenID en &lt;a href=&quot;https://www.google.com/accounts/&quot;&gt;Google Accounts&lt;/a&gt; y luego utilizarlo para ingresar a &lt;a href=&quot;http://www.scribd.com/&quot;&gt;Scribd&lt;/a&gt;, &lt;a href=&quot;http://stackoverflow.com/&quot;&gt;StackOverflow&lt;/a&gt;, &lt;a href=&quot;http://www.stumbleaudio.com/&quot;&gt;StumbleAudio&lt;/a&gt;, o cualquier otro.&lt;br /&gt;&lt;br /&gt;Es asi que cuando llego a uno de estos sitios, especifico que poseo un OpenID de Google, para luego ser redirigido a Google Accounts, donde verifico mi nombre de usuario y contraseña. Una vez que termina el proceso de identificación, Google me envía nuevamente al sitio inicial.&lt;br /&gt;&lt;br /&gt;En forma efectiva, OpenID es una tecnología para delegar el proceso de registro e identificación de usuarios a una empresa en la que el usuario confíe.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Pero... ¿No es poner todos los huevos en una sola canasta?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Efectivamente, uno puede utilizar esta tecnología para tener un solo usuario/password. En caso de que alguien descubra estos datos, puede ingresar a cualquier sitio donde el usuario esté registrado.&lt;br /&gt;&lt;br /&gt;Sin embargo, en la actualidad la situación no es mejor. Los métodos de recuperación de password mediante una cuenta de email presentan la misma vulnerabilidad. Un intruso que descubra el nombre de usuario y password de tu cuenta de email puede usarlo para &quot;recuperar&quot; la contraseña de cualquier sitio donde estés registrado con ese email. Mucha gente se preocupa en mantener distintos usuarios y contraseñas para miles de sitios, pero usa el mismo email en cada una de las cuentas, lo que implica que efectivamente solo tienen un password (el de la cuenta de email) mediante el cual se puede acceder a todas las otras cuentas.&lt;br /&gt;&lt;br /&gt;La solución es... no poner todos los huevos en una sola canasta. Esto significa que uno puede crear distintos OpenIDs y utilizarlos en distintos contextos. Uno puede tener un OpenID  para utilizar en sitios prescindibles, con un password fácil de recordar, y tener un OpenID  para usar en sitios donde la seguridad es crítica.&lt;br /&gt;&lt;br /&gt;OpenID es una herramienta destinada a simplificar la vida del usuario e incrementar la seguridad en muchos aspectos. Pero no es una solución invulnerable.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;¿Cómo conseguir un OpenID?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Eso es fácil, probablemente ya tengas un OpenID sin saberlo:&lt;br /&gt;&lt;/div&gt;&lt;ul style=&quot;text-align: justify;&quot;&gt;&lt;li&gt;Si tenés una cuenta de Google (gmail, google docs, analytics, etc) o Yahoo ya tenés un OpenID.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Si tenés un blog en &lt;a href=&quot;http://www.blogger.com/&quot;&gt;blogger&lt;/a&gt; o &lt;a href=&quot;http://wordpress.com/&quot;&gt;Wordpress&lt;/a&gt;, tu OpenID es la dirección misma del blog (Por ejemplo, ezequielpozzo.blogspot.com es un OpenID válido)&lt;/li&gt;&lt;/ul&gt;Pero también hay muchos &lt;a href=&quot;http://openid.net/get-an-openid/&quot;&gt;otros proveedores&lt;/a&gt; de OpenID, en particular hay empresas cuya única función es proveer OpenID de manera segura.&lt;br /&gt;&lt;br /&gt;&lt;div style=&quot;text-align: justify;&quot;&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;¿Cómo instalar OpenID en mi sitio?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Eso depende del sitio. Si uno tiene un blog en blogger, es posible activar OpenID para permitir a los lectores dejar comentarios identificándose con su OpenID.&lt;br /&gt;&lt;br /&gt;Pero todos los sistemas gestores de contenido como Joomla o Drupal y Frameworks como Django o Ruby on Rails tienen agregados que permiten soportar OpenID.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;En otros posts voy a explicar otros planteos frequentes contra OpenID y como OpenID es parte de una serie de tecnologías como Open Social que otorgan mas libertad al usuario sobre sus datos privados e información personal.&lt;br /&gt;&lt;/div&gt;</description><link>http://ezequielpozzo.blogspot.com/2009/08/la-necesidad-de-openid.html</link><author>noreply@blogger.com (2b)</author><thr:total>2</thr:total></item></channel></rss>