//-----------------------------------------------------------------------------
// Velmi jednoduchy prohlizec souboru typu DXF upraveny pro zobrazeni
// zakladnich 3D entit - 3DFACE
//
// Autor: Pavel Tisnovsky
//
//-----------------------------------------------------------------------------

#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>
#include <ctype.h>

#define WINDOW_TITLE    "simple DXF 3D viewer"  // titulek okna
#define WINDOW_WIDTH    800                     // pocatecni velikost okna
#define WINDOW_HEIGHT   600

#define MAX_LINE_LENGTH         1024
#define MAX_TEXT_LENGTH         256

void callback_begin_header(char *input);
void callback_end_header(char *input);
void callback_begin_blocks(char *input);
void callback_end_blocks(char *input);
void callback_begin_entities(char *input);
void callback_end_entities(char *input);

void callback_3dface_setcolor(char *input);
void callback_3dface_setlayer(char *input);
void callback_3dface_set_x1(char *input);
void callback_3dface_set_y1(char *input);
void callback_3dface_set_z1(char *input);
void callback_3dface_set_x2(char *input);
void callback_3dface_set_y2(char *input);
void callback_3dface_set_z2(char *input);
void callback_3dface_set_x3(char *input);
void callback_3dface_set_y3(char *input);
void callback_3dface_set_z3(char *input);
void callback_3dface_set_x4(char *input);
void callback_3dface_set_y4(char *input);
void callback_3dface_set_z4(char *input);
void callback_3dface_color(char *input);
void callback_3dface_layer(char *input);
void callback_3dface_x1(char *input);
void callback_3dface_y1(char *input);
void callback_3dface_z1(char *input);
void callback_3dface_x2(char *input);
void callback_3dface_y2(char *input);
void callback_3dface_z2(char *input);
void callback_3dface_x3(char *input);
void callback_3dface_y3(char *input);
void callback_3dface_z3(char *input);
void callback_3dface_x4(char *input);
void callback_3dface_y4(char *input);
void callback_3dface_z4(char *input);
void callback_3dface_store(char *input);

// graficke entity {{{

typedef struct Gfx3Dface {
    int     color;
    int     layer;
    double  x1;
    double  y1;
    double  z1;
    double  x2;
    double  y2;
    double  z2;
    double  x3;
    double  y3;
    double  z3;
    double  x4;
    double  y4;
    double  z4;
} Gfx3Dface;

// }}}
// linearni seznam s entitami {{{

typedef struct Item {
    Gfx3Dface   *entity;
    struct Item *next;          // vazba v linearnim seznamu
} Item;

// }}}

// stavy KA {{{

typedef enum t_dxf_status {
    Unknown,
    Delimiter,
    Section,
    SecName,
    Header,
    Blocks,
    Entities,
    Face,
    FaceLayer,
    FaceColor,
    FaceX1,
    FaceY1,
    FaceZ1,
    FaceX2,
    FaceY2,
    FaceZ2,
    FaceX3,
    FaceY3,
    FaceZ3,
    FaceX4,
    FaceY4,
    FaceZ4,
    End
} t_dxf_status;

// }}}
// prechod mezi stavy KA {{{

typedef struct t_transition {
    t_dxf_status status;
    char         *input;
    t_dxf_status newStatus;
    void     (*fce)(char *input);
} t_transition;

// }}}
// vlastni konfigurace KA {{{

static const t_transition transitions[]={
    {Unknown,    "  0",         Delimiter,     NULL},
    {Delimiter,  "SECTION",     Section,       NULL},
    {Section,    "  2",         SecName,       NULL},
    {SecName,    "HEADER",      Header,        callback_begin_header},
    {SecName,    "BLOCKS",      Blocks,        callback_begin_blocks},
    {SecName,    "ENTITIES",    Entities,      callback_begin_entities},
    {Header,     "  0",         Header,        NULL},
    {Header,     "ENDSEC",      Unknown,       callback_end_header},
    {Blocks,     "  0",         Blocks,        NULL},
    {Blocks,     "ENDSEC",      Unknown,       callback_end_blocks},
    {Entities,   "  0",         Entities,      NULL},
    {Entities,   "ENDSEC",      Unknown,       callback_end_entities},

    {Entities,   "3DFACE",      Face,          NULL},
    {Face,       "  8",         FaceLayer,     callback_3dface_layer},
    {Face,       " 62",         FaceColor,     callback_3dface_color},
    {Face,       " 10",         FaceX1,        callback_3dface_x1},
    {Face,       " 20",         FaceY1,        callback_3dface_y1},
    {Face,       " 30",         FaceZ1,        callback_3dface_z1},
    {Face,       " 11",         FaceX2,        callback_3dface_x2},
    {Face,       " 21",         FaceY2,        callback_3dface_y2},
    {Face,       " 31",         FaceZ2,        callback_3dface_z2},
    {Face,       " 12",         FaceX3,        callback_3dface_x3},
    {Face,       " 22",         FaceY3,        callback_3dface_y3},
    {Face,       " 32",         FaceZ3,        callback_3dface_z3},
    {Face,       " 13",         FaceX4,        callback_3dface_x4},
    {Face,       " 23",         FaceY4,        callback_3dface_y4},
    {Face,       " 33",         FaceZ4,        callback_3dface_z4},
    {Face,       "  0",         Entities,      callback_3dface_store},

    {End,        "",            End,           NULL} // jen ukonceni pro pruchod stavy KA
};

// }}}

// informace o okne {{{

struct {
    int width;
    int height;
} window={WINDOW_WIDTH, WINDOW_HEIGHT};

// }}}
// informace o mysi {{{

enum {                                          // operace, ktere se mohou provadet s mysi:
    ROTATE,                                     // rotace objektu
    TRANSLATE,                                  // posun objektu
} operation=ROTATE;

int   xnew=0, ynew=0, znew=20;                  // soucasna pozice mysi, ze ktere se pocitaji rotace a posuvy
int   xold=0, yold=0, zold=20;                  // minula pozice mysi, ze ktere se pocitaji rotace a posuvy
int   xx, yy, zz;                               // bod, ve kterem se nachazi kurzor mysi

// }}}

// globalni promenne {{{

// parametry, ktere ovlivnuji osvetleni
GLfloat materialAmbient[]={0.4f, 0.4f, 0.4f, 1.0f};  // ambientni slozka barvy materialu
GLfloat materialDiffuse[]={0.8f, 0.4f, 0.4f, 1.0f};  // difuzni slozka barvy materialu
GLfloat materialSpecular[]={1.0f, 1.0f, 1.0f, 1.0f}; // barva odlesku
GLfloat materialShininess[]={50.0f};                 // faktor odlesku
GLfloat light_position1[]={1.0f, 0.0f,  1.0f, 1.0f}; // pozice prvniho svetla
GLfloat light_position2[]={0.0f, 1.0f,100.0f, 1.0f}; // pozice druheho svetla
GLfloat light_color1[]={1.0f, 1.0f, 1.0f};           // barva prvniho svetla
GLfloat light_color2[]={1.0f, 1.0f, 0.0f};           // barva druheho svetla

int    redraw=1;
void   (*special_callback)(char *input)=NULL;
t_dxf_status    status=Unknown; // inicializace KA

Item *p_first;                  // ukazatele v linearnim seznamu
Item *p_last;
Gfx3Dface g_face;               // pomocne entity pro postupne naplnovani
int       colorStyle=1;



// }}}

// initItem() {{{



//-----------------------------------------------------------------------------
// Inicializace linearniho seznamu
//-----------------------------------------------------------------------------
void initItem(void)
{
    p_first=NULL;
    p_last=NULL;
}



// }}}
// addItem() {{{



//-----------------------------------------------------------------------------
// Vlozeni polozky do linearniho seznamu
//-----------------------------------------------------------------------------
void addItem(Gfx3Dface *entity)
{
    Item *item=(Item*)malloc(sizeof(Item));
    item->entity=entity;
    item->next=NULL;            // prvek bude umisten na konci seznamu
    // seznam je prazdny
    if (p_first==NULL) {
        p_first=item;           // prvni i posledni prvek
        p_last=item;            // seznamu jsou shodne
    }
    // pridani polozky do neprazdneho seznamu
    else {
        p_last->next=item;      // zretezeni prvku na konec seznamu
        p_last=item;            // aktualizace ukazatele na posledni prvek
        /*
        Item *p=p_first;
        while (p->next !=NULL)
            p=p->next;
        p->next=item;
        */
    }
}



// }}}

// callback_begin_header() {{{



void callback_begin_header(char *input)
{
    puts("    begin header");
}



// }}}
// callback_end_header() {{{



void callback_end_header(char *input)
{
    puts("    end header");
}



// }}}
// callback_begin_blocks() {{{



void callback_begin_blocks(char *input)
{
    puts("    begin blocks");
}



// }}}
// callback_end_blocks() {{{



void callback_end_blocks(char *input)
{
    puts("    end blocks");
}



// }}}
// callback_begin_entities() {{{



void callback_begin_entities(char *input)
{
    puts("    begin entities");
}



// }}}
// callback_end_entities() {{{



void callback_end_entities(char *input)
{
    puts("    end entities");
}



// }}}

// callback_3dface_setcolor() {{{



void callback_3dface_setcolor(char *input)
{
    g_face.color=atoi(input);
    special_callback=NULL;
    status=Face;
}



// }}}
// callback_3dface_setlayer() {{{



void callback_3dface_setlayer(char *input)
{
    int i, j;
    // velmi primitivni vypocet hashe
    for (i=0, j=0; input[i]; i++)
        j+=input[i];
    g_face.layer=j;
    special_callback=NULL;
    status=Face;
}



// }}}
// callback_3dface_set_x1() {{{



void callback_3dface_set_x1(char *input)
{
    g_face.x1=atof(input);
    special_callback=NULL;
    status=Face;
}



// }}}
// callback_3dface_set_y1() {{{



void callback_3dface_set_y1(char *input)
{
    g_face.y1=atof(input);
    special_callback=NULL;
    status=Face;
}



// }}}
// callback_3dface_set_z1() {{{



void callback_3dface_set_z1(char *input)
{
    g_face.z1=atof(input);
    special_callback=NULL;
    status=Face;
}



// }}}
// callback_3dface_set_x2() {{{



void callback_3dface_set_x2(char *input)
{
    g_face.x2=atof(input);
    special_callback=NULL;
    status=Face;
}



// }}}
// callback_3dface_set_y2() {{{



void callback_3dface_set_y2(char *input)
{
    g_face.y2=atof(input);
    special_callback=NULL;
    status=Face;
}



// }}}
// callback_3dface_set_z2() {{{



void callback_3dface_set_z2(char *input)
{
    g_face.z2=atof(input);
    special_callback=NULL;
    status=Face;
}



// }}}
// callback_3dface_set_x3() {{{



void callback_3dface_set_x3(char *input)
{
    g_face.x3=atof(input);
    special_callback=NULL;
    status=Face;
}



// }}}
// callback_3dface_set_y3() {{{



void callback_3dface_set_y3(char *input)
{
    g_face.y3=atof(input);
    special_callback=NULL;
    status=Face;
}



// }}}
// callback_3dface_set_z3() {{{



void callback_3dface_set_z3(char *input)
{
    g_face.z3=atof(input);
    special_callback=NULL;
    status=Face;
}



// }}}
// callback_3dface_set_x4() {{{



void callback_3dface_set_x4(char *input)
{
    g_face.x4=atof(input);
    special_callback=NULL;
    status=Face;
}



// }}}
// callback_3dface_set_y4() {{{



void callback_3dface_set_y4(char *input)
{
    g_face.y4=atof(input);
    special_callback=NULL;
    status=Face;
}



// }}}
// callback_3dface_set_z4() {{{



void callback_3dface_set_z4(char *input)
{
    g_face.z4=atof(input);
    special_callback=NULL;
    status=Face;
}



// }}}
// callback_3dface_color() {{{



void callback_3dface_color(char *input)
{
    special_callback=callback_3dface_setcolor;
}



// }}}
// callback_3dface_layer() {{{



void callback_3dface_layer(char *input)
{
    special_callback=callback_3dface_setlayer;
}



// }}}
// callback_3dface_x1() {{{



void callback_3dface_x1(char *input)
{
    special_callback=callback_3dface_set_x1;
}



// }}}
// callback_3dface_y1() {{{



void callback_3dface_y1(char *input)
{
    special_callback=callback_3dface_set_y1;
}



// }}}
// callback_3dface_z1() {{{



void callback_3dface_z1(char *input)
{
    special_callback=callback_3dface_set_z1;
}



// }}}
// callback_3dface_x2() {{{



void callback_3dface_x2(char *input)
{
    special_callback=callback_3dface_set_x2;
}



// }}}
// callback_3dface_y2() {{{



void callback_3dface_y2(char *input)
{
    special_callback=callback_3dface_set_y2;
}



// }}}
// callback_3dface_z2() {{{



void callback_3dface_z2(char *input)
{
    special_callback=callback_3dface_set_z2;
}



// }}}
// callback_3dface_x3() {{{



void callback_3dface_x3(char *input)
{
    special_callback=callback_3dface_set_x3;
}



// }}}
// callback_3dface_y3() {{{



void callback_3dface_y3(char *input)
{
    special_callback=callback_3dface_set_y3;
}



// }}}
// callback_3dface_z3() {{{



void callback_3dface_z3(char *input)
{
    special_callback=callback_3dface_set_z3;
}



// }}}
// callback_3dface_x4() {{{



void callback_3dface_x4(char *input)
{
    special_callback=callback_3dface_set_x4;
}



// }}}
// callback_3dface_y4() {{{



void callback_3dface_y4(char *input)
{
    special_callback=callback_3dface_set_y4;
}



// }}}
// callback_3dface_z4() {{{



void callback_3dface_z4(char *input)
{
    special_callback=callback_3dface_set_z4;
}



// }}}
// callback_3dface_store() {{{



void callback_3dface_store(char *input)
{
    Gfx3Dface *g=(Gfx3Dface *)malloc(sizeof(Gfx3Dface));
    g->x1=g_face.x1;
    g->y1=g_face.y1;
    g->z1=g_face.z1;
    g->x2=g_face.x2;
    g->y2=g_face.y2;
    g->z2=g_face.z2;
    g->x3=g_face.x3;
    g->y3=g_face.y3;
    g->z3=g_face.z3;
    g->x4=g_face.x4;
    g->y4=g_face.y4;
    g->z4=g_face.z4;
    g->color=g_face.color;
    g->layer=g_face.layer;
    addItem(g);
}



// }}}

// doConvert() {{{



// ---------------------------------------------------------------------
// Nacteni obsahu souboru DXF a konverze do internich datovych struktur
// ---------------------------------------------------------------------
int doConvert(char *fileName)
{
#define MIN 1e-10
#define MAX 1e+10
    // makra pro zjisteni maxima a minima
#define SET_MIN(a, min) if ((a)<(min)) (min)=(a)
#define SET_MAX(a, max) if ((a)>(max)) (max)=(a)

    FILE *fin;
    char line[MAX_LINE_LENGTH];
    int  lines=0;
    int  t;
    Item *item;

    double xmin=MAX, xmax=MIN, ymin=MAX, ymax=MIN, zmin=MAX, zmax=MIN;
    double x0, y0, z0;

    fin=fopen(fileName, "rt");                  // otevreni vstupniho souboru
    if (!fin) return 0;
    while (fgets(line, MAX_LINE_LENGTH, fin)) { // projit vsechny radky
        lines++;
        line[strlen(line)-1]=0;                 // oriznout <CR>
        if (special_callback!=NULL) {           // specialni callback funkce -> nacteni atributu
            special_callback(line);
        }
        else {                                  // normalni callback funkce -> prechod KA
            for (t=0; transitions[t].status!=End; t++) {
                if ((status==transitions[t].status) && (!strcmp(transitions[t].input, line))) {
                    if (transitions[t].fce!=NULL) transitions[t].fce(line);
                    status=transitions[t].newStatus;
                    break;
                }
            }
        }
    }
    printf("done %d lines\n", lines);
    fclose(fin);

    // uprava stredu 3D modelu
    item=p_first;
    while (item!=NULL) {
        SET_MIN(item->entity->x1, xmin); SET_MAX(item->entity->x1, xmax);
        SET_MIN(item->entity->x2, xmin); SET_MAX(item->entity->x2, xmax);
        SET_MIN(item->entity->x3, xmin); SET_MAX(item->entity->x3, xmax);
        SET_MIN(item->entity->x4, xmin); SET_MAX(item->entity->x4, xmax);
        SET_MIN(item->entity->y1, ymin); SET_MAX(item->entity->y1, ymax);
        SET_MIN(item->entity->y2, ymin); SET_MAX(item->entity->y2, ymax);
        SET_MIN(item->entity->y3, ymin); SET_MAX(item->entity->y3, ymax);
        SET_MIN(item->entity->y4, ymin); SET_MAX(item->entity->y4, ymax);
        SET_MIN(item->entity->z1, zmin); SET_MAX(item->entity->z1, zmax);
        SET_MIN(item->entity->z2, zmin); SET_MAX(item->entity->z2, zmax);
        SET_MIN(item->entity->z3, zmin); SET_MAX(item->entity->z3, zmax);
        SET_MIN(item->entity->z4, zmin); SET_MAX(item->entity->z4, zmax);
        item=item->next;
    }
    printf("Boundary:   [%f, %f, %f] - [%f, %f, %f]\n", xmin, ymin, zmin, xmax, ymax, zmax);

    // vypocet stredu objektu
    x0=(xmax+xmin)/2.0;
    y0=(ymax+ymin)/2.0;
    z0=(zmax+zmin)/2.0;
    printf("Center:     [%f, %f, %f]\n", x0, y0, z0);

    // vycentrovani obrazku
    item=p_first;
    while (item!=NULL) {
        item->entity->x1-=x0;
        item->entity->x2-=x0;
        item->entity->x3-=x0;
        item->entity->x4-=x0;
        item->entity->y1-=y0;
        item->entity->y2-=y0;
        item->entity->y3-=y0;
        item->entity->y4-=y0;
        item->entity->z1-=z0;
        item->entity->z2-=z0;
        item->entity->z3-=z0;
        item->entity->z4-=z0;
        item=item->next;
    }
    return 1;
}



// }}}

// setColor() {{{



//-----------------------------------------------------------------------------
// nastaveni barvy vykreslovani
//-----------------------------------------------------------------------------
void setColor(int color)
{
    // velmi zjednodusena paleta
    static float palette[][3]={
        {1.0, 1.0, 1.0},
        {0.0, 0.0, 1.0},
        {0.0, 1.0, 0.0},
        {0.0, 1.0, 1.0},
        {1.0, 0.0, 0.0},
        {1.0, 0.0, 1.0},
        {1.0, 1.0, 0.0},
        {0.5, 0.5, 0.5},
    };
    float r, g, b;
    r=palette[color & 0x07][0];
    g=palette[color & 0x07][1];
    b=palette[color & 0x07][2];
    glColor3f(r, g, b);
}



// }}}
// setPerspectiveProjection() {{{



//---------------------------------------------------------------------
// Nastaveni perspektivni projekce
//---------------------------------------------------------------------
void setPerspectiveProjection(void)
{
    glMatrixMode(GL_PROJECTION);                // zacatek modifikace projekcni matice
    glLoadIdentity();                           // vymazani projekcni matice (=identita)
    gluPerspective(70.0, (double)window.width/(double)window.height, 0.1f, 10000.0f);// nastaveni perspektivni kamery
    glMatrixMode(GL_MODELVIEW);                 // bude se menit modelova matice
    glLoadIdentity();                           // nahrat jednotkovou matici
}



// }}}
// drawFace() {{{



//-----------------------------------------------------------------------------
// vykresleni entity 3D face
//-----------------------------------------------------------------------------
void drawFace(Gfx3Dface *g)
{
    if (colorStyle)
        setColor(g->color);
    else
        setColor(g->layer);
    glBegin(GL_TRIANGLES);
        glVertex3d(g->x1, g->y1, g->z1);
        glVertex3d(g->x2, g->y2, g->z2);
        glVertex3d(g->x3, g->y3, g->z3);
        glVertex3d(g->x1, g->y1, g->z1);
        glVertex3d(g->x3, g->y3, g->z3);
        glVertex3d(g->x4, g->y4, g->z4);
    glEnd();
}



// }}}
// redrawDrawing() {{{



//-----------------------------------------------------------------------------
// Prekresleni vektorove kresby
//-----------------------------------------------------------------------------
void redrawDrawing()
{
    Item *item=p_first;

    // projit celym seznamem a aplikovat v nem ulozene prikazy
    while (item!=NULL) {
        drawFace(item->entity);
        item=item->next;
    }
}



// }}}

// onInit() {{{



//-----------------------------------------------------------------------------
// Funkce volana pro inicializaci vykreslovani
//-----------------------------------------------------------------------------
void onInit(void)
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);       // barva pozadi obrazku
    glClearDepth(1.0f);                         // implicitni hloubka ulozena v pameti hloubky
    glDepthFunc(GL_LESS);                       // funkce pro testovani fragmentu
    glShadeModel(GL_SMOOTH);                    // nastaveni stinovaciho rezimu
    glPolygonMode(GL_FRONT, GL_FILL);           // nastaveni rezimu vykresleni modelu
    glPolygonMode(GL_BACK, GL_FILL);            // jak pro predni tak pro zadni steny
    glDisable(GL_CULL_FACE);                    // zadne hrany ani steny se nebudou odstranovat
    glMaterialfv(GL_FRONT, GL_AMBIENT, materialAmbient);    // nastaveni ambientni slozky barvy materialu
    glMaterialfv(GL_FRONT, GL_DIFFUSE, materialDiffuse);    // nastaveni difuzni slozky barvy materialu
    glMaterialfv(GL_FRONT, GL_SPECULAR, materialSpecular);  // nastaveni barvy odlesku
    glMaterialfv(GL_FRONT, GL_SHININESS, materialShininess);// nastaveni faktoru odlesku
    glLightfv(GL_LIGHT0, GL_POSITION, light_position1);     // nastaveni pozice prvniho svetla
    glLightfv(GL_LIGHT1, GL_POSITION, light_position2);     // nastaveni pozice druheho svetla
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_color1);         // nastaveni barvy prvniho svetla
    glLightfv(GL_LIGHT1, GL_DIFFUSE, light_color2);         // nastaveni barvy druheho svetla
    glEnable(GL_LIGHTING);                      // globalni povoleni stinovani
    glEnable(GL_LIGHT0);                        // povoleni prvniho svetla
    glEnable(GL_LIGHT1);                        // povoleni prvniho svetla
}



// }}}
// onResize() {{{



//-----------------------------------------------------------------------------
// 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
    window.width=w;
    window.height=h;
}



// }}}
// onDisplay() {{{



//-----------------------------------------------------------------------------
// Tato callback funkce je zavolana pri kazdem prekresleni okna
//-----------------------------------------------------------------------------
void onDisplay(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // vymazani vsech bitovych rovin barvoveho bufferu
    setPerspectiveProjection();                 // nastaveni perspektivni kamery
    glTranslatef(0.0f, 0.0f, -50.0f);           // posun objektu dale od kamery
    glTranslatef(0.0f, 0.0f, znew);             // priblizeni ci vzdaleni objektu podle pohybu kurzoru mysi
    glRotatef(ynew, 1.0f, 0.0f, 0.0f);          // rotace objektu podle pohybu kurzoru mysi
    glRotatef(xnew, 0.0f, 1.0f, 0.0f);
 //   glScalef(0.1, 0.1, 0.1);
    //glPushMatrix();                             // ulozeni matice na zasobnik a zmena pozice druheho svetla
    //glLightfv(GL_LIGHT1, GL_POSITION, light_position2);
    //glPopMatrix();
    redrawDrawing();                         // vykresleni objektu
    glFlush();                                  // provedeni a vykresleni vsech zmen
    glutSwapBuffers();                          // a prohozeni predniho a zadniho bufferu
}



// }}}
// onKeyboard() {{{



//-----------------------------------------------------------------------------
// 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') { 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 'l': colorStyle=0; glutPostRedisplay(); break; // barvy podle hashe hladiny
        case 'c': colorStyle=1; glutPostRedisplay(); break; // barvy podle informaci z DXF
        case '1': glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); glutPostRedisplay(); break;
        case '2': glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);  glutPostRedisplay(); break;
        case '3': glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);  glutPostRedisplay(); break;
        case '4': glEnable(GL_DEPTH_TEST);                    glutPostRedisplay(); break;
        case '5': glDisable(GL_DEPTH_TEST);                   glutPostRedisplay(); break;
        case '6': glEnable(GL_LIGHTING);                      glutPostRedisplay(); break;
        case '7': glDisable(GL_LIGHTING);                     glutPostRedisplay(); break;
        default:           break;
    }
}
#ifdef __BORLANDC__
#pragma option -w+par
#endif



// }}}
// onSpecial() {{{



//-----------------------------------------------------------------------------
// 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 kresby a zmena meritka
    switch (key) {
        /*
        case GLUT_KEY_LEFT:      xpos-=25;   redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_RIGHT:     xpos+=25;   redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_UP:        ypos+=25;   redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_DOWN:      ypos-=25;   redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_PAGE_UP:   scale*=1.1; redraw=1; glutPostRedisplay(); break;
        case GLUT_KEY_PAGE_DOWN: scale/=1.1; redraw=1; glutPostRedisplay(); break;*/
        default:                                                                                 break;
    }
}
#ifdef __BORLANDC__
#pragma option -w+par
#endif



// }}}
// onMouse() {{{



//-----------------------------------------------------------------------------
// Tato callback funkce je zavolana pri stlaceni ci pusteni tlacitka mysi
//-----------------------------------------------------------------------------
void onMouse(int button, int state, int x, int y)
{
    if (button==GLUT_LEFT_BUTTON) {             // pri zmene stavu leveho tlacitka
        operation=ROTATE;
        if (state==GLUT_DOWN) {                 // pri stlaceni tlacitka
            xx=x;                               // zapamatovat pozici kurzoru mysi
            yy=y;
        }
        else {                                  // pri pusteni tlacitka
            xold=xnew;                          // zapamatovat novy pocatek
            yold=ynew;
        }
        glutPostRedisplay();                    // prekresleni sceny
    }
    if (button==GLUT_RIGHT_BUTTON) {
        operation=TRANSLATE;
        if (state==GLUT_DOWN) zz=y;             // pri stlaceni tlacitka zapamatovat polohu kurzoru mysi
        else zold=znew;                         // pri pusteni tlacitka zapamatovat novy pocatek
        glutPostRedisplay();                    // prekresleni sceny
    }
}



// }}}
// onMouseMotion() {{{



//-----------------------------------------------------------------------------
// Tato callback funkce je zavolana pri posunu kurzoru mysi
//-----------------------------------------------------------------------------
void onMouseMotion(int x, int y)
{
     switch (operation) {
        case ROTATE:                            // stav rotace objektu
            xnew=xold+x-xx;                     // vypocitat novou pozici
            ynew=yold+y-yy;
            glutPostRedisplay();                // a prekreslit scenu
            break;
        case TRANSLATE:                         // stav priblizeni/oddaleni objektu
            znew=zold+y-zz;                     // vypocitat novou pozici
            glutPostRedisplay();                // a prekreslit scenu
            break;
    }
}



// }}}

// main() {{{



//-----------------------------------------------------------------------------
// Hlavni funkce konzolove aplikace
//-----------------------------------------------------------------------------
int main(int argc, char **argv)
{
    glutInit(&argc, argv);                      // inicializace knihovny GLUT
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
    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
    glutMouseFunc(onMouse);                     // funkce volana pri stlaceni tlacitka mysi
    glutMotionFunc(onMouseMotion);              // funkce volana pri posunu mysi
    onInit();                                   // inicializace vykreslovani
    if (!doConvert(argv[1])) {                  // nacteni celeho souboru s kresbou
        puts("Nacteni souboru selhalo!\nUkonceni prohlizece...\n");
        return 0;
    }
    glutMainLoop();                             // nekonecna smycka, kde se volaji zaregistrovane funkce
    return 0;                                   // navratova hodnota vracena operacnimu systemu
}



// }}}

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

// vim: foldmethod=marker