[Version Verano 2016- Tatiana]
Recursion is a technique that is commonly used in programming. With this technique, problems are solved by solving similar problems but for smaller cases. We can construct sets of objects or tasks using recursive rules and initial values. Recursive functions are functions that are self-invoking, using smaller sets or elements each time, until reaching a point where an initial value is used instead of self-invoking. In this laboratory experience, you will implement some tools to draw and practice the use of recursive functions to fill with color some figures.
This laboratory experience is an adaptation of the homework GridPlotter presented by Alyce Brady and Pamela Cutter in [1]. The implementation of the grid and the ability to paint in it was presented by Sacha Schutz in [2] but it was fixed, modified and adapted for this laboratory experience.
Before arriving at the laboratory you should have:
Reviewed the concepts related to recursive functions.
Studied the concepts and instructions for the laboratory session.
Taken the Pre-Lab quiz, available in Moodle.
Probably many Windows OS users (if not all of them) have used the program called Paint, that is a simple drawing application. In that program, like many other drawing programs, there are several tools (for example: the pencil, the paint bucket and the line) that lets the user draw on the area in different ways.
In this laboratory experience, we will make the some tools (square, circle, triangle, some special lines) work … don’t worry!, we will do it in a very simple way.
The drawing will be on a grid. The tools will be used by clicking any cell in the grid and, from that point, the necessary cells to make the figure will be painted. For example, if we choose the vertical line tool and we click the cell in position (2,3), a vertical line will be drawn in all of the cells in column 2. That is, all of the cells in position $(2,y)$ will be marked for all of the $y$ in the grid.
Qt
Coordinates:The coordinate system in Qt
works a bit differently, as it is shown in Figure 1. The entries go from left to right, from 0 to the maximum width, and from top to bottom, from 0 to the maximum height.
Figure 1. The image shows the direction in which the coordinates are ordered in Qt
images.
When we want to insert two-dimensional data (like the entries of a grid that has coordinates in $x$ y $y$) in a one-dimensional array we use a formula to convert every coordinate $(x,y)$ to an index $i$ of the array. For every point with coordinates $(x,y)$ in the grid, we evaluate $i=(number-of-columns)*y+x$, where number-of-columns
represents the width of the two-dimensional array, and the result $i$ will be the index of the one-dimensional array that corresponds to the point with coordinates $(x,y)$ in the grid. For example, the index $i$ that corresponds to the point $(1,2)$ in the grid of width $5$ is $i=(5)*2+1=11$.
Para este proyecto necesitarás utilizar las funciones de QtGlobal
para la implementación del círculo:
int qFloor(qreal v)
// Devuelve el “piso” del valor $v$.qreal qSqrt(qreal v)
// Devuelve la raíz cuadrada del valor $v$.qreal qPow(qreal x, qreal y)
// Devuelve el valor de $x$ elevado a la potencia $y$.También necesitarás utilizar la función que pinta en la cuadrilla:
void switchOn(int x, int y, const QColor& color);
// Pinta la celda $(x,y)$ con el color dado. (No tienes que preocuparte por QColor
porque se pasa a la función por parámetro.)Aunque no se ve en el archivo tools.cpp
, hay una arreglo llamado mColors
que contiene el color de todas las celas de la cuadrilla. Esto te ayudará a saber cuál color está en una celda: mColors[columns * y + x]
. Nota que el índice de este arreglo se calcula utilizando la conversión para cambiar coordenadas $(x,y)$ a índices que explicamos arriba.
!INCLUDE “../../eip-diagnostic/gridplot/es/diag-gridplot-01.html”
!INCLUDE “../../eip-diagnostic/gridplot/es/diag-gridplot-02.html”
Carga a QtCreator
el proyecto GridPlotter
. Hay dos maneras de hacer esto:
* Utilizando la máquina virtual: Haz doble “click” en el archivo `GridPlotter.pro` que se encuentra en el directorio `/home/eip/labs/recursion-gridplotter` de la máquina virtual.
* Descargando la carpeta del proyecto de `Bitbucket`: Utiliza un terminal y escribe el commando `git clone http:/bitbucket.org/eip-uprrp/recursion-gridplotter` para descargar la carpeta `recursion-gridplotter` de `Bitbucket`. En esa carpeta, haz doble “click” en el archivo `GridPlotter.pro`.
El proyecto contiene el esqueleto para una aplicación para dibujar líneas o figuras en una cuadrilla. La aplicación tiene una interface que le permite al usuario seleccionar el color para pintar, el color para el trasfondo de la cuadrilla, la figura que se va a dibujar (por ejemplo, círculo, cuadrado) y el tamaño de la figura. La figura seleccionada se dibuja cuando el usuario marca una celda en la cuadrilla.
Estarás trabajando en el archivo tools.cpp
. Tu primera tarea es implementar las funciones RowMajorFill
, ColMajorFill
, DiagonalLeft
y DiagonalRight
que hacen que los botones para dibujar líneas funcionen. La función RowMajorFill
ya está implementada para que la tengas de ejemplo. Las funciones deben trabajar como se indica adelante.
RowMajorFill
Cuando se selecciona la figura de línea horizontal en la interface, se dibujará una línea horizontal en la cuadrilla en la fila en donde el usuario marcó. La línea se expandirá a la derecha y a la izquierda de la celda marcada hasta que encuentre una celda (píxel) de un color diferente al color en el trasfondo, o hasta que la cuadrilla termine. La Figura 2 ilustra este comportamiento.
| (a) | (b) | © |
|---|----|----|
| | | |
Figura 2 - (a) Un dibujo con trasfondo blanco y puntos rojos. (b) Cuando el usuario marca el botón de línea horizontal (RowMajorFill
) y marca la celda mostrada, © se dibuja una línea horizontal que se expande hacia la izquierda y hacia la derecha de la celda marcada, hasta que se encuantra una celda con un color diferente al color de trasfondo.
ColMajorFill
Esta función debe trabajar de manera similar a la función RowMajorFill
pero para columnas. La Figura 3 ilustra su comportamiento.
| (a) | (b) | © | |---|----|----| | | | |
Figura 3 - (a) Un dibujo con trasfondo blanco y puntos rojos. (b) Cuando el usuario marca el botón de línea vertical (ColMajorFill
) y marca la celda mostrada, © se dibuja una línea vertical que se expande hacia arriba y hacia abajo de la celda marcada, hasta que se encuantra una celda con un color diferente al color de trasfondo.
DiagonalLeft
Esta función debe trabajar de manera similar a la función RowMajorFill
pero produce una línea diagonal desde la izquierda superior hasta la derecha inferior. La Figura 4 ilustra su comportamiento.
| (a) | (b) | © | |---|----|----| | | | |
Figura 4 - (a) Un dibujo con trasfondo blanco y puntos rojos. (b) Cuando el usuario marca el botón de línea diagonal izquierda (DiagonalLeft
) y marca la celda mostrada, © se dibuja una línea diagonal izquierda que se expande hacia arriba a la izquierda y hacia abajo a la derecha de la celda marcada, hasta que se encuantra una celda con un color diferente al color de trasfondo.
DiagonalRight
Esta función debe trabajar de manera similar a la función DiagonalLeft
pero produce una línea diagonal desde la derecha superior hasta la izquierda inferior. La Figura 5 ilustra su comportamiento.
| (a) | (b) | © | |---|----|----| | | | |
Figura 5 - (a) Un dibujo con trasfondo blanco y puntos rojos. (b) Cuando el usuario marca el botón de línea diagonal derecha (DiagonalRight
) y marca la celda mostrada, © se dibuja una línea diagonal derecha que se expande hacia arriba a la derecha y hacia abajo a la izquierda de la celda marcada, hasta que se encuantra una celda con un color diferente al color de trasfondo.
Ahora implementarás la funcionalidad para dibujar cuadrados, círculos y líneas. El tamaño de la figura dibujada dependerá del tamaño seleccionado con la barra deslizante en la interface.
Para los cuadrados, ¡lo más fácil es pensar en ellos como si fueran cebollas! Un cuadrado de tamaño 1 es simplemente la celda marcada por el usuario. Un cuadrado de tamaño 2 es la celda marcada, cubierta por una capa de celdas de tamaño 1, y así sucesivamente. En otras palabras, un cuadrado de tamaño $n$ tendrá alto = ancho = $2n-1$.
Figura 6 - Cuadrados de tamaño 1 (verde), 2 (rojo), 3 (azul), y 4 (amarillo). En cada caso, el usuario marcó la celda del centro del cuadrado.
El botón de triángulo produce un triángulo isóceles como se muestra en la Figura 7. Para un tamaño $n$ seleccionado, el tamaño de la base será $2n + 1$. La altura debe ser $n+1$.
Figura 7 - Triángulos de tamaño 1 (verde), 2 (rojo), 3 (azul), y 4 (amarillo). En cada caso, el usuario marcó la celda del centro de la base del triángulo.
¡Felicitaciones! ¡Llegaste hasta la parte más difícil: círculos! Aquí tendrás que utilizar tus destrezas matemáticas … esperamos que te haya ido bien en tu clase de pre-cálculo …
Figura 8 - Círculos de tamaño 1 (verde), 2 (rojo), 3 (azul), y 4 (amarillo). En cada caso, el usuario marcó la celda del centro del círculo.
Ayuda para producir los círculos:
Primero necesitas entender las expresiones asociadas a un círculo con ecuación: $x^2+y^2=r^2$. Por ejemplo, consideremos un círculo con radio $r=1$. La ecuación $x^2+y^2=1$ nos dice que todo punto $(x,y)$ que satisfaga la ecuación es un punto en la circunferencia del círculo. La expresión para un círculo relleno es: $x^2 + y^2 <=r^2$. Un círculo relleno, de radio $r=1$ tiene expresión $x^2 + y^2 <= 1$, lo que dice que cualquier punto $(x,y)$ que satisfaga $x^2 + y^2 <= 1$ es un punto en el círculo relleno.
¿Cómo producimos el círculo? Una manera sería generar todos los puntos cercanos al centro del círculo y determinar si éstos satisfacen la expresión $x^2 + y^2 <= r^2$. Por ejemplo, podemos tratar todos los puntos que están en el cuadrado de tamaño $2r+1$. Para un círculo de radio $r=2$ tendríamos que generar los siguientes puntos y probarlos en la expresión $x^2 + y^2 <=4$:
(-2, 2) (-1, 2) ( 0, 2) ( 1, 2) ( 2, 2)
(-2, 1) (-1, 1) ( 0, 1) ( 1, 1) ( 2, 1)
(-2, 0) (-1, 0) ( 0, 0) ( 1, 0) ( 2, 0)
(-2,-1) (-1,-1) ( 0,-1) ( 1,-1) ( 2,-1)
(-2,-2) (-1,-2) ( 0,-2) ( 1,-2) ( 2,-2)
En este caso, solo los puntos que se muestran abajo satisfacen la expresión $x^2 + y^2 <=4$.
( 0, 2)
(-1, 1) ( 0, 1) ( 1, 1)
(-2, 0) (-1, 0) ( 0, 0) ( 1, 0) ( 2, 0)
(-1,-1) ( 0,-1) ( 1,-1)
( 0,-2)
En este ejercicio implementarás la funcionalidad para rellenar de color las figuras. Una de las maneras más convenientes para expresar el algoritmo para rellenar es utilizando recursión. Un algoritmo recursivo básico (pero bastante flojo) se encuentra en Wikipedia:
Relleno (celda, color-buscado, color-reemplazo):
1. Si color-buscado es igual al color-reemplazo, return.
2. Si el color de celda no es igual al color-buscado, return.
3. Ajusta el color de celda al color-reemplazo.
4. Ejecuta Relleno (un lugar a la izquerda de celda, color-buscado, color-reemplazo).
Ejecuta Relleno (un lugar a la derecha de celda, color-buscado, color-reemplazo).
Ejecuta Relleno (un lugar arriba de celda, color-buscado, color-reemplazo).
Ejecuta Relleno (un lugar abajo de celda, color-buscado, color-reemplazo).
5. Return.
Figura 9 - (a) El dibujo original con trasfondo blanco y celdas negras. (b) Se selecciona una celda y se ejecuta el algoritmo de rellenar en esa celda (1), © La celda se pinta anaranjada, entonces (d) invoca relleno
en la celda de la izquierda (2). (e) La celda 2 se pinta anaranjada, entonces (f) invoca relleno
en la celda de la izquierda (3). Esta celda no es de color-buscado (es negra), la función regresa (returns).
(g) relleno
se invoca en la celda de la derecha de la celda 2, pero esa celda ya está pintada del color-reemplazo. (h) relleno
se invoca en la celda de arriba de la celda 2. (i) Esta celda se pinta anaranjada e (j) invoca relleno
en la celda de la izquierda (4). Esta celda no es de color-buscado, por lo tanto la función regresa (k), celda (3) invoca relleno
en su celda derecha.
Invoca la función relleno (flood-fill
) y prueba su funcionamiento utilizando varias figuras. Asegúrate de probar figuras abiertas, como, por ejemplo, la siguiente:
Utiliza “Entrega” en Moodle para entregar el archivo tools.cpp
con las funciones que implementaste en esta experiencia de laboratorio. Recuerda utilizar buenas prácticas de programación, incluir el nombre de los programadores y documentar tu programa.
[1] Alyce Brady and Pamela Cutter, http://nifty.stanford.edu/2005/GridPlotter/
[2] Sacha Schutz, http://www.labsquare.org
[3] http://en.wikipedia.org/wiki/Flood_fill