30. Разбор задач предыдущего урока (без новых заданий)

Рассмотрим некоторые задачи предыдушего урока.
Не то, чтобы они были сильно важны для работы со списками, но не вижу причины, почему бы не сделать эти задачи сейчас.

Перетаскивание шарика.
Перетаскивать шарики можно разными способами.
Например так:

def p_on(event):
    canv.bind('<Button-3>',p_off)
    canv.bind('<Motion>',task)

def p_off(event):
    canv.bind('<Button-3>',p_on)
    canv.unbind('<Motion>')
    

def task(event):
    x = event.x
    y = event.y
    for b in balls:
        if math.sqrt((x-b.x)**2 + (b.y - y)**2) < b.r:
            b.x = x
            b.y = y
            b.paint()

У этого способа есть один недостаток — шарики могут «теряться» при резком движении мыши.

Что ж, зайдем с другой стороны:

def p_on(event): # пробуем подцепить шарик
    global bt
    x = event.x
    y = event.y
    for b in balls: # искать шарик под мышкой
        if math.sqrt((x-b.x)**2 + (b.y - y)**2) < b.r: # если нашли то запомнить
            bt = b 
            canv.bind('<Button-3>',p_off) # правая кнопка будет отключать перетаскивание
            canv.bind('<Motion>',move) # перетаскивание
            break # на что влияет эта строка?

def p_off(event): # пробуем отцепить шарик
    global bt
    canv.bind('<Button-3>',p_on) # правая кнопка будет включать перетаскивание
    canv.unbind('<Motion>') #  перетаскивать не надо
    if 'bt' in globals(): # если переменная существует, то удалить ее
        del(bt)
    
def move(event):
    if 'bt' in globals(): # если переменная существует, то переместить шарик к мышке
        bt.x = event.x
        bt.y = event.y
        bt.paint()
            


canv.bind('<Button-1>', fill_random)
canv.bind('<Button-3>',p_on) #  правая кнопка включит перетаскивание
 
mainloop()

В прошлом уроке было задание, которое должно было вызвать трудности: это вращение линии.
Для того, чтобы вращать линией, нужно вспомнить об углах, синуах и косинусах.

def p_on(event): # включить вращение 
    global an, le, work, x1, y1 # эти значения изменяются разными функциями
    canv.bind('<Button-3>',p_off)  # нажатие на ПКМ выключит вращение
    an = 0 # угол в радианах
    le = 100 # длина линии
    work = 1 # работать (изменяется нажатием на ПКМ)
    canv.delete('line') # удалить старые линии
    x2 = x1 # пока нет данных, рисуем точку
    y2 = y1
    line = canv.create_line(x1,y1,x2,y2, fill = 'orange', width = 3, tag = 'line') 
    while work: # пока нажатием ПКМ не отключено вращение - работать
        an += 0.03 # повернуть линию по часовой на 0,03 * 180 / 3,14 градусов, т.е. примерно на 1,5 градуса
        x2 = x1 + le*math.cos(an)  
        y2 = y1 + le*math.sin(an)
        canv.coords(line,x1,y1,x2,y2)
        time.sleep(0.01)
        canv.update()

def p_off(event):
    global work
    work = 0 # хватит там вращать
    canv.delete('line') # удалить линию
    canv.bind('<Button-3>',p_on) # ПКМ снова будет включать вращение
    
def move(event): # постоянно запоминаем координаты мыши
    global x1, y1
    x1 = event.x
    y1 = event.y
	
canv.bind('<Button-1>', fill_random)
canv.bind('<Button-3>',p_on)
canv.bind('<Motion>',move)	
 
mainloop()

Определить столкновение двух шариков проще, чем пересечении линии и шарика. Традиционно, для нахождения точек пересечения двух линий, нужно составить уравнения этих линий и найти решение системы. Звучит весело, не правда ли?
Вот неплохая статья на эту тему: http://e-maxx.ru/algo/circle_line_intersection
Но мы можем сильно упростить задачу, возьмем только один крайний случай — конец отрезка попадает в круг. Да, это не будет полноценным решением, зато временно избавит нас от необходимости глубоко нырять в геометрию (без которой мы не обойдемся, но это тема отдельных нескольких уроков).
Вот так можно сделать удаление шарика, по которому попал конец вращающейся линии:

def p_on(event):
    global an, le, work, x1, y1
    canv.bind('<Button-3>',p_off) 
    an = 0
    le = 100
    work = 1
    canv.delete('line')
    x2 = x1
    y2 = y1
    line = canv.create_line(x1,y1,x2,y2, fill = 'orange', width = 3, tag = 'line')
    while work:
        an += 0.03
        x2 = x1 + le*math.cos(an)
        y2 = y1 + le*math.sin(an)
        canv.coords(line,x1,y1,x2,y2)
        for b in balls:
            if math.sqrt((b.x-x2)**2 + (b.y-y2)**2) < b.r:
                canv.delete(b.id)
                balls.remove(b)
        time.sleep(0.01)
        canv.update()

Теперь рассмотрим «захват и уничтожение шарика»:

def p_on(event):
    global an, le, work, x1, y1
    canv.bind('<Button-3>',p_off) 
    an = 0
    le = 100
    work = 1
    canv.delete('line')
    x2 = x1
    y2 = y1
    line = canv.create_line(x1,y1,x2,y2, fill = 'orange', width = 3, tag = 'line')
    while work:
        an += 0.04
        x2 = x1 + le*math.cos(an)
        y2 = y1 + le*math.sin(an)
        canv.coords(line,x1,y1,x2,y2)
        for b in balls:
            if math.sqrt((b.x-x2)**2 + (b.y-y2)**2) < b.r:
                dx = -(b.x - x1) / 14
                dy = -(b.y - y1) / 14
                for z in range(15):
                    b.x += dx 
                    b.y += dy 
                    b.paint()
                    canv.update()
                    time.sleep(0.01)
                dr = b.r / 10
                for z in range(9):
                    b.r -= dr
                    b.paint()
                    canv.update()
                    time.sleep(0.01)
                canv.delete(b.id)
                balls.remove(b)
        time.sleep(0.015)
        canv.update()

При такой простой реализации видно, что линия «застревает», пока шарик приближается и уменьшается. Этот недостаток можно побороть, но мы оставим так. На текущий момент основная цель — показать работу со списками. Этот урок немного выбивается в сторону от главной линии, но иногда можно позволить себе поделать забавные вещи. Если не развлекаться, то зачем учиться?
Добавим реакцию на колесико: будем изменять длину линии:

def change_len(event):
    global le
    le += event.delta // 10
    
canv.bind('<Button-1>', fill_random)
canv.bind('<Button-3>',p_on)
canv.bind('<Motion>',move)
canv.bind('<MouseWheel>',change_len)
 
mainloop()

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *