LaTeX y compañía

Informacion sobre LaTeX y programas relacionados

9.5.11

Flechas con borde en TikZ

A veces es deseable añadir un borde a una línea, especialmente si ésta línea pasa por una zona del dibujo en la que hay tramas en tonos oscuros que dificultan la visión. Compara las dos imágenes inferiores:

Parece que debería ser sencillo añadir un borde a una línea, o que TikZ debería tener una opción para esto, pero lo cierto es que no la tiene, por lo que habrá que recurrir a trucos más o menos sucios.

Por ejemplo, TikZ tiene la capacidad de dibujar líneas dobles, lo cual en realidad es realizado por el método de pintar primero una más gruesa de lo deseado (por defecto en color negro), para después pintar encima de ella otra línea menor grosor (por defecto en color blanco). Esto crea la sensación de que hay una línea doble negra con un espacio blanco separando:

\tikz 
{
  \draw (0,0) node (A) {A}
        (2,1) node (B) {B};
  \draw[double] (A) -- (B);
}

Es posible hacer que la linea más gruesa inicial sea blanca, y la más fina interior sea negra, de modo que se consiga el efecto de "borde" buscado:

\tikz 
{
  \draw (0,0) node (A) {A}
        (2,1) node (B) {B};
  \fill[gray] (1,.5) circle (4mm);
  \draw[white,double=black] (A) -- (B);
}

Sin embargo la solución anterior plantea problemas si queremos colocar una punta de flecha al final (o al principio, o en ambos lados) de la línea, ya que la punta de flecha es dibujada en el mismo color que la línea exterior más gruesa(en el ejemplo anterior sería blanca, y por tanto no visible) y además sin borde.

Por tanto el añadir [double] a nuestra línea no nos sirve si ésta tiene una punta de flecha, como en el ejemplo inicial. Para conseguir el efecto deseado, tenemos que usar el mismo truco que usa TikZ con [double], pero incluyendo a la punta de flecha. Esto es, tenemos que dibujar primero la línea en blanco (o en el color que queramos darle al borde de la línea), y más gruesa de lo normal, incluyendo las puntas de flecha que también saldrán más gruesas, y seguidamente pintar encima en negro (o en el color que queramos que nuestra línea tenga por dentro) la misma línea y con la misma flecha, pero en un grosor menor (lo que hará que la flecha también sea más pequeña.

En realidad aparece otro problema. Si hacemos lo anterior, la punta de flecha "gorda" que pintamos en la primera pasada y la de la más fina que pintamos en la segunda, ambas terminarían en las mismas coordenadas, con lo que la punta de flecha no tendría el borde deseado. La figura inferior te aclarará a qué me refiero:

\usetikzlibrary{arrows}
\tikz 
{
  \fill[gray] (1,.5) circle (5mm);
  \draw[yellow, -latex', line width=2pt] (0,0) -- (1,.5);  
  \draw[black, -latex', line width=1pt] (0,0) -- (1,.5);
}

Observa cómo he creado la figura anterior. El primer \draw dibuja el "borde" en amarillo, mediante el truco de pintar la línea más gorda (line width=2pt). El segundo \draw pinta el interior de la línea, en negro y con un grosor menor (line width=1pt).

La punta de flecha se añade mediante la opción de sintaxis muy oscura -latex', que quizás merece la pena explicar. Cuando se quiere añadir una punta de flecha al final, habitualmente se pone la opción ->, que intenta parecerse a un segmento con una punta de flecha al final. Si queremos que sea el extremo inicial de la línea el que lleve la punta, la opción es <-. Para una línea con flechas en ambos extremos la opción es <->.

Las opciones anteriores añaden a la línea la punta de flecha "por defecto" que es más bien fea. Si se quieren añadir otros tipos de punta de flecha, es necesario poner el \usetikzlibrary{arrows} que aparece al principio. Esto nos da nuevos tipos de punta de flecha, y todos ellos tienen un nombre que se basa en las opciones antes decritas, pero cambiando > o < por otra cosa. Una de estas puntas de flecha se llama latex' (con apóstrofe y todo, es que hay otra que se llama latex a secas). Esto explica la extraña sintaxis de la opción -latex' que significa por tanto "añade al final de la línea una punta de flecha del tipo latex', que es, como ves en la figura anterior una punta con sus bordes levemente curvos.

Pero a lo que íbamos, el truco no ha funcionado porque las puntas de flecha se solapan a partir de la coordenada de sus puntas, en lugar de sus centros. La solución es por tanto dibujar la segunda línea (la negra) más corta que la primera, para que la flecha negra caiga "justo encima" de la amarilla. Es decir, en lugar de llegar hasta la coordenada (1,0.5) donde está el centro del círculo, debe terminar "un poco antes". El problema es ¿cuánto antes? Y ¿cómo calcular las coordenadas (x,y) del punto en que debe terminar?

La respuesta tiene dos partes. Primero ¿cuánto más corta hay que hacer la línea? Por desgracia la respuesta depende del tipo de flecha usado, y del grosor de la línea (tanto de la "exterior" como de la "interior"). Tras ensayos y errores he llegado a que hacer la línea interior 1.8pt más corta que la exterior da buenos resultados para este tipo de punta de flecha, y usando estos grosores de línea.

Segundo: si ya sé que la línea negra ha de ser 1.8pt más corta que la amarilla, ¿cómo calcular las coordenadas finales de esa línea? Parece que ha de ser enormemente complicado, pues hay que tener en cuenta la pendiente de la línea, etc. Por suerte TikZ tiene una opción llamada "shorten >", que permite especificar una cantida a acortar al final de la línea. Cuidado con el nombre de la opción, que es raro. El nombre de esta opción contiene un ">", como ves, y separado por un espacio de "shorten". A esta opción se le asigna un valor mediante el signo =, por lo que en nuestro caso quedaría así: "shorten >=1.8pt". Y el resultado es:

\usetikzlibrary{arrows}
\tikz 
{
  \fill[gray] (1,.5) circle (5mm);
  \draw[yellow, -latex', line width=2pt] (0,0) -- (1,.5);  
  \draw[black, -latex', line width=1pt, shorten >=1.8pt] 
                                         (0,0) -- (1,.5);
}

Esta es la idea básica. Ahora mejoraremos la legibilidad del código haciendo uso de los estilos de TikZ. Voy a crear un estilo llamado "flecha con borde" que se ocupe de hacer todo lo anterior, es decir, de añadir una punta de flecha al final de la línea, de pintar primero una línea más gorda en el color que el usuario prefiera, y después otra línea interior más fina y que sea 1.8pt más corta. Para poder hacer esto, TikZ proporciona la opción llamada preaction, que puede formar parte de un estilo y se ejecuta antes de que TikZ dibuje el código "normal" correspondiente a esa línea.

En nuestro caso, aprovecharemos la opción preaction del estilo flecha con borde para dibujar la línea gorda que va debajo, y en otro color. Después usaremos otras opciones del estilo flecha con borde para especificar el grosor y menor longitud de la que va encima. El código es el siguiente:

\usetikzlibrary{arrows}
\tikzset{
  flecha con borde/.style={
     line width=1pt, 
     shorten >=1.8pt,
     -latex',
     preaction={draw, #1, line width=2pt, shorten >=0pt}, 
  }
  flecha con borde/.default=white,
} 
\tikz 
{
  \fill[gray] (1,.5) circle (5mm);
  \draw[red, flecha con borde] (0,0) -- (1,.5);
}

Empecemos por el final, y fíjate en cómo ha quedado ahora la línea que dibuja la flecha. Simplemente \draw[red, flecha con borde] y las coordenadas de inicio y fin de la línea. Obviamente red es el color en el que quiero dibujar la línea, con su flecha, y flecha con borde es el nombre del estilo que se ocupa de añadirle un borde blanco, o de otro color si por ejemplo hubiera puesto flecha con borde=yellow. Fíjate que los nombres de estilos o de cualquier otra opción TikZ pueden llevar espacios, lo cual queda mucho más legible que algo como FlechaConBorde o flecha_con_borde.

Y ahora veamos cómo se ha definido el estilo. Se ha hecho fuera de la figura (de este modo este estilo puede ser usado por diferentes figuras del documento) a través de la macro \tikzset. En realidad esta macro no es sólo para definir estilos, sino para asignar valores a las "claves" que usa TikZ. TikZ maneja todas sus opciones y su configuración a través de un sofisticado mecanismo de "claves", similar en concepto a la que proporciona el paquete keyval, pero diferente. En ciertos aspectos más sencilla de usar. En nuestro caso, usamos \tikzset para crear un estilo, de modo que me centraré en este uso.

Para crear un nuevo estilo, basta inventar un nombre para él (pongamos "ejemplo", y hacer uso de la "clave" ejemplo/.style a la que asignamos todas las opciones que queremos que se ejecuten cuando usemos ese estilo. Así, puedo definir un estilo que fije un color de línea y un grosor y un tipo de punta de flecha, y cada vez que use ese estilo se aplicarán estas opciones (aunque algunas de ellas pueden ser sobreescritas por otras opciones presentes en cada figura).

Además, los estilos pueden recibir un parámetro (sólo uno), al cual nos referimos dentro del estilo como #1. Por ejemplo, en nuestra flecha con borde he elegido que el color del borde sea un parámetro, de modo que el usuario pueda poner, como dije antes, flecha con borde=yellow. Podría haber elegido que el parámetro fuese el grosor de la línea, por ejemplo, pero en este caso no lo encontré tan útil. Si quisiera tener varios aspectos parametrizables, tendría que hacer uso de otras "claves" y acceder a ellas. Otro día hablo de eso.

Observa lo que hay escrito tras flecha con borde/.style=, verás que se abre una llave y después se listan una serie de parejas "clave=valor", aunque en algunos casos aparece solo "clave" sin ningún valor asignado. Esta lista de parejas va separada por comas. Así, en nuestro caso tenemos las claves:

  • line width que recibe el valor 1pt y será el grosor de la línea a dibujar (la interna). Se dibujará en el color que se haya especificado en el comando \draw que esté usando este estilo, y si no se especifica otro, será en negro.
  • shorten > que recibe el valor 1.8pt y es la cantidad a acortar al final de la línea.
  • -latex' que es una opción que no recibe valor y que indica el tipo de flecha que se usará al final de la línea (TikZ busca si en el nombre de una opción hay un guión, e intenta interpretarlo como un tipo de flecha, de modo que la opción -loquesea se intenta interpretar como arrows=-loquesea).
  • preaction que recibe como valor otra lista de opciones entre llaves. Cuando aparece la clave preaction, TikZ dibuja dos veces la línea, la primera con las opciones dadas en esta clave, y la segunda con las opciones que se apliquen en ese momento. Esto nos permite dibujar el "borde gordo" antes de que se dibuje la línea más fina. Como opciones para este borde gordo tenemos:
    • draw esta opción (sin valores asignados) causa que TikZ dibuje realmente algo. Sin ella no dibujaría la línea gorda.
    • #1 esta opción es el parámetro que se le haya pasado a la clave flecha con borde. Se supone que es un color, y será por tanto el color con que se dibujará la "línea gorda con flecha gorda". Veremos después cómo hacer que si el usuario no especifica ningún color, se dibuje en blanco.
    • line width con el valor 2pt fija el grosor de esta "línea gorda".
    • shorten > con el valor cero hace que esta línea no sea más corta de lo normal. ¿Por qué es necesario especificar ésto? ¿No es eso la opción por defecto? Ocurre que cuando se ejecuta la preaction, están también activas todas las opciones de estilo de ese momento, en particular todas las opciones de flecha con borde, por lo que hay que sobreescribir (redefinir) las que queramos que sean diferentes, tales como el grosor de línea, el color y la cantidad a acortar al final.
      Puedes ver que no he especificado en el preaction la punta de flecha. No es necesario por que sigue activa la del estilo principal.

Finalmente, cuando el estilo que definimos usa parámetros, es conveniente especificar un valor por defecto para cuando el usuario no se lo asigna. En nuestro caso queremos que el borde sea blanco, y eso se logra con la clave flecha con borde/.default. El valor que asignemos a esa clave, será el valor que se reciba en #1 dentro del estilo, si el usuario no asigna otro.

La explicación ha sido larga, pero han aparecido en ella muchas cosas interesantes. La más importante en mi opinión, la capacidad de crear estilos parametrizados que ayudarán mucho a simplificar el código de las figuras que lo usen, además de servir para uniformizar el aspecto de estas figuras y simplificar los cambios de estilo que afecten a varias de ellas.

Etiquetas:

Comentarios:

  1. Hola, espectacular la explicación y el ejemplo. Muchas Gracias!
     
  2. Excelente la explicación, al principio parece más complejo, pero como siempre una buena explicación salva todo. Nosotros armamos con otros físicos un grupo de FB y uno de whatsapp y nos vamos pasando datas para enseñar. Se puede descargar whatsapp gratis o crearse una cuenta de facebook, es de toque y es muy útil. Además en el curso surgen debates.
     
Publicar un comentario en la entrada
 
 

Powered by Blogger