Lobishomen, página personal

  • Páginas

Archive for the ‘Programación En General’ Category

Cinemática de Cuerpos Rígidos y Velocidad Espacial

Posted by luison.cpp en septiembre 21, 2015

This post is in spanish mainly because I’m just posting something that I previously wrote down.

Como trabajo final para una materia de la carrera escribí esto.

Para poner en contexto; algunos de los algoritmos mas eficientes de simulación física se encuentran en el libro Rigid Body Dynamics Algorithms de Featherstone, pero es muy complicado entender bien el álgebra que utiliza sin introducción previa, incluso para científicos en computación; ya que en lugar de utilizar velocidad angular y velocidad lineal, los junta en unas estructuras llamadas vectores espaciales.

Este escrito lo que pretende hacer es explicar qué son los vectores espaciales en el contexto clásico de la velocidad lineal y angular. Se trata de una revisión de la cinemática de cuerpos rígidos donde caracteriza los movimientos de cuerpos rígidos explicando el sentido que tiene hablar de la velocidad angular y al mismo tiempo sirve como introducción para la velocidad espacial.

CInematica y Velocidad Espacial

Posted in Programación En General | Leave a Comment »

Some less known advices of C++ coding for large projects

Posted by luison.cpp en enero 10, 2015

In case that you don’t know it, I’m a programer from GameCoder Studios, and we have been developing an FPS game in C++ with our own in-house engine (btw the game is named Attractio).

After developing the engine, the game, and port it for multiple platforms with different libraries, we have learned a lot in the process.

One thing that I want to share with you today are some coding advices or «principles» that may help a lot with large projects (more than 100 000 lines), and some times we didn’t notice them because in the middle-size projects are not that important:

  • In the .h files, try to only include external and standard libraries. Any object of your own project should be referenced in the .h file using  forward declarations.
    Why? because you don’t want to spend 10 minutes recompiling everything everytime you change a .h file.
  • Make your own rules for object ownership and respect them.
    Every object created by the «new» keyword should have its owner (responsible to delete it). In C++11 the smart pointers are a good tool to expressing ownership within the program itself.
  • Prefer composition over inheritance.
    Probably you are aware of the dangers of using multiple inheritance, so, if you want to incorporate functionality of multiple classes in a single one, it’s better to use composition to avoid reimplementing the same functionality over and over again (because with single inheritance you can only incorporate the functionality of a single class)
  • Use namespaces
    After you have hundreds of classes in your project, the use of namespaces made easier to find them using the class browser of your IDE. Most of IDE also allows to create virtual folders, but some times is not easy to remember in what virtual folder each class is.
  • Fix the style your own code when you can
    Sometimes the programmers are too afraid to change code that it’s already working well, because that can produce bugs. But in large projects, if you don’t fix the style of your own code, it will dirt with the time, even if it was pretty at the beginning, and in the final stages, with each change that you do to the code, you will be afraid of breaking something barely related with your change.
  • Don’t allow your classes to grow too much
    If a class is growing too much, it’s better to split it in other classes via composition, that can be tedious, but it will save huge amounts of time. Because if a class is too large, it’s likely that many programmers will be working with that class at the same time, and also, you will lost a lot of time scrolling up and down in the same file(and I’m not kidding, we use more time scrolling than coding).
  • Try to put the closely related methods near in the cpp
    That saves time in the scrolling
  • Instead of creating constructors with parameters, create constructors with not parameters and init methods
    Is tempting to create constructors with parameters to avoid using uninitialized objects, but with the time it will become common that some objects will require another objects to initialize themselves, and if you put all the initialization functions in the constructors, it will be very confusing to find in what order create the objects.
    So, it’s a lot more easier to first create all the objects, and then initialize them in any order.
  • Try to avoid the code repetition
    This advice may look pretty common, but I include it here to make something clear: even if avoid code repetition leads you to write more code, you should avoid it.
    If you find in one place of your code, a piece of code that made the same that another one, you should change them to not repeat code. Even if it’s already working.
    The reason of that is because it’s a lot easier to make changes in your code if it doesn’t have repetition (if you repeat code, you need to change many places when you make a change and you may miss some places).

That’s all I remember right now (I remember other advices but are too mainstream to rewrite them now), happy codding 😀

Posted in Programación En General | 3 Comments »

Wedge Product and Their Use in Euclidean 3D Geometry

Posted by luison.cpp en diciembre 24, 2014

Hi everyone, I have noticed that this mathematical tool is incredible useful and overlooked by most engineering careers; in math careers most of the times is looked like a weird mathematical tool that is needed for the differential geometry. But this tool have a lot of real-life applications in programming, and in my opinion, it shouldn’t be overlooked.

The Exterior Product

The Exterior Product(or Wedge Product) is a product that can be used to represent areas, volumes, subspaces and higher dimensional analogs. If we have two vectors u, v \in \mathbb{R}^n, its product is represented by:

u \wedge v

The product of two vectors is called bi-vector and doesn’t live in \mathbb{R}^n but in a space named \Lambda^2(\mathbb{R}^n).

Similarly, for a vector w \in \mathbb{R}^n, the product

(u \wedge v) \wedge w

Is called tri-vector and the space of all the tri-vectors is named \Lambda^3(\mathbb{R}^n).

Before to tell you how to do the math, I think is better to see what’s the geometrical meaning of this bi-vectors and tri-vectors, and with that in mind, the algebra will have more sense.

Geometrical Meaning

If you are reading this article, you should be already familiar with the geometrical interpretation of a vector: an algebraic object that have magnitude and direction and it’s represented by an arrow.

Similarly, an external  product of m vectors it’s called an m-blade and has an associated subspace(literally, a subspace of the \mathbb{R}^n), weight(a real number), and orientation (there are only two orientations, if v has one orientation, -v has the other one.

For example, an 1-vector v has like subspace all the multiples of v(that is a line that passes by the origin), has like weight |v|(its norm), and the orientation it’s a little harder to describe, but notice that v and -v has the same subspace and same norm; the difference between them is their orientation.

A bi-vector(or 2-vector) u \wedge v has like subspace the span of u and v, like weight the area of the parallelogram defined by u and v, and like orientation the direction of turning u to v (that can be clockwise or counterclockwise).

Wikipedia has a nice image to illustrate this:

Another thing that we should notice, is that the m-vector 0 has 0 weight, doesn’t have orientation and it’s subspace is just a set containing the 0 vector.

Algebraic Properties

This product is associative:

a^(b^c)=(a^b)^c

Anti commutative:

a^b=-b^a

Also vectors can be summed in \Lambda^m(\mathbb{R}^n). Notice that the sum of m-blades it’s still an m-vector but it’s not necessary an m-blade (an m-vector needs to have a factorization like a product of 1-vectors to be a blade).

The sum is commutative:

u+v=v+u for u, v \in \Lambda^m(\mathbb{R}^n)

Associative

(u+v)+w=u+(v+w) for u, v, w \in \Lambda^m(\mathbb{R}^n)

And also the sum and the product are distributive:

u\wedge(v+w)=u\wedge v+u\wedge w for u, v, w \in \mathbb{R}^n

Important Consequences

There are some consequences of the basic properties that are very important.

For every m-vector v, we have that:

v \wedge v = 0

The sum of two blades with the same orientation and the same subspace is also a blade and has also the same subspace and the same orientation, but the weight of the sum is the sum of the weights.

The sum of two blades with the same subspace but different orientations has the same subspace but its weight is the difference of the weights and the orientation correspond to the orientation of the blade with higher weight. The only «exception» to this consequence is when both blades have the same weight; in that case the sum is 0.

Any n-vector y a \Lambda ^n (\mathbb{R}^n) space can be factorized like:

x(e_1 \wedge e_2 .... \wedge e_n)

Where x\in \mathbb{R} (Maybe some of you are not familiar with the e_1, ..., e_n notation, so, I should say that e_i means the vector that has 1 in its i-th input and 0 in the other ones).

For that reason, we call e_1 \wedge e_2 \wedge …. \wedge e_n the pseudoscalar and we represent it by the symbol \omega

So, for \mathbb{R}^3 the 1-vectors have e_1, e_2, e_3 like base, the bi-vectors have e_1\wedge e_2, e_1\wedge e_3, e_2\wedge e_3 like base, and for tri-vectors the base is just the vector \omega. Some times it’s useful to represent the elements of the bases with bitmasks.

For any v_1, v_2, ... , v_n \in \mathbb{R}^n, we have that:

v_1 \wedge v_2 .... \wedge v_n=Det(v_1, v_2, ..., v_n ) \omega

Applications

To compute the area of the parallelogram defined by the vectors u, v, we only need to compute the weight of the blade u, v, for example:

(a, b) \wedge (c, d) = (ad-bc) \omega

Maybe the most useful application is to intersect some geometrical object given in explícit forms without needing to change them to implicit ones.

For example, a line given by p = q_1+d t_1 with a plane given by p = q_2+a t_2 +b t_3. We need to solve this equation:

q_1+d t_1=q_2+a t_2 +b t_3

The trick it’s pretty simple:

(q_1+d t_1) \wedge a \wedge b=(q_2+a t_2 +b t_3) \wedge a \wedge b

q_1 \wedge a \wedge b +t_1 d \wedge a \wedge b=q_2 \wedge a \wedge b

t_1 d \wedge a \wedge b=(q_2-q1) \wedge a \wedge b

t_1 Det (d, a, b)=Det(q_2-q1, a, b)

t_1 =\frac{Det(q_2-q1, a, b)}{Det (d, a, b)}

So, the point of intersection is q_1+d\frac{Det(q_2-q1, a, b)}{Det (d, a, b)}

Similar tricks can be made to find intersection between planes in the space, etc.

Another useful trick can be know if a point is inside a line. For example, if the line is defined by a = b+d t and the point is c.  We have that c is on the line if and only if

(c-b)\wedge d=0

Similar tricks can be applied to know if a line is inside a plane, or a point inside a plane.

I want more

If you liked this mathematical tool, you should read about Geometric Algebra (it’s not the same that Algebraic Geometry):

http://en.wikipedia.org/wiki/Geometric_algebra

Posted in Programación En General | Leave a Comment »

C++ Advantages

Posted by luison.cpp en noviembre 17, 2014

Hi everyone, I’m just changing the language of my blog posts. Previously I was too lazy to write my blog posts in a foreign language, but I just realized that its harder for me to write in English because I need more practice, so I’m going to write most of my future posts in English.

The topic of this post is about how useful can be C++ if it’s used correctly.

I have heard and read many times that C++ is a fast language, but it’s too hard to maintain. I’m not sure about the origin of this myth, but I think that it became popular when Java didn’t include many of the C++ features and many users understood that these features are dangerous and it’s better to not have them in the language.

The reality is that most of these features make the language harder to learn, and many requires to be careful, but also, they can make the code easier to maintain and easier to read. Large projects are more suitable to be coded in C++ because these features can improve the code legibility.

I will show some examples here:

Pass by Value and pass by Reference

Suppose we have two classes named Acquis and Book. The class Book has these 3 public members:

int id;
string author;
string title;

And the class Acquis has these methods:

Book getBookById(int id);
void removeBookById(int id);
void addBook(Book newBook);

So, in C++ this is very straightforward, it’s obvious that the only way to modify the books from the Acquis is using addBook or removeBookById.

But in other languages that don’t have the option to pass objects by value, this situation has the issue that it’s not clear if getBookById returns a copy of the Book object or a reference to the object that is inside the Acquis, and also, it’s not clear if you can modify the Book that you just added to the Acquis or you should add a copy.

All of these should be written in the documentation, and that make the code looks harder to read, in contrast, in C++ the declaration of each method told explicitly that the Book that you get is a copy of the original and also, that when you add a new book, a copy will be stored instead of the same object.

I found this issue specially annoying when working with position and velocity vectors of bodies inside a videogame.

Also, think about the Book class, in C++ it’s clear that there are an integer and two strings, but in other languages, that two strings may be null pointers, and that’s another opportunity to cause bugs or confusions (in fact, some people prefer to never pass or return null, in order to avoid thouse kind of situations).

Pointers and References

Speaking of null, sometimes the null pointers are useful, because sometimes we want to wait to initialize an object and sometimes not all the members of a class need to be initialized.

But also, sometimes we need to have methods that receive not null objects, and it can be annoying(and also make the code harder to read and less efficient) if we check in each method call if a pointer is null or not.

C++ fixes this problem with the references (if the reader doesn’t know them, the references are similar to the pointers, but always points to a previously existing object(cannot be null) and it’s impossible to change where a reference is pointing), so, if a function call needs a not null pointer, it’s very easy to just ask for a reference.

Separation Between Declaration and Definition

Ok,  I admit, this can be annoying, but also has a huge advantage: if you just use a reference for each class, without using their former methods, it’s possible just declare the class, and that helps to have some control of which classes are using the methods of which other classes.

That’s very important if you want to code a program that respects the Law of Demeter:

http://en.wikipedia.org/wiki/Law_of_Demeter

Classes and Structs

At first, it may look silly that C++ has classes and also has structs, and both do exactly the same thing,  but this has an unsuspected advantage: programmers know the C structs and also know the encapsulation principles, so, it’s possible and intuitive to use the keyword «struct» to refer to C-like structs (all the members public, and no methods) and the keyword class to use for classes following the encapsulation principles.

It’s useful being able to distinguish between classes and classic structures.

shared_ptr and unique_ptr

I assume that many of the readers don’t know what are these things, briefly: unique_ptr is a pointer that deletes its content when it goes out of scope and shared_ptr are pointers that deletes their contents when all the copies of the same pointer are deleted from memory.

When a unique_ptr is declared inside a class, it’s clear that the class owns the pointer, so, you know that the content of the pointer will be deleted when the «parent» object is deleted and no one else should store a copy of that pointer.

When a shared_ptr is declared inside a class, that’s explicitly saying that many classes have access to that pointer, and that should be taken in account.

Hiding the Details of Some Structures

Sometimes there are structures that we want that only a single class is being able to access its contents, but we want the other classes to be able to store pointers to that structure (for example, the node of a tree).

In thouse cases, it’s possible to declare the struct in the header file, and define the struct in the cpp file, that trick it’s a pretty easy and clear form of telling other programmers not to modify that struct in other classes.

Operator Overloading

Make a lot cleaner the algebraic operations, some Java programmers think that Java doesn’t support them because they can obfuscate the code, but the reality is that operator overloading doesn’t work very well with references, it’s necessary to store the objects by value in order to make the operator overloading really useful.

Many Tools for Many Circumstances

Some programming languages are not very fast but they are very easy to read (for example, Java, C#, AS, Python) and the compilers need to make a large amount of heuristics in order to guess how to make the code perform well. These language are called «high level languages».

Other programming languages, like C or Assembler have a more direct access to the memory and the resources and can make more intelligent choices about how to do each thing. These languages are called «low level languages».

The charm of C++ is that C++ has both advantages, it can be high level  sometimes, and low level another times (and consequently, it has many forms of doing the same thing), being easier to maintain but slower in the high level parts, and harder to maintain but faster in the low level parts.

Some people don’t like the idea of mixing low level and high level, but that capability is a huge advantage, because it’s possible to use low level just in the bottle necks(less than the 5% of the code) and use the advantages of a high level language in the rest of the code.

In conclusion, if you have a team with many experienced programmers, you should consider C++ for your projects, not only for the performance, but also for the tools to make the projects easier to maintain.

Posted in Programación En General | Leave a Comment »

Programar en 2D vs programar en 3D

Posted by luison.cpp en octubre 1, 2014

Llevo unas cuantas semanas sin publicar cosas en el blog, estuve tentado a publicar un artículo explicando las diferencias entre C y C++, o un artículo explicando qué son realmente los números (como entres abstractos, no como símbolos) pero cuando comencé a escribirlos ninguno de esos me convenció.

Ayer en el podcast (para quienes no lo sepan, algunos desarrolladores de videojuegos hemos estado transmitiendo un podcast las últimas semanas, y lo subimos a YouTube https://www.youtube.com/channel/UCtIT1XPTMmJFhqar50nEaLg ) surgió el tema de qué clase de conocimientos técnicos son necesarios para desarrollar en 3D, y creo que aquí es un buen lugar para explicar eso.

Primero que nada, el viejo sistema de coordenadas que enseñan en muchas prepas e incluso en el tronco común de muchas carreras profesionales en 3D se vuelve inútil: no tiene sentido hablar de «la ecuación de la recta» o «la pendiente», ni siquiera el «ángulo de rotación», de esas cosas, lo único que conserva sentido es hablar de las coordenadas x, y, z.

Toda la geometría se maneja con vectores, matrices y cuaterniones.

Quizá el ejemplo mas notable de cómo algo muy simple en 2D se puede convertir en algo muy complicado en 3D son las rotaciones:

En 2D sabemos que si rotamos un objeto 45 grados y luego 30 grados, el objeto final tendrá una rotación de 75 grados, y también sería lo mismo rotarlo primero 30 grados y luego 45 que rotarlo primero 30 y luego 45.

Sin embargo, en 3D el panorama es muy diferente, rotar un objeto 90 grados alrededor del eje X y luego rotarlo 90 grados alrededor del eje Y no es lo mismo que rotar 90 grados alrededor del eje Y y luego 90 grados alrededor del eje X.

En 2D es muy fácil pensar en la rotación como un ángulo y ya, pero en 3D no hay una manera de representar rotaciones que resulte ser la mejor en todos los casos y por lo general es necesario conocer muchas y hacer conversiones entre ellas:

  • Ángulos de Euler. Se trata de 3 números, que pueden ser llamados a, b y c, de manera que el vector (a, b, c) representa una rotación alrededor del eje X con un ángulo de a, seguida de una rotación alrededor del eje Z con un ángulo de b para finalizar con otra rotación alrededor del eje X con un ángulo de c (los ejes y el órden de los mismos pueden variar, sólo di un ejemplo).
    Esta representación se utiliza mucho en programas de animación, ya que es muy fácil que el usuario escriba una rotación, y al mismo tiempo la interpolación de estos vectores produce una animación que en muchos casos se ve bien.
    Pero tiene muchos problemas, como por ejemplo que es posible representar de muchas maneras diferentes una misma rotación, y es difícil componerlas (es decir, si rotas un objeto por (a, b, c) y luego por (d, e, f), no es fácil dar un vector que de la rotación resultante; pista: NO es (a+d, b+e, c+f) ).
    Otro problema que tienen los ángulos de Euler es el llamado Gimbal lock, que se refiere a la pérdida de un grado de libertad en algunas configuraciones desafortunadas. Se puede ver en este video
    http://youtu.be/zc8b2Jo7mno
  • Configuración ángulo-eje. Se trata de tener un vector v y a parte un ángulo a, este par representaría la rotación de un ángulo a alrededor del eje v.
    Algunas veces esto se simplifica utilizando el producto del vector por el ángulo. Esta configuración me gusta porque es bastante fácil de visualizar, fácil de preguntar al usuario, la interpolación funciona bastante bien y no tiene el problema del Gimbal Lock. Lo malo es que, al igual que con los ángulos de Euler, es que la composición no se comporta tan bien (es decir, si se aplica una rotación y luego otra, es difícil decir cuál es la rotación final.
  • Matrices de Rotación. Matrices que al multiplicar un vector por ellas, rotan el mismo. La composición de rotaciones con matrices es bastante directa, y no es muy complicado de visualizar qué rotación representa cada matriz (basta con observar que cada columna indica a dónde se mueve cada uno de los ejes).
    Lo malo con esta representación es que requiere de 9 números(o 6 usando artes arcanas), lo que hace difícil preguntarsela a los usuarios en programas de animación; la composición aunque sea intuitiva, es lenta; y la interpolación no sirve.
  • Cuaterniones. Este es el favorito de nosotros, los programadores, ya que su composición es bastante rápida, su interpolación funciona muy bien y sólo requiere de 4 números (o 3 usando algunas artes arcanas), por desgracia no son tan fáciles de visualizar.
    Matemáticamente hablando, los cuaterniones son números con una parte real y 3 partes imaginarias, y suelen representarse como a+bi+cj+dk (donde, a, b, c, d son números reales), y se cumple que i^2=j^2=k^2=ijk=-1. Para representar una rotación se toma la variable a como la mitad del coseno del ángulo, mientras que (a, b, c) se toma como el eje de rotación multiplicado por la mitad del seno del ángulo.

En cuanto a las velocidades, la velocidad angular se considera como el eje de rotación multiplicado por la derivada del ángulo.

Otra pregunta que salió en el podcast era si los engines nos puden salvar de tener que lidiar con todo esto; y la respuesta es que no, sería como pedir que un engine nos permitiera hacer juegos en 2D sin utilizar coordenadas X y Y. Unity3D, el engine que es popular en este momento, utiliza cuaterniones para representar sus rotaciones, y provee herramientas para hacer conversiones con ángulos de Euler y matrices de rotación, así que aunque facilita la parte de escribir el código de esta geometría, aún así hay que entenderla.

Finalmente está la duda de si Unity ya trae movimientos de cámaras, entre otras cosas, que hagan innecesario aprender estas cosas, y la respuesta es que Unity trae unos cuantos ejemplos de cosas que se necesitan hacer comunmente en juegos; pero las que yo he visto, resultan inadecuadas para ponerse  en un producto final.

Por ejemplo, este programador escribe sus experiencias con la plantilla de side-scrolling de Unity y explica por qué tuvo que hacer la suya:

http://www.gamasutra.com/blogs/YoannPignole/20131010/202080/The_hobbyist_coder_1_2D_platformer_controller.php

También en 2:44 del siguiente video , este diseñador de videojuegos nos comienza a mostrar como el uso de plantillas no muy adecuado cuando se está desarrollando un juego, ya que quien diseñó la plantilla es probable que no haya tomado en consideración todas las situaciones en las que podría ser usada:

En sí en este artículo me concentré en hablar de las rotaciones, pero ahora que lo estoy pensando bien, también en la parte de los gráficos hay bastantes complicaciones, ya que las cosas dejan de ser tan sencillas como manipular pixeles y se pasa a manipular mallas de triangulos y las ecuaciones de iluminación se vuelven muy importantes.

Incluso en Unity para lograr ciertos efectos de iluminación es necesario programar el GPU, aunque por ahí leí que Unreal Engine 4 ya puede ahorrar esa parte.

Respecto a la animación en 3D, las cosas dejan de funcionar por frames y pasan a funcionar por esqueletos y splines, y a veces detalles como el hecho de que los personajes coloquen sus pies sobre una escalera cuando la están subiendo puede llegar a requerir conocimientos técnicos muy fuertes. Sin embargo no me sorprendería que algunos engines de los más avanzados faciliten las cosas; pero sin que dejen de ser tan complicadas.

Si pudiera destacar algo de programar en 3D además de que es exigente a nivel técnico, es que puede ser muy gratificante debido principalmente, no tanto porque el producto final sea mejor o peor que un producto en 2D, sino por el mismo motivo que nos gusta un gameplay profundo en un videojuego o que nos gusta encontrar personajes tridimensionales en una historia: es mentalmente estimulante.

El mayor problema con trabajar en 3D(incluso si se supera la barrera técnica), es que requiere una parte artística demasiado fuerte, de tal manera que hay que conseguir dinero para que alguien más haga la parte artística o tener un equipo de desarrollo bastante diverso en varias especialidades.

Posted in Programación En General | Leave a Comment »

Attractio

Posted by luison.cpp en septiembre 2, 2014

Prestando algo de atención, me doy cuenta de lo desatendida que he tenido esta página, entre un post y el siguiente pasaron 3 años XD, tal parece que mi motivación para escribir cayó en gran medida al no ser el centro de atención en la OMI.

Recientemente me puse en mente la idea de volver a escribir en este blog, debido a que me he dado cuenta de que me gusta escribir, y aunque no es precisamente lo mas productivo escribir aquí; es mejor que procastinar.

¿Por qué les cuento todas estas cavilaciones?, pues mas que nada porque sentí necesario justificar por qué nunca les había hablado de Attractio.

 

Probablemente muchos de ustedes ya lo sabrán, pero para los que no: este videojuego es muy importante para mi, por el claro motivo de que yo programé buena parte de él (el gameplay entre otras cosas).

Si en este post escribiera todas las experiencias que he vivido durante el desarrollo de este juego, no terminaría nunca; sin embargo a manera de introducción daré un MUY breve resumen de cómo fue el desarrollo de este juego y una breve descripción del mismo.

Desarrollo del Juego

Todo empezó en el año 2010 cuando Marcel me invitó a desarrollar un videojuego junto con él y Beto. Se trataba de un side scroller 2.5D donde el protagonista era una flama, y el nombre tentativo del juego era Flame.

Antes de comenzar a desarrollar el juego, decidimos empezar haciendo un engine (Para quienes no lo sepan, un engine es básicamente una colección de rutinas pre programadas que se pueden utilizar en aplicaciones interactivas, en particular en videojuegos. Otra forma de verlo podría ser como la parte de programación que las compañías reutilizan de un videojuego anterior en sus siguientes videojuegos).

Casi un año después de haber empezado a hacer el engine, decidimos dejar a un lado la idea de Flame y por el momento comenzar a pesar en otra cosa (algo que según nosotros podríamos hacer rápido :P), y así fue como nació la idea de Attractio: un FPS-puzzle basado en la manipulación de la gravedad.

En verano de 2012 los 3 calificamos para asistir a un internship en Microsoft, este fue un punto muy importante porque después de este evento fue cuando empezamos a manejar dinero en la empresa (mismo que vino del internship).

En el segundo semestre de 2012 ya teníamos un demo jugable (que se lo mostramos a conocidos, pero nunca subimos), y en un evento llamado Dev Hour MX mostramos por primera vez gameplay de nuestro juego de manera pública.

A finales de ese mismo año nos constituimos como empresa.

Durante 2013 conseguimos los assets de arte(dibujos, modelos, música) a través de outsourcing, se nos unieron 2 personas más al equipo, para complementarnos en otros aspectos: Mafa en guión y Poncho en negocios.

Ese mismo 2013 publicamos 2 trailers, asistimos a más convenciones, subimos un demo privado y tiempo después un demo público y también subimos nuestro juego a Greenlight.

Este año(2014) salió a la venta el Early Access de Attractio, gracias a ese Early Access junto con unas cuantas promociones (la vez que estuvo a 75% de descuento en Humble Bundle Store, la vez que estuvo en Indie Game Stand y la vez que estuvo en BAGB) y el creciente número de juegos que han estado recibiendo Greenlit, obtuvimos Greenlit.

Recientemente recibimos kits de desarrollo de PS Vita, y gracias a una muy buena respuesta que tuvimos en Twitter respecto a un posible port de Attractio para PS Vita, decidimos iniciar el port.

Descripción del Juego

Voy a copiar la descripción de Greenlight porque ya me voy a dormir 😛 :

Attractio is an indie first-person puzzle game developed with the GC Engine (GameCoder Studios’ in-house engine) where the player has to solve challenging puzzles manipulating gravity. These puzzles challenge the usual way of thinking about physics and gravity. In Attractio each object has its own gravity direction which can be changed by the player.

The interaction between objects with different gravity directions presents various possibilities. Thus, the player can solve in many ways each of the different puzzles across the game.
Some examples of these possibilities are:

  • Creating a zero gravity object by colliding two objects of the same weight and opposite gravity direction.
  • Softening the character’s landing with a box with gravity direction perpendicular to a wall. This happens due to the friction between the wall and the box.
  • Gravity-Boxes: A special kind of box that, when touched by another box, flips the second box’s gravity direction. This is useful to automate some mechanics in the game and perform simultaneous tasks.

The player will play with three different characters, each one with different abilities, adding variety to the puzzles. Some puzzles can be solved by a contestant alone, but for some others the player will need to switch between the contestants. Attractio has a unique gameplay and puzzles that will entertain the player for hours. This game was inspired by Valve’s Portal.

Si quieren saber más del juego, en Greenlight hay una muy buena descripción:

http://steamcommunity.com/sharedfiles/filedetails/?id=182738655

También pueden buscar sobre este juego en Youtube, y actualmente lo pueden adquirir aquí:

http://www.attractio-game.com/shop/

Posted in Personal, Programación En General, Videojuegos | 1 Comment »

Level Up: De las Coordenadas a los Vectores (parte 2)

Posted by luison.cpp en agosto 7, 2014

Dado el buen recibimiento que tuvo el primer post, aquí va la continuación :D.

En este nuevo post hablaré, entre otras cosas, de las soluciones a los problemas propuestos al final de la parte 1. Sin embargo, no quiero ponerlos como un solucionario, sino que quiero explicar matemáticas usándolos.

Un Poco de Notación

No soy fan de introducir notación a menos que se justifique cuando recién se acaba de ver algo interesante, pero desafortunadamente tendré que cometer ese sacrilegio aquí.

En 2D hay 2 vectores bastante importantes

(1, 0) y (0, 1)

Esos los llamaremos e_1 y e_2 respectivamente, de esta manera ya tenemos una nueva forma de escribir vectores:

(a, b)=a e_1+b e_2

Para 3D también hay una notación análoga:

e_1=(1, 0, 0), e_2=(0, 1, 0)  y e_3=(0, 0, 1)

Un posible problema aquí es que el mismo símbolo e_1 se usa para representar un vector en 2D y un vector en 3D; ante esta posible confusión, sólo nos queda confiar en que el contexto explique de qué e_1 o de qué e_2 estamos hablando.

En algunos textos se utilizan las letras i, j y k para llamar a e_1, e_2, e_3 respectivamente, pero esto tiene serios problemas:

  • En dimensión mayor a 3 se vuelve confuso
  • Con los números complejos la letra i representa un vector diferente: e_2
  • Con los cuaterniones la letra j y la letra k representan otros 2 vectores diferentes: e_3e_4.
  • En la programación se suelen utilizar estas 3 letras para representar índices (recomendación: nunca llamen «j» a una variable, se confunde con la i).

Otra aplicación de esta notación, es que es posible recuperar las coordenadas de un vector sin necesidad de inventar funciones raras:

  • e_1 \cdot v representa la primera entrada(o coordenada x) de v
  • e_2 \cdot v representa la segunda entrada(o coordenada y) de v.

Ángulos

Como decía en  la parte 1, es preferible usar vectores de dirección que usar ángulos directamente. El vector (\cos a, \sin a),  es un vector de magnitud 1 de ángulo a; normalmente no es necesario conocer el valor de a, ya que por lo general usaremos siempre cos a y sen a en lugar de usar directamente el valor de a.

De igual manera, si queremos sumar ángulos podemos usar el producto complejo:

(\cos \alpha, \sin \alpha) (\cos \beta, \sin \beta)=(\cos (\alpha + \beta), \sin (\alpha+\beta))

Ahora, hablando de propiedades importantes de los ángulos, quizá la que mas vamos a utilizar es que si dos vectores son ortogonales o perpendiculares, entonces su producto punto es 0, y además, si el producto punto de 2 vectores es 0 entonces son ortogonales.

Esta propiedad viene implícita en la interpretación geométrica del producto punto que vimos en la parte 1 (el producto de las magnitudes por el coseno).

De hecho esta propiedad es tan conveniente que ortogonal se define como producto punto igual a 0, por tanto, el vector (0,0) se considera que es ortogonal con todos los vectores del plano.

¿Qué son las Rectas?

Las rectas son un componente importante en la geometría y ya nos tardamos en abordarlas, así que ahora es el momento :).

Es común que la gente exprese las rectas usando ecuaciones tales como:

y = \frac{1}{3}x+4

Hay que estar conscientes también de que a pesar de que todos hemos llegado a trabajar con ecuaciones de rectas, las rectas no son ecuaciones, ya que varias ecuaciones pueden representar la misma recta. Las rectas son todos los puntos que cumplen con las ecuaciones, es decir, las rectas son conjuntos.

Por ejemplo, la anterior ecuación representa este conjunto de puntos:

\{ (x, y) | y = \frac{1}{3}x+4 \}

Lo cual se traduce como el conjunto de todos los puntos (x, y) tales que cumplen con esa ecuación.

La pregunta obligatoria es ¿qué clase de conjuntos son una recta? y ¿cómo expresar una recta a través de una fórmula?

En general no podemos definir las rectas a partir de ecuaciones con sólo escalares, ya que en 3D una ecuación con sólo escalares no determina una recta, sino un plano; necesitamos una definición que sirva tanto para 2D como para 3D y de ser posible, extensible a otras dimensiones.

La primer cosa a notar, es qué sucede con la multiplicación de un vector por un escalar:

Al multiplicar un vector por un escalar positivo, cambia su magnitud, incluso para cualquier vector a distinto de 0 y cualquier escalar k \ge 0 se cumple que k\frac{a}{|a|} tiene magnitud k y la misma dirección que el vector a. Esto quiere decir que el conjunto de todos los multiplos por escalares positivos de un vector es una semirecta en la dirección del vector.

Con respecto a los múltiplos por escalares negativos, se obtendría una semirecta en dirección opuesta:

Es decir, una recta que pasa por el origen, es el conjunto de todos los múltiplos escalares de un vector.

Si tenemos una recta que pasa por el origen, es posible trasladarla sumándole un mismo vector a cada punto de la recta.

Dicho de otra manera:

tD para t \in \mathbb{R} y D \in \mathbb{R}^n es un punto en la recta que pasa por el origen y que tiene la misma dirección que el vector D.
Mientras que P+tD para t \in \mathbb{R} y P, D \in \mathbb{R}^n es un punto en la recta que pasa por P y que tiene la dirección D.

Por tanto, una manera de definir recta es la siguiente:

DEFINICIÓN: RECTA. Una recta r es un conjunto de puntos para los cuales existe un punto Q y un vector D, tal que |D|>0 y que:
r=\{ P \in \mathbb{R}^n | P=Q+tD para algún escalar t \}

Esta definición sirve para 2D, 3D e incluso dimensiones más arriba.

Para propósitos de programación, lo mas sencillo es guardar el punto Q y el vector D.

TIP: Dados dos puntos A y B, la recta que pasa por ellos es:
{P | P=A+(B-A)t}

Intersección de Rectas

Primero que nada, es necesario resaltar que a pesar de que en 2D casi todas las rectas se intersectan, en 3D muy rara vez 2 rectas se intersectan; así que por el momento vamos a dejar el caso 3D a un lado y nos vamos a centrar en el 2D.

Tengo que admitir que por bastante tiempo yo me veía forzado a regresar temporalmente al uso de coordenadas y representaciones implícitas de rectas cada que quería encontrar el punto de intersección.

Sin embargo, luego aprendí un truco donde el producto cuña nos salva :D.

En la sección pasada se utilizó un hecho algebrárico, y es que el producto cuña de un vector consigo mismo siempre es 0 ; esto se deriva del hecho de que es antisimétrico, por tanto a^a=-a^a y el único número real que es su propio negativo es el 0.

Definiendo el  problema, consideremos dos rectas r={P | P=A+Bt} y s={P | P=C+Dt}, queremos encontrar un punto que se encuentre en 2 rectas; lo cual es equivalente que hayar 2 números t_1 y t_2 tales que:

A+B t_1 = C + D t_2

Ahora, si encontramos sólamente t_1 o sólamente t_2, no hay necesidad de encontrar el otro, y al mismo tiempo no queremos tener 2 incognitas en una misma ecuación, así que vamos a deshacernos de t_2 con la magia del producto cuña:

(A+B t_1)\wedge D = (C + D t_2) \wedge D

A\wedge D+B\wedge Dt_1=C\wedge D

Luego de hacer esto ya se vuelve sencillo despejar t_1:

t_1=\frac{C\wedge D-A\wedge D}{B\wedge D}

O de manera equivalente:

t_1=\frac{(C-A)\wedge D}{B\wedge D}

Nótese que esta solución sólo tiene sentido cuando B\wedge D\neq 0, y eso pasa cuando los vectores B y D no tienen la misma dirección; podría parecer una limitante, pero no lo es, ya que el producto cuña de B y D es 0 cuando el área del paralelogramo definido por (0, 0), B, D y B+D es 0, y eso sólo pasa cuando B y D tienen la misma dirección y si 2 rectas tienen la misma dirección entonces son paralelas y era de esperarse que el sistema de ecuaciones no tuviera solución,

Proyecciones Ortogonales

Muchas veces tenemos 3 vectores v, a, b, c \in \mathbb{R}^3, (con a, b y c ortogonales entre sí) y queremos encontrar escalares \lambda_1, \lambda_2, \lambda_3 tales que:

 v=\lambda_1 a+\lambda_2 b+\lambda_3 c

Este manera de sumar con pesos se le conoce como combinación lineal.

¿Ejemplos de cuándo es necesario hacer esto?

  • En un FPS cuando el jugador presiona «W»(o cuando mueve el stick hacia adelante), el personaje no se mueve siempre en la misma dirección, sino que la dirección depende de hacia dónde esté mirando. Tampoco se mueve siempre en la dirección de la cámara, ya que puede estar mirando hacia arriba o hacia abajo pero siempre se debe de mover sobre el suelo. Algo que podría servir sería simplemente hacer 0 el eje Y, pero el suelo a veces está inclinado y también hay ocasiones en las que se desea trepar por el techo o las paredes, así que hacer 0 el eje Y no es una solución universal.
  • La IA de un enemigo que intente esquivar un misil que se mueve en línea recta: necesita saber cómo está su posición con respecto a la recta por la que se mueve el misil, para saber si está a una distancia prudente de la recta o si se tiene que alejar, y también debe de saber cómo alejarse.
  • Si una esfera cae al suelo con una velocidad v, y el suelo está inclinado, hay que encontrar un vector perpendicular al suelo y expresar la velocidad como combinación lineal de estos vectores.
    De manera que el coeficiente del vector perpendicular al suelo es proporcional a qué tanto debe de rebotar la esfera, los otros coeficientes indican cómo aplicar la fricción.
  • Como último ejemplo, si una esfera se está moviendo sobre el suelo con una velocidad v, si el suelo está inclinado entonces la gravedad modifica la velocidad de la esfera, para saber de qué manera la modifica es necesario expresar la gravedad como combinación lineal de los vectores de dirección del suelo y del vector perpendicular al suelo. Los coeficientes de los vectores de dirección indicarían hacia donde va a cambiar la velocidad la gravedad, mientras que el vector perpendicular al suelo es proporcional a la fricción.

Ahora volviendo al tema: ¿cómo expresar un vector como combinación lineal de otros vectores ortogonales?

La respuesta radica en la fórmula que vimos en la parte 1: A \cdot B = |A||B| \cos \theta y un poco de trigonometria a la antigüa usanza. En particular tenemos la situación de esta imagen:

Al escalar |A| \cos \theta se le llama la proyección escalar de A sobre B, y al vector \frac{B}{|B|}|A|\cos \theta se le llama la proyección de A sobre B. Otra forma de expresar la proyección de A sobre B es multiplicando por |A| y dividiendo entre |A|, de esta manera no cambia su valor:

\frac{B}{|B|^2}|A||B|\cos \theta

Usando la fórmula que vimos en la parte 1, la proyección se puede calcular así:

\frac{B}{|B|^2}A\cdot B

En general es preferible siempre proyectar sobre vectores de magnitud 1, ya que si B tuviera magnitud 1, podríamos calcular la proyección sin  necesidad de dividir entre |B|^2.

A estas alturas ya debería ser claro que la solución al problema planteado al  inicio de la sección es:

v=\frac{v\cdot a}{|a|^2}a+\frac{v\cdot b}{|b|^2}b+\frac{v\cdot c}{|c|^2}c

Segmentos

Los segmentos de recta, o también llamados simplemente «segmentos», son líneas rectas que unen 2 puntos en el plano, O dicho de otra manera, si A y B son dos puntos, P está en el segmento que una a A con B, si P es colineal con A y con B y además P está entre A y B.

La primera parte(saber si P es colineal con A y con B) creo que ya la tenemos lo suficientemente clara, pero la segunda parte «P está entre A y B», aún hace falta aterrizarla.

SI estuviéramos hablando de números reales podríamos decir que P está entre A y B si A < P < B o si B < P < A; pero como estamos hablando de vectores, no podemos comparar así que necesitamos un sustituto para esto.

Una manera de hacerlo es observar que si A, B y P son colineales, entonces P-A y B-P  van a ser un múltiplos escalares de B-A, si ambos son multiples escalares positivos, eso querría decir que tienen la misma dirección que B-A, lo que significaría que al moverse desde A hasta P y luego de P hasta B se sigue la misma dirección siempre.

Así que una definición mas aterrizada podría ser: P está en el segmento que une a A con B, si P es colineal con A y B; y además P-A y B-P tienen la misma dirección. Pero esta definición de segmento aún puede volverse más simple.

Recordemos que A+(P-A)+(B-P)=B, por tanto (P-A)+(B-P)=B-A, si P está en el segmento entonces P-A, B-P y B-A tienen exactamente la misma dirección, la cual llamaremos D, por lo cual existen escalares positivos j, k y m tales que:

P-A=jD, P-B=kD, y B-A=mD

Esto quiere decir que  jD+kD=mD, lo cual es equivalente a:

\frac{j}{m}mD+\frac{k}{m}mD=mD

Sustituyendo mD por B-A y m por |B-A|:

\frac{j}{|B-A|}(B-A)+\frac{k}{|B-A|}(B-A)=B-A

Esto implica que j+k=|B-A|, ya que ambos son positivos, así que \frac{j}{|B-A|} y \frac{k}{|B-A|} son números reales positivos que suman 1, lo cual quiere decir que existe un número entre 0 y 1 al que llamaremos t, tal que t=\frac{j}{B-A} y que 1-t=\frac{k}{B-A}

De esta manera P=A+(B-A)t, y después de ver todo esto, no es difícil convencerse de que cualquier t entre 0 y 1 que se elija, va a estar en el segmento, así que nuestra definición de segmento será la siguiente:

El segmento de recta que une a A con B es el conjunto de puntos
{ P | P=A+(B-A)t para t\in [0,1] }

Esta definición trae explícito cual es el significado geométrico de A y de B, mientras que P es cada uno de los puntos del segmento, pero vale la pena pensar, ¿cuál es la relación entre P y t?

La relación entre P y t es la distancia entre P y A con respecto a la distancia entre A y B; es decir, |P-A|/|B-A|, si queremos partir el segmento en 3 segmentos de la misma longitud, lo haríamos elegiendo los valores 1/3 y 2/3 para t. Si queremos encontrar el punto que está a la mitad del segmento, basta con elegir t=0.5, de tal forma que P=A+(B-A)*0.5=(A+B)/2.

Si queremos saber si un punto P está en el segmento, podemos usar el producto punto de B-A y P-A, si el producto punto es exactamente igual a |B-A||P-A| (o muy cercano, en caso de que estemos programando y haya errores de precisión) eso querría decir que el coseno del ángulo entre los 2 vectores es 1, por tanto B-A y P-A tendrían la misma dirección y P estaría en la recta.

Una vez sabiendo que P está en la recta, para saber si está en el segmento, podemos usar la proyección orotogonal escalar de (P-A) sobre (B-A) y ver que sea menor que |B-A| y mayor que 0. Afortunadamente para nosotros, podemos reutilizar el mismo producto punto:

\frac{(P-A) \cdot (B-A) }{|B-A|^2}

Distancia entre un Punto y una Recta

La distancia entre un punto P y una recta l se puede definir como la longitud del segmento mas corto que une un punto de l con P. Dicho segmento mas corto siempre forma un ángulo recto con l.

Ejem, saqué esta imagen de http://astarmathsandphysics.com/a-level-maths-notes/C4/a-level-maths-notes-c4-finding-the-minimum-distance-between-a-point-and-a-line.html

Es decir, la distancia entre el punto y la recta, sería |P-A|

Siendo l una recta, vamos a suponer que la tenemos representada a través de un punto Q y una dirección D:

l={B | B=Q+Dt} donde |D|=1

Hay que observar que A-Q es la proyección ortogonal del vector P-Q sobre el vector D.

Repasando, en este punto conocemos Q, conocemos D y conocemos P; por tanto podemos calcular P-Q; y dado que sabemos cómo hacer la proyección ortogonal, también podemos calcular A-Q, y por tanto podemos calcular A:

A-Q=((P-Q)\cdot D)D

Lo que implica:

A=((P-Q)\cdot D)D+Q

Dado que |P-A| es la distancia entre el punto y la recta, entonces la esta distancia se puede calcular así:

|P-A|=|P-((P-Q)\cdot D)D-Q|

Significado Vectorial de la Representación Implícita de Rectas

A la representación de rectas como P=Q+tD se le llama representación explícita, mientras que a la representación por Ax+By+C=0 se le llama representación implícita. Estas denominaciones se deben a que la representación explícita dice cómo encontrar puntos de la recta(osea, sustituyendo valores de t), mientras que la implícita no dice eso, al menos no tan directamente.

Para explorar el significado vectorial de la representación implícita de rectas, veremos que pasa si en lugar de expresar la ecuación como una suma de productos, la expresamos como un producto punto:

(x, y) \cdot (A, B) + C = 0

Sabemos que si la recta pasa por el origen, entonces
A(0) + B(0) + C = C = 0

Esto quiere decir, que cuando una recta pasa por el origen, la ecuación expresada como producto punto toma esta forma:

(x, y) \cdot (A, B) = 0

Esto quiere decir, que (A, B) es un vector ortogonal a la recta.

Ahora, si la recta no pasa por el origen, entonces C \neq 0 y el sistema de ecuaciones
Ax+By+C=0
Ax+By=0
No tiene solución, por tanto, la recta correspondiente a la ecuación Ax+By+C=0 es paralela a la recta correspondiente con la ecuación Ax+By=0; lo cual implica que (A, B) es un vector perpendicular a la recta.

Ahora que ya tenemos significado geométrico tanto de A como de B, el único que nos falta es C.
Para encontrar este significado vamos a tomar el punto P=(x, y) de la recta más cercano al origen. Tenemos que el punto Q=P+(-B, A) también debe estar en la recta.

Ahora, consideremos el paralelogramo formado por los vértices 0, P, Q, y P+Q. Ya habíamos visto en la parte 1 que este paralelogramo tiene área |P \wedge Q|. Por tanto:

P \wedge Q = P \wedge (P+(-B,A)) = P \wedge (-B, A) = -Ax-By

Es decir, el área del paralelogramo es |-Ax-By|, pero recordemos también que -Ax-By=C, por tanto, |C| es el área de ese paralelogramo.

Tenemos que la base de ese paralelogramo es el segmento que une al orígen con P, mismo que es la distancia de la recta al orígen, mientras que el segmento que une a P con Q es la altura del paralelogramo, y tiene longitud |P-Q|=|(-B, A)|=|(A, B)|, dado que el área del paralelogramo es igual al producto de la longitud de la base por la longitud de la altura, entonces la longitud de la base es:

\frac{|C|}{|(A, B)|}

Misma que es la distancia de la recta al orígen.

Por tanto, ya sabemos el significado geométrico de C.

El Producto Complejo

El producto complejo es un producto exclusivo de \mathbb{R}^2 y se define de la siguiente manera:

(a, b)(c, d) = (ac-bd, bc+ad) para a, b, c, d \in \mathbb{R}

Este producto es conmutativo, asociativo y distributivo, y también tiene la propiedad de que el producto de 2 vectores es otro vector.

Una manera sencilla de recordar la definición de este producto es con el hecho de que (e_2)(e_2)=-e_1 y v e_1=v para cualquier vector v:
(a e_1 + b e_2)(c e_1 + d e_2)= ac (e_1)(e_1) +ad (e_1)(e_2)+ bc (e_2)(e_1)+bd(e_2)(e_2)

Lo cual es igual a:

ac (e_1)+ad(e_2)+bc(e_2)-bd(e_1)=(ac-bd, bc+ad)

La interpretación geométrica de este producto es fascinante: Si a, b \in \mathbb{R}^2 entonces |ab|=|a||b| y además la dirección de ab corresponde a la suma de los ángulos de a y de b, es decir:

(a\cos \alpha, a\sin \alpha) (b\cos \beta, b\sin \beta)=ab(\cos (\alpha + \beta), \sin (\alpha+\beta))

Para a, b, \alpha, \beta \in \mathbb{R}

Lo cual significa que este geométricamente, este producto sirve para rotar y además tiene una gran propiedad algebrárica: es invertible. A grandes rasgos ser invertible quiere decir que es posible dividir, La forma de hacerlo es la siguiente:

\frac{1}{(a, b)} = \frac{(a, -b)}{a^2+b^2} para a, b \in \mathbb{R}

Soluciones a Problemas de la Parte 1

A lo largo del texto mencioné entre párrafos maneras de solucionar la mayoría de los problemas, pero hubo unos cuantos problemas, que no encontré donde meterlos así que para cumplir lo prometido, aquí va una breve descripción(y poco de mala gana :S) de los problemas que faltaron:

  • Dados los 3 vértices de un triángulo, encuentra una fórmula para saber su área.
    El producto cuña da el área con signo del paralelogramo definido por (0, 0), A, B y A+B; el área del triángulo es la mitad de eso.
    Por tanto si el triángulo tiene vértices A, B, C, el área del triángulo sería \frac{|(B-A)\wedge(C-A)|}{2}
  • Sobre las fórmulas de la longitud de las 3 alturas; si se quiere saber la longitud de la altura que pasa por un vértice C; simplemente se toman los otros 2 vértices del triángulo(llamémosles A y B), y vemos la distancia entre C y la recta que pasa por A y B:
    |C-((C-A)\cdot \frac{B-A}{|B-A|^2})(B-A)-A|
  • Dados 3 puntos A, P y Q, encontrar la reflexión de A sobre la recta que pasa por P y Q.
    Esto se hace básicamente obteniendo la proyección B de A-Q sobre P-Q, de manera que Q+B sea el punto de la recta más cercano a A, y posteriormente sólo hay que sumarle a Q+B el negativo del vector A-(Q+B)
    Q+B-(A-Q-B)=2(Q+B)-A, donde
    B=\frac{(P-Q)\cdot(A-Q)}{|P-Q|^2}(P-Q)

Posted in Programación En General | Leave a Comment »

Level Up: De Las Coordenadas a los Vectores

Posted by luison.cpp en agosto 3, 2014

Introducción

Aunque mi trabajo es bastante semejante al trabajo de un ingeniero, mi carrera no es de ingeniería, sino de ciencias y llevé muchas materias con matemáticos.

Me llamó la atención como muchos físicos e ingenieros solían hacer las cuentas de geometría analítica de la manera que yo las hacía en la prepa: con coordenadas.

Así que pensé en escribir a manera de tutorial cómo pasar de usar coordenadas a usar vectores y desde el punto de vista de la programación.

Ahora, ¿por qué preocuparse por usar vectores cuando se pueden usar coordenadas?. Aquí va una lista de razones que los podrían motivar:

  • Para poder manejar el 3D fluídamente es necesario utilizar vectores
  • Cualquier libro o artículo de gráficos por computadora los utiliza ampliamente
  • Las cuentas con vectores usualmente requieren menos ecuaciones
  • Al mismo tiempo, requieren usar menos letras
  • Al hacer una cuenta usando vectores, cada ecuación tiene un significado geométrico, por lo cual es mas fácil intuir las soluciones y es mas fácil darse cuenta cuando se cometen errores
  • Además de simplificar el trabajo en papel, también simplifican el trabajo en la programación

Dividiré este tutorial en varios posts, no puedo prometer que lo terminaré, pero si veo que a la gente le sirve, es mas probable que lo continué.

Puntos y Vectores

Como ya podrán saber, para identificar un punto en el plano se suelen utilizar 2 números, a los cuales les llamamos coordenada x y coordenada y.

Un problema que surge aquí es qué hacer cuando tenemos varios puntos, no podemos usar la misma x para todos ni la misma y para todos. A veces se recurre a usar subíndices, pero esta práctica puede traer varias confusiones,

La alternativa que nos proponen las matemáticas es la siguiente: en lugar de que las letras representen siempre un sólo número, podemos hacer que las letras representen pares de números. De manera que en lugar de decir:

x_1=5

y_1=3

x_2=4

y_2=5

podemos simplemente decir:

A=(5, 3)

B=(4, 5)

De esta manera, en lugar de tener 4 variables con nombres muy parecidos entre ellas, ahora sólo tenemos 2 variables, y con nombres difíciles de confundir.

De esta manera, cada punto se puede identificar por un par de números, y de hecho matemáticamente un punto es un par de números.

Definición: Un punto en el plano es un par ordenado de números reales, se expresa como P=(x, y) donde P es el punto, mientras que x e y son los números reales.

Dado que ahora las variables pueden significar tanto puntos como números, hay que hacer ciertas distinciones,  digamos que si A es un número, vamos a decir que A \in \mathbb{R}, si A es un punto en el plano(en 2D) vamos a decir que A \in \mathbb{R}^2 y si A es un punto en el espacio(en 3D) vamos a decir que A \in \mathbb{R}^3.

Es una costumbre bastante común nombrar a los puntos con letras mayúsculas y a los números con letras minúsculas, sin embargo no es una regla, cada quien puede nombrar a los puntos como se le antoje.

Ahora, sobre los vectores, un vector de dimensión n es una secuencia de n números, por ejemplo (0, 5) es un vector de dimensión 2, (0, 10, 4) es un vector de dimensión 3, (10, 9, 8, 8, 7, 6) es un vector de dimensión 6.

Si V es un vector de dimensión n, decimos que V \in \mathbb{R}^n ; si el lector ha prestado atención, ya debió de haberse dado cuenta que los puntos y los vectores son exactamente lo mismo. Ahora, ¿por qué llamarlos diferente?

Existen varias razones, la principal es que a veces ese par ordenado de números nos lo imaginamos como la marca unidimensional que deja una pluma al apenas tocar un papel, y por eso los llamamos puntos; pero también a veces nos imaginamos ese par de números como una flecha que va de un punto a otro, y cuando imaginamos eso les llamamos vectores, pero tanto matemáticamente como en programación son lo mismo.

Otra razón para hacer la distinción es que en áreas mas avanzadas de las matemáticas se usan los nombres «punto» y «vector» para hablar de otros objetos que sí son diferentes.

Otra cosa que hay que hacer notar, es que a veces cuando se piensa en términos de puntos se utilizan letras mayúsculas, pero cuando se piensa en términos de vectores, se utilizan letras minúsculas.

Esto no debe de confundir, ya que como decía anteriormente, cada quien puede nombrarlos como quiera. si se utilizan letras mayúsculas o minúsculas, suele ser con la intensión de hacer mas fácil la lectura, no porque tenga un significado matemático (es como  cuando se eligen los nombres de las variables en programación, aunque el nombre es irrelevante para el compilador, si tiene un significado vuelve la lectura del código mas sencilla).

El Sentido Geométrico de los Vectores

Toda la sección anterior estuve diciendo qué significaba un punto y qué significaba un vector, pero hasta ahora todo eso ha sido irrelevante ya que falta la interpretación geométrica y falta también de la aritmética de los vectores.

Geométricamente podemos pensar en los vectores como objetos que tienen magnitud y dirección, y suelen ser dibujados por una flecha. Por ejemplo, el vector (2, 3) se puede dibujar como una flecha que va desde el origen hasta el punto con coordenadas (2, 3):

La magnitud es la longitud coincide con la longitud de la flecha y la dirección es en el sentido que está apuntando. La magnitud de un vector a=(x, y) se puede expresar como |a| y se calcula como:

|(x, y)|=\sqrt{x^2+y^2}

Por otro lado, la dirección se puede expresar como un ángulo, pero a la larga resulta mas conveniente usar el vector (cos \alpha, sen \alpha) en lugar del ángulo $\alpha$; luego discutiremos cómo cambiar entre ambas representaciones.

Por último, para terminar de entender el sentido geométrico de los vectores, hay que conocer como se suman y se restan.

Tanto la suma como la resta de los vectores se realizan de manera independiente en cada entrada:

(a, b)+(c, d)=(a+c,b+d)

(a, b)-(c, d)=(a-c,b-d)

(a, b, c)+(x, y, z)=(a+x,b+y, c+z)

(a, b, c)-(x, y, z)=(a-x, b-y, c-z)

Un vector sólo se puede sumar y restar con vectores de su misma dimensión.

La importancia de estas sencillas definiciones se descubre al mirar lo que sucede geométricamente:

Es decir, para a, b \in \mathbb{R}^2, si se dibuja a, luego se dibuja b donde termina a, entonces el vector que inicia en el mismo punto que a y termina en el mismo punto que b es precisamente a+b. Esta operación es conmutativa, y se utiliza frecuentemente para estudiar los paralelogramos(la imagen de la izquierda).

Otra interpretación geométrica importante de esto es que el vector que va desde b hasta a,  es precisamente ab:

Así que una forma  de definir la distancia entre dos puntos P y Q es como |P-Q|.

Producto de Vectores

Cuando se están usando números y vectores en un mismo discurso, es muy frecuente referirse a los números como escalares. Podría escribir todo este tema sin mencionar esa palabra, pero dado que el lector probablemente se encontrará después con otros textos, es conveniente acostumbrarnos a usar esa palabra.

Los vectores tienen la peculiaridad de que no se usa sólo un tipo de producto, sino varios, cada uno con sus distintas propiedades e interpretación geométrica:

  • Producto de un vector por un escalar o viceversa. Este es el más simple  de todos, se trata de multiplicar un escalar por un vector o un vector por un escalar, y se define de la siguiente manera:
    \lambda (x, y) =(\lambda x, \lambda y) para \lambda, x, y \in \mathbb{R}
    O bien para vectores en \mathbb{R}^3:
    \lambda (x, y,z) =(\lambda x, \lambda y, \lambda z) para \lambda, x, y,z \in \mathbb{R}
    El resultado de este producto es un vector.
    La interpretación geométrica que esto tiene es la de escalamiento, cuando el escalar es positivo:

    Y la de escalamiento seguido de reflexión cuando el escalar es negativo:

    También frecuentemente se utiliza este producto en la forma de una división de un vector entre un escalar:
    \frac{a}{x} = \frac{1}{x}a para a \in \mathbb{R}^n y x \in \mathbb{R}.
    Multiplicar un escalar por un vector o un vector por un escalar es  exactamente lo mismo:
    \lambda a = a \lambda para a \in \mathbb{R}^n y \lambda \in \mathbb{R}.
    Desgraciadamente por ahora no hay una manera sencilla de dividir 2 vectores y tener como resultado un escalar; es decir, el escalar que al multiplicarlo por uno de los vectores, da como resultado el otro, este problema se puede resolver con proyecciones o con álgebras geométricas mas avanzadas (tal vez hable de ellas en un futuro, pero no lo prometo).
    Este producto es conmutativo y también es asociativo
  • Producto Punto  o Producto Escalar. Este producto se define de la siguiente manera:
    (a, b, c) \cdot (x, y, z) = ax+by+cz para a, b, c, x, y, z \in \mathbb{R}
    En 2D es análogo:
    (a, b) \cdot (x, y) = ax+by para a, b, x, y \in \mathbb{R}
    Sólo se pueden multiplicar vectores con las mismas dimensiones y el resultado de este producto es un escalar.
    La interpretación geométrica es que si a, b \in \mathbb{R}^n entonces:
    a cdot b = |a| |b| cos \alpha donde \alpha es el ángulo entre los 2 vectores.
    Esto esta fórmula es MUY importante, ya que muestra una manera sencilla de calcular el coseno del ángulo entre 2 vectores, si bien en 2D esta fórmula simplifica un poco las cuentas, en 3D es casi indispensable usarla para hablar de ángulos.
    Este producto es conmutativo, asociativo y también es disociativo y distributivo.
  • Producto Cuña. Este es un producto extremadamente útil para usarlo en geometría en 2 dimensiones, y francamente no me explico por qué  nunca se ve en tronco común en las ingenierías.
    Por el momento vamos a imaginar que este producto es exclusivo de \mathbb{R}^2 (tal vez en el futuro llegue a escribir más a fondo de este producto, pero por ahora nos quedaremos con la versión \mathbb{R}^2):
    (a, b) \wedge (x, y)=ax-by para a, b, x, y \in \mathbb{R}^2
    Esta definición coincide con la de un determinante de una matriz de 2 x 2
    El resultado de este producto es un escalar, y tiene 2 interpretaciones geométricas:
    Por un lado, para 2 vectores a y b, a \wedge b = |a| |b| sen \alpha donde \alpha es el ángulo  entre los 2 vectores, y por otro |a \wedge b| es el  área del paralelogramo de esta imagen:

    Mientras que el signo de a \wedge b indica si el ángulo entre a y b es positivo o negativo(es decir, si para girar a hacia la dirección de b, es necesario girar en sentido contrario de las manecillas del reloj(positivo), o en el sentido de las manecillas del reloj(negativo)), esto último puede sonar como una mera curiosidad matemática, pero tiene aplicaciones en las ciencias de la computación, y muchas.
    Es importante recalcar que este producto no es conmutativo, sino «todo lo contrario»:
    a \wedge b = -b \wedge a
    Tampoco se puede decir que sea asociativo, ya que no es posible multiplicar 2 vectores y el resultado multiplicarlo por otro; pero sí es distributivo:
    a \wedge (b+c) = a \wedge b + a \wedge c
  • Producto Cruz. Este es un producto exclusivo de \mathbb{R}^3, y se define de esta forma:
    (a, b, c) \times (x, y, z) = ( ( b, c) \wedge (y, z), -( a, c) \wedge (x, z), ( a, b) \wedge (x, y))
    Si el lector está familiarizado con determinantes de matrices de 3 por 3, una manera fácil de recordar esto es imaginar momentáneamente que estuviera permitido poner vectores en las entradas de las matrices, colocar en la fila de arriba los vectores (1, 0, 0), (0, 1, 0), (0, 0, 1)  y posteriormente sacar el determinante de eso.
    Si el lector no está familiarizado con determinantes, una manera de recordarlo sería  que la primera entrada del resultado es el producto cuña de los 2 vectores omitiendo su primera entrada, la segunda entrada del resultado el ménos el producto cuña de los 2 vectores omitiendo su segunda entrada, y la tercera entrada es el producto cuña de los otros 2 vectores omitiendo su tercera entrada (es decir, la entrada del resultado es la entrada que se omite, y la segunda entrada se multiplica por -1).
    La interpretación geométrica de este producto es muy importante:

    Si a y b son 2 vectores, |a \times b| es el área del paralelogramo definido por esos 2 vectores, mientras que la dirección del vector a \times b es perpendicular a los otros 2.
    Siendo atentos podemos darnos cuenta que hay 2 vectores que cumplen estas 2 propiedades, ¿cuál  de los 2 es el que da el producto cruz? para esto hay que visualizar lo siguiente: colocamos una cámara en |a \times b| , desde el lugar de esa cámara siempre parecerá que a está mas adelante que b en el sentido de las manecillas del reloj.
    Sobre las propiedades algebráricas, este producto es anticonmutativo(al igual que el producto cuña):
    a \times b = -b \times a
    También es distributivo; y tiene la peculiaridad de que no es asociativo.

Problemas/Aplicaciones

Aquí dejo una lista de problemas/aplicaciones con el uso de vectores; si escribo más de este tema, hablaré de las soluciones a estos problemas.
Tengo que recalcar que son importantes, y tienen muchas aplicaciones de uso cotidiano(al menos cuando se programan cosas que usen geometría, por ejemplo, videojuegos),
Recomiendo intentar resolverlos uno mismo para acostumbrarse al uso de vectores y si de alguno no se encuentra la solución, investigarla.
Por último, todos estos problemas se pueden resolver sin usar coordenadas y la intensión es que sean resueltos sin usarlas (es tentador seguir usándolas al principio, pero con el tiempo resulta mucho más práctico y limpio no usarlas).

  • Encuentra la fórmula del punto medio entre 2 puntos.
  • Si dos vectores a y b son perpendiculares, ¿cuál es su producto punto?
  • Si el producto punto de 2 vectores es 0, ¿cuál es el ángulo entre ellos?
  • ¿Cuál es el coseno del ángulo entre 2 vectores a y b?
  • ¿Cual es el seno del ángulo entre 2 vectores a y b?
  • Suponiendo que tenemos dos vectores a y b, de los cuales conocemos su magnitud pero no su dirección, ¿cuál es el máximo que puede ser su producto punto? (este resultado se conoce como desigüaldad de Cauchy-Schwarz)
  • Dados los 3 vértices de un triángulo, encuentra una fórmula para saber su área.
  • Dados los 3  vértices de un triángulo, encuentra fórmulas para la longitud de sus 3 alturas
  • Dados  2 puntos P y Q, y otro punto A, encuentra la fórmula de la distancia entre la recta que pasa por P y Q y el punto A.
  • Dado un vector a, y un vector unitario d, escribe la fórmula para encontrar un escalar k tal que el vector a-kd sea perpendicular al vector a (esto se conoce como la proyección, y también algunos lo llaman encontrar las componentes de un vector, refiriéndose a las componentes como el vector a y el vector a-kd).
  • Dados 2 puntos P y Q, y otro punto A, encontrar una fórmula para reflejar A a través de la recta que pasa por P y Q.
  • Dados 2 puntos P y Q, y otro punto A, encuentra una fórmula para determinar si A está en el segmento que conecta a los puntos P y Q.

Posted in Programación En General | Leave a Comment »

Otra Forma de Declarar Matrices con Memoria Dinámica en C++

Posted by luison.cpp en abril 1, 2011

Pues creo que todos están bastante familiarizados con la clásica manera de pedir memoria para matrices en C++ que a todos nos enseñan en nuestros cursos elementales de programación:

int** matriz;
....
matriz=new int*[n];
for(i=0;i<n;i++){
         matriz[i]=new int[n];
}

No sé si todos estén de acuerdo conmigo de que esta manera de declarar matrices es horrible, es tedioso liberar la memoria a parte de que hay que pedir memoria muchas veces(pedir memoria casi seguro no es O(1), y es posible que se desperdicie memoria debido a que a veces por cuestiones de eficiencia el SO te reserva mas memoria de la que pides, multiplica ese pequeño excedente por n+1).

Tan tedioso llego a ser que a veces optaba por no usar matrices y en lugar de eso acceder a índices de arreglos unidimensionales con algo parecido a arreglo[i*n+k]. Pero hace poco se me ocurrió una alternativa, no es una idea brillante pero sirve para ilustrar que a veces pasamos por alto cosas tan sencillas simplemente por eso: porque son sencillas.

matriz=new int*[n];
int *data=new int[n*n];
for(i=0;i<n;i++){
        matriz[i]=&data[i*n];
}

Como podemos observar, ahora sólo se está pidiendo memoria al SO dos veces. Y liberar la memoria ahora se tornó algo mucho mas simple:

delete [] matriz[0];
delete [] matriz;

Posted in Programación En General | 2 Comments »