Interacción con el ratón
El conjunto de procedimientos/reports de NetLogo para manejar el ratón es muy reducido pero suficientemente expresivo. Esencialmente, consta de 4 reports:
- mouse-xcor: Devuelve la coordenada x del ratón.
- mouse-ycor: Devuelve la coordenada y del ratón.
- mouse-down?: Devuelve true/false para indicar si el botón izquierdo del ratón está siendo presionado.
- mouse-inside?: Devuelve true/false para indicar si el ratón se encuentra dentro del widget del mundo o no.
Debe tenerse en cuenta que estos procedimientos solo informan del comportamiento del ratón en la vista actual del mundo, por lo que sus coordenadas se expresan en unidades de patches (aunque decimales) y por tanto dependen del tamaño del mundo y de la posición del origen en el mismo. No están pensadas (ni pueden) controlar el ratón cuando éste se encuentra fuera del mundo, ni aunque esté dentro del área de interfaz dentro de la ventana de NetLogo (salvo mouse-inside?, que nos devolvería false).
Si quieres ver rápidamente cómo funcionan estos reports basta que crees un control de tipo monitor en un modelo y copies el siguiente código:
(word "Coor: (" (precision mouse-xcor 1) "," (precision mouse-ycor 1) ") " "Down?: " mouse-down? " Inside?: " mouse-inside?)
Observa que hemos reducido el número de decimales mostrados para las coordenadas a 1.
Debemos tener en cuenta que el control del ratón precisa de un proceso constante de observación del mundo (en el ejemplo anterior del monitor es el propio NetLogo el que se encarga de esa observación continua), por lo que será necesario envolver nuestro procedimiento dentro de un bucle que se encargue de mantenerlo vivo. El problema es que si simplemente hacemos uso de un loop infinito podemos perder la capacidad de ejecutar asíncronamente más procedimientos necesarios, por lo que una solución habitual suele ser usar la propiedad forever de los botones, y de esa forma activar/desactivar la observación continua del mundo por medio de un botón (además de que los procedimientos ejecutados por los botones no impiden la ejecución de otros procedimientos en paralelo).
En esta entrada vamos a mostrar algunos ejemplos sencillos de cómo programar algunos comportamientos usando estos procedimientos:
Encender/Apagar patches
Vamos a comenzar por un pequeño código que permite usar el ratón para encender/apagar patches aprovechando que las coordenadas que devuelve el ratón identifican el patch sobre el que está.
Recordemos que un patch se identifica por sus dos coordenadas enteras:
patch px py
Pero si introducimos coordenadas decimales, NetLogo automáticamente las redondea al entero más cercano (con round) para identificar el pacth correspondiente. Así pues:
patch 1.1 2.3 = patch 1 2
patch -2.7 3 = patch -3 3
Puedes comprobarlo por medio de un monitor donde incluyas el código:
patch mouse-xcor mouse-ycor
Nuestra primera aproximación para hacer el procedimiento de encendido/apagado de pacthes (simplemente, los alternaremos entre blanco/negro al pulsar en ellos) es por medio del siguiente código (que debes asociar a un botón de tipo forever):
to encender-apagar
if mouse-down? [
ask patch mouse-xcor mouse-ycor [set pcolor white - pcolor]
]
end
Si lo compruebas, en efecto hace lo que queremos, pero el control del ratón es tan rápido que una sola pulsación del botón izquierdo se interpreta como varias pulsaciones seguidas (ya que, realmente, el report no indica si "se ha pulsado", sino si "está siendo pulsado"). La solución es sencilla: cuando NetLogo reconozca una pulsación, haz que se produzca una pequeña pausa para que dé tiempo al usuario a soltarlo. Por ejemplo:
to encender-apagar
if mouse-down? [
ask patch mouse-xcor mouse-ycor [set pcolor white - pcolor]
wait .3
]
end
Nota: La forma de alternar entre blanco y negro hace uso simplemente del hecho de que el color asociado al negro es 0.
Crear tortugas con el ratón
Vamos a hacer un pequeño procedimiento en el que el usuario decide dónde crear tortugas pulsando en la posición del mundo que desee:
to crea-tortugas
if mouse-down? [
crt 1 [ setxy mouse-xcor mouse-ycor ]
wait .3
]
end
Ahora podemos añadir otro procedimiento que haga que las tortugas sigan al ratón cuando se encuentra dentro del mundo:
to follow-mouse
if mouse-inside? [
ask turtles [
facexy mouse-xcor mouse-ycor
fd .001
]
]
end
Observa que si la topología del mundo es un cilindro o un toro, el procedimiento anterior puede provocar que a veces las tortugas vayan en dirección contrario. Simplemente, están buscando el camino más corto al ratón en la topología correspondiente.
Drag & Drop
Para crear un procedimiento Drag&Drop hemos de tener en cuenta que el procedimiento ahora es:
- Seleccionar una tortuga por medio de una pulsación del botón izquierdo.
- Arrastrar la tortuga seleccionada manteniendo pulsado el botón izquierdo.
- Soltar la tortuga donde esté el ratón cuando se deja de presionar el botón izquierdo.
Un procedimiento que realiza esta tarea es (ten en cuenta que para simplificar la explicación no estamos controlando posibles errores que pueden aparecer, como que no haya tortugas que seleccionar y se pulse en algún sitio del mundo):
to drag&drop
if mouse-down? [
let selec min-one-of turtles [distancexy mouse-xcor mouse-ycor]
ask selec [
if distancexy mouse-xcor mouse-ycor < 1 [
while [mouse-down?] [
setxy mouse-xcor mouse-ycor
]
]
]
wait .3
]
end
Para probar este procedimiento, asócialo a un botón del interfaz de tipo forever.
Ten en cuenta lo siguiente:
- selec almacena la tortuga más cercana al ratón (o una de ellas, si hay empate), pero solo se considera baja la influencia del drag&drop si la distancia al ratón es menor que 1. No podemos exigir que sea 0 porque entonces sería difícil seleccionar ninguna tortuga.
- Seguimos manteniendo la espera al finalizar el proceso, para que de tiempo a soltar el botón izquierdo y no seleccionemos la misma tortuga por error.
- El proceso de arrastre se hace por medio de un bucle de tipo while que mantiene el foco mientras el botón izquierdo del ratón se mantenga pulsado, por lo que detiene cualquier otro proceso en ejecución (puedes comprobar este hecho manteniendo pulsados, por ejemplo, los botones asociados a drag&drop y a follow-mouse).
- Si quisiéramos que la tortuga se soltase en el patch donde está el ratón (y no en las coordenadas exactas del ratón), basta cambiar la línes setxy mouse-xcor mouse-ycor por move-to patch mouse-xcor mouse-ycor.
Con los procedimientos vistos aquí ya se pueden realizar procedimientos que permiten una interacción rica entre el usuario y el modelo.