9/10/14

Datos: matemáticas, listas y ramas. # 4 tutorial

Este es el tutorial básico quizás más importante, ya que trata sobre la gestión de datos y geometría en un entorno de programación visual como Grasshopper. Puede parecer un poco lioso al principio, pero conviene acostumbrarse a trabajar con esta lógica para realizar geometría compleja de manera sencilla.

Ya hemos visto qué son las listas, una colección de datos organizados por índices que van de 0 en adelante (primer dato indice 0, segundo dato indice 1, tercero indice 2...). Utilizamos los índices para acceder a cada dato de la lista y pueden contener todo tipo de datos (geometría, valores numéricos, texto, booleanas, colores, ...).

Los componentes para gestionar listas y ramas están en la pestaña "Sets", especialmente en los paneles "List" y "Tree".

El componente Panel nos permite observar las listas de datos que nos devuelven los componentes. Este está situado en la pestaña "Params" en el panel "Input". Es recomendable para los principiantes que utilicen siempre el "Panel component" para observar el resultado de los componentes.




Las ramas son algo más complejo, pero una vez que te acostumbras a ellas no suponen ningún esfuerzo, más bien nos facilitan el trabajo porque utilizamos menos componentes. Cuando se ve por ahí una definición de gh llena de cables infinitos que van un lado para otro (conocidos como espaguetis), suele ser síntoma de no saber manejar las ramas. La estructura de ramas hace de grasshopper algo elegante, pudiendo hacer mucho con muy poco de manera eficiente.
Para comprender qué son las ramas, primero hay que entender que los datos necesitan ser transportados de algún modo. Si tu quisieras que el resultado que te devuelve una función o comando, llevarla a una función o comando posterior, necesitas empaquetarla de algún modo para comunicarla con la siguiente función. Permítame este ejemplo, imagina que eres director de un colegio y haces el registro de alumnos de la Aula A del cuarto curso de primaria, el cual tiene 20 alumnos, solo necesitaríamos una lista de alumnos para tenerlos organizados eficientemente, supón también que el encargado de clase es el primero de esa lista de alumnos. Pues bien, para las otras aulas también de 20 alumnos, Aula B, Aula C y Aula D, si pusiéramos a todos los alumnos dentro de la misma lista, tendría un tamaño de 80 alumnos (20+20+20+20) y si necesitásemos encontrar al encargado de cada clase, tendríamos que recorrer casi toda la lista para encontrarlos, los cuales tendrían los índices 0, 20, 40 y 60, dado que estaban de primeros en cada Aula. Esto es muy ineficiente, lo que haríamos sería separar los alumnos por aulas en nuestro registro, quizás en una tabla, y simplemente viendo al primer alumno de cada Aula ya tendríamos el nombre del encargado de clase. Algo tan simple como esto es el concepto de las ramas. Ahora veamos su representación en Grasshopper.

 La metáfora habitual para explicarlo es un árbol donde tenemos ramas como {0;0}, {0;1}, {0;2}, {0;3}, {1;0}, {1;1}, {1;2}, {1;3}... en cada una de ellas puede haber una lista de datos, un elemento o estar vacías. Otra metáfora podría ser un edificio, en la que cada piso es una rama, y dentro de cada piso puede haber distintas habitaciones que serían distintas ramas como {0;0}, {0;1}, {0;2}, {1;0}, {1;1}, {1;2}, {2;0}, {2;1}, {2;2}, {3;0}, {3;1}, {3;2}. En este caso tendríamos cuatro pisos con tres habitaciones en cada piso, es decir, doce ramas jerarquizadas en cuatro originales y tres secundarias.

Imagine que tenemos una lista con tres curvas y las dividimos en cuatro partes, obtendríamos tres ramas con una lista de cuatro curvas en cada rama. Si volvemos a dividir cada curva, esta vez en dos partes, obtendríamos una rama por cada curva que contiene dos curvas partidas. Mejor decirlo más gráficamente:

 {0}(CurvaA, CurvaB, CurvaC)

Tenemos una lista de 3 curvas.
Partimos cada curva en 4 partes --->

 {0;0}(CurvaA0, CurvaA1, CurvaA2, CurvaA3),
 {0;1}(CurvaB0, CurvaB1, CurvaB2, CurvaB3),
 {0;2}(CurvaC0, CurvaC1, CurvaC2, CurvaC3)

Tenemos 3 ramas con cuatro curvas cada una.
Partimos cada curva por la mitad --->

{0;0;0}(CurvaA00, CurvaA01),
{0;0;1}(CurvaA10, CurvaA11),
{0;0;2}(CurvaA20, CurvaA21),
{0;0;3}(CurvaA30, CurvaA31),

{0;1;0}(CurvaB00, CurvaB01),
{0;1;1}(CurvaB10, CurvaB11),
{0;1;2}(CurvaB20, CurvaA21),
{0;1;3}(CurvaB30, CurvaB31),

{0;2;0}(CurvaC00, CurvaC01),
{0;2;1}(CurvaC10, CurvaC11),
{0;2;2}(CurvaC20, CurvaC21),
{0;2;3}(CurvaC30, CurvaC31)



Si se fija, verá que la jerarquía de ramas no se pierde, sino que se acumula. En principio nos da igual tener canales vacíos (como los primeros {0;...}) pero según qué necesitemos podemos simplificarlas para eliminarlas, injertar nuevas o aplanar toda la jerarquía para deshacerse de las ramas.

Elimina las ramas insignificantes.

 Añade una rama para cada dato.
Elimina las ramas y se queda en la original {0}.

Para observar la jerarquía de las ramas tenemos el componente Param Viewer, que encontramos en la pestaña Params, panel Util. Si pulsamos sobre este con el botón derecho, podemos observar las ramas en modo gráfico con Draw Tree activado.

Quizás se haya fijado, que los cables que conectan los componentes son distintos según que datos haya. Cuando el cable procesa datos con ramas, tendremos un cable discontinuo, si procesa una lista de datos el cable será doble y si procesa un solo dato el cable será simple.



Hemos visto como tratar con árboles de datos desde una lista de cuatro curvas. Si quisiéramos ahora utilizar esas curvas recortadas que están organizadas en todas aquellas ramas, para hacer por ejemplo, una superficie a partir de una curva que se extruye hacia un punto, necesitaríamos el mismo número de puntos que de curvas y organizados en el mismo número de ramas y con la misma jerarquía, es decir, para combinar árboles, las rutas deben ser iguales.

En conclusión, los árboles son una eficiente manera de gestionar datos complejos. También son uno de los principales errores cuando algo no sale como se esperaba, porque las rutas o la jerarquía no coinciden. Tenga esto en cuenta si encuentra un error y compruebe las rutas.


Ahora veamos un caso práctico.


Para hacer esta pieza paramétrica, que podría funcionar como colgante o pendiente, hemos partido de una malla de hexágonos, del componente Hexagonal. Extrae el primer input y oculta el plano para que no nos moleste en el visor de Rhino e introduce valores para el tamaño de los hexágonos y la cantidad de ellos en X e Y.

Observe los outputs con los componentes Param Viewer y Panel. Nos devuelve X ramas con Y elementos cada una, en este caso polilíneas.


Primero conecta a Cells un componente genérico de curvas, al final del tutorial entenderás para qué. Como no queremos todos esos hexágonos, vamos a coger algunos del centro. Para ello seleccionamos el hexágono central con el componente Tree item, al que le damos la rama y el índice del elemento a escoger, es decir, la mitad de Extent X y Extent Y con ayuda del componente Division que está en la pestaña Maths. Como el componente de curvas tiene varias ramas con dos canales, tenemos que simplicar la entrada Path del Tree item para que sólo tenga un canal y así coincida con el valor de Extent X entre 2. Haga click derecho sobre la entrada Tree y active Simplify. Seleccione el componente y observe en Rhino el hexágono que hemos seleccionado.

Sobre esta célula vamos hacer una circunferencia. Primero, como la célula es una polilínea plana, podemos sacar su punto central con el componente Area, que nos servirá como posición para la circunferencia, y dele a este un radio de 12. Ahora vamos a utilizar esta circunferencia para filtrar sólo las células que están dentro de ella, utilizando el componente Point in Curve. Este nos pide una curva cerrada, que será la circunferencia, y los puntos a filtrar, que serán los centros de las células utilizando el componente Area. Point in Curve nos devuelve 0 para los valores que están fuera de la curva, 1 para los que coinciden con la curva y 2 para los que están dentro de la curva. Podemos utilizarlos como valores booleanos, ya que 0 significa False y todos los números enteros positivos significan True, a si que podemos utilizar Cull Pattern en los hexágonos. Es decir, utilizamos puntos referentes a los hexágonos para saber cuáles están dentro de la circunferencia y esa relación la filtramos con los hexágonos.

Para su interés, compruebe con Panel y Param Viewer los parámetros de salida de todos estos componentes. Haga lo mismo según vallamos añadiendo nuevos componentes. Todo este proceso se podría haber hecho sin datos con ramas, pero es lo mismo. Para eliminar las ramas, haga click derecho sobre la salida del Cull Pattern y active Flatten.


Ahora vamos a desfasar los hexágonos para tener una distancia mínima que haga la pieza resistente. Primero unimos todos los hexágonos con Region Union y desfasamos a 0,4 mm la curva resultante. Para cada hexágono hacemos lo mismo pero con el valor negativo con el componente Negative, en la pestaña maths.


Utilizamos los hexágonos desfasados para realizar las estrellas. Conecta a este un nuevo componente Area y otro componente Explode ya que necesitaremos el punto central y los vértices de cada hexágono. Con el componente Line creamos una línea desde los vértices a los centros de los hexágonos, para luego extraer un punto de esa línea con el componente Evaluate Length, al que conectamos un numeric slider que valla de 0 a 1. Compruebe el efecto al variar el slider.

Por otro lado utilizamos otro Evaluate Length y otro slider con valor 0.5 para conectarlo a los segmentos explotados de los hexágonos.

Los Evaluate Length nos devuelven puntos que tenemos que ordenar para realizar la polilínea con forma de estrella. En este caso tendríamos que combinar las ramas de los Evaluate de modo un punto de uno otro punto de otro, un punto de uno un punto de otro... Es decir, con un patrón 01010101... Para eso utilizamos el componente Weave, nos viene con ese patrón 01 por defecto, a si que sólo tenemos que conectar los puntos. Si se fija, los árboles de los Evaluate coinciden, tienen las mismas rutas, pero no coinciden la cantidad de datos que hay en las listas de esas ramas, pero para este caso no importa. Después, conecta un componente Polyline y haga Flatten en su salida porque necesitamos deshacernos de las ramas. Ahora vuelva a variar el slider del primer Evaluate que hicimos y verá el resultado.


Pero de esta manera todas las estrellas cambian del mismo modo. Podemos hacer algo más interesante como que cada estrella cambie según lo cerca que está de un punto. Añade un componente genérico de punto, dele a Set One Point con el boton derecho y cree un punto en el visor de rhino. Para obtener la distancia entre este punto y los centros de los hexágonos, utilice el componente Distance. Así obtenemos un valor específico de cada hexágono respecto al punto, pero el componente Evaluate Length trabaja con valores de 0 a 1, a si que tenemos que remapear estos valores. Para ello utilizamos el componente Remap Numbers, lo que hace es cambiar valores siguiendo una regla de tres pero con dominios. Por ejemplo, tenemos 5 números (0, 10, 20, 30, 40, 50) con un dominio de 0 a 50, y queremos transformarlos en un dominio de 0 a 1, el resultado sería (0.0, 0.25, 0.5, 0.75, 1.0). Pues bien, tenemos los valores a cambiar, que es la distancia, necesitamos también el dominio de esa distancia, por lo que utilizamos el componente Bounds, pero tenemos que aplanar el parámetro de entrada para que todos los valores formen parte de un único dominio. Por defecto la otra entrada de Remap Numbers es 0 to 1, por lo que ya podríamos conectarlo al primer Evaluate Length, pero para más control, invocamos el componente Construct Domain y añadimos dos sliders que vallan de 0 a 1. Conéctelo todo como en la imagen y a continuación pruebe a variar los valores de los sliders y la posición del punto.


En lugar de hacerlo con un punto, también se podría hacer con una curva, utilizando Curve Closest Point.


Para finalizar, tenemos que hacer la superficie de la chapa de metal. Recuperamos el desfase exterior que hicimos y tenemos que restarle las estrellas a esta región con el componente Region Difference. Ahora podemos hacer una superficie con esta región con Boundary Surfaces y la extruimos en el eje Z con un valor de 0.8 mm con Extrude para darle volumen y obtener un sólido (Closed Brep).



Por último puedes probar a enchufar otros tipos de rejillas, como la cuadrada o la triangular. Conecta todos los parámetros de entrada y como ya hemos colocado al principio un componente genérico de curvas, solo tenemos que conectar la salida al componente de curvas. Ajuste los parámetros del algoritmo y diviértase.



Como todos los tutoriales básicos, he realizado una definición de GH con la ejecución de los componentes más habituales, en este caso para matemáticas, listas y ramas. Puede descargarlo desde aquí. Estudie bien los componentes, entienda que hacen con ayuda del Panel y Param Viewer y experimente por si mismo.

No dude en plantear aquí sus dudas y sugerencias. Su duda puede beneficiar a otras personas.


*********************************************************************************
El primer workshop de joyería generativa en España, impartido por dos expertos en Grasshopper: Rodrigo Carbajal, arquitecto y diseñador de interacción y Daniel Abalde, diseñador generativo de joyería y creador de Peacock. Los alumnos aprenderán a modelar algoritmos con Grasshopper, las habilidades de la joyería generativa y a renderizar e imprimir en cera sus propios diseños. Visita el enlace para más información.
*********************************************************************************


No hay comentarios:

Publicar un comentario