Ve třetím článku o fyzikálním enginu, který je součástí knihovny LÖVE, si ukážeme, jakým způsobem je možné v tomto systému vytvořit takzvané pružné (či elastické) vazby mezi několika tělesy. Taktéž si vysvětlíme, jak tyto vazby ovlivní vzájemnou interakci těles v simulovaném světě.

Obsah

1. Fyzikální engine implementovaný v knihovně LÖVE (3.část)

2. Systémy několika těles propojených s využitím elastických vazeb

3. Vytvoření simulovaného světa s tělesy spojenými elastickými vazbami

4. Objekt typu DistanceJoint

5. Vytvoření pružné vazby mezi dvojicí těles

6. Zobrazení pružné vazby v simulovaném světě

7. První demonstrační příklad – vytvoření pružné vazby mezi dvěma míčky

8. Složitější topologie vazeb

9. Druhý demonstrační příklad – 25 volně se pohybujících míčků

10. Programové vytvoření elastických vazeb

11. Třetí demonstrační příklad – vazby mezi míčky v mřížce 25×25

12. Čtvrtý demonstrační příklad – vytvoření horizontálních i vertikálních vazeb

13. Repositář se zdrojovými kódy všech čtyř dnešních demonstračních příkladů

14. Odkazy na Internetu

1. Fyzikální engine implementovaný v knihovně LÖVE (3.část)

V úvodním článku o fyzikálním enginu love.physics, který je součástí knihovny LÖVE, jsme se seznámili s tím, k čemu se vlastně tento engine využívá a jakým způsobem je ho možné zařadit do vyvíjených aplikací napsaných v programovacím jazyku Lua. Současně jsme se seznámili se základními typy objektů, s nimiž tento engine pracuje a z nichž se vytváří simulovaný (například herní) svět. Na díl první velmi úzce navázal druhý díl věnovaný konceptu takzvaných obalových těles používaných při detekci kolizí v simulovaném světě a taktéž se způsobem řešení některých problémů plynoucích z nepřesných výpočtů těchto kolizí. Připomeňme si, že kvůli přesnějšímu výpočtu kolizí je nutné některá tělesa označit příznakem „střela“.

07

Obrázek 1: Obalová tělesa (bounding boxes) zobrazená pro všechny objekty v simulovaném světě. Princip obalových těles jsme si vysvětlili v předchozí části seriálu.

Fyzikální engine love.physics dokáže mezi tělesy vytvořit i přímé vazby, z nichž nejpoužívanější je elastická vazba (můžeme si zjednodušeně představit, že tělesa jsou spojena pružinkami). Z hlediska simulace objektů s elastickými vazbami je nejdůležitější silou ovlivňující trajektorii těchto těles pružná (elastická) síla. Jedná se, na rozdíl od globálně působící gravitační síly či odporu okolního prostředí, o sílu lokální, protože působí vždy mezi dvojicí těles, která jsou explicitně spojena elastickou vazbou. Tato síla může mít vliv na pohyb hmotného středu těles (těžiště pevných těles), popř. i na jejich otáčení v případě, že je konec elastické vazby umístěn mimo těžiště. Směr a velikost této síly je vypočten s využitím Hookova zákona. Nejdůležitější vlastností elastické vazby, kterou si můžeme představit jako neviditelnou pružinu nataženou mezi dvojici těles, je klidová délka, které se pružina při svém natažení či stlačení snaží dosáhnout a dále konstanty odpovídající síle pružiny a míře jejího tlumení (čím větší je tlumení, k tím menším zákmitům dochází a naopak).

11

Obrázek 2: Při kolizi těles dochází k protnutí jejich bounding boxů (podrobnější informace byly opět uvedeny minule).

2. Systémy několika těles propojených s využitím elastických vazeb

Pružné (elastické) vazby je možné použít pro vytvoření takzvaného systému vázaných těles. Jedná se o takové systémy, ve kterých jsou mezi jednotlivými tělesy vytvořeny pevné nebo elastické vazby. Pevné vazby nedovolují změnit vzdálenost mezi tělesy ve vazbě a řešení vzájemné polohy těles je v tomto případě zajištěno pomocí poměrně složité inverzní kinematiky, která však není v současné verzi systému LÖVE implementována. Naproti tomu u elastických vazeb se vzdálenost těchto těles může v určitém intervalu hodnot měnit. V převážné většině případů se jedná o vazby vytvořené mezi dvojicí těles (vazby totiž simulují pružiny natažené mezi dvojicí těles). Složitější vazby se vytváří složením těchto jednoduchých vazeb, což si ukážeme později na demonstračních příkladech.

03

Obrázek 3: Začátek prvního demonstračního příkladu. Vidíme zde dvojici míčků spojených elastickou vazbou (ta je naznačena úsečkou).

Vazby mezi tělesy jsou většinou zadávány už při vytváření celého modelu, ovšem v systému LÖVE je možné počet vazeb i jejich vlastnosti měnit i v průběhu simulace, což povoluje například simulovat i rozpad celého objektu či naopak jeho složení z několika částí. Mimochodem, v enginu love.physics je dostupných hned několik typů vazeb, nejenom vazby elastické. Ostatně stačí se podívat na následující tabulku, v níž jsou podporované vazby vypsány:

# Objekt představující vazbu mezi tělesy
1 DistanceJoint
2 FrictionJoint
3 GearJoint
4 MouseJoint
5 PrismaticJoint
6 PulleyJoint
7 RevoluteJoint
8 RopeJoint
9 WeldJoint
10 WheelJoint

04

Obrázek 4: První demonstrační příklad: kvádr se nachází těsně před dopadem na podložku. To stejné platí pro dvojici míčků propojených elastickými vazbami.

3. Vytvoření simulovaného světa s tělesy spojenými elastickými vazbami

Při inicializaci aplikace, ve které je počítán stav simulovaného dvourozměrného světa, se musí nastavit hmotnosti, počáteční pozice, rychlosti a samozřejmě taktéž tvary těles, což je téma, které již důvěrně známe z obou předchozích částí tohoto seriálu. Následně jsou vytvořeny vazby mezi tělesy, přičemž v závislosti na potřebách aplikace je možné tyto vazby vygenerovat buď ručně nebo automaticky (obojí způsob si ukážeme v demonstračních příkladech). Manuální i automatická tvorba musí brát v úvahu geometrickou stabilitu celého systému. Této stability se dosáhne vytvořením vazeb na hranách zadaných těles a dále vytvořením pomocných vazeb po úhlopříčkách, což si taktéž ukážeme v jednom demonstračním příkladu (bude se samozřejmě jednat pouze o dvojrozměrný případ). Systémy s elastickými vazbami jsou vhodné pro použití především u těch modelů, které v průběhu simulace (a s ní souvisejících deformací modelu) nemění svoji topologii.

05

Obrázek 5: První demonstrační příklad: nyní došlo k odrazu kvádru od podložky a následně i k odrazu obou spojených míčků (od kvádru).

V některých případech však může být žádoucí, aby bylo možné modelovaný objekt rozdělit, tj. programově přerušit některé elastické vazby (a to kdykoli během simulace). Této vlastnosti lze dosáhnout specifikací maximální možné síly, která může působit mezi dvěma tělesy spojenými elastickou vazbou. V případě, že skutečná síla překročí tuto mez, dojde k „přetržení“ pružiny a elastická vazba je zrušena. Pokud vznikne nutnost modelovat objekt, u nějž se topologie mění zásadnějším způsobem, je vhodnější použít dynamicky vázané systémy. Tyto pokročilejší techniky sice systém LÖVE (a engine love.physics) ve své současné verzi nepodporuje, ovšem lze je napodobit programově, neboť informace o stavu každé vazby (nejenom elastické) je možné poměrně jednoduše získat v kterémkoli okamžiku simulace.

06

Obrázek 6: První demonstrační příklad: dopad kvádru zpět na podložku.

4. Objekt typu DistanceJoint

Elastická vazba, která je v systému LÖVE (resp. přesněji řečeno v modulu love.physics) představovaná objektem typu DistanceJoint, se vytvoří voláním konstruktoru love.physics.newDistanceJoint(). Tento konstruktor musí být zavolaný se šesti či nově dokonce se sedmi parametry. První dva parametry představují dvojici těles, které mají být spojeny pomocí elastické vazby (musí se tedy jednat o objekty typu Body). Další dvojice parametrů udává souřadnice prvního konce vazby, tj. místo, kde je pružina ukotvena v prvním tělese, a následující dvojice parametrů pak specifikuje souřadnice druhého konce vazby, tj. bod nalézající se ve druhém tělese (většinou oba koncové body leží v těžišti těles, viz všechny následující demonstrační příklady, ve skutečnosti to však není nutné). Posledním parametrem je možné specifikovat, zda se mají obě tělesa spojená pružnou vazbou vzájemně odrážet či nikoli.

07

Obrázek 7: První demonstrační příklad: kvádr již pouze klouže po podložce.

Konstruktor love.physics.newDistanceJoint() vytvoří objekt, nad nímž je možné volat metody vypsané v následující tabulce:

# Metoda objektu DistanceJoint Popis metody Poznámka
1 getAnchors() získání bodů, ve kterých je pružina k tělesům připojena.
2 getBodies() získání obou těles, ke kterým je pružina. přidáno ve verzi 0.9.2
3 getLength() získání klidové vzdálenosti mezi oběma tělesy.
4 getFrequency() získání rychlosti reakce pružiny na její natažení či stlačení.
5 getDamping() získání míry tlumení pružiny (eliminace zákmitů). odstraněno ve verzi 0.8.0
6 getDampingRatio() získání míry tlumení pružiny (eliminace zákmitů). přidáno do verze 0.8.0
7 setLength() nastavení klidové vzdálenosti mezi oběma tělesy.
8 setFrequency() nastavení rychlosti reakce pružiny na její natažení či stlačení.
9 setDamping() nastavení míry tlumení. odstraněno ve verzi 0.8.0
10 setDampingRatio() nastavení míry tlumení. přidáno do verze 0.8.0
11 getReactionForce() aktuálně působící síla mezi tělesy. přidáno do verze 0.8.0
12 destroy() zrušení elastické vazby působící mezi dvěma tělesy.

08

Obrázek 8: První demonstrační příklad: kvádr již pouze klouže po podložce.

5. Vytvoření pružné vazby mezi dvojicí těles

V prvním demonstračním příkladu, jehož úplný zdrojový kód bude uveden v sedmé kapitole, je ukázáno, jak je možné s využitím pružných (elastických) vazeb navzájem propojit dvojici míčků. Mezi dva míčky je vložena neviditelná „pružina“ představující elastickou vazbu. Oba konce pružiny jsou umístěny do středu míčků – jedná se o třetí až šestý parametr funkce love.physics.newDistanceJoint(), které se předávají jak objekty představující obě tělesa spojená vazbami, tak i souřadnice koncových bodů pružin (sami si můžete vyzkoušet, jak se simulace bude lišit v případě, že elastické vazby nebudou ukončeny v geometrických středech míčků). Následně je s využitím metody joint:setLength() nastavena klidová délka pružiny a dále metodou joint:setFrequency() rychlost odezvy pružiny na její natažení či naopak stlačení.

Vytvoření těles (body), jejich tvarů (shape) i elastické vazby (joint) je realizováno v callback funkci love.load():

--
-- Funkce volaná při inicializaci aplikace.
--
function love.load()
    ...
    ...
    ...
    -- těleso představující první míček
    objects.ball1 = {}
    -- počáteční umístění tělesa
    objects.ball1.body = 
    -- tvar a rozměry
    objects.ball1.shape = 
    -- propojení tvaru s tělesem
    objects.ball1.fixture = 
 
    -- těleso představující druhý míček
    objects.ball2 = {}
    -- počáteční umístění tělesa
    objects.ball2.body = 
    -- tvar a rozměry
    objects.ball2.shape = 
    -- propojení tvaru s tělesem
    objects.ball2.fixture = 
 
    -- vytvoření pružné vazby mezi míčky
    local b1 = objects.ball1.body
    local b2 = objects.ball2.body
    local joint = love.physics.newDistanceJoint(b1, b2,
                b1:getX(), b1:getY(),
                b2:getX(), b2:getY())
    -- klidová délka pružiny
    joint:setLength(100)
    -- rychlost odezvy pružiny na její natažení či naopak stlačení
    joint:setFrequency(10)
    ...
    ...
    ...
end

09

Obrázek 9: První demonstrační příklad: pokračování animace.

6. Zobrazení pružné vazby v simulovaném světě

Pozice pružin (elastických vazeb) je při vykreslování simulovaného světa naznačena s využitím úseček, které spojují středy obou míčků. Vzhledem k tomu, že nemáme přímé informace o pozici „pružiny“ v ploše simulovaného světa, použijeme namísto toho známé informace – víme totiž, že oba konce pružin jsou navázány na středy obou míčků a ty dokážeme zjistit snadno, jelikož středy míčků přesně odpovídají jejich těžištím. O vykreslení scény se stará zdrojový kód umístěný do callback funkce love.draw(), takže si nyní ukažme tu část těla této funkce, která vykreslení elastické vazby zajišťuje:

--
-- Tato funkce je volána automaticky při překreslení obsahu
-- okna či obrazovky.
--
function love.draw()
    ...
    ...
    ...
    -- vykreslení pružné vazby
    love.graphics.setColor(250, 150, 250)
    -- zjistíme těžiště obou míčků a propojíme je úsečkou
    love.graphics.line(objects.ball1.body:getX(), objects.ball1.body:getY(),
                       objects.ball2.body:getX(), objects.ball2.body:getY())
    ...
    ...
    ...
end

10

Obrázek 10: První demonstrační příklad: pokračování animace.

7. První demonstrační příklad – vytvoření pružné vazby mezi dvěma míčky

Všechny důležité informace o způsobu vytvoření elastické vazby mezi dvojicí míčků jsme si již řekli v předchozích dvou kapitolách, takže nám již pouze zbývá si ukázat úplný zdrojový kód dnešního prvního demonstračního příkladu. V něm jsou vytvořena čtyři tělesa: nehybná zešikmená podlaha, kvádr, který na tuto podlahu dopadá a dvojice míčků. Ovládání tohoto příkladu je velmi jednoduché – klávesou [Esc] je ho možné ukončit a klávesou [R] simulaci znovu nastartovat (v podstatě se znovu zavolá callback funkce love.load()):

--
-- Knihovna LÖVE
--
-- Dvacátý čtvrtý demonstrační příklad
--
-- Vytvoření pružné vazby mezi dvěma tělesy.
--
 
 
 
-- rozměry okna, do něhož se bude provádět vykreslování
width = 600
height = 600
ground_height = 50
 
-- tíhové zrychlení
G = 9.81
 
-- počet pixelů odpovídající jednomu metru v simulovaném světě
pixels_per_meter = 64
 
 
 
--
-- Funkce volaná při inicializaci aplikace.
--
function love.load()
    -- načtení standardního fontu a nastavení grafického režimu
    local font = love.graphics.newFont(love.default_font, 40)
 
    -- inicializace grafického režimu
    love.graphics.setMode(width, height, false, false, 0)
 
    -- nastavení rozměrů simulovaného světa
    -- (mapování fyzikálních jednotek na pixely)
    love.physics.setMeter(pixels_per_meter)
 
    -- vytvoření simulovaného světa
    world = love.physics.newWorld(0, G * pixels_per_meter, true)
 
    -- tabulka s tělesy
    objects = {}
 
    -- těleso představující zemi
    objects.ground = {}
    -- počáteční umístění tělesa
    objects.ground.body = love.physics.newBody(world, width/2, height-ground_height/2-40)
    -- tvar a rozměry
    objects.ground.shape = love.physics.newRectangleShape(0, 0, width, ground_height, -0.15)
    -- propojení tvaru s tělesem
    objects.ground.fixture = love.physics.newFixture(objects.ground.body, objects.ground.shape, 5)
    objects.ground.fixture:setRestitution(0.7)
 
    -- těleso představující první míček
    objects.ball1 = {}
    -- počáteční umístění tělesa
    objects.ball1.body = love.physics.newBody(world, 390, 50, "dynamic")
    -- tvar a rozměry
    objects.ball1.shape = love.physics.newCircleShape(30)
    -- propojení tvaru s tělesem
    objects.ball1.fixture = love.physics.newFixture(objects.ball1.body, objects.ball1.shape, 5)
    objects.ball1.fixture:setRestitution(0.9)
 
    -- těleso představující druhý míček
    objects.ball2 = {}
    -- počáteční umístění tělesa
    objects.ball2.body = love.physics.newBody(world, 510, 50, "dynamic")
    -- tvar a rozměry
    objects.ball2.shape = love.physics.newCircleShape(30)
    -- propojení tvaru s tělesem
    objects.ball2.fixture = love.physics.newFixture(objects.ball2.body, objects.ball2.shape, 5)
    objects.ball2.fixture:setRestitution(0.9)
 
    -- vytvoření pružné vazby mezi míčky
    local b1 = objects.ball1.body
    local b2 = objects.ball2.body
    local joint = love.physics.newDistanceJoint(b1, b2,
                b1:getX(), b1:getY(),
                b2:getX(), b2:getY())
    joint:setLength(100)
    joint:setFrequency(10)
 
    -- těleso představující kvádr
    objects.block3 = {}
    -- počáteční umístění tělesa
    objects.block3.body = love.physics.newBody(world, 450, 150, "dynamic")
    -- tvar a rozměry
    objects.block3.shape = love.physics.newRectangleShape(200, 100)
    -- propojení tvaru s tělesem
    objects.block3.fixture = love.physics.newFixture(objects.block3.body, objects.block3.shape, 5)
    objects.block3.fixture:setRestitution(0.0)
end
 
 
 
--
-- Funkce volaná cca 30x za sekundu
--
function love.update(dt)
    -- přepočítat parametry celého "světa"
    world:update(dt)
 
    local delay = 1/30
    if dt < delay then
        love.timer.sleep(delay - dt)
    end
end
 
 
 
--
-- Tato funkce je volána automaticky při překreslení obsahu
-- okna či obrazovky.
--
function love.draw()
    -- vykreslení země
    love.graphics.setColor(72, 160, 14)
    love.graphics.polygon("line", objects.ground.body:getWorldPoints(objects.ground.shape:getPoints()))
 
    -- vykreslení prvního tělesa (míčku)
    love.graphics.setColor(250, 150, 150)
    love.graphics.circle("line", objects.ball1.body:getX(), objects.ball1.body:getY(),
                                 objects.ball1.shape:getRadius())
 
    -- vykreslení druhého tělesa (míčku)
    love.graphics.setColor(150, 150, 250)
    love.graphics.circle("line", objects.ball2.body:getX(), objects.ball2.body:getY(),
                                 objects.ball2.shape:getRadius())
 
    -- vykreslení pružné vazby
    love.graphics.setColor(250, 150, 250)
    love.graphics.line(objects.ball1.body:getX(), objects.ball1.body:getY(),
                       objects.ball2.body:getX(), objects.ball2.body:getY())
 
    -- vykreslení třetího tělesa (kvádru)
    love.graphics.setColor(150, 250, 150)
    love.graphics.polygon("line", objects.block3.body:getWorldPoints(objects.block3.shape:getPoints()))
 
    -- výpis zprávy
    love.graphics.setColor(250, 250, 250)
    love.graphics.print("Escape: to exit.", 20, 615)
end
 
 
 
--
-- Callback funkce zavolaná při stisku klávesy.
--
function love.keypressed(k)
    if k == 'escape' then
    -- klávesou "Escape" se aplikace ukončí
        love.event.quit()
    elseif k == 'r' then
    -- restart celé simulace po stisku klávesy [R]
        love.load()
    end
end
 
 
 
--
-- finito
--

11

Obrázek 11: První demonstrační příklad: simulace je téměř u konce.

8. Složitější topologie vazeb

V úvodních kapitolách jsme si mj. řekli, že pro zajištění stability celého systému těles, které jsou navzájem propojeny elastickými vazbami, je většinou nutné mezi tato tělesa umístit větší množství vazeb. V případě trojrozměrných těles se většinou jedná o stěnové a tělesové úhlopříčky, ovšem v dvojrozměrném prostoru je situace poněkud jednodušší – většinou totiž postačuje vytvořit vazby takovým způsobem, aby každá trojice nejbližších těles (myšleno samozřejmě v klidovém stavu celého systému) byla propojena trojicí elastických vazeb. Například ve třetím demonstračním příkladu, jehož popis následuje níže, je celý systém složen z 25 míčků tvořících mřížku 5×5 míčků.

12

Obrázek 12: Druhý demonstrační příklad – začátek simulace.

Zdánlivě by mohlo postačovat propojit tělesa pouze horizontálními a vertikálními vazbami (takže se při zobrazení vazeb skutečně použije pravidelná mřížka), ovšem takto vytvořený systém po nárazu na podložku samovolně „splaskne“, protože neexistuje žádná vazba, která by zabránila vzájemnému přiblížení či naopak vzdálení dvou protilehlých vrcholů čtverců. Aby k tomuto jevu nedocházelo, postačuje většinou vytvořit i úhlopříčné vazby v každém čtverci mřížky. Taktéž je možné vytvořit úhlopříčné vazby mezi dvojicí či trojicí čtverců atd. – výsledný systém potom klade při pokusu o ohyb mnohem větší odpor.

13

Obrázek 13: Druhý demonstrační příklad – změna vzdáleností míčků na začátku simulace.

14

Obrázek 14: Druhý demonstrační příklad – pokračování simulace.

9. Druhý demonstrační příklad – 25 volně se pohybujících míčků

Vliv elastických vazeb na pohyb těles, která jsou těmito vazbami svázána, nelze v žádném případě podceňovat. Ostatně se můžeme podívat na další demonstrační příklad, v němž elastické vazby nejsou použity. Tento příklad bude sloužit jako základ pro oba příklady následující. Při inicializaci je kromě již obligátní podlahy v simulovaném světě vytvořeno i dvacet pět míčků, které ve výchozím stavu (na začátku simulace) tvoří pravidelnou mřížku 5×5 míčků. Po spuštění programu míčky dopadnou na podlahu a začnou se od ní i od sebe odrážet různými směry, přičemž na ně nepůsobí žádná elastická vazba. Podívejme se nyní na několik snímků z této simulace (další tři snímky již byly uvedeny v předchozí kapitole):

15

Obrázek 15: Druhý demonstrační příklad – pokračování simulace.

16

Obrázek 16: Druhý demonstrační příklad – pokračování simulace.

17

Obrázek 17: Druhý demonstrační příklad – pokračování simulace.

18

Obrázek 18: Druhý demonstrační příklad – pokračování simulace.

19

Obrázek 19: Druhý demonstrační příklad – pokračování simulace.

20

Obrázek 20: Druhý demonstrační příklad – pokračování simulace.

Následuje úplný zdrojový kód druhého demonstračního příkladu:

--
-- Knihovna LÖVE
--
-- Dvacátý pátý demonstrační příklad
--
-- 25 míčků v mřížce 5x5
--
 
 
 
-- rozměry okna, do něhož se bude provádět vykreslování
width = 600
height = 600
ground_height = 50
 
-- tíhové zrychlení
G = 9.81
 
-- počet pixelů odpovídající jednomu metru v simulovaném světě
pixels_per_meter = 64
 
 
 
--
-- Funkce volaná při inicializaci aplikace.
--
function love.load()
    -- načtení standardního fontu a nastavení grafického režimu
    local font = love.graphics.newFont(love.default_font, 40)
 
    -- inicializace grafického režimu
    love.graphics.setMode(width, height, false, false, 0)
 
    -- nastavení rozměrů simulovaného světa
    -- (mapování fyzikálních jednotek na pixely)
    love.physics.setMeter(pixels_per_meter)
 
    -- vytvoření simulovaného světa
    world = love.physics.newWorld(0, G * pixels_per_meter, true)
 
    -- tabulka s tělesy
    objects = {}
 
    -- těleso představující zemi
    objects.ground = {}
    -- počáteční umístění tělesa
    objects.ground.body = love.physics.newBody(world, width/2, height-ground_height/2-40)
    -- tvar a rozměry
    objects.ground.shape = love.physics.newRectangleShape(0, 0, width, ground_height, -0.15)
    -- propojení tvaru s tělesem
    objects.ground.fixture = love.physics.newFixture(objects.ground.body, objects.ground.shape, 5)
    objects.ground.fixture:setRestitution(0.7)
 
    objects.mesh = {}
    for i = 1, 5 do
        objects.mesh[i] = {}
        for j = 1, 5 do
            -- těleso představující míček
            local obj = {}
            -- počáteční umístění tělesa
            obj.body = love.physics.newBody(world, 300 + i * 30, 50 + j * 30, "dynamic")
            -- tvar a rozměry
            obj.shape = love.physics.newCircleShape(10)
            -- propojení tvaru s tělesem
            obj.fixture = love.physics.newFixture(obj.body, obj.shape, 5)
            obj.fixture:setRestitution(0.9)
            objects.mesh[i][j] = obj
        end
    end
end
 
 
 
--
-- Funkce volaná cca 30x za sekundu
--
function love.update(dt)
    -- přepočítat parametry celého "světa"
    world:update(dt)
 
    local delay = 1/30
    if dt < delay then
        love.timer.sleep(delay - dt)
    end
end
 
 
 
--
-- Tato funkce je volána automaticky při překreslení obsahu
-- okna či obrazovky.
--
function love.draw()
    -- vykreslení země
    love.graphics.setColor(72, 160, 14)
    love.graphics.polygon("line", objects.ground.body:getWorldPoints(objects.ground.shape:getPoints()))
 
    -- vykreslení všech míčků
    love.graphics.setColor(250, 150, 150)
    for i = 1, 5 do
        for j = 1, 5 do
            local obj = objects.mesh[i][j]
            love.graphics.circle("line", obj.body:getX(), obj.body:getY(),
                                         obj.shape:getRadius())
        end
    end
 
    -- výpis zprávy
    love.graphics.setColor(250, 250, 250)
    love.graphics.print("Escape: to exit.", 20, 615)
end
 
 
 
--
-- Callback funkce zavolaná při stisku klávesy.
--
function love.keypressed(k)
    if k == 'escape' then
    -- klávesou "Escape" se aplikace ukončí
        love.event.quit()
    elseif k == 'r' then
    -- restart celé simulace po stisku klávesy [R]
        love.load()
    end
end
 
 
 
--
-- finito
--

10. Programové vytvoření elastických vazeb

Na druhý demonstrační příklad, jehož zdrojový kód byl uveden v předchozí kapitole, navážeme příkladem třetím, v němž se bude taktéž používat 25 míčků v mřížce 5×5 míčků, ovšem míčky již budou vzájemně svázány vertikálními vazbami. Co to v praxi bude znamenat? Mezi každými dvěma míčky ležícími pod sebou bude natažena „pružina“, takže celkový počet elastických vazeb dosáhne dvaceti (pět sloupců míčků, v každém sloupci čtyři elastické vazby). Podívejme se nyní na programový kód, který tyto elastické vazby vytvoří při inicializaci aplikace:

--
-- Funkce volaná při inicializaci aplikace.
--
function love.load()
    ...
    ...
    ...
    -- vytvoření mřížky 5×5 z míčků
    for i = 1, 5 do
        objects.mesh[i] = {}
        for j = 1, 5 do
            -- těleso představující míček
            local obj = {}
            -- počáteční umístění tělesa
            obj.body = love.physics.newBody(world, 300 + i * 30, 50 + j * 30, "dynamic")
            -- tvar a rozměry
            obj.shape = love.physics.newCircleShape(10)
            -- propojení tvaru s tělesem
            obj.fixture = love.physics.newFixture(obj.body, obj.shape, 5)
            obj.fixture:setRestitution(0.9)
            objects.mesh[i][j] = obj
        end
    end
 
    -- vytvoření pružné vazby mezi míčky
    for i = 1, 5 do
        for j = 1, 4 do
            local obj = {}
            local b1 = objects.mesh[i][j].body
            local b2 = objects.mesh[i][j+1].body
            local joint = love.physics.newDistanceJoint(b1, b2,
                        b1:getX(), b1:getY(),
                        b2:getX(), b2:getY())
            joint:setLength(30)
            joint:setFrequency(10)
        end
    end
end

Pro lepší ilustraci vlivu elastických vazeb na simulaci je vhodné si vazby zobrazit přímo v simulovaném světě. To je poměrně snadné, což ukazuje i následující příklad:

--
-- Tato funkce je volána automaticky při překreslení obsahu
-- okna či obrazovky.
--
function love.draw()
    ...
    ...
    ...
 
    -- vykreslení pružné vazby
    love.graphics.setColor(150, 150, 250)
    for i = 1, 5 do
        for j = 1, 4 do
            local obj1 = objects.mesh[i][j]
            local obj2 = objects.mesh[i][j+1]
            love.graphics.line(obj1.body:getX(), obj1.body:getY(),
                               obj2.body:getX(), obj2.body:getY())
        end
    end
 
    ...
    ...
    ...
end

Takto vytvořené elastické vazby ovlivní stav simulovaného světa, což si opět můžeme ověřit na sérií snímků:

21

Obrázek 21: Třetí demonstrační příklad – začátek simulace.

22

Obrázek 22: Třetí demonstrační příklad – pokračování simulace.

23

Obrázek 23: Třetí demonstrační příklad – pokračování simulace.

24

Obrázek 24: Třetí demonstrační příklad – pokračování simulace.

25

Obrázek 25: Třetí demonstrační příklad – pokračování simulace.

26

Obrázek 26: Třetí demonstrační příklad – pokračování simulace.

27

Obrázek 27: Třetí demonstrační příklad – pokračování simulace.

11. Třetí demonstrační příklad – vazby mezi míčky v mřížce 25×25

Elastické vazby mezi míčky, které se v mřížce 5×5 míčků nachází nad sebou, jsou implementovány v dnešním třetím demonstračním příkladu, jehož úplný zdrojový kód je umístěn pod tímto odstavcem:

--
-- Knihovna LÖVE
--
-- Dvacátý šestý demonstrační příklad
--
-- Vytvoření pružných vazeb mezi několika tělesy.
--
 
 
 
-- rozměry okna, do něhož se bude provádět vykreslování
width = 600
height = 600
ground_height = 50
 
-- tíhové zrychlení
G = 9.81
 
-- počet pixelů odpovídající jednomu metru v simulovaném světě
pixels_per_meter = 64
 
 
 
--
-- Funkce volaná při inicializaci aplikace.
--
function love.load()
    -- načtení standardního fontu a nastavení grafického režimu
    local font = love.graphics.newFont(love.default_font, 40)
 
    -- inicializace grafického režimu
    love.graphics.setMode(width, height, false, false, 0)
 
    -- nastavení rozměrů simulovaného světa
    -- (mapování fyzikálních jednotek na pixely)
    love.physics.setMeter(pixels_per_meter)
 
    -- vytvoření simulovaného světa
    world = love.physics.newWorld(0, G * pixels_per_meter, true)
 
    -- tabulka s tělesy
    objects = {}
 
    -- těleso představující zemi
    objects.ground = {}
    -- počáteční umístění tělesa
    objects.ground.body = love.physics.newBody(world, width/2, height-ground_height/2-40)
    -- tvar a rozměry
    objects.ground.shape = love.physics.newRectangleShape(0, 0, width, ground_height, -0.15)
    -- propojení tvaru s tělesem
    objects.ground.fixture = love.physics.newFixture(objects.ground.body, objects.ground.shape, 5)
    objects.ground.fixture:setRestitution(0.7)
 
    objects.mesh = {}
    for i = 1, 5 do
        objects.mesh[i] = {}
        for j = 1, 5 do
            -- těleso představující míček
            local obj = {}
            -- počáteční umístění tělesa
            obj.body = love.physics.newBody(world, 300 + i * 30, 50 + j * 30, "dynamic")
            -- tvar a rozměry
            obj.shape = love.physics.newCircleShape(10)
            -- propojení tvaru s tělesem
            obj.fixture = love.physics.newFixture(obj.body, obj.shape, 5)
            obj.fixture:setRestitution(0.9)
            objects.mesh[i][j] = obj
        end
    end
 
    -- vytvoření pružné vazby mezi míčky
    for i = 1, 5 do
        for j = 1, 4 do
            local obj = {}
            local b1 = objects.mesh[i][j].body
            local b2 = objects.mesh[i][j+1].body
            local joint = love.physics.newDistanceJoint(b1, b2,
                        b1:getX(), b1:getY(),
                        b2:getX(), b2:getY())
            joint:setLength(30)
            joint:setFrequency(10)
        end
    end
end
 
 
 
--
-- Funkce volaná cca 30x za sekundu
--
function love.update(dt)
    -- přepočítat parametry celého "světa"
    world:update(dt)
 
    local delay = 1/30
    if dt < delay then
        love.timer.sleep(delay - dt)
    end
end
 
 
 
--
-- Tato funkce je volána automaticky při překreslení obsahu
-- okna či obrazovky.
--
function love.draw()
    -- vykreslení země
    love.graphics.setColor(72, 160, 14)
    love.graphics.polygon("line", objects.ground.body:getWorldPoints(objects.ground.shape:getPoints()))
 
    -- vykreslení všech míčků
    love.graphics.setColor(250, 150, 150)
    for i = 1, 5 do
        for j = 1, 5 do
            local obj = objects.mesh[i][j]
            love.graphics.circle("line", obj.body:getX(), obj.body:getY(),
                                         obj.shape:getRadius())
        end
    end
 
    -- vykreslení pružné vazby
    love.graphics.setColor(150, 150, 250)
    for i = 1, 5 do
        for j = 1, 4 do
            local obj1 = objects.mesh[i][j]
            local obj2 = objects.mesh[i][j+1]
            love.graphics.line(obj1.body:getX(), obj1.body:getY(),
                               obj2.body:getX(), obj2.body:getY())
        end
    end
 
    -- výpis zprávy
    love.graphics.setColor(250, 250, 250)
    love.graphics.print("Escape: to exit.", 20, 615)
end
 
 
 
--
-- Callback funkce zavolaná při stisku klávesy.
--
function love.keypressed(k)
    if k == 'escape' then
    -- klávesou "Escape" se aplikace ukončí
        love.event.quit()
    elseif k == 'r' then
    -- restart celé simulace po stisku klávesy [R]
        love.load()
    end
end
 
 
 
--
-- finito
--

12. Čtvrtý demonstrační příklad – vytvoření horizontálních i vertikálních vazeb

Předchozí demonstrační příklad můžeme velmi snadno upravit takovým způsobem, aby se kromě vertikálních vazeb vytvořených mezi míčky, které se v mřížce nachází nad sebou, použily i vazby mezi sousedními horizontálními míčky. Úprava zdrojového kódu je snadná a především přímočará, takže se jen podívejme na jeho výslednou podobu:

--
-- Knihovna LÖVE
--
-- Dvacátý sedmý demonstrační příklad
--
-- Vytvoření pružných vazeb mezi několika tělesy.
--
 
 
 
-- rozměry okna, do něhož se bude provádět vykreslování
width = 600
height = 600
ground_height = 50
 
-- tíhové zrychlení
G = 9.81
 
-- počet pixelů odpovídající jednomu metru v simulovaném světě
pixels_per_meter = 64
 
 
 
--
-- Funkce volaná při inicializaci aplikace.
--
function love.load()
    -- načtení standardního fontu a nastavení grafického režimu
    local font = love.graphics.newFont(love.default_font, 40)
 
    -- inicializace grafického režimu
    love.graphics.setMode(width, height, false, false, 0)
 
    -- nastavení rozměrů simulovaného světa
    -- (mapování fyzikálních jednotek na pixely)
    love.physics.setMeter(pixels_per_meter)
 
    -- vytvoření simulovaného světa
    world = love.physics.newWorld(0, G * pixels_per_meter, true)
 
    -- tabulka s tělesy
    objects = {}
 
    -- těleso představující zemi
    objects.ground = {}
    -- počáteční umístění tělesa
    objects.ground.body = love.physics.newBody(world, width/2, height-ground_height/2-40)
    -- tvar a rozměry
    objects.ground.shape = love.physics.newRectangleShape(0, 0, width, ground_height, -0.15)
    -- propojení tvaru s tělesem
    objects.ground.fixture = love.physics.newFixture(objects.ground.body, objects.ground.shape, 5)
    objects.ground.fixture:setRestitution(0.7)
 
    objects.mesh = {}
    for i = 1, 5 do
        objects.mesh[i] = {}
        for j = 1, 5 do
            -- těleso představující míček
            local obj = {}
            -- počáteční umístění tělesa
            obj.body = love.physics.newBody(world, 300 + i * 30, 50 + j * 30, "dynamic")
            -- tvar a rozměry
            obj.shape = love.physics.newCircleShape(10)
            -- propojení tvaru s tělesem
            obj.fixture = love.physics.newFixture(obj.body, obj.shape, 5)
            obj.fixture:setRestitution(0.9)
            objects.mesh[i][j] = obj
        end
    end
 
    -- vytvoření pružné vazby mezi míčky
    for i = 1, 5 do
        for j = 1, 4 do
            local obj = {}
            local b1 = objects.mesh[i][j].body
            local b2 = objects.mesh[i][j+1].body
            local joint = love.physics.newDistanceJoint(b1, b2,
                        b1:getX(), b1:getY(),
                        b2:getX(), b2:getY())
            joint:setLength(22)
            joint:setFrequency(20)
        end
    end
 
    -- vytvoření kolmé pružné vazby mezi míčky
    for i = 1, 4 do
        for j = 1, 5 do
            local obj = {}
            local b1 = objects.mesh[i][j].body
            local b2 = objects.mesh[i+1][j].body
            local joint = love.physics.newDistanceJoint(b1, b2,
                        b1:getX(), b1:getY(),
                        b2:getX(), b2:getY())
            joint:setLength(22)
            joint:setFrequency(20)
        end
    end
end
 
 
 
--
-- Funkce volaná cca 30x za sekundu
--
function love.update(dt)
    -- přepočítat parametry celého "světa"
    world:update(dt)
 
    local delay = 1/30
    if dt < delay then
        love.timer.sleep(delay - dt)
    end
end
 
 
 
--
-- Tato funkce je volána automaticky při překreslení obsahu
-- okna či obrazovky.
--
function love.draw()
    -- vykreslení země
    love.graphics.setColor(72, 160, 14)
    love.graphics.polygon("line", objects.ground.body:getWorldPoints(objects.ground.shape:getPoints()))
 
    -- vykreslení všech míčků
    love.graphics.setColor(250, 150, 150)
    for i = 1, 5 do
        for j = 1, 5 do
            local obj = objects.mesh[i][j]
            love.graphics.circle("line", obj.body:getX(), obj.body:getY(),
                                         obj.shape:getRadius())
        end
    end
 
    -- vykreslení pružné vazby
    love.graphics.setColor(150, 150, 250)
    for i = 1, 5 do
        for j = 1, 4 do
            local obj1 = objects.mesh[i][j]
            local obj2 = objects.mesh[i][j+1]
            love.graphics.line(obj1.body:getX(), obj1.body:getY(),
                               obj2.body:getX(), obj2.body:getY())
        end
    end
    for i = 1, 4 do
        for j = 1, 5 do
            local obj1 = objects.mesh[i][j]
            local obj2 = objects.mesh[i+1][j]
            love.graphics.line(obj1.body:getX(), obj1.body:getY(),
                               obj2.body:getX(), obj2.body:getY())
        end
    end
 
    -- výpis zprávy
    love.graphics.setColor(250, 250, 250)
    love.graphics.print("Escape: to exit.", 20, 615)
end
 
 
 
--
-- Callback funkce zavolaná při stisku klávesy.
--
function love.keypressed(k)
    if k == 'escape' then
    -- klávesou "Escape" se aplikace ukončí
        love.event.quit()
    elseif k == 'r' then
    -- restart celé simulace po stisku klávesy [R]
        love.load()
    end
end
 
 
 
--
-- finito
--

Podívejme se nyní na sérii snímků získaných v průběhu simulace prováděné čtvrtým demonstračním příkladem:

28

Obrázek 28: Čtvrtý demonstrační příklad – začátek simulace.

29

Obrázek 29: Čtvrtý demonstrační příklad – pokračování simulace.

30

Obrázek 30: Čtvrtý demonstrační příklad – pokračování simulace.

31

Obrázek 31: Čtvrtý demonstrační příklad – pokračování simulace.

32

Obrázek 32: Čtvrtý demonstrační příklad – pokračování simulace.

33

Obrázek 33: Čtvrtý demonstrační příklad – pokračování simulace.

34

Obrázek 34: Čtvrtý demonstrační příklad – pokračování simulace.

Vidíme, že elastické vazby zcela nezabránily deformaci mřížky – bylo by nutné zavést ještě diagonální vazby, takový příklad si však ukážeme až v dalším pokračování tohoto seriálu.

13. Repositář se zdrojovými kódy všech čtyř dnešních demonstračních příkladů

Všechny čtyři demonstrační příklady, s nimiž jsme se v dnešním článku seznámili, byly uloženy do Git repositáře umístěného na GitHubu (https://github.com/tisnik/presentations). Každý demonstrační příklad je, podobně jako tomu bylo i příkladů z předchozích dvou článků, tvořen pouze zdrojovým kódem uloženým v souboru main.lua:

# Příklad Zdrojový kód
1 example24 https://github.com/tisnik/presentations/tree/master/love/example24
2 example25 https://github.com/tisnik/presentations/tree/master/love/example25
3 example26 https://github.com/tisnik/presentations/tree/master/love/example26
4 example27 https://github.com/tisnik/presentations/tree/master/love/example27

Poznámka: demonstrační příklady jsou číslovány průběžně, protože tento článek (alespoň nepřímo) navazuje na seriál o programovacích jazycích vhodných pro výuku počítačové grafiky.

14. Odkazy na Internetu

  1. Bounding Boxes
    http://www.3dkingdoms.com/weekly/weekly.php?a=21
  2. Box2D (stránka projektu)
    http://box2d.org/
  3. Box2D FAQ
    https://github.com/erincatto/Box2D/wiki/FAQ
  4. Box2D v2.2.0 User Manual
    http://www.box2d.org/manual.html
  5. Bounding Box (OpenStreetMap)
    http://wiki.openstreetmap.org/wiki/Bounding_Box
  6. Minimum Bounding Rectange (Wikipedia)
    https://en.wikipedia.org/wiki/Minimum_bounding_rectangle
  7. Minimum bounding box (Wikipedia)
    https://en.wikipedia.org/wiki/Minimum_bounding_box
  8. 2D Bounding Box Collision Detection
    http://www.dreamincode.net/forums/topic/180069-xna-2d-bounding-box-collision-detection/
  9. Chipmunk2D (další fyzikální engine)
    http://chipmunk-physics.net/
  10. Physics Engine (Wikipedia)
    https://en.wikipedia.org/wiki/Physics_engine
  11. Modul love.physics
    https://love2d.org/wiki/love.physics
  12. Modul love.physics: Objekt typu World
    https://love2d.org/wiki/World
  13. Modul love.physics: Objekt typu Body
    https://love2d.org/wiki/Body
  14. Modul love.physics: Objekt typu Shape
    https://love2d.org/wiki/Shape
  15. Modul love.physics: Objekt typu Joint
    https://love2d.org/wiki/Joint
  16. Modul love.physics: Objekt typu Fixture
    https://love2d.org/wiki/Fixture
  17. Particle Systems From the Ground Up
    http://buildnewgames.com/particle-systems/
  18. Particle Systems
    http://natureofcode.com/book/chapter-4-particle-systems/
  19. Domovská stránka systému LÖVE
    http://love2d.org/
  20. Dokumentace k systému LÖVE
    http://love2d.org/wiki/love
  21. Domovská stránka programovacího jazyka Lua
    http://www.lua.org/
  22. Seriál o programovacím jazyku Lua (root.cz):
    http://www.root.cz/serialy/programovaci-jazyk-lua/
  23. Domovská stránka systému LÖVE
    http://love2d.org/
  24. Domovská stránka programovacího jazyka Lua
    http://www.lua.org/
  25. Web o Lieru, Gusanos, GeneRally, Atari atd.
    http://karelik.wz.cz/
  26. Web o Lieru, Gusanos
    http://karelik.wz.cz/gusanos.php
  27. GUSANOS
    http://gusanos.sourceforge.net/
  28. GUSANOS Download
    http://sourceforge.net/projects/gusanos/
  29. Lua
    http://www.linuxexpres.cz/praxe/lua
  30. Lua
    http://cs.wikipedia.org/wiki/Lua
  31. Lua (programming language)
    http://en.wikipedia.org/wiki/Lua_(programming_language)
  32. The Lua Programming Language
    http://www.tiobe.com/index.php/paperinfo/tpci/Lua.html
  33. Lua Programming Gems
    http://www.lua.org/gems/
  34. LuaForge
    http://luaforge.net/
  35. Forge project tree
    http://luaforge.net/softwaremap/trove_list.php
  36. SdlBasic home page
    http://www.sdlbasic.altervista.org/main/
  37. SdlBasic examples
    http://nitrofurano.linuxkafe.com/sdlbasic/
  38. SdlBasic na Wikipedii
    http://en.wikipedia.org/wiki/SdlBasic
  39. Simple DirectMedia Layer
    http://en.wikipedia.org/wiki/Simple_DirectMedia_Layer
  40. SDLBASIC – The high-level interpreter for all?
    http://openbytes.wordpress.com/2008/11/08/sdlbasic-the-high-level-interpreter-for-all/
  41. FreeBasic home page
    http://www.freebasic.net/
  42. FreeBASIC (Wikipedia EN)
    https://en.wikipedia.org/wiki/FreeBASIC
  43. FreeBASIC Wiki
    http://www.freebasic.net/wiki/wikka.php?wakka=FBWiki
  44. FreeBASIC Manual
    http://www.freebasic.net/wiki/wikka.php?wakka=DocToc
  45. FreeBASIC (Wikipedia CZ)
    http://cs.wikipedia.org/wiki/FreeBASIC
  46. The Griffon Legend
    http://syn9.thingie.net/?table=griffonlegend
  47. Seriál Letní škola programovacího jazyka Logo
    http://www.root.cz/serialy/letni-skola-programovaciho-jazyka-logo/
  48. Scratch: oficiální stránka projektu
    http://scratch.mit.edu/
  49. Scratch: galerie projektů vytvořených ve Scratchi
    http://scratch.mit.edu/galleries/browse/newest
  50. Scratch: nápověda
    file:///usr/share/scratch/Help/en/index.html
  51. Scratch: obrazovky nápovědy
    file:///usr/share/scratch/Help/en/allscreens.html
  52. Scratch (Wikipedie CZ)
    http://cs.wikipedia.org/wiki/Scratch
  53. Scratch (programming language)
    http://en.wikipedia.org/wiki/Scratch_(programming_language)
  54. Scratch Modification
    http://wiki.scratch.mit.edu/wiki/Scratch_Modification
  55. Scratch Lowers Resistance to Programming
    http://www.wired.com/gadgetlab/2009/03/scratch-lowers/
  56. Snap!
    http://snap.berkeley.edu/
  57. Prostředí Snap!
    http://snap.berkeley.edu/snapsource/snap.html
  58. Alternatives to Scratch
    http://wiki.scratch.mit.edu/wiki/Alternatives_to_Scratch
  59. Basic-256 home page
    http://www.basic256.org/index_en
  60. Basic-256 Language Documentation
    http://doc.basic256.org/doku.php
  61. Basic-256 Art Gallery
    http://www.basic256.org/artgallery
  62. Basic-256 Tutorial
    http://www.basic256.org/tutorials
  63. Why BASIC?
    http://www.basic256.org/whybasic
  64. A book to teach ANYBODY how to program a computer (using BASIC)
    http://www.basicbook.org/
  65. BASIC Computer Games (published 1978) – Hammurabi
    http://atariarchives.org/basicgames/showpage.php?page=78
  66. Hamurabi – zdrojový kód v BASICu
    http://www.dunnington.u-net.com/public/basicgames/HMRABI