Час 6 - релативне координате - утврђивање¶
На прошлом часу смо се упознали са релативно задатим координатама и предностима тог механизма цртања. Утврдимо ово кроз још неколико примера.
Кућа - положај¶
Рецимо да сте написали овај програм, а циљ вам је да преправите програм тако да кућица може једноставно да се премешта:
import pygame as pg
pg.init() # uključivanje rada biblioteke PyGame
pg.display.set_caption("Кућа") # podešavamo naslov prozora
(sirina, visina) = (300, 300) # otvaramo prozor dimenzije 300x300
prozor = pg.display.set_mode((sirina, visina))
prozor.fill(pg.Color("darkgreen")) # bojimo pozadinu ekrana u svetlo plavo
pg.draw.polygon(prozor, pg.Color("red"), [(50, 150), (120, 100), (190, 150)]) # krov
pg.draw.rect(prozor, pg.Color("khaki"), ( 50, 150, 140, 100)) # kuca
pg.draw.rect(prozor, pg.Color("brown"), ( 60, 170, 30, 30)) # levi prozor
pg.draw.rect(prozor, pg.Color("brown"), (150, 170, 30, 30)) # desni prozor
pg.draw.rect(prozor, pg.Color("brown"), (100, 180, 40, 70)) # vrata
pg.display.update() # prikazujemo nacrtano na ekranu
# petlja obrade događaja - čekamo dok korisnik ne isključi prozor
while pg.event.wait().type != pg.QUIT:
pass
# isključivanje rada biblioteke PyGame
pg.quit()
(PyGame_house_detailed_fixed)
Нека је главна тачка (сидро) (x, y) = (50, 150)
. Довршите
започето преправљање програма у пољу испод, у коме се цртање обавља у
функцији kuca(x, y, boja_zidova)
. Када се уверите да цртежи у
два програма изгледају исто (осим што су прозори различите величине),
замените позив kuca(50, 150, pg.Color("khaki"))
са следећа 4,
да бисте добили слику као кад се кликне на дугме „Прикажи пример”:
kuca(150, 90, pg.Color(220, 220, 220))
kuca(220, 130, pg.Color("white"))
kuca(350, 160, (255,255,150))
kuca( 50, 150, pg.Color("khaki"))
import pygame as pg
pg.init() # uključivanje rada biblioteke PyGame
pg.display.set_caption("Кућа") # podešavamo naslov prozora
(sirina, visina) = (500, 300) # otvaramo prozor dimenzije 500x300
prozor = pg.display.set_mode((sirina, visina))
prozor.fill(pg.Color("darkgreen")) # bojimo pozadinu ekrana u tamno zeleno
def kuca(x, y, boja_zidova):
pg.draw.polygon(prozor, pg.Color("red"), [(x, y), (x+???, y-???), (x+140, y)]) # krov
pg.draw.rect(prozor, boja_zidova, (x, y, 140, 100)) # kuca
pg.draw.rect(prozor, pg.Color("brown"), (x + ???, y + ???, 30, 30)) # levi prozor
pg.draw.rect(prozor, pg.Color("brown"), (x + ???, y + ???, ???, ???)) # desni prozor
pg.draw.rect(prozor, pg.Color("brown"), (x + ???, y + ???, ???, ???)) # vrata
kuca( 50, 150, pg.Color("khaki"))
pg.display.update() # prikazujemo nacrtano na ekranu
# petlja obrade događaja - čekamo dok korisnik ne isključi prozor
while pg.event.wait().type != pg.QUIT:
pass
# isključivanje rada biblioteke PyGame
pg.quit()
(PyGame_house_detailed_movable)
Саобраћајни знак од једнакостраничних троуглова¶
Напиши програм који исцртава саобраћајни знак укрштања са путем са првенством пролаза који је у облику жутог једнакостраничног троугла са црвеном ивицом, окренутог тако да му је хоризонтална основица горе.
Ефекат троугла који је жуте боје и има дебелу црвену ивицу постићи ћемо тако што ћемо нацртати прво већи црвени троугао, а затим мањи жути троугао. Та два троугла ћемо поставити тако да им се тежишта (уједно и све значајне тачке) поклапају и да су идентично оријентисани.
Приликом цртања саобраћајног знака потребно је да одредимо поступак којим се црта једнакостранични троугао ако му је познато сидро постављено у тежиште T за које ћемо претпоставити да има координате (tx,ty) и ако му је позната димензија (то може бити било дужина странице a, било висина h, јер се из једне од ових димензија друга једноставно израчунава на основу чувене везе h=a√32, која се лако изводи применом Питагорине теореме на правоугли троугао чија је једна катета висина једнакостраничног троугла, друга половина странице, а хипотенуза је страница једнакостраничног троугла).

Пошто, како из математике знамо, тежиште троугла дели тежишну дуж (у овом случају то је уједно и висина) у односу 2:1, координате темена A и B су у односу на ову тачку померене (транслиране) на горе за 13h, док је тачка C померена на доле за 23h.
Координате x ових тачака одређујемо у односу на тачку T, односно дужину странице a. Тачка А је померена од тежишта за а2 ка левој ивици прозора. Тачка В је померена од тежишта за а2 средину ка десној ивици. Тачка C се хоризонално налази на линији тежишта.
Дакле, тачка A има координате (tx−a2,ty−h3), тачка B има координате (tx+a2,ty−h3), док тачка C има координате (tx,ty+2h3).
Пошто је потребно да нацртамо два троугла, можемо дефинисати функцију за цртање троугла и позвати је два пута (за исто тежиште, али различите боје и димензије). Тежиште ћемо поставити хоризонтално на средину прозора, док ћемо га вертикално поставити тако да троугао делује центриран по средини екрана. Оставићемо простор (маргину) од по 30 пиксела изнад и испод троугла, а тежиште троугла ћемо поставити тако да преосталу висину дели у односу 1:2.
На основу претходне дискусије допуни наредни програм.
import math
import pygame as pg
import pygamebg
(sirina, visina) = (300, 300) # otvaramo prozor
prozor = pygamebg.open_window(sirina, visina, "Саобраћајни знак")
def jedakostranicni_trougao(tx, ty, h, boja):
a = h * 2 / math.sqrt(3) # dužina stranice
# koordinate temena - težiste deli visinu u odnosu 1 : 2
A = (tx - a/2, ty - h/3)
B = (???, ???)
C = (???, ???)
pg.draw.polygon(prozor, ???, ???)
# bojimo pozadinu prozora u belo
prozor.fill(pg.Color("white"))
margina = 30
h = visina - 2*margina
(tx, ty) = (sirina / 2, margina + h / 3)
jedakostranicni_trougao(tx, ty, h, pg.Color("red"))
jedakostranicni_trougao(tx, ty, 0.65*h, pg.Color("yellow"))
# prikazujemo prozor i čekamo da ga korisnik isključi
pygamebg.wait_loop()
(obojeni_trougao)
Проблеми приликом израчунавања параметара кругова¶
Подсетимо се да за разлику од функција за цртање линија и
правоугаоника које примају и реалне аргументе, функција за цртање
кругова захтева да су координате центра круга и дужина полупречника
искључиво цели бројеви. Ово може довести до одређених проблема у
програмима у којима се ти аргументи израчунавају у програму. Када
видиш поруку TypeError: integer argument expected, got float
, тада
знај да је проблем у томе што је функцији за цртање круга уместо целог
прослеђен неки реалан број и тај проблем можеш лако решити коришћењем
неког облика заокруживања бројева.
Зато ћемо често у задацима у којима цртамо кругове, за израчунавање
координата центара и полупречника кругова уместо реалног дељења
(оператора /
) користити целобројно дељење (оператор //
) или
ћемо користити заокруживање реалних бројева (функцијом round
или
функцијом int
).
Провери да ли ово разумеш тако што ћеш одговорити на наредно питање.
Q-44: Након позива pg.draw.circle(prozor, boja, (x, y), r) пријављена је грешка TypeError: integer argument expected, got float. Шта може бити узрок те грешке?
Наредни програм црта цвет састављен од кругова, али не ради исправно и твој задатак је да га поправиш.
Цвет¶
Напиши програм који исцртава цвет који се састоји од централног жутог круга пречника 100 пиксела, око којег се налази 6 правилно распоређених латица розе боје, свака у облику круга, такође пречника 100 пиксела (центри латица се налазе у теменима правилног шестоугла, чији је центар у центру цвета, а дужина странице је 100 пиксела).

За цртање круга потребно је знати координате центра и дужину полупречника круга. Пречник свих кругова је једнак, самим тим и полупречник r=a2. Нека је тачка О центар жутог, централног круга. Координате ове тачке означимо са (cx,cy). Ова тачка се налази у центру прозора и њене координате једнаке су половини висине, односно ширине прозора. Координате осталих центара кругова изразићемо такође преко координата (cx,cy) . Координате тачке А1 означимо са (x1,y1). Тачка А1 је за a померена (транслирана) од тачке О по оси x, тако да је x1, прва координата ове тачке једнака x1=cx+a, а друга координата ове тачке y1 једнака је y координати тачке О, тј. y1=cy. Координате тачке А2 означимо са (x2,y2). Ова тачка је у односу на тачку О померена (транслирана) за a2 по оси x, односно за висину h=a√32 једнакостраничног троугла △OA1A2 по оси y. На основу овога закључујемо да су координате тачке А2, (x2,y2)=(cx+a2,cy+h). Координате центра осталих кругова одређујемо на сличан начин.
На основу претходне дискусије, допуни наредни програм и поправи грешке везане за тип података бројева.
import math
import pygame as pg, pygamebg
(sirina, visina) = (400, 400) # uključujemo prozor dimenzije 400x400 piksela
prozor = pygamebg.open_window(400, 400, "Blue circle")
# boje koje ćemo koristiti
BELA = (255, 255, 255)
ZUTA = (255, 255, 0)
ROZE = (255, 200, 200)
# bojimo pozadinu u belo
prozor.fill(BELA)
# koordinate centra prozora
(cx, cy) = (sirina / 2, visina / 2)
# precnici krugova - duzina stranice pravilnog sestougla u cijim se
# temenima nalaze centri krugova
a = 100
# visina karakteristicnog trougla sestougla
h = a * math.sqrt(3) / 2
# sva temena šestougla dele ove koordinate
x1 = cx - a
x2 = cx - a/2
x3 = cx + a/2
x4 = cx + a
y1 = ???
y2 = cy
y3 = ???
# koordinate temena šestougla
O = (cx, cy)
A1 = (x1, y2)
A2 = (???, ???)
A3 = (???, ???)
A4 = (???, ???)
A5 = (???, ???)
A6 = (???, ???)
# poluprecnik krugova
r = a / 2
# iscrtavamo krugove
pg.draw.circle(prozor, ZUTA, O, r)
pg.draw.circle(prozor, ROZE, A1, r)
pg.draw.circle(prozor, ROZE, A2, ???)
pg.draw.circle(prozor, ROZE, ???, ???)
pg.draw.circle(prozor, ROZE, ???, ???)
pg.draw.circle(prozor, ROZE, ???, ???)
pg.draw.circle(prozor, ROZE, ???, ???)
# prikazujemo prozor i čekamo da ga korisnik isključi
pygamebg.wait_loop()
(cvet)
Прелазак са апсолутних на релативне координате¶
Иако цртеже који се задају у односу на неко сидро обично креирамо имајући ово у старту у виду, постоји систематичан поступак којим од цртежа који је задат у апсолутним координатама можемо доћи до цртежа који је нацртан у односу на неко задато сидро (можемо усидрити цртеж). Покушајмо да резимирамо како можемо да уведемо сидро тј. да од цртежа у ком се јављају апсолутне координате уведемо релативне координате.
На пример, ако се црта круг помоћу pg.draw.circle(prozor, boja, (cx,
cy), r)
, тада га можемо усидрити у тачку (x, y)
тиме што позив
заменимо са pg.draw.circle(prozor, boja, (x + (cx - x), y + (cy -
y)), r)
. На пример, Ако круг нацртан помоћу pg.draw.circle(prozor,
boja, (100, 50), r)
желимо да усидримо у тачку (x, y) = (50,
100)
, тада ћемо га цртати помоћу pg.draw.circle(prozor, boja, (x +
50, y - 50), r)
. Слично можемо урадити и у случају осталих облика.
Провери да ли ово разумеш тако што ћеш одговорити на наредних неколико питања.
Q-45: Желимо да прилагодимо цртеж који се састоји од наредних облика, тако да се све црта у односу на сидро са координатама x=100, y=100.
pg.draw.circle(prozor, pg.Color("red"), (100, 100), 50, 1)
pg.draw.line(prozor, pg.Color("red"), (50, 50), (150, 150))
pg.draw.line(prozor, pg.Color("red"), (150, 50), (50, 150))
pg.draw.rect(prozor, pg.Color("red"), (50, 50, 100, 100))
(pygame_quiz_uvodjenje_sidra_code)
Које наредбе ће бити део прилагођеног цртежа?
Q-46: Круг нацртан наредбом pg.draw.circle(prozor, boja, (180, 80), 60) део је цртежа који желимо да прилагодимо тако да му основна тачка (сидро) буде у одређено променљивама x = 100 и y = 100. Која наредба ће бити део тако прилагођеног цртежа?
Покрени сада наредни програм и видећеш лице човечуљка. Прилагоди цртеж тако да се црта релативно у односу на сидро које се налази у центру плавог круга (у почетку је то (100,100)). Покретањем програма провери да ли ти је решење добро. Ако је све урађено како треба, цртеж ће се исправно померати док се миш помера.
import pygame as pg
import pygamebg
(sirina, visina) = (300, 300) # otvaramo prozor
prozor = pygamebg.open_window(sirina, visina, "Релативно цртање")
x = sirina // 2
y = visina // 2
a = 5
def crtanje():
prozor.fill(pg.Color("white"))
pg.draw.circle(prozor, pg.Color("blue"), (100, 100), 60)
pg.draw.circle(prozor, pg.Color("yellow"), (75, 75), 15)
pg.draw.circle(prozor, pg.Color("black"), (80, 80), 5)
pg.draw.circle(prozor, pg.Color("yellow"), (125, 75), 15)
pg.draw.circle(prozor, pg.Color("black"), (120, 80), 5)
pg.draw.ellipse(prozor, pg.Color("red"), (75, 110, 50, 10))
def obradi_dogadjaj(dogadjaj):
global x, y, a
if dogadjaj.type == pg.MOUSEMOTION:
(x, y) = dogadjaj.pos
return True
elif dogadjaj.type == pg.MOUSEBUTTONDOWN:
if dogadjaj.button == 1:
a += 1
return True
elif dogadjaj.button == 3:
a -= 1
return True
return False
pygamebg.event_loop(crtanje, obradi_dogadjaj)
(PyGame_movable)
Размотримо сада како да поред цртања у односу на неки релативан положај (сидро) направимо наше цртеже скалабилним, тј. да се цртају у односу на релативно задату димензију.
Q-47: Круг нацртан наредбом pg.draw.circle(prozor, boja, (180, 80), 60) део је цртежа који желимо да прилагодимо тако да му главна тачка (сидро) буде у одређено променљивама x = 100 и y = 100, и да му основна величина буде a=5. Која наредба ће бити део тако прилагођеног цртежа?
Прилагоди сада додатно програм тако да се све црта релативно и у односу на јединичну величину (нека у почетку то буде 5). Ако је све урађено како треба, величина ће му се мењати кликом на лево тј. десно дугме миша.
import pygame as pg
import pygamebg
(sirina, visina) = (300, 300) # otvaramo prozor
prozor = pygamebg.open_window(sirina, visina, "Релативно цртање")
x = sirina // 2
y = visina // 2
a = 5
def crtanje():
prozor.fill(pg.Color("white"))
pg.draw.circle(prozor, pg.Color("blue"), (100, 100), 60)
pg.draw.circle(prozor, pg.Color("yellow"), (75, 75), 15)
pg.draw.circle(prozor, pg.Color("black"), (80, 80), 5)
pg.draw.circle(prozor, pg.Color("yellow"), (125, 75), 15)
pg.draw.circle(prozor, pg.Color("black"), (120, 80), 5)
pg.draw.ellipse(prozor, pg.Color("red"), (75, 110, 50, 10))
def obradi_dogadjaj(dogadjaj):
global x, y, a
if dogadjaj.type == pg.MOUSEMOTION:
(x, y) = dogadjaj.pos
return True
elif dogadjaj.type == pg.MOUSEBUTTONDOWN:
if dogadjaj.button == 1:
a += 1
return True
elif dogadjaj.button == 3:
a -= 1
return True
return False
pygamebg.event_loop(crtanje, obradi_dogadjaj)
(PyGame_movable_scalable)