Obrázky a PHP – šikmý vrh

30. září 2003

Třetí článek série o tvorbě obrázků v PHP jsem se rozhodl pojmout trochu netradičním způsobem, a to sestavením aplikace, která bude generovat dráhu šikmého vrhu. Konečně si tedy budete moci vyzkoušet všechny dosud získané znalosti na komplexním příkladě a poznat sílu PHP při práci s obrázkem.

Celá aplikace se bude skládat ze dvou souborů formular.php a vysledek.php, přičemž v prvním z nich my zadáme údaje potřebné k výpočtu (křivka bude generována na základě zadaného úhlu a rychlosti) a na druhém již uvidíme očekávaný výsledek společně se všemi zadanými i dopočítanými hodnotami (zdrojový kód ke stažení).

Stránka formular.php

V souboru formular.php uživatel zadá rychlost letícího tělesa, gravitační zrychlení v daném místě a úhel, pod kterým je těleso vrženo. Počet znaků u jednotlivých položek je použitím parametru „maxlength“ limitován, aby se předešlo problémům, kdy by nepozorný uživatel dovedl aplikaci ke zhroucení například zadáním pětimístného celého čísla v položce úhel.

<form action=“vysledek.php“ method=“post“>
<table width=“400″ cellpadding=“5″ cellspacing=“0″>
  <tr>
    <th colspan=“2″ width=“400″><h1>Aplikace šikmý vrh</h1></th>
  </tr>
  <tr>
    <td width=“200″>Zadejte úhel [°]:</TD>
    <td width=“200″><input type=“text“ name=“uhel“ maxlength=“2″ /></td>
  </tr>
  <tr>
    <td width=“200″>Zadejte rychlost [m*s<sup>-1</sup>]:</td>
    <td width=“200″><input type=“text“ name=“rychlost“ maxlength=“3″ /></td>
  </tr>
  <tr>
    <td width=“200″>Zadejte grav.zrychlení [m*s<sup>-1</sup>]:</td>
    <td width=“200″><input type=“text“ name=“zrychleni“ maxlength=“4″ value=“9.81″ /></td>
  </tr>
  <tr>
    <td colspan=“2″><input type=“submit“ value=“Odeslat“ /></td>
  </tr>
</table>
</form>

Stránka formular.php

Druhý soubor, který tvoří jádro celé aplikace a zpracovává data z předchozího formuláře, je již podstatně delší a náročnější, rozebereme si ho proto podrobněji a po částech. Ta první je asi nejjednodušší a v ní se provede kontrola správnosti zadaných údajů na předchozím formuláři a následně výpis nám známých údajů včetně dopočítaného dostřelu:

<? /* Kontrola správnosti zadaných údajů */
 if (!$rychlost)
   die („<div>Nevyplnili jste položku ‚rychlost'</div>“);
 if ((ereg („[[:digit:]]“, $rychlost))!=1)
   die („<div>Formát zadané rychlosti není správný.</div>“);
 if (!$uhel)
   die („<div>Nevyplnili jste položku ‚úhel'</div>“);
 if ((ereg („[[:digit:]]“, $rychlost))!=1)
   die („<div>Formát zadaného úhlu není správný.</div>“);
 if (!$zrychleni)
   die („<div>Nevyplnili jste položku ‚zrychlení'</div>“);
 if ((ereg („[[:digit:]]“, $zrychleni))!=1)
   die („<div>Formát zadaného zrychlení není správný.</div>“);
?>
<table width=“640″ cellpadding=“5″ cellspacing=“0″>
  <tr>
    <th colspan=“4″ width=“640″><h1>Aplikace šikmý vrh – Výsledky</h1></th>
  </tr>
  <tr>
    <th width=“160″>Úhel</th>
    <th width=“160″>Rychlost</th>
    <th width=“160″>Grav.zrychlení</th>
    <th width=“160″>Dostřel</th>
  </tr>
  <tr>
    <td width=“160″ align=“center“><? echo($uhel); ?> °</td>
    <td width=“160″ align=“center“><? echo($rychlost); ?> m*s<sup>-1</sup></td>
    <td width=“160″ align=“center“><? echo($zrychleni); ?> m*s<sup>-2</sup></td>
    <td width=“160″ align=“center“><?
    $uhel = deg2rad ($uhel);
    $dostrel = $rychlost*$rychlost*sin(2*$uhel)/$zrychleni;
    $dostrel = sprintf („%.2f“, $dostrel);
    echo („$dostrel m“);
    ?></td>
  </tr>
  <tr>
    <td colspan=“4″><hr /></td>
  </tr>

V dalších částech souboru nás již čeká pouze zpracování a vygenerování obrázku. Nejprve však vytvoříme prázdný rastr o rozměrech 600×400 pixelů a alokujeme barvu pozadí, křivek a os. Určíme též cestu k souboru s fontem, který chceme do obrázku pro vykreslení textu použít, a nadefinujeme také $iterace, která určí, po jaké době se bude iterovat při vykreslování křivky, přičemž platí, že čím vyšší číslo, tím větší hustota spojovacích puntíků. Vykreslování křivky totiž stojí na principu opakovaného průběhu v cyklu, kdy vždy spočítáme souřadnice x, y podle platných fyzikálních vzorců x = v0*t*cos(Uhel) a y = v0*t*sin(Uhel)-1/2*g*t2 a v onom místě vykreslíme vyplněnou malou kružnici.

Ještě před samotným vykreslováním musíme spočítat konstantu, kterou vynásobíme dopočítané souřadnice x a y, abychom křivku buď roztáhli nebo zmenšili tak, aby se vešla do rastru a celý ho zaplnila. Konstantu spočítáme pro každou souřadnici zvlášť, porovnáme je a použijeme tu menší. Pro osu x je konstanta spočítána prostým vydělením šířky rastru dostřelem tělesa (záměrně je hodnota šířka udávána jako 560 místo 600, aby byl dodržen okraj). U osy y to tak jednoduché není – musíme spočítat maximální výšku, kterou by těleso dosáhlo při vrhu. Takže spustíme cyklus a porovnáváme aktuální výšku $aktual_y$nejvyssi_y. Pokud je aktuální výška větší, pak $nejvyssi_y přebírá její hodnotu a cyklus pokračuje. Pokud ne, dosáhli jsme maxima a těleso již začíná padat, proto cyklus stopneme. Zároveň s tím však získáme i čas, po který těleso stoupalo. Protože je stejný jako čas, po který by klesalo, stačí ho vynásobit dvěma a máme i celkovou dobu letu $doba_letu, kterou tu v závěru této části necháme vypsat do pravého horního rohu.

<?
/* Vytvoření obrázku a definice některých konstant */
 $obrazek = ImageCreate (600, 400); // Vytvoření obrázku
 $pozadi = ImageColorAllocate ($obrazek, 242, 242, 173); // Alokace barvy pozadí
 $krivka = ImageColorAllocate ($obrazek, 0, 0, 255); // Alokace barvy křivky
 $osy = ImageColorAllocate ($obrazek, 204, 0, 51); // Alokace barvy osy x,y
 $cesta = „c:\\windows\\fonts\\tahoma.ttf“; // Cesta k souboru s fontem
 $iterace = $rychlost/2000; // Spočítání hodnoty pro iteraci
/* Cyklus pro zjištění nejvyššího položeného místa trajektorie a délky letu */
 while (true) {
   $time += $iterace;
   $aktual_y = $rychlost*$time*sin($uhel)-1/2*$zrychleni*$time*$time;
   if ($aktual_y>$nejvyssi_y)
     $nejvyssi_y=$aktual_y;
   else {
     $doba_letu = $time*2;
     break;
   }
 }
/* Výpočty konstant pro umístění křivky do obrázku */
 $konstanta_x = 560/$dostrel;
 $konstanta_y = 360/$nejvyssi_y;
 if ($konstanta_x>$konstanta_y)
   $konstanta=$konstanta_y;
 else
   $konstanta=$konstanta_x;

/* Cyklus vykreslující trajektorii křivky */
 while (true) {
   $cas += $iterace;
   $x = $rychlost*$cas*cos($uhel)*$konstanta+20;
   $y = ($rychlost*$cas*sin($uhel)-1/2*$zrychleni*$cas*$cas)*$konstanta+20;
   $prepocet_y = 400-$y;
   if ($y>20)
     ImageFilledArc ($obrazek, $x, $prepocet_y, 5, 5, 0, 360, $krivka, „IMG_ARC_PIE“);
   else
     break;
   }
 ImageTTFText ($obrazek, 8, 360, 450, 15, $krivka, $cesta, „Doba letu: $doba_letu s“);

V poslední části nás čeká vykreslení os x, y a umístění zarážek na ně v místě maximální výšky a dostřelu, přičemž pod tyto zářezy ještě zapíšeme dosažené metry. Do pole $body i $body_2 vložíme souřadnice tří bodů – trojúhelníku – který vyplníme barvou osy a tento trojúhelník bude v konečné podobě tvořit šipku na konci osy. K šipce ještě napíšeme jméno osy (x, y) a dílo je dokonáno. Vše zbývá pouze uložit a nechat následně zobrazit.

/* Vykreslení osy x se šipkami */
 $konec_osy_x = $dostrel*$konstanta+35;
 $konec_osy_y = 400-($nejvyssi_y*$konstanta+35);
 $maxim_osa = 400-($nejvyssi_y*$konstanta+20);
 $body[0] = 20;
 $body[1] = $konec_osy_y;
 $body[2] = 16;
 $body[3] = $konec_osy_y+8;
 $body[4] = 24;
 $body[5] = $konec_osy_y+8;
 ImageFilledPolygon ($obrazek, $body, 3, $osy); // Vykreslení šipky ve směru x
 ImageLine ($obrazek, 20, 380, $konec_osy_x, 380, $osy); // Vykreslení osy ve směru x
 ImageTTFText ($obrazek, 12, 360, $konec_osy_x-10, 375, $osy, $cesta, „x“); // Popis osy
 ImageLine ($obrazek, $konec_osy_x-15, 375, $konec_osy_x-15, 386, $krivka); // Zářez pro dostřel
 $dostrel=(int) $dostrel; // Převod dostřelu na integer a jeho vypsání pod zářez
 ImageTTFText ($obrazek, 10, 360, $konec_osy_x-32, 398, $krivka, $cesta, „$dostrel m“);
/* Vykreslení osy y se šipkami */
 $body_2[0] = $konec_osy_x;
 $body_2[1] = 380;
 $body_2[2] = $konec_osy_x-8;
 $body_2[3] = 376;
 $body_2[4] = $konec_osy_x-8;
 $body_2[5] = 384;
 ImageFilledPolygon ($obrazek, $body_2, 3, $osy); // Vykreslení šipky ve směru y
 ImageLine ($obrazek, 20, 380, 20, $konec_osy_y, $osy); // Vykreslení osy ve směru y
 ImageTTFText ($obrazek, 12, 360, 25, $konec_osy_y+10, $osy, $cesta, „y“); // Popis osy
 ImageLine ($obrazek, 15, $konec_osy_y+15, 25, $konec_osy_y+15, $krivka); // Zářez pro max.výšku
 $nejvyssi_y = (int) $nejvyssi_y; // Převod maximální výšky na integer a jeho vypsání pod zářez
 ImageTTFText ($obrazek, 8, 90, 10, $maxim_osa+20, $krivka, $cesta, „$nejvyssi_y m“);
 ImagePNG ($obrazek, „vrh.png“); // Uložení obrázku do souboru
 ImageDestroy ($obrazek);
?>
  <tr>
    <td colspan=“4″ align=“center“>
      <img src=“vrh.png“ width=“600″ height=“400″ alt=“Trajektorie šikmého vrhu“>
    </td>
  </tr>
</table>

Příklad samotný můžete ještě o mnohé obohatit po estetické stránce, já sám to zde nedělal, neboť si myslím, že ještě více použitých funkcí by jen zbytečně rozptylovalo a bránilo v pochopení. Ostatně tato křivka je pouze křivkou, kterou by těleso dosáhlo ve vakuu – ve vzduchu či jiném plynu by trajektorie vypadala trošku jinak.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *