//-----------------------------------------------------------------------------
// Fraktaly v pocitacove grafice
// Autor: Pavel Tisnovsky
//
// Vykresleni fraktalnich obrazcu pomoci algoritmu "Fractal Flame" navrzeneho
// a posleze implementovaneho Scottem Dravesem.
// Po prekladu a spusteni programu se ve spodni casti okna zobrazi kratka
// napoveda spolu s klavesovymi zkratkami.
// Pixmapu s vykreslenym fraktalem je mozne ulozit do souboru ve formatu TGA
// pomoci klavesove zkratky [S]. Zmena typu fraktalu se provadi stiskem klaves
// [0]-[9]. Pouzite transformace jsou vypsany v dolni casti obrazovky.
// Ukonceni aplikace se provede klavesou [Esc] nebo klavesou [Q].
//-----------------------------------------------------------------------------

#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 39.1"         // titulek okna
#define WINDOW_WIDTH    640                     // pocatecni velikost okna
#define WINDOW_HEIGHT   480
#define FILE_NAME       "fraktaly391.tga"       // jmeno souboru pro ulozeni pixmapy

#define PIXMAP_WIDTH    512                     // sirka pixmapy
#define PIXMAP_HEIGHT   384                     // vyska pixmapy

typedef struct {                                // struktura popisujici pixmapu
    unsigned int width;
    unsigned int height;
    unsigned char *pixels;
} pixmap;

typedef enum {                                  // typy vykreslovacich rezimu
    IterPutPixel,
    IterAddPixel,
    IterLogPixel,
    TransfPutPixel,
    TransfAddPixel,
    TransfLogPixel,
} DrawType;

pixmap *pixFractalFlame;                        // pixmapa pro obrazek IFS fraktalu
float  floatMap[PIXMAP_HEIGHT][PIXMAP_WIDTH];   // pixmapa s floaty pro logaritmovani histogramu
float  floatMapRGB[PIXMAP_HEIGHT][PIXMAP_WIDTH][3];

int    ifs=1;                                   // cislo systemu iterovanych funkci
int    maxiter=1000;                            // maximalni pocet iteraci
int    rf=1, gf=1, bf=1;                        // priznaky barvovych slozek
int    deltar=1, deltag=1, deltab=1;            // prirustky barvovych slozek
double scale=30.0;                              // meritko fraktalu
double xpos=PIXMAP_WIDTH/2.0;                   // posun fraktalu
double ypos=PIXMAP_HEIGHT/2.0;                  // posun fraktalu
int    redraw=1;
DrawType    drawType=IterPutPixel;



//-----------------------------------------------------------------------------
// Funkce pro vytvoreni pixmapy o zadane velikosti
//-----------------------------------------------------------------------------
pixmap * createPixmap(const unsigned int width, const unsigned int height)
{
    pixmap *p=(pixmap *)malloc(sizeof(pixmap)); // alokace struktury pixmapy
    if (!p) return NULL;
    p->width=width;                             // naplneni struktury
    p->height=height;
    p->pixels=(unsigned char *)malloc(3*width*height);
    if (!p->pixels) {                           // alokace pole pro pixely
        free(p);                                // alokace se nepovedla
        return NULL;
    }
    else {
        memset(p->pixels, 0, 3*width*height);   // smazani pixmapy
    }
    return p;
}



//-----------------------------------------------------------------------------
// Funkce pro zruseni pixmapy
//-----------------------------------------------------------------------------
void destroyPixmap(pixmap *p)
{
    if (p->pixels) free(p->pixels);             // uvolnit vlastni pixmapu
    if (p) free(p);                             // i okolni strukturu
}



//-----------------------------------------------------------------------------
// Vymazani pixmapy
//-----------------------------------------------------------------------------
void clearPixmap(const pixmap *p)
{
    if (!p) return;
    if (!p->pixels) return;
    memset(p->pixels, 0, 3*p->width*p->height);
    if (drawType==IterLogPixel)
        memset(floatMap, 0, PIXMAP_WIDTH*PIXMAP_HEIGHT*sizeof(float));
    if (drawType==TransfLogPixel)
        memset(floatMapRGB, 0, PIXMAP_WIDTH*PIXMAP_HEIGHT*sizeof(float)*3);
}



//-----------------------------------------------------------------------------
// Funkce pro vykresleni pixmapy do barvoveho bufferu
//-----------------------------------------------------------------------------
void drawPixmap(const pixmap *p)
{
    if (!p || !p->pixels) return;
    glDrawPixels(                               // vykresleni pixmapy
            p->width, p->height,                // sirka a vyska pixmapy
            GL_RGB,                             // format dat pixelu
            GL_UNSIGNED_BYTE,                   // datovy typ kazde barevne slozky
            p->pixels);                         // ukazatel na pamet s barvami pixelu
}



//-----------------------------------------------------------------------------
// Ulozeni pixmapy do externiho souboru
//-----------------------------------------------------------------------------
void savePixmap(const pixmap *p, const char *fileName)
{
    FILE *fout;
    unsigned int i, j, k;
    unsigned char tgaHeader[18]={               // hlavicka formatu typu TGA
                        0x00,                   // typ hlavicky TGA
                        0x00,                   // nepouzivame paletu
                        0x02,                   // typ obrazku je RGB TrueColor
                        0x00, 0x00,             // delka palety je nulova
                        0x00, 0x00, 0x00,       // pozice v palete nas nezajima
                        0x00, 0x00, 0x00, 0x00, // obrazek je umisteny na pozici [0, 0]
                        0x00, 0x00, 0x00, 0x00, // sirka a vyska obrazku (dva byty na polozku)
                        0x18,                   // format je 24 bitu na pixel
                        0x20                    // orientace pixmapy v obrazku
    };
    if (!p || !p->pixels) return;
    memcpy(tgaHeader+12, &(p->width), 2);       // do hlavicky TGA zapsat velikost obrazku
    memcpy(tgaHeader+14, &(p->height), 2);
    fout=fopen(fileName, "wb");
    if (fout) {
        fwrite(tgaHeader, 18, 1, fout);         // zapsat hlavicku TGA do souboru
        for (j=p->height; j; j--) {             // radky zapisovat v opacnem poradi
            unsigned int yoff=(j-1)*3*p->width; // y-ovy offset v poli
            unsigned int xoff=0;                // x-ovy offset v poli
            for (i=0; i<p->width; i++) {        // pro kazdy pixel na radku
                for (k=0; k<3; k++) {           // prohodit barvy RGB na BGR
                    fputc(p->pixels[xoff+yoff+2-k], fout); // a zapsat do souboru
                }
                xoff+=3;                        // posun na dalsi pixel
            }
        }
        fclose(fout);
    }
}



//-----------------------------------------------------------------------------
// Zmena barvy pixelu na zadanych souradnicich
//-----------------------------------------------------------------------------
void putpixel(pixmap *p,
              const unsigned int x,             // pozice pixelu v pixmape
              const unsigned int y,
              const unsigned char r,            // barva pixelu
              const unsigned char g,
              const unsigned char b)
{
    int pos;
    // zde se vyuziva zkraceneho vyhodnoceni vyrazu - pokud plati !p, nic se dale netestuje
    if (!p || !p->pixels || x>=p->width || y>=p->height) return;
    pos=3*(x+y*p->width);
    p->pixels[pos++]=r;                         // nastaveni barvy pixelu
    p->pixels[pos++]=g;
    p->pixels[pos]=b;
}



//-----------------------------------------------------------------------------
// Prirustek barvy pixelu na zadanych souradnicich
//-----------------------------------------------------------------------------
void addpixel(pixmap *p, const unsigned int x, const unsigned int y,
                         const unsigned char dr, const unsigned char dg, const unsigned char db)
{
    int pos;
    // zde se vyuziva zkraceneho vyhodnoceni vyrazu - pokud plati !p, nic se dale netestuje
    if (!p || !p->pixels || x>=p->width || y>=p->height) return;
    pos=3*(x+y*p->width);
    if (p->pixels[pos]<256-dr) p->pixels[pos]+=dr;// nastaveni cervene slozky barvy pixelu
    pos++;
    if (p->pixels[pos]<256-dg) p->pixels[pos]+=dg;// nastaveni zelene slozky barvy pixelu
    pos++;
    if (p->pixels[pos]<256-db) p->pixels[pos]+=db;// nastaveni modre slozky barvy pixelu
}



//-----------------------------------------------------------------------------
// Vynasobeni kazdeho pixelu konstantou vypoctenou tak, aby se provedl prevod
// z linearniho do logaritmickeho meritka
//-----------------------------------------------------------------------------
void multiplepixel(pixmap *p, const unsigned int x, const unsigned int y, float pixelR, float pixelG, float pixelB)
{
    int pos;
    // zde se vyuziva zkraceneho vyhodnoceni vyrazu - pokud plati !p, nic se dale netestuje
    if (!p || !p->pixels || x>=p->width || y>=p->height) return;
    pos=3*(x+y*p->width);
    p->pixels[pos]*=pixelR;
    pos++;
    p->pixels[pos]*=pixelG;
    pos++;
    p->pixels[pos]*=pixelB;
}



//-----------------------------------------------------------------------------
// Prirustek barvy pixelu na zadanych souradnicich ve floatove monochromaticke
// mape
//-----------------------------------------------------------------------------
void addfloat(const unsigned int x, const unsigned int y)
{
    if (x>=PIXMAP_WIDTH || y>=PIXMAP_HEIGHT) return;
    floatMap[y][x]++;
}



//-----------------------------------------------------------------------------
// Prirustek barvy pixelu na zadanych souradnicich ve floatove mape typu RGB
//-----------------------------------------------------------------------------
void addfloatRGB(const unsigned int x, const unsigned int y, int r, int g, int b)
{
    if (x>=PIXMAP_WIDTH || y>=PIXMAP_HEIGHT) return;
    floatMapRGB[y][x][0]+=r;
    floatMapRGB[y][x][1]+=g;
    floatMapRGB[y][x][2]+=b;
}



//-----------------------------------------------------------------------------
// Kopie pixelu z floatove pixmapy do pixmapy kompatibilni s OpenGL spolu
// s logaritmickym prevodem meritka
//-----------------------------------------------------------------------------
void convertFloatMap(void)
{                                           // logaritmovani histogramu a prevod na byty
    float maxp=0;
    float pixelFactor;                      // faktor konverze
    int   i, j;

    for (j=0; j<PIXMAP_HEIGHT; j++)         // zjistit maximalni hodnotu ulozenou
        for (i=0; i<PIXMAP_WIDTH; i++)      // ve floatove pixmape
            if (maxp<floatMap[j][i]) maxp=floatMap[j][i];

    pixelFactor=255.0/log(maxp);            // vypocitat faktor konverze

    for (j=0; j<PIXMAP_HEIGHT; j++) {       // konverze a zmena na logaritmicke meritko
        for (i=0; i<PIXMAP_WIDTH; i++) {    // s tim, ze vysledne barvove slozky jsou
            float pixel=log(floatMap[j][i])*pixelFactor; // v rozsahu 0..255
            putpixel(pixFractalFlame, i, j, (int)pixel, (int)pixel, (int)pixel);
        }
    }
}



//-----------------------------------------------------------------------------
// Zmena pixelu v pixmape tak, aby se zmenila linearni zavislost mezi poctem
// zasahu pixelu na zavislost logaritmickou
//-----------------------------------------------------------------------------
void convertFloatMap2(void)
{                                           // logaritmovani histogramu a prevod na byty
    float max[3]={0, 0, 0};
    float factor[3];
    int   i, j, k;

    for (j=0; j<PIXMAP_HEIGHT; j++)         // zjistit maximalni hodnoty ulozene
        for (i=0; i<PIXMAP_WIDTH; i++)      // ve floatove pixmapy
            for (k=0; k<3; k++)
                if (max[k]<floatMapRGB[j][i][k]) max[k]=floatMapRGB[j][i][k];

    for (k=0; k<3; k++)                     // vypocitat vektor faktoru konverze
        factor[k]=255.0/log(max[k]);

    for (j=0; j<PIXMAP_HEIGHT; j++) {
        for (i=0; i<PIXMAP_WIDTH; i++) {
            float pixel[3];
            for (k=0; k<3; k++)
                pixel[k]=log(floatMapRGB[j][i][k])*factor[k];
            putpixel(pixFractalFlame, i, j, (int)pixel[0], (int)pixel[1], (int)pixel[2]);
        }
    }
}



//-----------------------------------------------------------------------------
// Vypocet x-ove souradnice pomoci funkce Vn()
//-----------------------------------------------------------------------------
double vx(int type, double x, double y)
{
    double r=sqrt(x*x+y*y);             // vzdalenost od pocatku
    double phi=atan2(y,x);              // uhel od x-ove osy
    double result;
    switch (type) {
        case 0:                         // linear
            result=x;
            break;
        case 1:                         // sinusoidal
            result=sin(x);
            break;
        case 2:                         // spherical
            result=x/(r*r);
            break;
        case 3:                         // swirl
            result=r*cos(phi+r);
            break;
        case 4:                         // horseshoe
            result=r*cos(2*phi);
            break;
        case 5:                         // polar
            result=phi/M_PI;
            break;
        case 6:                         // handkerchief
            result=r*sin(phi+r);
            break;
        case 7:                         // heart
            result=r*sin(phi*r);
            break;
        case 8:                         // disc
            result=phi*sin(M_PI*r)/M_PI;
            break;
        case 9:                         // spiral
            result=(cos(phi)+sin(r))/r;
            break;
        case 10:                        // hyperbolic
            result=sin(phi)/r;
            break;
        case 11:                        // diamond
            result=sin(phi)*cos(r);
            break;
        case 12:                        // ex
            result=r*pow(sin(phi+r),3);
            break;
        case 13:                        // Julia
            result=sqrt(r)*cos(phi/2);
            break;
        case 16:                        // fisheye
            result=2*r/((r+1)*x);
            break;
        case 20:                        // cosine
            result=cos(M_PI*x)*cosh(y);
            break;
        default:
            result=x;
            break;
    };
    return result;
}



//-----------------------------------------------------------------------------
// Vypocet y-ove souradnice pomoci funkce Vn()
//-----------------------------------------------------------------------------
double vy(int type, double x, double y)
{
    double r=sqrt(x*x+y*y);             // vzdalenost od pocatku
    double phi=atan2(y,x);              // uhel od x-ove osy
    double result;
    switch (type) {
        case 0:                         // linear
            result=y;
            break;
        case 1:                         // sinusoidal
            result=sin(y);
            break;
        case 2:                         // spherical
            result=y/(r*r);
            break;
        case 3:                         // swirl
            result=r*sin(phi+r);
            break;
        case 4:                         // horseshoe
            result=r*sin(2*phi);
            break;
        case 5:                         // polar
            result=r-1;
            break;
        case 6:                         // handkerchief
            result=r*cos(phi-r);
            break;
        case 7:                         // heart
            result=-r*cos(phi*r);
            break;
        case 8:                         // disc
            result=phi*cos(M_PI*r)/M_PI;
            break;
        case 9:                         // spiral
            result=(sin(phi)-cos(r))/r;
            break;
        case 10:                        // hyperbolic
            result=cos(phi)/r;
            break;
        case 11:                        // diamond
            result=cos(phi)*sin(r);
            break;
        case 12:                        // ex
            result=r*pow(cos(phi-r),3);
            break;
        case 13:                        // Julia
            result=sqrt(r)*sin(phi/2);
            break;
        case 16:                        // fisheye
            result=2*r/((r+1)*y);
            break;
        case 20:                        // cosine
            result=-sin(M_PI*x)*sinh(y);
            break;
        default:
            result=y;
            break;
    };
    return result;
}



//-----------------------------------------------------------------------------
// Prekresleni fraktalu typu FractalFlame
//-----------------------------------------------------------------------------
void recalcFractalFlame( pixmap *pix,               // pixmapa pro vykreslovani
                  int    maxiter,                   // maximalni pocet iteraci
                  double scale,                     // meritko obrazce
                  double xpos,                      // posun obrazce
                  double ypos,
                  int    type,                      // vybrany IFS system
                  int    rf, int gf, int bf,        // priznaky barvovych slozek
                  int    dr, int dg, int db,        // prirustky barvovych slozek
                  DrawType drawType)                // zpusob vykresleni
{
    int    iter=0;                                  // pocitadlo iteraci
    int    threshold=50;                            // hranice poctu iteraci pro vykreslovani

    double x, y;                                    // poloha iterovaneho bodu
    double xn, yn;                                  // nova poloha iterovaneho bodu
    int    k;                                       // cislo vybraneho ridiciho bodu

    unsigned char red=rf ? dr : 0;                  // realne prirustky barvovych slozek
    unsigned char green=gf ? dg : 0;
    unsigned char blue=bf ? db : 0;

    double coefs[][3][7]={                          // koeficienty jednotlivych transformaci
        {
            { 0.500000,  0.0000000,  0.000000,  0.5000000,  0.000000,  0.000000,  2},
            { 0.500000,  0.0000000,  0.000000,  0.5000000,  0.000000,  1.000000,  0},
            { 0.500000,  0.0000000,  0.000000,  0.5000000,  1.000000,  1.000000,  0}
        },
        {
            { 0.983960,  0.2983280,  0.359416, -0.5835410, -0.850590, -0.378754,  0},
            {-0.900388,  0.2930630,  0.397598,  0.0225629,  0.465126, -0.277212,  9},
            {-0.329863, -0.0855261, -0.369381, -0.8583790,  0.977861,  0.547595,  3},
        },
        {
            { 0.562482,  0.3978610, -0.539599,  0.5010880, -0.429920, -0.112404,  2},
            { 0.830039, -0.4961740,  0.162480,  0.7504680,  0.910220,  0.288389,  2},
            { 1.000000,  0.0000000,  0.000000,  1.0000000,  0.000000,  0.000000,  0},
        },
        {
            { 0.983960,  0.2983280,  0.359416, -0.5835410, -0.850590, -0.378754,  2},
            { 0.830039, -0.4961740,  0.162480,  0.7504680,  0.910220,  0.288389,  2},
            { 0.500000,  0.0000000,  0.000000,  0.5000000,  1.000000,  1.000000,  0}
        },
        {
            { 0.983960,  0.2983280,  0.359416, -0.5835410, -0.850590, -0.378754,  3},
            { 0.830039, -0.4961740,  0.162480,  0.7504680,  0.910220,  0.288389,  3},
            { 0.500000,  0.0000000,  0.000000,  0.5000000,  1.000000,  1.000000,  3}
        },
        {
            { 0.500000, -0.4000000,  0.000000,  0.5000000,  0.000000,  0.000000,  20},
            { 0.500000,  0.1000000,  0.000000,  0.5000000,  0.000000,  0.300000,  3},
            { 0.500000,  0.0000000,  0.100000,  0.4000000,  0.500000,  1.000000,  5}
        },
        {
            { 0.983960,  0.2983280,  0.359416, -0.5835410, -0.850590, -0.378754,  1},
            {-0.900388,  0.2930630,  0.397598,  0.0225629,  0.465126, -0.277212,  2},
            {-0.329863, -0.0855261, -0.369381, -0.8583790,  0.977861,  0.547595,  3}
        },
        {
            { 0.983960,  0.2983280,  0.359416, -0.5835410, -0.850590, -0.378754,  4},
            {-0.900388,  0.2930630,  0.397598,  0.0225629,  0.465126, -0.277212,  5},
            {-0.329863, -0.0855261, -0.369381, -0.8583790,  0.977861,  0.547595,  6}
        },
        {
            { 0.983960,  0.2983280,  0.359416, -0.5835410, -0.850590, -0.378754,  7},
            {-0.900388,  0.2930630,  0.397598,  0.0225629,  0.465126, -0.277212,  8},
            {-0.329863, -0.0855261, -0.369381, -0.8583790,  0.977861,  0.547595,  9}
        },
        {
            { 0.983960,  0.2983280,  0.359416, -0.5835410, -0.850590, -0.378754,  10},
            {-0.900388,  0.2930630,  0.397598,  0.0225629,  0.465126, -0.277212,  11},
            {-0.329863, -0.0855261, -0.369381, -0.8583790,  0.977861,  0.547595,  12}
        },
    };
    unsigned char pal[8][3]={                       // barvova paleta
        {0xff, 0x00, 0x00},
        {0x00, 0xff, 0x00},
        {0x00, 0x00, 0xff},
        {0xff, 0xff, 0x00},
        {0x00, 0xff, 0xff},
        {0xff, 0x00, 0xff},
        {0xff, 0xff, 0xff},
        {0x80, 0x80, 0x80},
    };

    x=0;
    y=0;                                            // nastaveni pocatecni polohy bodu
    srand(0);

    while (iter++<maxiter*100) {                    // iteracni smycka
        k=rand()%3;                                 // zvolit jednu ze tri transformaci
        xn=x*coefs[type][k][0]+y*coefs[type][k][2]+coefs[type][k][4]; // aplikovat linearni
        yn=x*coefs[type][k][1]+y*coefs[type][k][3]+coefs[type][k][5]; // transformaci
        x=vx((int)coefs[type][k][6], xn, yn);       // aplikovat nelinearni transformaci
        y=vy((int)coefs[type][k][6], xn, yn);
        if (iter>threshold) {                       // je-li dosazeno hranice iteraci
            double xx=x*scale+xpos;
            double yy=y*scale+ypos;
            switch (drawType) {                     // vyber vykreslovaciho rezimu
                case IterPutPixel:
                    putpixel(pix, xx, yy, rf ? 0xff:0x00, gf ? 0xff:0x00, bf ? 0xff:0x00);
                    break;
                case IterAddPixel:
                    addpixel(pix, xx, yy, red, green, blue);
                    break;
                case IterLogPixel:
                    addfloat(xx, yy);
                    break;
                case TransfPutPixel:
                    putpixel(pix, xx, yy, pal[k&7][0], pal[k&7][1], pal[k&7][2]);
                    break;
                case TransfAddPixel:
                    addpixel(pix, xx, yy, (!!pal[k&7][0])*dr, (!!pal[k&7][1])*dg, (!!pal[k&7][2])*db);
                    break;
                case TransfLogPixel:
                    addfloatRGB(xx, yy, (!!pal[k&7][0]), (!!pal[k&7][1]), (!!pal[k&7][2]));
                    break;
                default:
                    break;
            }
        }
        x=xn;
        y=yn;
    }
    // u logaritmickych prevodu provest finalni konverzi pixmap
    if (drawType==IterLogPixel) convertFloatMap();
    if (drawType==TransfLogPixel) convertFloatMap2();
}



//-----------------------------------------------------------------------------
// 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);
    }
}



//-----------------------------------------------------------------------------
// Vypis informaci o vykreslovanem fraktalu
//-----------------------------------------------------------------------------
void drawInfo(int maxiter, double xpos, double ypos, double scale, int ifs, int r, int g, int b, int deltar, int deltag, int deltab, DrawType drawType)
{
#define drawString2(x, y, r, g, b, str) drawString((x)+1, (y), 0.0, 0.0, 0.0, (str)); drawString((x), (y)+1, (r), (g), (b), (str));
    char str[100];
    char *ifsNames[10]={
        "Chaos 1",
        "Chaos 2",
        "Chaos 3",
        "Chaos 4",
        "Chaos 5",
        "Chaos 6",
        "Chaos 7",
        "Chaos 8",
        "Chaos 9",
        "Chaos 10",
    };
    sprintf(str, "[0]-[9]      IFS type = %s", ifsNames[ifs]);
    drawString2( 10, 112, 0.0, 1.0, 1.0, str);
    sprintf(str, "[F1] draw:   IterPutPixel:   %s", drawType==IterPutPixel ? "set":"");
    drawString2( 10, 96, 0.2, 1.0, 1.0, str);
    sprintf(str, "[F2] draw:   IterAddPixel:   %s", drawType==IterAddPixel ? "set":"");
    drawString2( 10, 80, 0.4, 1.0, 0.8, str);
    sprintf(str, "[F3] draw:   IterLogPixel:   %s", drawType==IterLogPixel ? "set":"");
    drawString2( 10, 64, 0.6, 1.0, 0.6, str);
    sprintf(str, "[F4] draw:   TransfPutPixel: %s", drawType==TransfPutPixel ? "set":"");
    drawString2( 10, 48, 0.8, 1.0, 0.4, str);
    sprintf(str, "[F5] draw:   TransfAddPixel: %s", drawType==TransfAddPixel ? "set":"");
    drawString2( 10, 32, 1.0, 1.0, 0.2, str);
    sprintf(str, "[F6] draw:   TransfLogPixel: %s", drawType==TransfLogPixel ? "set":"");
    drawString2( 10, 16, 1.0, 1.0, 0.0, str);

    sprintf(str, "[<][>][,][.]  maxiter = %d", maxiter*10);
    drawString2(320, 112, 1.0, 0.0, 0.6, str);
    sprintf(str, "[PgUp][PgDn]  scale = %4.2f ", scale);
    drawString2(320, 96, 1.0, 0.0, 1.0, str);
    sprintf(str, "[Arrows]      pan = %5.3f, %5.3f", xpos, ypos);
    drawString2(320, 80, 0.8, 0.2, 1.0, str);
    sprintf(str, "[R][G][B]     colors = %d %d %d", r, g, b);
    drawString2(320, 64, 0.6, 0.4, 1.0, str);
    sprintf(str, "[F7][F8]      delta R = %d", deltar);
    drawString2(320, 48, 0.4, 0.6, 1.0, str);
    sprintf(str, "[F9][F10]     delta G = %d", deltag);
    drawString2(320, 32, 0.2, 0.8, 1.0, str);
    sprintf(str, "[F11][F12]    delta B = %d", deltab);
    drawString2(320, 16, 0.0, 1.0, 1.0, str);
}



//-----------------------------------------------------------------------------
// 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
    pixFractalFlame=createPixmap(PIXMAP_WIDTH, PIXMAP_HEIGHT);
}



//-----------------------------------------------------------------------------
// 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)
{
    glClear(GL_COLOR_BUFFER_BIT);               // vymazani vsech bitovych rovin barvoveho bufferu
    glDrawBuffer(GL_BACK);                      // pixmapa se bude kreslit do zadniho barvoveho bufferu
    if (redraw) {
        clearPixmap(pixFractalFlame);           // vymazani pixmapy a prepocet fraktalu
        recalcFractalFlame(pixFractalFlame, maxiter, scale, xpos, ypos, ifs, rf, gf, bf, deltar, deltag, deltab, drawType);// prepocet fraktalu
        redraw=0;
    }
    glRasterPos2i(0, WINDOW_HEIGHT-PIXMAP_HEIGHT); // nastaveni souradnic leveho spodniho rohu pixmapy
    drawPixmap(pixFractalFlame);                         // vykresleni pixmapy
    glRasterPos2i(0, 0);
    drawInfo(maxiter, xpos, ypos, scale, ifs, rf, gf, bf, deltar, deltag, deltab, drawType);
    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)
{
    key=(key>='A' && key<='Z') ? key-'A'+'a': key;
    if (key>='0' && key<='9') { ifs=key-'0'; redraw=1; glutPostRedisplay(); } // nastaveni barvove palety
    switch (key) {
        case 27:                                                              // pokud byla stlacena klavesa ESC, konec programu
        case 'q': exit(0);                                             break; // totez co klavesa ESC
        case 's': savePixmap(pixFractalFlame, FILE_NAME);              break; // ulozeni pixmapy
        case '<': if (maxiter>1000)   maxiter-=1000; redraw=1; glutPostRedisplay();  break;
        case ',': if (maxiter>100)    maxiter-=100;   redraw=1; glutPostRedisplay(); break; // zmena poctu iteraci
        case '>': maxiter+=1000; redraw=1; glutPostRedisplay();                      break;
        case '.': maxiter+=100; redraw=1; glutPostRedisplay();                       break;
        case 'r': rf=!rf;                        redraw=1; glutPostRedisplay();      break; // priznaky barvovych slozek
        case 'g': gf=!gf;                        redraw=1; glutPostRedisplay();      break;
        case 'b': bf=!bf;                        redraw=1; glutPostRedisplay();      break;
        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_LEFT:      xpos-=5;                         redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_RIGHT:     xpos+=5;                         redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_UP:        ypos+=5;                         redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_DOWN:      ypos-=5;                         redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_PAGE_UP:   scale*=1.1;                      redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_PAGE_DOWN: if (scale>1) scale/=1.1;         redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_F1:        drawType=IterPutPixel ;          redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_F2:        drawType=IterAddPixel;           redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_F3:        drawType=IterLogPixel;           redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_F4:        drawType=TransfPutPixel;         redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_F5:        drawType=TransfAddPixel;         redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_F6:        drawType=TransfLogPixel;         redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_F7:        if (deltar>1) deltar--;          redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_F8:        deltar++;                        redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_F9:        if (deltag>1) deltag--;          redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_F10:       deltag++;                        redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_F11:       if (deltab>1) deltab--;          redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_F12:       deltab++;                        redraw=1; 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_RGB | 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
//-----------------------------------------------------------------------------