Inspiro 4 - Bézierovy hodiny

PhDr. Mgr. Jeroným Klimeš, Ph.D. 2016-04-03

http://klimes.mysteria.cz

Toto je čtvrtá varianta hry Inspiro, která kreslí Bézierovy hodiny.

Časem jsem naprogramoval ještě jiné varianty.
Pokud byste měli zájem o zdrojové soubory, není problém zaslat. Je to pár řádek v polárních souřadnicích.

Na této stránce ukazuje nahodilý čas, který můžete změnit klávesou F5 - reload.

Na tomto odkazu jsou hodiny, které se automaticky aktualizují, ale prosím nenechávejte je na běžet příliš dlouho (>5 minut), ať se zbytečně nepřetěžuje server. (Zastavit je můžete pomocí klávesy ESC a znova spustit pomocí klávesy F5)


Bohužel ještě nemám dodělaný výstup a nastavování parametrů, každopádně použitelné parametry jsou některé z následujících :

background4.php?nahodile=1&hodina=13&minuta=23&sekunda=33&pozadir=255&pozadig=255&pozadib=255&popredir=0&popredig=0&popredib=0&rafier=200&rafieg=0&rafieb=0&picWidth=400&picHeight=400&pomer=0.8&pomers=0.2&krok=100&vzdalenost=3&body=1

Nahodilost 0/1
Časové paramtery jsou jasné
koncová písmena RGB jsou barvy 0-255
picWidth, picHeight - Velikost obrázku v pixelech
pomer je poměr mezi velkou a malou rafičkou
pomers je míra zakřivení beriezovy křivky sekundovou rafií, normálně je 0.2, při hodnotách okolo 2 už je to hodně odvážná křivka
krok a vzdálenost udávají plynulost křivky. Krok optimálně 100, pokud se stane že skok je větší než 3 pixely, tak to dopočítá i mezi body (pojistka vzdálenosti mezi dvěma body)
body 0/1 - zobrazí čtyři body, které definují, Bezierovu křivku (občas jsou mimo hranice obrázku)


<?php
if ($vypis!=1Header("Content-type: image/png");

//defaultni hodnoty proměnných
if (! isset($cara))          $cara=3;//sila cary
if (! isset($pozadir))      $pozadir=255;//128  0 cerna 255 bila
if (! isset($pozadig))      $pozadig=255;//0
if (! isset($pozadib))      $pozadib=255;//0
if (! isset($popredir))     $popredir=0;//150 barva ciferniku
if (! isset($popredig))     $popredig=0;//60
if (! isset($popredib))     $popredib=0;//0
if (! isset($rafier))         $rafier=200;
if (! isset(
$rafieg))         $rafieg=0;
if (! isset(
$rafieb))         $rafieb=0;
if (! isset(
$bodyr))         $bodyr=0;
if (! isset(
$bodyg))         $bodyg=100;
if (! isset(
$bodyb))         $bodyb=0;
if (! isset(
$picWidth))     $picWidth=400
if (! isset(
$picHeight))    $picHeight=400;
if (! isset(
$pomer))        $pomer=0.6//mezi velkou a malou ručičkou
if (! isset($pomers))        $pomers=0.2//poměr sekundove rafiky a prohnutí krivky
if (! isset($krok))            $krok=100//čím je krok menší, tím je křivka zubatější, ale vypočet rychlejší
if (! isset($vzdalenost))    $vzdalenost=3//zpresni vypocet, pokud ke vzdalenost mezi dvema body v pixelech mensi nez tato promenna 
    // plynulost křivky je tedy kompromis mezi krokem a vzdalenosti, vypočet ale bude rychlejší, když je víc kroků a menší vzdálenost. Když je málo kroků, tak to víc iteruje. Vzdálenost je jen pojistka pro nečekané skoky.
    
    
$stredx $picWidth  2
    
$stredy $picHeight 2;  
    
$prumer=min($stredx,$stredy)-10//průměr ciferníku

    
$image=ImageCreate($picWidth+1,$picHeight+1); 
    
$cPozadi=ImageColorAllocate($image,$pozadir,$pozadig,$pozadib); //pozadi
    
$cPopredi=ImageColorAllocate($image,$popredir,$popredig,$popredib); //popredi
    
$cRafie=ImageColorAllocate($image,$rafier,$rafieg,$rafieb); //popredi
    
$cBody=ImageColorAllocate($image,$bodyr,$bodyg,$bodyb); //body
    
imagesetthickness($image$cara);
    
$cas=localtime(time(),true);
    if (
$vypis==1print_r($cas); 

if (isset(
$sekunda)==false) {
    if (
$nahodile==1$sekunda=rand(0,59);
    else 
$sekunda=$cas["tm_sec"];
    }
    
$alfas=2*pi()*$sekunda/60//úhel sekundové rafie, samozřejmě v radianech
    
$vsekundax=$stredx+($prumer+6)*sin($alfas);  // počítá koordináty ručiček hodin
    
$vsekunday=$stredy-($prumer+6)*cos($alfas);
    
$sekundax=$stredx+$pomers*$prumer*sin($alfas);  // počítá koordináty ručiček hodin
    
$sekunday=$stredy-$pomers*$prumer*cos($alfas);

if (isset(
$minuta)==false) {
    if (
$nahodile==1$minuta=rand(0,59);
    else 
$minuta=$cas["tm_min"];
    }
    
$alfam=2*pi()*$minuta/60;
    
$minutax=$stredx+$prumer*sin($alfam);
    
$minutay=$stredy-$prumer*cos($alfam);
    
$iminutax=$stredx-$prumer*sin($alfam); //inverzni vektory pro vychozi body bezierovy krivky
    
$iminutay=$stredy+$prumer*cos($alfam);

if (isset(
$hodina)==false) {
    if (
$nahodile==1$hodina=rand(0,11);
    else 
$hodina=$cas["tm_hour"];
    }
    
$alfah=2*pi()*$hodina/12 2*pi()*$minuta/60/12//zohledňuje minutový pohyb hodinové rafie
    
$hodinax=$stredx+$pomer*$prumer*sin($alfah );
    
$hodinay=$stredy-$pomer*$prumer*cos($alfah);
    
$ihodinax=$stredx-$pomer*$prumer*sin($alfah);
    
$ihodinay=$stredy+$pomer*$prumer*cos($alfah);

//spočítá výchozí body bezierovy křivky - volné pole pro experimentování - $p2 a $p3 je možno definovat libovolně
$p1=array($minutax$minutay); //vychozí bod
$p2=array($iminutax+($sekundax-$stredx), $iminutay+($sekunday-$stredy)); //kam ukazuje prvni vektor
$p3=array($ihodinax-($sekundax-$stredx), $ihodinay-($sekunday-$stredy)); //kam ukazuje druhy vektor
$p4=array($hodinax$hodinay); //koncový bod


// Calculate the coordinate of the Bezier curve at $t = 0..1
function Bezier_eval($p1,$p2,$p3,$p4,$t) {
    
//čtyři body (vektory - $p) zredukuje na tři vážením přes $t, což je něco jako procento od 0 do 100% čili od začátku do konce křivky.
    //Například pro $t=1% vezme 99% z bodu $p1 a jen 1% z bodu $p2.
    //Vzniknou tři vektory $q. Z nich pak uděla stejným stylem dva vektory $r a nakonec jeden, který vrátí (return). 
    //Tak se ke každému procentu $t výsledné křivky přiřadí jeden bod křivky. 
    //Stejně by se postupovalo při jakémkoli jiném počtu výchozích vektorů. 
    //Každopádně toto je podstata Bezierovy křivky, kterou jsem díky tomuto pochopil ve svých 49 letech. Ach, co jsem dělal doposud, že jsem toto ještě nevěděl! :-) 
    // lines between successive pairs of points (degree 1)
    
$q1  = array((1-$t) * $p1[0] + $t $p2[0],(1-$t) * $p1[1] + $t $p2[1]);
    
$q2  = array((1-$t) * $p2[0] + $t $p3[0],(1-$t) * $p2[1] + $t $p3[1]);
    
$q3  = array((1-$t) * $p3[0] + $t $p4[0],(1-$t) * $p3[1] + $t $p4[1]);
    
// curves between successive pairs of lines. (degree 2)
    
$r1  = array((1-$t) * $q1[0] + $t $q2[0],(1-$t) * $q1[1] + $t $q2[1]);
    
$r2  = array((1-$t) * $q2[0] + $t $q3[0],(1-$t) * $q2[1] + $t $q3[1]);
    
// final curve between the two 2-degree curves. (degree 3)
    
return array((1-$t) * $r1[0] + $t $r2[0],(1-$t) * $r1[1] + $t $r2[1]);
}

// Calculate the squared distance between two points - pythagorova věta. Alespoň člověk ví, k čemu je dobrá.
function Point_distance2($p1,$p2) {
    
$dx $p2[0] - $p1[0];
    
$dy $p2[1] - $p1[1];
    return 
$dx $dx $dy $dy;
}

// Convert the curve to a polyline
function Bezier_convert($p1,$p2,$p3,$p4,$vzdalenost$krok) {
    
$t1 0.0;
    
$prev $p1//od bodu $p1 az po bod $p4 spočítá po $krok- krocích body bezierovy křivky
    
$t2 1/$krok//alespon dva kroky
    
$tol2 $vzdalenost $vzdalenost//kvadrat k pythagorově větě, viz Point_distance2
    
$result []= $prev[0]; // [] automaticky přida další index či prvek ve vektoru result
    
$result []= $prev[1];
    while (
$t1 1.0) {
            if (
$vypis==1) print("t2 je $t2; t1 je $t1<br>");
        if (
$t2 1.0) { $t2 1.0; } //blbovzdorná pojistka
        
$next Bezier_eval($p1,$p2,$p3,$p4,$t2);
        
$dist Point_distance2($prev,$next);
//        while (0==1) { // vypíná efekt vzdálenosti
         
while ($dist $tol2) {
            
// Halve the distance until small enough; pojistka pro nenadále skoky křivky, radeji dat vic kroku, pak je rychlejší výpočet
            
$t2 $t1 + ($t2 $t1) * 0.5;
            
$next Bezier_eval($p1,$p2,$p3,$p4,$t2);
            
$dist Point_distance2($prev,$next);
            }
        
// the image*polygon functions expect a flattened array of coordiantes
        
$result []= $next[0]; //přidává další a další body
        
$result []= $next[1];
        
$t1 $t2;
        
$prev $next;
        
$t2 $t1 1/$krok;
    }
    
$result []= $p4[0]; //nakonec vložit i koncový bod křivky
    
$result []= $p4[1];
    return 
$result//vraci dlouhy seznam bodů bezierovy křivky
}

        
$polygon Bezier_convert($p1$p2$p3$p4$vzdalenost$krok);
        if (
$vypis==1print_r($polygontrue);
        
$pocetbodu=count($polygon)/2-1// děleno dvěma protože jsou to souřadnice xy, tedy bodu je pravě polovina
        
for ($i=0;$i<$pocetbodu;$i++) {
            
imageline($image,  $polygon[2*$i], $polygon[2*$i+1], $polygon[2*$i+2], $polygon[2*$i+3],  $cRafie); //dva po sobě jdoucí body XY
            
}
//         imagepolygon($image,$polygon,count($polygon)/2,$cRafie); 
//         imageline($image,  $minutax, $minutay, $hodinax, $hodinay,  $cPozadi);  //toto odmaže přímku polynomu mezi koncovými body $p1 a $p4
//        starsi verze - může se hodit, pokud bychom chtěli uzavřenou křivku        
          
imagefilledellipse ($image $stredx,          $stredy$cPopredi); //střed ciferníku
           
imagefilledellipse ($image $vsekundax,      $vsekunday$cPopredi ); //sekundová ručička - přesněji sekundovy bod
          
if ($body==1){
            
imagefilledellipse ($image $p1[0], $p1[1], 10 10 $cBody); //zakladní body
            
imagefilledellipse ($image $p2[0], $p2[1], 10 10 $cBody); //zakladní body
            
imagefilledellipse ($image $p3[0], $p3[1], 10 10 $cBody); //zakladní body
            
imagefilledellipse ($image $p4[0], $p4[1], 10 10 $cBody); //zakladní body
            
}
        for (
$i=0$i<60;$i++) //cifernik minuty (polarni souradnice)
            
{
            
imagefilledellipse ($image ,     $stredx+$prumer*sin(2*pi()*$i/60),  
                                            
$stredy-$prumer*cos(2*pi()*$i/60), $cPopredi);
            }
         for (
$i=0$i<12;$i++) //cifernik hodiny
            
{
            
imagefilledellipse ($image ,     $stredx+$pomer*$prumer*sin(2*pi()*$i/12),  
                                            
$stredy-$pomer*$prumer*cos(2*pi()*$i/12), ,$cPopredi);
            
imagefilledellipse ($image ,     $stredx+$prumer*sin(2*pi()*$i/12),  
                                            
$stredy-$prumer*cos(2*pi()*$i/12), ,$cPopredi);
            }
        
$cas=str_pad($hodina2'0''STR_PAD_LEFT').":".str_pad($minuta2'0''STR_PAD_LEFT').":".str_pad($sekunda2'0''STR_PAD_LEFT');
        if (
$text==1imagefttext ($image1001020$cRafie"arial.ttf""$cas");
    
Imagepng($image); 
    
ImageDestroy($image); //Například baba jaga, když letí ve hmoždíři, tak za sebou zametá. Toto má stejnou funkci.

if ($vypis==1): //chaoticke ad hoc vypisy pro debugování     
    
$f fopen("background4.htm""w");
    
fputs($f"<html><body>");
     
$promenne=print_r($polygontrue);
    
fputs($f"$promenne");
    
$promenne="picWidth=".$picWidth."&picHeight=".$picHeight."&pozadir=".$pozadir."&pozadig=".$pozadig."&pozadib=".$pozadib."&popredir=".$popredir."&popredig=".$popredig."&popredib=".$popredib."&pomer=".$pomer."&elipsa=".$elipsa."&r1=".$r1."&r2=".$r2."&r3=".$r3."&orientace=".$orientace."&krok=".$krok."&kroku=".$kroku;
     
fputs($f$promenne);
    
//     fputs($f, "<a href=inspiro3.php?".$promenne.">hodnoty vlozit do formulare inspiro3.php</a><br>");
    //     fputs($f, "<a href=background3.php?".$promenne.">ulozene hodnoty z background3.php</a><br>");
    
fputs($f"</body></html>");
    
fclose($f);
endif;
?>