//-----------------------------------------------------------------------------
// Fraktaly v pocitacove grafice
// Ukazkovy priklad cislo 46.1
// Autor: Pavel Tisnovsky
//
// Tato demonstracni aplikace slouzi k rekurzivnimu deleni usecky s posunem
// prostredniho bodu.
// Ukonceni aplikace se provede klavesou [Esc].
//-----------------------------------------------------------------------------

#ifdef __BORLANDC__
#include <windows.h>
#endif

#include <GL/glut.h>                            // hlavickovy soubor funkci GLUTu a OpenGL
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define WINDOW_TITLE    "Fraktaly 46.1"         // titulek okna
#define WINDOW_WIDTH    512                     // pocatecni velikost okna
#define WINDOW_HEIGHT   440

int   aa=0;                                     // antialiasing
float Delta=50.0;                               // "amplituda" posunu bodu



//-----------------------------------------------------------------------------
// Vykresleni retezce na obrazovku
//-----------------------------------------------------------------------------
void drawString(const int x, const int y,                    // umisteni retezce
                const float r, const float g, const float b, // barva pisma
                char *str)                                   // ukazatel na retezec
{
    char *c;
    glColor3f(r, g, b);
    glRasterPos2i(x, y);
    for (c=str; *c!=0; c++) {
        glutBitmapCharacter(GLUT_BITMAP_9_BY_15, *c);
    }
}



//-----------------------------------------------------------------------------
// Vypsani informaci o zadanych parametrech
//-----------------------------------------------------------------------------
void drawInfo(float Delta)
{
    char str[100];
    sprintf(str, "[up][down]  Delta: %5.0f", Delta);
    drawString(10, 10, 1.0, 1.0, 0.0, str);
}



//-----------------------------------------------------------------------------
// Vygenerovani nahodneho cisla v rozsahu 0..1
//-----------------------------------------------------------------------------
float my_random(void)
{
    return (float)rand()/(float)RAND_MAX;
}



//-----------------------------------------------------------------------------
// Rekurzivne volana metoda, ktera provede rozdeleni usecky a posun
// prostredniho bodu
//-----------------------------------------------------------------------------
void mdaRecursive1D(float x1, float y1, float x2, float y2, float Delta, int iter)
{
    float x,y;
    if (!iter) {                                // pokud jsme dosahli maximalniho mnozstvi iteraci
        glVertex2f(x1, y1);                     // vykresleni rozdelene usecky na nejnizsi urovni
        glVertex2f(x2, y2);
        return;                                 // a ukonceni rekurze
    }
    x=(x1+x2)/2.0;                              // vypocet polohy prostredniho bodu
    y=(y1+y2)/2.0;
    y+=my_random()*Delta-Delta/2.0;             // posun prostredniho bodu
    mdaRecursive1D(x1, y1, x, y, Delta/2.0, iter-1); // rekurzivni volani na prvni polovinu usecky
    mdaRecursive1D(x, y, x2, y2, Delta/2.0, iter-1); // rekurzivni volani na druhou polovinu usecky
}



//-----------------------------------------------------------------------------
// Prekresleni fraktalu
//-----------------------------------------------------------------------------
void recalcFractal(float Delta)
{
    char str[100];
    int i;
    for (i=0; i<10; i++) {                      // pro zadany maximalni pocet iteraci
        srand(123456);                          // nastavit vzdy stejne seminko RNG
        drawString(10, 30+i*40, 0.0, 1.0, 1.0, itoa(i, str, 10));
        glColor3f(1.0, 1.0, 1.0);
        glBegin(GL_LINES);                      // vykresleni rozdelene usecky pro dany pocet iteraci
        mdaRecursive1D(20, 30+i*40, WINDOW_WIDTH-10, 30+i*40, Delta, i);
        glEnd();
    }
}



//-----------------------------------------------------------------------------
// Funkce volana pro inicializaci vykreslovani
//-----------------------------------------------------------------------------
void onInit(void)
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);       // barva pozadi
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);      // mod ulozeni pixelu
    // parametry pro antialiasing
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}



//-----------------------------------------------------------------------------
// Nastaveni souradneho systemu v zavislosti na velikosti okna
//-----------------------------------------------------------------------------
void onResize(int w, int h)                     // argumenty w a h reprezentuji novou velikost okna
{
    glViewport(0, 0, w, h);                     // viditelna oblast pres cele okno
    glMatrixMode(GL_PROJECTION);                // zacatek modifikace projekcni matice
    glLoadIdentity();                           // vymazani projekcni matice (=identita)
    glOrtho(0, w, 0, h, -1, 1);                 // mapovani abstraktnich souradnic do souradnic okna
}



//-----------------------------------------------------------------------------
// Tato callback funkce je zavolana pri kazdem prekresleni okna
//-----------------------------------------------------------------------------
void onDisplay(void)
{
    if (aa) {                                   // zapnuti ci vypnuti antialiasingu
        glLineWidth(1.5f);
        glEnable(GL_LINE_SMOOTH);
    }
    else {
        glLineWidth(1.0f);
        glDisable(GL_LINE_SMOOTH);
    }
    glClear(GL_COLOR_BUFFER_BIT);               // vymazani vsech bitovych rovin barvoveho bufferu
    drawInfo(Delta);                            // prekresleni informaci o fraktalu
    recalcFractal(Delta);                       // prekresleni fraktalu
    glFlush();                                  // provedeni a vykresleni vsech zmen
    glutSwapBuffers();
}



//-----------------------------------------------------------------------------
// Tato callback funkce je zavolana pri stlaceni ASCII klavesy
//-----------------------------------------------------------------------------
#ifdef __BORLANDC__
#pragma option -w-par
#endif
void onKeyboard(unsigned char key, int x, int y)
{
    switch (key) {
        case 'a':
        case 'A': aa=!aa; glutPostRedisplay(); break; // zapnuti ci vypnuti antialiasingu
        case 32:  glutPostRedisplay(); break;
        case 27:  exit(0);  break;              // pokud byla stlacena klavesa ESC, konec programu
        default:  break;
    }
}
#ifdef __BORLANDC__
#pragma option -w+par
#endif



//-----------------------------------------------------------------------------
// Tato callback funkce je zavolana pri stlaceni non-ASCII klavesy
//-----------------------------------------------------------------------------
#ifdef __BORLANDC__
#pragma option -w-par
#endif
void onSpecial(int key, int x, int y)
{
    // posun fraktalu a zmena meritka
    switch (key) {
        case GLUT_KEY_UP:         Delta+=5.0; glutPostRedisplay();    break;
        case GLUT_KEY_DOWN:       Delta-=5.0; glutPostRedisplay();    break;
        default:                                          break;
    }
}
#ifdef __BORLANDC__
#pragma option -w+par
#endif



//-----------------------------------------------------------------------------
// Hlavni funkce konzolove aplikace
//-----------------------------------------------------------------------------
int main(int argc, char **argv)
{
    glutInit(&argc, argv);                      // inicializace knihovny GLUT
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
    glutCreateWindow(WINDOW_TITLE);             // vytvoreni okna pro kresleni
    glutReshapeWindow(WINDOW_WIDTH, WINDOW_HEIGHT);// zmena velikosti okna
    glutPositionWindow(100, 100);               // pozice leveho horniho rohu okna
    glutDisplayFunc(onDisplay);                 // registrace funkce volane pri prekreslovani okna
    glutReshapeFunc(onResize);                  // registrace funkce volane pri zmene velikosti okna
    glutKeyboardFunc(onKeyboard);               // registrace funkce volane pri stlaceni klavesy
    glutSpecialFunc(onSpecial);                 // registrace funkce volane pri stlaceni specialni klavesy
    onInit();                                   // inicializace vykreslovani
    glutMainLoop();                             // nekonecna smycka, kde se volaji zaregistrovane funkce
    return 0;                                   // navratova hodnota vracena operacnimu systemu
}



//-----------------------------------------------------------------------------
// finito
//-----------------------------------------------------------------------------