Arrastando círculos
Neste exemplo vamos usar três funções que o Processing dispara pra nós em eventos do mouse (mousePressed()
, mouseReleased()
e mouseDragged()
) para criar um elemento de interação interessante, um círculo que possa ser arrastado.
A ideia é que você possa adaptar este código para, por exemplo, arrastar pontos de controle de uma curva/polígono, ou então, outros ‘elementos gráficos’ do seu sketch.
Arrastando um círculo
-
Vamos precisar de um indicador de estado (flag) parara saber se o arraste começou, para isso vamos usar a variável global
arrastando
, e vamos precisar também duas variáveis para a posição do círculo,x_circulo
ey_circulo
. -
Dentro de
mousePressed()
vamos checar se o mouse está sobre o círculo. A estratégia escolhida foi usar a funçãodist()
para comparar a distância do mouse até o centro do círculo com o raio do círculo (se a distância for menor que o raio, o mouse está sobre o círculo). Esse tipo de checagem é conhecida em programação de jogos e interfaces como “checagem de colisão”.Neste caso estamos fazendo uma checagem de colisão ponto-círculo (a posição do mouse é o ponto), e para outros casos, outros elementos gráficos, é preciso encontrar a estratégia apropriada. Veja, por exemplo, o código do botão simples para ver como é a checagem de colisão ponto-retângulo.
Caso o mouse esteja dentro do círculo quando for apertado, mudamos
arrastando
deFalse
paraTrue
. -
Dentro de
mouseReleased()
vamos só mudararrastando
paraFalse
. Isto significa que quando um botão do mouse for solto acabou qualquer arraste que por ventura estivesse em andamento. Se não havia círculo sendo arrastado, nada muda. -
Dentro de
mouseDragged()
, executado quando o mouse é movido apertado, isto é, em ‘arraste’ (drag), se o indicadorarrastando
forTrue
, indicando que o círculo estava sob o mouse, vamos atualizar as variáveis globaisx_circulo
ey_circulo
com o deslocamento do mouse. O deslocamento é obtido pela diferença da posição atual do mouse,mouseX
emouseY
, para a posição imediatamente anterior (previous) que temos compmouseX
epmouseY
.
arrastando = False
x_circulo, y_circulo = 150, 150
D_CIRCULO = 100 # diâmetro do círculo
def setup():
size(400, 400)
strokeWeight(3)
fill(0)
def draw():
background(0, 0, 200)
if arrastando:
stroke(200, 0, 0)
else:
stroke(255)
ellipse(x_circulo, y_circulo, D_CIRCULO, D_CIRCULO)
def mousePressed(): # quando um botão do mouse é apertado
global arrastando
dist_mouse_circulo = dist(mouseX, mouseY, x_circulo, y_circulo)
raio = D_CIRCULO / 2
if dist_mouse_circulo < raio:
arrastando = True
def mouseReleased(): # quando um botão do mouse é solto
global arrastando
arrastando = False
def mouseDragged(): # quando o mouse é movido apertado
global x_circulo, y_circulo
if arrastando:
# mouseX - pmouseX é o que o mouse foi arrastado em X
x_circulo += mouseX - pmouseX
# mouseY - pmouseY é o que o mouse foi arrastado em Y
y_circulo += mouseY - pmouseY
Arrastando vários círculos
Para acompanhar o próximo exemplo você precisa estar familiarizado com sequências e laços de repetição, uma vez que vamos usar uma estrutura de dados, uma lista, com tuplas dentro, para manter a posição e tamanho de vários círculos, permitindo que qualquer um deles seja arrastado!. Um dos elementos sofisticados deste exemplo é que pegamos para olhar os dados de um círculo por vez, mas ao mesmo tempo, graças à função enumerate()
recebemos a informação do índice, da posição desses dados na lista circulos
. Usamos esse índice para indicar qual círculo está sendo arrastado.
-
A variável global
arrastando
vai manter registro da situação de arraste, como no exemplo anterior, só que agora também indicando o índice de posição de um círculo na listacirculos
. Vamos estabelecer queNone
significa que nenhum círculo está sendo arrastado (um papel feito porFalse
no exemplo anterior). -
Na função
mousePressed()
vamos checar uma a uma cada tupla da lista, contendo X, Y e diâmetro dos círculos, e caso algum deles esteja sob o mouse, vamos atualizar a variávelarrastando
apontando o índice dessa tupla na lista. O primeiro círculo encontrado interrompe a busca, só um círculo pode ser arrastado por vez. No caso de vários círculos estarem sob o mouse, é selecionado o que vier antes na lista, por conta disso, é selecionado aquele que é desenhado primeiro, o ‘mais de baixo’ entre eles. -
A função
mouseReleased()
alteraarrastando
paraNone
. Nenhum círculo está sendo arrastado. -
A função
mouseDragged()
, casoarrastando
não sejaNone
, é criada uma nova tupla com a posição atualizada do círculo e é alterada a lista na posição indicada porarrastando
.
arrastando = None # None quer dizer nenhum círculo sendo arrastado
circulos = [] # lista com coordenadas e tamanhos dos círculos
def setup():
size(400, 400)
strokeWeight(3)
fill(0, 200) # preenchimento translúcido
for _ in range(5): # vamos sortear 5 círculos
d = random(30, 100)
x = random(d, width - d)
y = random(d, height - d)
circulos.append((x, y, d))
def draw():
background(0, 0, 200)
for i, circulo in enumerate(circulos):
x, y, d = circulo
if i == arrastando:
stroke(200, 0, 0)
else:
stroke(255)
ellipse(x, y, d, d)
def mousePressed(): # quando um botão do mouse é apertado
global arrastando
# vamos olhar um círculo por vez da lista `circulos`
for i, circulo in enumerate(circulos): # i é o índice na lista
x, y, d = circulo
dist_mouse_circulo = dist(mouseX, mouseY, x, y)
raio = d / 2
if dist_mouse_circulo < raio: # se o mouse estiver dentro
arrastando = i
break # interrompe o laço, não checa mais outros!
def mouseReleased(): # quando um botão do mouse é solto
global arrastando
arrastando = None
def mouseDragged(): # quando o mouse é movido apertado
if arrastando is not None:
x, y, d = circulos[arrastando]
x += mouseX - pmouseX
y += mouseY - pmouseY
circulos[arrastando] = (x, y, d)
Desafio
Note que quando os círculos se sobrepõe, o clique do mouse “captura” o mais de baixo para o arraste. Você conseguiria mudar este comportamento?
Dica: É possível usar reversed()
para inverter uma lista, mas o problema é que enumerate()
não nos entrega uma lista… é possível converter o “objeto enumerador” entregue por enumerate()
em uma lista com list()
.