//-----------------------------------------------------------------------------
// Fraktaly v pocitacove grafice
// Autor: Pavel Tisnovsky
//
// Vykresleni trojrozmernych fraktalnich obrazcu pomoci algoritmu pro tvorbu
// systemu iterovanych funkci IFS. Pomoci mysi je mozne vytvorenym trojrozmernym
// modelem natacet a pohybovat s nim. Klavesove zkratky urcene k ovladani
// tohoto programu jsou popsany primo v clanku.
// Ukonceni aplikace se provede klavesou [Esc] nebo klavesou [Q].
//-----------------------------------------------------------------------------
#ifdef __BORLANDC__
#include <windows.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <GL/glut.h>

#define SURFEL_LIST_FLOAT   1
#define FONT          GLUT_BITMAP_8_BY_13
#define WINDOW_WIDTH  620
#define WINDOW_HEIGHT 460
#define WINDOW_LEFT   10
#define WINDOW_TOP    10
#define WINDOW_TITLE  "Fraktaly 40.1 - trojrozmerne IFS    author: Pavel Tisnovsky"

#define MIN(A,B)    ((A)<(B))?(A):(B)
#define MAX(A,B)    ((A)>(B))?(A):(B)

#define PI          3.14159279
#define PI2         PI*2.0

#define MINX        0.0
#define MAXX        100.0
#define MINY        0.0
#define MAXY        100.0
#define MINZ        0.0
#define MAXZ        100.0
#define ORIGIN_X   (MINX+MAXX)/2.0
#define ORIGIN_Y   (MINY+MAXY)/2.0
#define ORIGIN_Z   (MINZ+MAXZ)/2.0
#define RANGE_X    (MAXX-MINX)
#define RANGE_Y    (MAXY-MINY)
#define RANGE_Z    (MAXZ-MINZ)
#define RADIUS_X   (RANGE_X)/2.0
#define RADIUS_Y   (RANGE_Y)/2.0
#define RADIUS_Z   (RANGE_Z)/2.0
#define RADIUS_T   16

#ifdef HOTKEYS
#define _ "&"
#else
#define _
#endif

#ifdef nil
#undef nil
#endif

#define nil NULL

#ifndef __cplusplus

#ifndef true
#define true 1
#endif

#ifndef false
#define false 0
#endif

#endif

// typedefs {{{



typedef struct  TagGpeMenu {
    char       *caption;
    int         id;
    struct      TagGpeMenu *subMenu;
} GpeMenu;

typedef struct  Mouse {
    int         xnew, ynew, znew;
    int         xold, yold, zold;
    int         x1,   y1,   z1;
    int         status;
    int         xtran0, ytran0;
    int         xtran1, ytran1;
    int         xtran2, ytran2;
} Mouse;

typedef struct  View {
    float       fov;
    float       nearPlane;
    float       farPlane;
    int         windowWidth;
    int         windowHeight;
    int         surfelsSize;
    int         help;
    int         info;
    int         axis;
    int         shading;
    int         zBuffer;
} View;

typedef struct  Object {
    int         type;
    int         size;
    float       colorR;
    float       colorG;
    float       colorB;
    float       alpha;
    int         blending;
    int         surfelList;
} Object;

typedef struct  ScrollBar{
    int         xmin;
    int         xmax;
    int         ymin;
    int         ymax;
    int         position;
} Scrollbar;

typedef struct  ButtonStruct {
    void        (*callback)(void);
    int         menu;
    int         xmin;
    int         xmax;
    int         ymin;
    int         ymax;
} ButtonStruct;

typedef enum Commands {
    CommandNone=0,
    CommandModelPyramid=0,
    CommandModelTetrahedron1,
    CommandModelTetrahedron2,
    CommandModelHexahedron,
    CommandModelCube,
    CommandModelOctahedron,
    CommandModelDuodecahedron,
    CommandModelFern,
    CommandModelWing,
    CommandModelLeaf,
    CommandModelBush,
    CommandModelFlash,
    CommandModelTower,
    CommandModelArtefact1,
    CommandModelArtefact2,
    CommandModelArtefact3,
    CommandModelArtefact4,
    CommandModelArtefact5,
    CommandModelArtefact6,
    CommandModelArtefact7,
    CommandSurfelCount100,
    CommandSurfelCount400,
    CommandSurfelCount625,
    CommandSurfelCount2500,
    CommandSurfelCount10000,
    CommandSurfelCount22500,
    CommandSurfelCount40000,
    CommandSurfelCount62500,
    CommandSurfelSize1,
    CommandSurfelSize2,
    CommandSurfelSize3,
    CommandSurfelSize4,
    CommandSurfelSize5,
    CommandSurfelSize6,
    CommandSurfelSize7,
    CommandSurfelSize8,
    CommandSurfelSize9,
    CommandBlendingEnable,
    CommandBlendingDisable,
    CommandBlending01,
    CommandBlending02,
    CommandBlending03,
    CommandBlending04,
    CommandBlending05,
    CommandBlending06,
    CommandBlending07,
    CommandBlending08,
    CommandBlending09,
    CommandBlending10,
    CommandSavePositions,
    CommandSavePositionsSize,
    CommandShadingNone,
    CommandShadingX,
    CommandShadingY,
    CommandShadingZ,
    CommandShadingFog,
    CommandZBufferEnable,
    CommandZBufferDisable,
    CommandRedraw,
    CommandShowAxis,
    CommandHideAxis,
    CommandShowHelp,
    CommandHideHelp,
    CommandShowInfo,
    CommandHideInfo,
    CommandViewOriginal,
    CommandViewTop,
    CommandViewBottom,
    CommandViewLeft,
    CommandViewRight,
    CommandSettingsReset,
    CommandSettingsLoad,
    CommandSettingsSave,
    CommandSettingsSurfelsCount,
    CommandSettingsModelForeground,
    CommandSettingsModelBackground,
    CommandSettingsFogColor,
    CommandSettingsAxesColor,
    CommandSettingsActiveItemColor,
    CommandSettingsHelpForeground,
    CommandSettingsHelpBackground,
    CommandSettingsInfoForeground,
    CommandSettingsInfoBackground,
    CommandSettingsScrollBarColor,
    CommandQuitYes,
    CommandQuitNo
} MenuCommands;

typedef struct Vector3f {
    float x;
    float y;
    float z;
} Vector3f;

typedef struct      SurfelF {
    Vector3f        position;
    Vector3f        normal;
    unsigned char   size;
    unsigned int    color;
} SurfelF;

typedef struct      SurfelList {
    int             type;
    int             itemsCount;
    int             currentItem;
    union {
        SurfelF         *itemsF;
    } items;
} SurfelList;



// }}}

// function headers {{{



void     confInit(View *view, Scrollbar *scrollbar, Mouse *mouse, int *settings, int *activeItem);
int      inButton(const ButtonStruct buttons[], const int i, const int x, const int y);
int      inMainMenuButton(int x, int y);
int      inScrollbar(Scrollbar *s, int x, int y);
void     setMenuForButton(const ButtonStruct buttons[], const int x, const int y);
void     setMenuForMainButton(int x, int y);
void     processScrollbars(int x, int y);
void     processLeftMouseButton(const ButtonStruct buttons[], const int state, const int x, const int y);
void     processRightMouseButton(int state, int x, int y);
GLfloat *getAxisColor(void);
GLfloat *getHelpColor(void);
GLfloat *getInfoColor(void);
GLfloat *getModelColor(void);
GLfloat *getActiveItemColor(void);
GLfloat *getScrollBarColor(void);
GLfloat *getHelpBackground(void);
GLfloat *getInfoBackground(void);
GLfloat *getModelBackground(void);
GLfloat *getFogColor(void);
void     setModelAlpha(GLfloat alpha);
void     getCurrentColor(GLfloat *r, GLfloat *g, GLfloat *b, GLfloat *a);
void     setCurrentColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
void     settingsReset(void);
void     settingsLoad(void);
void     settingsSave(void);
float    arandom(float max);
void     objectInit(Object *o);
void     objectCreate(Object *o, int type, int grid);
void     objectDestroy(Object *o);
void     objectSizeMove(Object *o);
void     objectSave(Object *o);
void     objectSaveSize(Object *o, View *v);
void     drawObject(Object *o, View *v);
void     drawObjectNoShading(Object *o);
void     drawObjectXShading(Object *o);
void     drawObjectYShading(Object *o);
void     drawObjectZShading(Object *o);
void     drawObjectFogShading(Object *o);
void     drawString(char *string, int x, int y, int z);
void     drawAxis(void);
void     drawScrollbar(View *v, Scrollbar *s);
void     drawInfo(Object *o, View *v, char *objectType, int activeItem);
void     drawSurfelsInfo(Object *o, View *v, Mouse *m, int activeItem);
void     drawColorScrollbars(View *v, int settings, int activeItem);
void     drawHelp(View *v);
void     drawMainMenu(View *v, int activeItem);
void     onInit(void);
void     onResize(int w, int h);
void     onDisplay(void);
void     onKeyboard(unsigned char key, int x, int y);
void     onSpecialKeyboard(int key, int x, int y);
void     onMouseButton(int button, int state, int x, int y);
void     onMouseMotion(int x, int y);
void     onPassiveMotion(int x, int y);
void     onMenu(int command);
void     mouseInit(Mouse *m);
void     viewInit(View *v);
void     updateScrollbar(Scrollbar *s, int x, int y);
void     buttonsInit(void);
void     buttonObjectClick(void);
void     buttonSurfelsSizeClick(void);
void     buttonShadingClick(void);
void     buttonZBufferClick(void);
void     buttonSurfelsCountClick(void);
void     buttonBlendingClick(void);
void     buttonSettingsClick(void);
void     createMenu(void);
int      surfelInit(void);
int      surfelListInit(int type);
int      surfelListDone(int surfelList);
int      surfelListAlloc(int surfelList, int itemCount);
int      surfelListDestroy(int surfelList);
int      surfelListSetSurfelF(int surfelList, SurfelF *surfel);
int      surfelListGetSurfelF(int surfelList, SurfelF *surfel);
int      surfelListFirst(int surfelList);
int      surfelListNext(int surfelList);
int      surfelListIsLast(int surfelList);
int      surfelGetCount(int surfelList);
int      surfelListSaveP(int surfelList, const char * fileName);
int      surfelListSavePS(int surfelList, const char * fileName);
int      gpeCreateMenuFromData(GpeMenu *menu, void (*f)(int command));
int      gpeCreateMenu(GpeMenu *menu, void (*f)(int command));
int      main(int argc, char *argv[]);



// }}}

// variables {{{



Object    o;
int       modelMenu;
int       surfelMenu;
int       shadingMenu;
int       zBufferMenu;
int       surfelCountMenu;
int       blendingMenu;
int       settingsMenu;
int       mainMenu;
const int CommandModelFirst=CommandModelPyramid;
const int CommandModelLast=CommandModelArtefact7;
static Mouse     m;
static View      v;
static Scrollbar s={230, 480, 55, 68, 100};
static float     rotX=0.0f;
static float     rotY=0.0f;
static float     rotXd=0.0f;
static float     rotYd=0.0f;
static int       settings=CommandSettingsSurfelsCount;
static int       activeItem=0;
View      *v_conf=nil;
Scrollbar *s_conf=nil;
Mouse     *m_conf=nil;
int       *set=nil;
int       *active=0;
GLfloat   axisColor[4];
GLfloat   helpColor[4];
GLfloat   infoColor[4];
GLfloat   modelColor[4];
GLfloat   scrollBarColor[4];
GLfloat   activeItemColor[4];
GLfloat   helpBackground[4];
GLfloat   infoBackground[4];
GLfloat   modelBackground[4];
GLfloat   fogColor[4];



// }}}
// ButtonStruct buttons[] {{{



ButtonStruct buttons[]={
    {buttonObjectClick,      0,  10, 200, 70, 85},
    {buttonSurfelsSizeClick, 0,  10, 200, 55, 70},
    {buttonShadingClick,     0,  10, 200, 40, 55},
    {buttonZBufferClick,     0,  10, 200, 25, 40},
    {buttonSurfelsCountClick,0, 220, 556, 40, 54},
    {buttonBlendingClick,    0,  10, 200, 10, 25},
    {buttonSettingsClick,    0, 230, 556, 69, 85},
    {nil,                    0,   0,   0,  0,  0}
};



// }}}
// char *modelString() {{{



char *modelString[]={
    "Pyramid",
    "Tetrahedron1",
    "Tetrahedron2",
    "Hexahedron",
    "Cube",
    "Octahedron",
    "Duodecahedron",
    "Fern",
    "Wing",
    "Leaf",
    "Bush",
    "Flash",
    "Tower",
    "Artefact 1",
    "Artefact 2",
    "Artefact 3",
    "Artefact 4",
    "Artefact 5",
    "Artefact 6",
    "Artefact 7"
};



// }}}
// char *shadingString[] {{{



char *shadingString[]={
    "none",
    "X-axis",
    "Y-axis",
    "Z-axis",
    "fog"
};



// }}}
// menuModel1[] {{{



// ----------------------------------------------------------------------------
// submenu definition
// ----------------------------------------------------------------------------
GpeMenu menuModel1[]={
    {_"Pyramid",                        CommandModelPyramid,            nil},
    {_"Tetrahedron 1",                  CommandModelTetrahedron1,       nil},
    {"Tetrahedron "_"2",                CommandModelTetrahedron2,       nil},
    {_"Hexahedron",                     CommandModelHexahedron,         nil},
    {_"Cube",                           CommandModelCube,               nil},
    {_"Octahedron",                     CommandModelOctahedron,         nil},
    {_"Duodecahedron",                  CommandModelDuodecahedron,      nil},
    {nil,                               CommandNone,                    nil}
};



// }}}
// menuModel2[] {{{



// ----------------------------------------------------------------------------
// submenu definition
// ----------------------------------------------------------------------------
GpeMenu menuModel2[]={
    {_"Fern",                           CommandModelFern,               nil},
    {_"Wing",                           CommandModelWing,               nil},
    {_"Leaf",                           CommandModelLeaf,               nil},
    {_"Bush",                           CommandModelBush,               nil},
    {"Fl"_"ash",                        CommandModelFlash,              nil},
    {nil,                               CommandNone,                    nil}
};



// }}}
// menuModel3[] {{{



// ----------------------------------------------------------------------------
// submenu definition
// ----------------------------------------------------------------------------
GpeMenu menuModel3[]={
    {_"Tower",                          CommandModelTower,              nil},
    {"Artefact "_"1",                   CommandModelArtefact1,          nil},
    {"Artefact "_"2",                   CommandModelArtefact2,          nil},
    {"Artefact "_"3",                   CommandModelArtefact3,          nil},
    {"Artefact "_"4",                   CommandModelArtefact4,          nil},
    {"Artefact "_"5",                   CommandModelArtefact5,          nil},
    {"Artefact "_"6",                   CommandModelArtefact6,          nil},
    {"Artefact "_"7",                   CommandModelArtefact7,          nil},
    {nil,                               CommandNone,                    nil}
};



// }}}
// menuModel[] {{{



// ----------------------------------------------------------------------------
// submenu definition
// ----------------------------------------------------------------------------
GpeMenu menuModel[]={
    {_"Geometric models",               CommandNone,                    menuModel1},
    {_"Nature models",                  CommandNone,                    menuModel2},
    {_"Artifical structures",           CommandNone,                    menuModel3},
    {nil,                               CommandNone,                    nil}
};



// }}}
// menuSurfelCount[] {{{



// ----------------------------------------------------------------------------
// submenu definition
// ----------------------------------------------------------------------------
GpeMenu menuSurfelCount[]={
    {_"100",                            CommandSurfelCount100,          nil},
    {_"400",                            CommandSurfelCount400,          nil},
    {_"625",                            CommandSurfelCount625,          nil},
    {_"2500",                           CommandSurfelCount2500,         nil},
    {"1"_"0000",                        CommandSurfelCount10000,        nil},
    {"22"_"500",                        CommandSurfelCount22500,        nil},
    {"40000",                           CommandSurfelCount40000,        nil},
    {_"62500",                          CommandSurfelCount62500,        nil},
    {nil,                               CommandNone,                    nil}
};



// }}}
// menuSurfelSize[] {{{



// ----------------------------------------------------------------------------
// submenu definition
// ----------------------------------------------------------------------------
GpeMenu menuSurfelSize[]={
    {"Surfel size "_"1",                CommandSurfelSize1,             nil},
    {"Surfel size "_"2",                CommandSurfelSize2,             nil},
    {"Surfel size "_"3",                CommandSurfelSize3,             nil},
    {"Surfel size "_"4",                CommandSurfelSize4,             nil},
    {"Surfel size "_"5",                CommandSurfelSize5,             nil},
    {"Surfel size "_"6",                CommandSurfelSize6,             nil},
    {"Surfel size "_"7",                CommandSurfelSize7,             nil},
    {"Surfel size "_"8",                CommandSurfelSize8,             nil},
    {"Surfel size "_"9",                CommandSurfelSize9,             nil},
    {nil,                               CommandNone,                    nil}
};



// }}}
// menuShading[] {{{



// ----------------------------------------------------------------------------
// submenu definition
// ----------------------------------------------------------------------------
GpeMenu menuShading[]={
    {_"None\tN",                        CommandShadingNone,             nil},
    {"Along "_"X-axis\tX",              CommandShadingX,                nil},
    {"Along "_"Y-axis\tY",              CommandShadingY,                nil},
    {"Along "_"Z-axis\tZ",              CommandShadingZ,                nil},
    {"F"_"og\tO",                       CommandShadingFog,              nil},
    {nil,                               CommandNone,                    nil}
};



// }}}
// menuBlendFactor[] {{{



// ----------------------------------------------------------------------------
// submenu definition
// ----------------------------------------------------------------------------
GpeMenu menuBlendFactor[]={
    {"0."_"1\tF1",                      CommandBlending01,              nil},
    {"0."_"2\tF2",                      CommandBlending02,              nil},
    {"0."_"3\tF3",                      CommandBlending03,              nil},
    {"0."_"4\tF4",                      CommandBlending04,              nil},
    {"0."_"5\tF5",                      CommandBlending05,              nil},
    {"0."_"6\tF6",                      CommandBlending06,              nil},
    {"0."_"7\tF7",                      CommandBlending07,              nil},
    {"0."_"8\tF8",                      CommandBlending08,              nil},
    {"0."_"9\tF9",                      CommandBlending09,              nil},
    {"1."_"0\tF10",                     CommandBlending10,              nil},
    {nil,                               CommandNone,                    nil}
};



// }}}
// menuBlending[] {{{



// ----------------------------------------------------------------------------
// submenu definition
// ----------------------------------------------------------------------------
GpeMenu menuBlending[]={
    {_"Enable blending",                CommandBlendingEnable,          nil},
    {_"Disable blending",               CommandBlendingDisable,         nil},
    {"Blend "_"factor",                 CommandNone,                    menuBlendFactor},
    {nil,                               CommandNone,                    nil}
};



// }}}
// menuZBuffer[] {{{



// ----------------------------------------------------------------------------
// submenu definition
// ----------------------------------------------------------------------------
GpeMenu menuZBuffer[]={
    {_"Enable depth buffer",            CommandZBufferEnable,           nil},
    {_"Disable depth buffer",           CommandZBufferDisable,          nil},
    {nil,                               CommandNone,                    nil}
};



// }}}
// menuSave[] {{{



// ----------------------------------------------------------------------------
// submenu definition
// ----------------------------------------------------------------------------
GpeMenu menuSave[]={
    {_"Position only\tP",               CommandSavePositions,           nil},
    {"Position and "_"size\tS",         CommandSavePositionsSize,       nil},
    {nil,                               CommandNone,                    nil}
};



// }}}
// menuAxis[] {{{



// ----------------------------------------------------------------------------
// submenu definition
// ----------------------------------------------------------------------------
GpeMenu menuAxis[]={
    {_"Show axes",                      CommandShowAxis,                nil},
    {_"Hide axes",                      CommandHideAxis,                nil},
    {nil,                               CommandNone,                    nil}
};



// }}}
// menuHelp[] {{{



// ----------------------------------------------------------------------------
// submenu definition
// ----------------------------------------------------------------------------
GpeMenu menuHelp[]={
    {_"Show help",                      CommandShowHelp,                nil},
    {_"Hide help",                      CommandHideHelp,                nil},
    {nil,                               CommandNone,                    nil}
};



// }}}
// menuInfo[] {{{



// ----------------------------------------------------------------------------
// submenu definition
// ----------------------------------------------------------------------------
GpeMenu menuInfo[]={
    {_"Show info",                      CommandShowInfo,                nil},
    {_"Hide info",                      CommandHideInfo,                nil},
    {nil,                               CommandNone,                    nil}
};



// }}}
// menuQuit[] {{{



// ----------------------------------------------------------------------------
// submenu definition
// ----------------------------------------------------------------------------
GpeMenu menuQuit[]={
    {_"Quit now",                       CommandQuitYes,                 nil},
    {_"Not yet",                        CommandQuitNo,                  nil},
    {nil,                               CommandNone,                    nil}
};



// }}}
// menuView[] {{{



// ----------------------------------------------------------------------------
// submenu definition
// ----------------------------------------------------------------------------
GpeMenu menuView[]={
    {_"Original view\tInsert",          CommandViewOriginal,            nil},
    {"View from "_"top\tPage Up",       CommandViewTop,                 nil},
    {"View from "_"bottom\tPage Down",  CommandViewBottom,              nil},
    {"View from "_"left\tHome",         CommandViewLeft,                nil},
    {"View from "_"right\tEnd",         CommandViewRight,               nil},
    {nil,                               CommandNone,                    nil}
};



// }}}
// menuSettings[] {{{



// ----------------------------------------------------------------------------
// submenu definition
// ----------------------------------------------------------------------------
GpeMenu menuSettings[]={
    {_"Surfels count",                  CommandSettingsSurfelsCount,    nil},
    {"",                                CommandNone,                    nil},
    {_"Model foreground",               CommandSettingsModelForeground, nil},
    {"Model "_"background",             CommandSettingsModelBackground, nil},
    {_"Fog color",                      CommandSettingsFogColor,        nil},
    {_"Axes color",                     CommandSettingsAxesColor,       nil},
    {"",                                CommandNone,                    nil},
    {"Active item color",               CommandSettingsActiveItemColor, nil},
    {_"Help foreground",                CommandSettingsHelpForeground,  nil},
    {"H"_"elp background",              CommandSettingsHelpBackground,  nil},
    {_"Info foreground",                CommandSettingsInfoForeground,  nil},
    {"I"_"nfo background",              CommandSettingsInfoBackground,  nil},
    {"",                                CommandNone,                    nil},
    {_"Reset settings\t^R",             CommandSettingsReset,           nil},
    {_"Load settings\t^L",              CommandSettingsLoad,            nil},
    {_"Save settings\t^S",              CommandSettingsSave,            nil},
    {nil,                               CommandNone,                    nil}
};



// }}}
// menuMain[] {{{



// ----------------------------------------------------------------------------
// main menu definition
// ----------------------------------------------------------------------------
GpeMenu menuMain[]={
    {_"Model type",                     CommandNone,                    menuModel},
    {"Surfel count\tC",                 CommandNone,                    menuSurfelCount},
    {_"Redraw\tR",                      CommandRedraw,                  nil},
    {_"Save",                           CommandNone,                    menuSave},
    {"",                                CommandNone,                    nil},
    {_"View",                           CommandNone,                    menuView},
    {"S"_"urfel size\t1-9",             CommandNone,                    menuSurfelSize},
    {"Sha"_"ding",                      CommandNone,                    menuShading},
    {_"Blending\tB",                    CommandNone,                    menuBlending},
    {_"Depth buffer\tD",                CommandNone,                    menuZBuffer},
    {"",                                CommandNone,                    nil},
    {_"Axes\tA",                        CommandNone,                    menuAxis},
    {_"Help\tH",                        CommandNone,                    menuHelp},
    {_"Info\tI",                        CommandNone,                    menuInfo},
    {"",                                CommandNone,                    nil},
    {"S"_"ettings",                     CommandNone,                    menuSettings},
    {_"Quit\tQ",                        CommandNone,                    menuQuit},
    {nil,                               CommandNone,                    nil}
};



// }}}
// ifs_artefact1[][] {{{



float ifs_artefact1[][13]={
    {-0.101858f,-0.150842f, 0.308003f,-0.481334f, 0.166339f,-0.579352f, 0.044036f,-0.514029f, 0.164516f, 0.492382f, 0.146172f, 0.064375f, -0.225623f},
    {-0.022852f, 0.296328f, 0.814079f,-0.746566f,-0.254458f, 0.766551f,-0.297938f, 0.454955f,-0.947957f,-0.212907f, 0.060350f,-1.015431f,  1.000000f}
};



// }}}
// ifs_artefact2[][] {{{



float ifs_artefact2[][13]={
    {-0.071315f, 0.200444f,-0.210993f, 0.308965f,-0.258325f,-0.059292f, 0.008314f,-0.599626f,-0.017553f, 0.058221f, 0.734876f,-0.583363f, -0.267495f},
    {-0.198031f, 0.444311f,-0.710999f, 0.611886f, 0.584716f,-0.338408f,-0.473771f, 0.949758f,-0.500297f,-0.571381f,-0.272282f,-0.087467f,  0.809442f},
    { 0.328285f, 0.149096f,-0.492353f,-0.100800f, 0.207878f, 0.073338f, 0.864173f, 0.175989f, 0.339481f,-0.189086f,-0.053052f, 1.111021f,  0.989044f},
    {-0.501541f,-0.532668f,-0.115721f,-0.198741f, 0.118444f,-0.628877f, 0.263749f, 0.964651f,-0.406249f, 0.474261f, 0.219763f,-0.686276f,  1.000000f}
};



// }}}
// ifs_artefact3[][] {{{



float ifs_artefact3[][13]={
    {-0.149219f,-0.218931f,-0.076128f,-0.176103f, 0.119121f,-0.149684f, 0.491947f,-0.820411f, 0.134988f,-0.109922f,-0.518273f, 0.132760f, -0.073428f},
    { 0.887463f,-0.026903f,-0.067110f,-0.148618f, 0.130967f,-0.538636f, 0.174685f,-0.248019f,-0.193720f,-0.487402f,-0.189343f, 0.704726f,  0.400433f},
    {-0.894439f,-0.336771f, 0.024013f,-0.434810f, 0.081183f,-0.514214f,-0.139469f,-0.340855f, 0.365726f,-0.709481f, 0.089686f, 0.706895f,  0.891415f},
    { 0.075945f,-0.080308f,-0.207685f, 0.178539f,-0.315027f, 0.237405f,-0.052465f, 0.850269f,-0.120275f,-0.672524f, 0.006280f, 0.122273f,  1.000000f}
};



// }}}
// ifs_artefact4[][] {{{



float ifs_artefact4[][13]={
    { 0.364565f, 0.038670f, 0.008728f, 0.394244f, 0.338922f,-0.050364f, 0.005845f,-0.428889f,-0.596382f,-0.004983f, 0.008657f,-1.043205f, -0.010163f},
    {-0.243982f,-0.465263f,-0.424944f, 0.456958f, 0.076742f, 0.739098f,-0.369429f,-0.000908f, 0.412581f,-0.412611f,-0.182578f,-0.889257f,  0.032746f},
    { 0.099827f, 0.140258f,-0.312280f,-0.525080f, 0.185134f, 0.376114f, 0.122140f, 0.772410f, 0.578573f,-0.144551f, 0.014798f,-1.088071f,  0.129276f},
    { 0.059707f, 0.522051f,-0.160214f,-0.187567f, 0.516023f,-0.140507f,-0.005455f, 0.919340f,-0.078583f,-0.526006f,-0.157552f, 0.819254f,  0.207984f},
    { 0.250889f,-0.030707f, 0.890280f,-0.180622f, 0.586237f, 0.306830f,-0.171777f,-1.132464f, 0.513647f,-0.335193f,-0.238801f, 0.232543f,  0.563189f},
    {-0.769092f, 0.011206f, 0.444428f, 0.870842f, 0.082195f,-0.147360f, 0.177503f,-0.338234f,-0.565983f,-0.036628f,-0.578138f, 0.185934f,  0.587542f},
    {-0.009563f,-0.485699f,-0.086817f, 0.690251f, 0.006773f, 0.439292f,-0.104347f, 0.384353f, 0.016006f,-0.476090f,-0.007712f,-0.282015f,  0.873501f},
    {-0.144162f, 0.017065f, 0.848653f,-0.640743f, 0.331367f, 0.063816f, 0.066138f,-0.681175f, 0.316759f,-0.058992f, 0.317047f,-0.268826f,  1.000000f}
};



// }}}
// ifs_artefact5[][] {{{



float ifs_artefact5[][13]={
    {-0.014308f,-0.028272f, 0.916465f, 0.792812f,-0.010415f, 0.421076f,-0.042898f, 0.848069f,-0.033863f,-0.117565f,-0.374037f, 0.320556f, -0.333811f},
    {-0.382406f,-0.083106f, 0.146682f,-0.587406f,-0.294822f, 0.855944f,-0.007971f, 0.041135f,-0.789595f,-0.279347f,-0.068063f, 0.858230f,  0.511887f},
    {-0.467792f, 0.235933f,-0.073547f,-0.870659f,-0.156239f,-0.730864f, 0.192101f, 0.145745f, 0.010314f,-0.370541f,-0.425734f, 0.950115f,  1.000000f}
};



// }}}
// ifs_artefact6[][] {{{



float ifs_artefact6[][13]={
    { 0.088555f,-0.326833f,-0.211715f,-0.475499f, 0.056903f, 0.576230f, 0.288616f, 0.572544f,-0.006163f, 0.624204f,-0.377288f,-0.179581f,-0.441847f},
    {-0.242021f, 0.059674f, 0.805669f,-0.445266f,-0.704437f, 0.107637f,-0.279418f,-0.452460f,-0.096128f,-0.939020f, 0.019171f,-0.526309f, 1.000000f}
};



// }}}
// ifs_artefact7[][] {{{



float ifs_artefact7[][13]={
    { 0.118002f,-0.367313f,-0.082903f, 0.432253f,-0.391844f, 0.012465f,-0.390282f,-0.042140f,-0.519441f,-0.092846f, 0.275579f, 1.331511f,-0.264534f},
    {-0.192128f,-0.237729f, 0.644365f,-0.236597f,-0.622120f, 0.312954f, 0.225223f, 0.411871f,-0.689610f,-0.216094f,-0.382704f,-0.759115f, 1.000000f}
};



// }}}
// ifs_tower[][] {{{



float ifs_tower[][13]={
    {-0.972522f, 0.035970f, 0.055638f, 0.712389f,-0.057478f,-0.836075f,-0.384403f,-0.326122f,-0.035977f, 0.363412f,-0.889874f, 0.681205f,-0.966287f},
    { 0.245092f,-0.498526f,-0.160138f,-0.375638f, 0.473636f, 0.395872f,-0.091942f,-0.649507f, 0.352847f,-0.185108f, 0.234650f, 0.442588f, 0.200000f}
};



// }}}
// ifs_fern[][] {{{



float ifs_fern[][13]={
    { 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.180000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.010000f},
    { 0.850000f, 0.000000f, 0.000000f, 0.000000f, 0.850000f, 0.100000f, 0.000000f,-0.100000f, 0.850000f, 0.000000f, 1.600000f, 0.000000f, 0.850000f},
    { 0.200000f,-0.200000f, 0.000000f, 0.200000f, 0.200000f, 0.000000f, 0.000000f, 0.000000f, 0.300000f, 0.000000f, 0.800000f, 0.000000f, 0.070000f},
    {-0.200000f, 0.200000f, 0.000000f, 0.200000f, 0.200000f, 0.000000f, 0.000000f, 0.000000f, 0.300000f, 0.000000f, 0.800000f, 0.000000f, 0.070000f},
};



// }}}
// ifs_bush[][] {{{



float ifs_bush[][13]={
    {-0.373749f, 0.184692f, 0.105947f,-0.832341f,-0.453663f,-0.152631f, 0.124736f, 0.460184f, 0.197566f,-0.001087f, 0.486855f, 0.378884f,-0.020051f},
    { 0.166209f,-0.133945f,-0.080310f, 0.495707f,-0.797325f,-0.134969f,-0.012847f,-0.015179f, 0.145659f,-0.585965f, 0.021317f, 0.128416f, 0.161809f},
    {-0.648603f, 0.270537f, 0.003552f, 0.794061f,-0.293407f,-0.643705f,-0.003235f,-0.441188f, 0.077220f,-0.173481f, 0.017543f,-0.928678f, 0.506607f},
    { 0.292940f, 0.142934f,-0.216388f, 0.393835f,-0.063308f,-0.879776f,-0.043157f,-0.004582f,-0.916890f, 0.106412f,-0.066155f,-0.670893f, 1.000000f}
};



// }}}
// ifs_wing[][] {{{



float ifs_wing[][13]={
    {-0.863301f, 0.015541f, 0.106781f,-0.233617f, 0.098683f, 0.547759f, 0.401727f,-0.492123f,-0.098135f, 0.414103f,-0.535395f, 0.254419f, -0.496811f},
    {-0.510493f, 0.370321f,-0.285759f, 0.295526f,-0.107999f,-0.794018f,-0.174678f, 0.809436f, 0.719463f, 0.143569f,-0.228981f,-0.054059f,  1.000000f}
};



// }}}
// ifs_leaf[][] {{{



float ifs_leaf[][13]={
    {-0.492737f,-0.010404f, 0.180206f, 0.351519f,-0.530563f, 0.570329f,-0.108801f, 1.057364f,-0.380220f,-0.782360f,-0.081711f,-0.458607f, -0.217750f},
    {-0.856946f,-0.299423f, 0.035138f, 0.708119f,-0.322467f, 0.841126f,-0.041265f, 0.669464f, 0.073766f, 0.198542f, 0.227810f, 0.531253f,  1.000000f}
};



// }}}
// ifs_flash[][] {{{



float ifs_flash[][13]={
    {-0.009669f, 0.031249f, 0.794159f,-0.190141f, 0.035952f, 0.572048f, 0.123748f, 0.045626f, 0.026162f,-0.774558f, 0.123434f,-0.211259f, -0.228614f},
    { 0.067100f, 0.338221f,-0.383701f, 0.483533f,-0.052567f, 0.242058f,-0.618321f,-0.859729f, 0.015527f,-0.642140f,-0.435178f,-0.834466f,  1.000000f}
};



// }}}
// ifs_pyramid[][] {{{



float ifs_pyramid[][13]={
    { 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f, 0.000000f, 1.000000f, 0.000000f,  0.200000f},
    { 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f, 0.500000f, 0.000000f, 0.500000f,  0.200000f},
    { 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f,-0.500000f, 0.000000f, 0.500000f,  0.200000f},
    { 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f,-0.500000f, 0.000000f,-0.500000f,  0.200000f},
    { 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f, 0.500000f, 0.000000f,-0.500000f,  0.200000f}
};



// }}}
// ifs_tetrahedron1[][] {{{



float ifs_tetrahedron1[][13]={
    { 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f, 0.000000f, 0.000000f, 1.000000f,  0.250000f},
    { 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f, 0.000000f, 0.870000f,-0.500000f,  0.250000f},
    { 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f,-0.870000f,-0.500000f,-0.500000f,  0.250000f},
    { 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f, 0.000000f, 0.000000f, 0.000000f, 0.500000f, 0.870000f,-0.500000f,-0.500000f,  0.250000f},
};



// }}}
// ifs_tetrahedron2[][] {{{



float ifs_tetrahedron2[][13]={
    { 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.000000f, 0.000000f, 1.000000f,  0.200000f},
    { 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.000000f, 0.870000f,-0.500000f,  0.200000f},
    { 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f,-0.870000f,-0.500000f,-0.500000f,  0.200000f},
    { 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.870000f,-0.500000f,-0.500000f,  0.200000f},
    { 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.000000f, 0.000000f, 0.000000f,  0.200000f},
};



// }}}
// ifs_hexahedron[][] {{{



float ifs_hexahedron[][13]={
    { 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.000000f, 0.000000f, 0.900000f,  0.200000f},
    { 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.870000f,-0.500000f, 0.000000f,  0.200000f},
    { 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f,-0.870000f,-0.500000f, 0.000000f,  0.200000f},
    { 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.000000f, 1.000000f, 0.000000f,  0.200000f},
    { 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.000000f, 0.000000f, 0.000000f, 0.440000f, 0.000000f, 0.000000f,-0.900000f,  0.200000f},
};



// }}}
// ifs_cube[][] {{{



float ifs_cube[][13]={
    { 0.350000f, 0.000000f, 0.000000f, 0.000000f, 0.350000f, 0.000000f, 0.000000f, 0.000000f, 0.350000f, 1.000000f, 1.000000f, 1.000000f,  0.120000f},
    { 0.350000f, 0.000000f, 0.000000f, 0.000000f, 0.350000f, 0.000000f, 0.000000f, 0.000000f, 0.350000f, 1.000000f, 1.000000f,-1.000000f,  0.130000f},
    { 0.350000f, 0.000000f, 0.000000f, 0.000000f, 0.350000f, 0.000000f, 0.000000f, 0.000000f, 0.350000f, 1.000000f,-1.000000f, 1.000000f,  0.120000f},
    { 0.350000f, 0.000000f, 0.000000f, 0.000000f, 0.350000f, 0.000000f, 0.000000f, 0.000000f, 0.350000f, 1.000000f,-1.000000f,-1.000000f,  0.130000f},
    { 0.350000f, 0.000000f, 0.000000f, 0.000000f, 0.350000f, 0.000000f, 0.000000f, 0.000000f, 0.350000f,-1.000000f, 1.000000f, 1.000000f,  0.120000f},
    { 0.350000f, 0.000000f, 0.000000f, 0.000000f, 0.350000f, 0.000000f, 0.000000f, 0.000000f, 0.350000f,-1.000000f, 1.000000f,-1.000000f,  0.130000f},
    { 0.350000f, 0.000000f, 0.000000f, 0.000000f, 0.350000f, 0.000000f, 0.000000f, 0.000000f, 0.350000f,-1.000000f,-1.000000f, 1.000000f,  0.120000f},
    { 0.350000f, 0.000000f, 0.000000f, 0.000000f, 0.350000f, 0.000000f, 0.000000f, 0.000000f, 0.350000f,-1.000000f,-1.000000f,-1.000000f,  0.130000f},
};



// }}}
// ifs_octahedron[][] {{{



float ifs_octahedron[][13]={
    { 0.400000f, 0.000000f, 0.000000f, 0.000000f, 0.400000f, 0.000000f, 0.000000f, 0.000000f, 0.400000f, 0.000000f, 0.000000f, 1.000000f,  0.170000f},
    { 0.400000f, 0.000000f, 0.000000f, 0.000000f, 0.400000f, 0.000000f, 0.000000f, 0.000000f, 0.400000f, 1.000000f, 0.000000f, 0.000000f,  0.160000f},
    { 0.400000f, 0.000000f, 0.000000f, 0.000000f, 0.400000f, 0.000000f, 0.000000f, 0.000000f, 0.400000f, 0.000000f, 1.000000f, 0.000000f,  0.170000f},
    { 0.400000f, 0.000000f, 0.000000f, 0.000000f, 0.400000f, 0.000000f, 0.000000f, 0.000000f, 0.400000f,-1.000000f, 0.000000f, 0.000000f,  0.170000f},
    { 0.400000f, 0.000000f, 0.000000f, 0.000000f, 0.400000f, 0.000000f, 0.000000f, 0.000000f, 0.400000f, 0.000000f,-1.000000f, 0.000000f,  0.160000f},
    { 0.400000f, 0.000000f, 0.000000f, 0.000000f, 0.400000f, 0.000000f, 0.000000f, 0.000000f, 0.400000f, 0.000000f, 0.000000f,-1.000000f,  0.170000f},
};



// }}}
// ifs_duodecahedron[][] {{{



float ifs_duodecahedron[][13]={
    { 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.000000f, 0.000000f, 0.960000f,  0.090000f},
    { 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.000000f, 0.850000f, 0.430000f,  0.080000f},
    { 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.810000f, 0.260000f, 0.430000f,  0.080000f},
    { 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f,-0.810000f, 0.260000f, 0.430000f,  0.090000f},
    { 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.500000f,-0.690000f, 0.430000f,  0.080000f},
    { 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f,-0.500000f,-0.690000f, 0.430000f,  0.080000f},
    { 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.500000f, 0.690000f,-0.430000f,  0.090000f},
    { 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f,-0.500000f, 0.690000f,-0.430000f,  0.080000f},
    { 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.810000f,-0.260000f,-0.430000f,  0.080000f},
    { 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f,-0.810000f,-0.260000f,-0.430000f,  0.090000f},
    { 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.000000f,-0.850000f,-0.430000f,  0.080000f},
    { 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.000000f, 0.000000f, 0.000000f, 0.280000f, 0.000000f, 0.000000f,-0.960000f,  0.080000f},
};



// }}}
// surfelInit() {{{



int surfelInit(void)
{
    return false;
}



// }}}
// surfelListSaveP() {{{



int surfelListSaveP(int surfelList, const char * fileName)
{
    FILE *fout;
    int  i;
    SurfelList *sl=(SurfelList *)surfelList;
    fout=fopen(fileName, "wt");
    if (fout) {
        fprintf(fout, "pf\n"); // p-position n-normal c-color s-size o-object
        fprintf(fout, "%d\n", sl->itemsCount);
        for (i=0; i<sl->itemsCount; i++)
            fprintf(fout, "%6.3f %6.3f %6.3f\n",
                    sl->items.itemsF[i].position.x,
                    sl->items.itemsF[i].position.y,
                    sl->items.itemsF[i].position.z);
        fclose(fout);
        return true;
    }
    else {
        return false;
    }
}



// }}}
// surfelListSavePS() {{{



int surfelListSavePS(int surfelList, const char * fileName)
{
    FILE *fout;
    int  i;
    SurfelList *sl=(SurfelList *)surfelList;
    fout=fopen(fileName, "wt");
    if (fout) {
        fprintf(fout, "pfsb\n"); // p-position n-normal c-color s-size o-object
        fprintf(fout, "%d\n", sl->itemsCount);
        for (i=0; i<sl->itemsCount; i++)
            fprintf(fout, "%6.3f %6.3f %6.3f %d\n",
                    sl->items.itemsF[i].position.x,
                    sl->items.itemsF[i].position.y,
                    sl->items.itemsF[i].position.z,
                    (int)sl->items.itemsF[i].size);
        fclose(fout);
        return true;
    }
    else {
        return false;
    }
}



// }}}
// surfelListInit() {{{



int surfelListInit(int type)
{
    SurfelList *surfelList;
    switch (type) {
        case SURFEL_LIST_FLOAT:
            surfelList=(SurfelList *)malloc(sizeof(SurfelList));
            if (!surfelList) return 0;
            surfelList->type=SURFEL_LIST_FLOAT;
            surfelList->itemsCount=0;
            surfelList->currentItem=-1;
            surfelList->items.itemsF=nil;
            return (int)surfelList;
        default:
            return false;
    }
}



// }}}
// surfelListDone() {{{



int surfelListDone(int surfelList)
{
    SurfelList *sl=(SurfelList *)surfelList;
    free(sl);
    return true;
}



// }}}
// surfelListDestroy() {{{



int surfelListDestroy(int surfelList)
{
    SurfelList *sl=(SurfelList *)surfelList;
    switch (sl->type) {
        case SURFEL_LIST_FLOAT:
            if (sl->items.itemsF) {
                free(sl->items.itemsF);
                return true;
            }
            else {
                return false;
            }
        default:
            return false;
    }
}



// }}}
// surfelListAlloc() {{{



int surfelListAlloc(int surfelList, int itemCount)
{
    SurfelList *sl=(SurfelList *)surfelList;
    switch (sl->type) {
        case SURFEL_LIST_FLOAT:
            sl->items.itemsF=(SurfelF*)malloc(sizeof(SurfelF)*itemCount);
            sl->itemsCount=itemCount;
            if (sl->items.itemsF) return true;
            else return false;
        default:
            return false;
    }
}



// }}}
// surfelListSetSurfelF() {{{



int surfelListSetSurfelF(int surfelList, SurfelF *surfel)
{
    SurfelList *sl=(SurfelList *)surfelList;
    switch (sl->type) {
        case SURFEL_LIST_FLOAT:
            memcpy(&(sl->items.itemsF[sl->currentItem]), surfel, sizeof(SurfelF));
            return true;
        default:
            return false;
    }
}



// }}}
// surfelListGetSurfelF() {{{



int surfelListGetSurfelF(int surfelList, SurfelF *surfel)
{
    SurfelList *sl=(SurfelList *)surfelList;
    switch (sl->type) {
        case SURFEL_LIST_FLOAT:
            memcpy(surfel, &(sl->items.itemsF[sl->currentItem]), sizeof(SurfelF));
            return true;
        default:
            return false;
    }
}



// }}}
// surfelListFirst() {{{



int surfelListFirst(int surfelList)
{
    SurfelList *sl=(SurfelList *)surfelList;
    if (sl->itemsCount) sl->currentItem=0;
    return true;
}



// }}}
// surfelListNext() {{{



int surfelListNext(int surfelList)
{
    SurfelList *sl=(SurfelList *)surfelList;
    sl->currentItem++;
    return true;
}



// }}}
// surfelListIsLast() {{{



int surfelListIsLast(int surfelList)
{
    SurfelList *sl=(SurfelList *)surfelList;
    return sl->currentItem==sl->itemsCount;
}



// }}}
// surfelGetCount() {{{



int surfelGetCount(int surfelList)
{
    SurfelList *sl=(SurfelList *)surfelList;
    return sl->itemsCount;
}



// }}}
// confInit() {{{



// ----------------------------------------------------------------------------
// This function initializes configuration module for this application.
// ----------------------------------------------------------------------------
void confInit(View *view, Scrollbar *scrollbar, Mouse *mouse, int *settings, int *activeItem)
{
    v_conf=view;
    s_conf=scrollbar;
    m_conf=mouse;
    set=settings;
    active=activeItem;
}



// }}}
// inButton() {{{



// ----------------------------------------------------------------------------
// This function returns "true", if mouse pointer is positioned over some
// button. It returns "false" otherwise.
// ----------------------------------------------------------------------------
int inButton(const ButtonStruct buttons[], const int i, const int x, const int y)
{
    return (x>buttons[i].xmin &&
            x<buttons[i].xmax &&
            (v_conf->windowHeight-y)>buttons[i].ymin &&
            (v_conf->windowHeight-y)<buttons[i].ymax);
}



// }}}
// inMainMenuButton() {{{



// ----------------------------------------------------------------------------
// This function returns "true", if mouse pointer is positioned over main menu
// button. It returns "false" otherwise.
// ----------------------------------------------------------------------------
int inMainMenuButton(int x, int y)
{
    return (x>=10 && x<=220 && y>=8 && y<=24);
}



// }}}
// inScrollbar() {{{



// ----------------------------------------------------------------------------
// This function returns "true", if mouse pointer is positioned over scrollbar.
// It returns "false" otherwise.
// ----------------------------------------------------------------------------
int inScrollbar(Scrollbar *s_conf, int x, int y)
{
    return (x>s_conf->xmin) && (x<s_conf->xmax) && (y>s_conf->ymin) && (y<s_conf->ymax);
}



// }}}
// setMenuForButton() {{{



// ----------------------------------------------------------------------------
// This function sets context menu for button under mouse pointer.
// ----------------------------------------------------------------------------
void setMenuForButton(const ButtonStruct buttons[], const int x, const int y)
{
    int i;
    int old=*active;
    *active=0;
    for (i=0; buttons[i].callback; i++) {           // check if mouse cursor is in some button
        if (*set==CommandSettingsSurfelsCount || i!=4) {
            if (inButton(buttons, i, x, y)) {
                glutSetMenu(buttons[i].menu);
                glutAttachMenu(GLUT_RIGHT_BUTTON);  // context menu under right mouse button
                *active=buttons[i].menu;
                break;
            }
        }
    }
    if (*active!=old) glutPostRedisplay();
}



// }}}
// setMenuForMainButton() {{{



// ----------------------------------------------------------------------------
// This function sets context menu for main menu button, but only when mouse
// pointer is positioned over this button.
// ----------------------------------------------------------------------------
void setMenuForMainButton(int x, int y)
{
    int old=*active;
    if (inMainMenuButton(x, y)) {
        glutSetMenu(mainMenu);
        glutAttachMenu(GLUT_LEFT_BUTTON);
        glutAttachMenu(GLUT_RIGHT_BUTTON);
        *active=mainMenu;
    }
    if (*active!=old) glutPostRedisplay();
}



// }}}
// processScrollbars() {{{



// ----------------------------------------------------------------------------
// This function processes all scrollbar messages.
// ----------------------------------------------------------------------------
void processScrollbars(int x, int y)
{
    if (*set==CommandSettingsSurfelsCount) {
        if (inScrollbar(s_conf, x, v_conf->windowHeight-y)) { // surfel count scrollbar
            updateScrollbar(s_conf, x, y);
            glutSetCursor(GLUT_CURSOR_WAIT);
            objectCreate(&o, o.type, s_conf->position);
            objectSizeMove(&o);
            glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
            glutPostRedisplay();
        }
    }
    else {
        if (x>=340 && x<=540) {                     // color scrollbars
            GLfloat r, g, b, a;
            int yy=v_conf->windowHeight-y;
            getCurrentColor(&r, &g, &b, &a);
            if (yy>=56 && yy<=62) {                 // red color component
                r=(x-340)/200.0f;
                setCurrentColor(r, g, b, a);
                glutPostRedisplay();
            }
            if (yy>=41 && yy<=47) {                 // green color component
                g=(x-340)/200.0f;
                setCurrentColor(r, g, b, a);
                glutPostRedisplay();
            }
            if (yy>=26 && yy<=32) {                 // blue color component
                b=(x-340)/200.0f;
                setCurrentColor(r, g, b, a);
                glutPostRedisplay();
            }
            if (yy>=11 && yy<=17) {                 // alpha color component
                a=(x-340)/200.0f;
                setCurrentColor(r, g, b, a);
                glutPostRedisplay();
            }
        }
    }
}



// }}}
// processRightMouseButton() {{{



// ----------------------------------------------------------------------------
// This function processes messages for right mouse button press/release.
// ----------------------------------------------------------------------------
#ifdef __BORLANDC__                                 // avoid compiler warnings
#pragma option -w-par
#endif
void processRightMouseButton(int state, int x, int y)
{
    if (state==GLUT_DOWN) {                         // mouse button press
        m_conf->status=2;
        m_conf->z1=y;
    }
    else {                                          // mouse button release
        m_conf->status=0;
        m_conf->zold=m_conf->znew;
    }
}
#ifdef __BORLANDC__
#pragma option -w+par
#endif



// }}}
// processLeftMouseButton() {{{



// ----------------------------------------------------------------------------
// This function processes messages for left mouse button press/release.
// ----------------------------------------------------------------------------
void processLeftMouseButton(const ButtonStruct buttons[], const int state, const int x, const int y)
{
    int doTransformation=true;
    int i;
    if (state==GLUT_DOWN) {
        if (!v_conf->info) {                             // rotate fractal object
            m_conf->status=1;
            m_conf->x1=x;
            m_conf->y1=y;
            glutPostRedisplay();
            return;
        }
        if (*set==CommandSettingsSurfelsCount) {    // change surfels count
            if (inScrollbar(s_conf, x, v_conf->windowHeight-y)) {
                updateScrollbar(s_conf, x, y);
                glutSetCursor(GLUT_CURSOR_WAIT);
                objectCreate(&o, o.type, s_conf->position);
                objectSizeMove(&o);
                glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
                doTransformation=false;
            }
            for (i=0; buttons[i].callback; i++) {   // check if mouse pointer
                if (inButton(buttons, i, x, y)) {   // is in some button
                    buttons[i].callback();
                    doTransformation=false;
                    break;
                }
            }
        }
        else {
            if (x>=340 && x<=540) {                 // color scrollbars
                GLfloat r, g, b, a;
                int yy=v_conf->windowHeight-y;
                getCurrentColor(&r, &g, &b, &a);
                if (yy>=56 && yy<=62) {             // red color component
                    r=(x-340)/200.0f;
                    setCurrentColor(r, g, b, a);
                    doTransformation=false;
                }
                if (yy>=41 && yy<=47) {             // green color component
                    g=(x-340)/200.0f;
                    setCurrentColor(r, g, b, a);
                    doTransformation=false;
                }
                if (yy>=26 && yy<=32) {             // blue color component
                    b=(x-340)/200.0f;
                    setCurrentColor(r, g, b, a);
                    doTransformation=false;
                }
                if (yy>=11 && yy<=17) {             // alpha color component
                    a=(x-340)/200.0f;
                    setCurrentColor(r, g, b, a);
                    doTransformation=false;
                }
            }
            for (i=0; buttons[i].callback; i++) {   // check if mouse pointer
                if (inButton(buttons, i, x, y) && i!=4) {// is in some button
                    buttons[i].callback();
                    doTransformation=false;
                    break;
                }
            }
        }
        if (doTransformation) {                     // if user wants to do some transformation
            if (glutGetModifiers() & GLUT_ACTIVE_CTRL) {
                m_conf->xtran1=x;
                m_conf->ytran1=y;
                m_conf->status=3;
            }
            else {
                m_conf->status=1;
                m_conf->x1=x;
                m_conf->y1=y;
            }
        }
    }
    else {                                          // GLUT_UP
        m_conf->status=0;
        m_conf->xold=m_conf->xnew;
        m_conf->yold=m_conf->ynew;
        m_conf->xtran2=m_conf->xtran0;
        m_conf->ytran2=m_conf->ytran0;
    }
}



// }}}
// getAxisColor() {{{



// ----------------------------------------------------------------------------
// This function returns current axis color
// (pointer to four color components).
// ----------------------------------------------------------------------------
GLfloat *getAxisColor(void)
{
    return axisColor;
}



// }}}
// getHelpColor() {{{



// ----------------------------------------------------------------------------
// This function returns current help foreground color
// (pointer to four color components).
// ----------------------------------------------------------------------------
GLfloat *getHelpColor(void)
{
    return helpColor;
}



// }}}
// getInfoColor() {{{



// ----------------------------------------------------------------------------
// This function returns current info foreground color
// (pointer to four color components).
// ----------------------------------------------------------------------------
GLfloat *getInfoColor(void)
{
    return infoColor;
}



// }}}
// getModelColor() {{{



// ----------------------------------------------------------------------------
// This function returns current model color
// (pointer to four color components).
// ----------------------------------------------------------------------------
GLfloat *getModelColor(void)
{
    return modelColor;
}



// }}}
// getScrollBarColor() {{{



// ----------------------------------------------------------------------------
// This function returns current scroll bar color
// (pointer to four color components).
// ----------------------------------------------------------------------------
GLfloat *getScrollBarColor(void)
{
    return scrollBarColor;
}



// }}}
// getActiveItemColor() {{{



// ----------------------------------------------------------------------------
// This function returns current active (highlited) item color
// (pointer to four color components).
// ----------------------------------------------------------------------------
GLfloat *getActiveItemColor(void)
{
    return activeItemColor;
}



// }}}
// getHelpBackground() {{{



// ----------------------------------------------------------------------------
// This function returns current help background color
// (pointer to four color components).
// ----------------------------------------------------------------------------
GLfloat *getHelpBackground(void)
{
    return helpBackground;
}



// }}}
// getInfoBackground() {{{



// ----------------------------------------------------------------------------
// This function returns current info background color
// (pointer to four color components).
// ----------------------------------------------------------------------------
GLfloat *getInfoBackground(void)
{
    return infoBackground;
}



// }}}
// getModelBackground() {{{



// ----------------------------------------------------------------------------
// This function returns current model background color
// (pointer to four color components).
// ----------------------------------------------------------------------------
GLfloat *getModelBackground(void)
{
    return modelBackground;
}



// }}}
// getFogColor() {{{



// ----------------------------------------------------------------------------
// This function returns current fog color
// (pointer to four color components).
// ----------------------------------------------------------------------------
GLfloat *getFogColor(void)
{
    return fogColor;
}



// }}}
// getCurrentColor() {{{



// ----------------------------------------------------------------------------
// This function gets current color for some GUI item.
// ----------------------------------------------------------------------------
void getCurrentColor(GLfloat *r, GLfloat *g, GLfloat *b, GLfloat *a)
{
    #ifndef __BORLANDC__
    GLfloat *color=nil;
    #else
    GLfloat *color;
    #endif
    switch (*set) {
        case CommandSettingsModelForeground: color=modelColor;      break;
        case CommandSettingsModelBackground: color=modelBackground; break;
        case CommandSettingsFogColor:        color=fogColor;        break;
        case CommandSettingsAxesColor:       color=axisColor;       break;
        case CommandSettingsActiveItemColor: color=activeItemColor; break;
        case CommandSettingsHelpForeground:  color=helpColor;       break;
        case CommandSettingsHelpBackground:  color=helpBackground;  break;
        case CommandSettingsInfoForeground:  color=infoColor;       break;
        case CommandSettingsInfoBackground:  color=infoBackground;  break;
        case CommandSettingsScrollBarColor:  color=scrollBarColor;  break;
        default:                             color=modelColor;      break;
    }
    if (color) {
        *r=color[0];
        *g=color[1];
        *b=color[2];
        *a=color[3];
    }
}



// }}}
// setModelAlpha() {{{



// ----------------------------------------------------------------------------
// This function sets alpha color component for blending.
// ----------------------------------------------------------------------------
void setModelAlpha(GLfloat alpha)
{
    modelColor[3]=alpha;
}



// }}}
// setCurrentColor() {{{



// ----------------------------------------------------------------------------
// This function sets current color for some GUI item.
// ----------------------------------------------------------------------------
void setCurrentColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
{
    GLfloat *color=nil;
    switch (*set) {
        case CommandSettingsModelForeground: color=modelColor;      break;
        case CommandSettingsModelBackground: color=modelBackground; break;
        case CommandSettingsFogColor:        color=fogColor;        break;
        case CommandSettingsAxesColor:       color=axisColor;       break;
        case CommandSettingsActiveItemColor: color=activeItemColor; break;
        case CommandSettingsHelpForeground:  color=helpColor;       break;
        case CommandSettingsHelpBackground:  color=helpBackground;  break;
        case CommandSettingsInfoForeground:  color=infoColor;       break;
        case CommandSettingsInfoBackground:  color=infoBackground;  break;
        case CommandSettingsScrollBarColor:  color=scrollBarColor;  break;
    }
    if (color) {
        color[0]=r;
        color[1]=g;
        color[2]=b;
        color[3]=a;
    }
}



// }}}
// readOneLine() {{{



// ----------------------------------------------------------------------------
// This function reads one line from config file.
// ----------------------------------------------------------------------------
void readOneLine(FILE *fin, GLfloat (*colorArray)[4])
{
    char str[1000];
    GLfloat r, g, b, a;                             // four color components
    if (fscanf(fin, "%s%f%f%f%f\n", str, &r, &g, &b, &a)==5) {
        (*colorArray)[0]=r;
        (*colorArray)[1]=g;
        (*colorArray)[2]=b;
        (*colorArray)[3]=a;
    }
}



// }}}
// settingsReset() {{{



// ----------------------------------------------------------------------------
// This function resets all color settings.
// ----------------------------------------------------------------------------
void settingsReset(void)
{
    const GLfloat c_axisColor[]=      {0.5f, 0.5f, 1.0f, 1.0f};
    const GLfloat c_helpColor[]=      {0.2f, 0.9f, 0.9f, 1.0f};
    const GLfloat c_infoColor[]=      {0.2f, 0.9f, 0.9f, 1.0f};
    const GLfloat c_modelColor[]=     {1.0f, 1.0f, 1.0f, 0.5f};
    const GLfloat c_scrollBarColor[]= {0.5f, 0.5f, 1.0f, 1.0f};
    const GLfloat c_activeItemColor[]={1.0f, 1.0f, 0.5f, 1.0f};
    const GLfloat c_helpBackground[]= {0.3f, 0.3f, 0.7f, 0.7f};
    const GLfloat c_infoBackground[]= {0.3f, 0.3f, 0.7f, 0.7f};
    const GLfloat c_modelBackground[]={0.0f, 0.0f, 0.0f, 1.0f};
    const GLfloat c_fogColor[]=       {0.1f, 0.1f, 0.1f, 0.0f};
    // set default colors
    memcpy(axisColor,       c_axisColor,       sizeof(c_axisColor));
    memcpy(helpColor,       c_helpColor,       sizeof(c_helpColor));
    memcpy(infoColor,       c_infoColor,       sizeof(c_infoColor));
    memcpy(modelColor,      c_modelColor,      sizeof(c_modelColor));
    memcpy(scrollBarColor,  c_scrollBarColor,  sizeof(c_scrollBarColor));
    memcpy(activeItemColor, c_activeItemColor, sizeof(c_activeItemColor));
    memcpy(helpBackground,  c_helpBackground,  sizeof(c_helpBackground));
    memcpy(infoBackground,  c_infoBackground,  sizeof(c_infoBackground));
    memcpy(modelBackground, c_modelBackground, sizeof(c_modelBackground));
    memcpy(fogColor,        c_fogColor,        sizeof(c_fogColor));
}



// }}}
// settingsLoad() {{{



// ----------------------------------------------------------------------------
// This function reads all color settings from config file.
// ----------------------------------------------------------------------------
void settingsLoad(void)
{
    FILE *fin;

    fin=fopen("surf_ifs.ini", "rt");                // try to open config file for reading
    if (fin) {
        readOneLine(fin, &axisColor);
        readOneLine(fin, &helpColor);
        readOneLine(fin, &infoColor);
        readOneLine(fin, &modelColor);
        readOneLine(fin, &scrollBarColor);
        readOneLine(fin, &activeItemColor);
        readOneLine(fin, &helpBackground);
        readOneLine(fin, &infoBackground);
        readOneLine(fin, &modelBackground);
        readOneLine(fin, &fogColor);
        fclose(fin);
    }
    else {
        settingsReset();                            // file could not be opened
    }
}



// }}}
// settingsSave() {{{



// ----------------------------------------------------------------------------
// This function writes color settings to config file.
// ----------------------------------------------------------------------------
void settingsSave(void)
{
    FILE *fout;
    fout=fopen("surf_ifs.ini", "wt");               // try to open config file for writing
    if (fout) {
        fprintf(fout, "AxisColor\t %5.3f %5.3f %5.3f %5.3f\n",
                axisColor[0], axisColor[1], axisColor[2], axisColor[3]);
        fprintf(fout, "HelpColor\t %5.3f %5.3f %5.3f %5.3f\n",
                helpColor[0], helpColor[1], helpColor[2], helpColor[3]);
        fprintf(fout, "InfoColor\t %5.3f %5.3f %5.3f %5.3f\n",
                infoColor[0], infoColor[1], infoColor[2], infoColor[3]);
        fprintf(fout, "ModelColor\t %5.3f %5.3f %5.3f %5.3f\n",
                modelColor[0], modelColor[1], modelColor[2], modelColor[3]);
        fprintf(fout, "ScrollBarColor\t %5.3f %5.3f %5.3f %5.3f\n",
                scrollBarColor[0], scrollBarColor[1], scrollBarColor[2], scrollBarColor[3]);
        fprintf(fout, "ActiveItemColor\t %5.3f %5.3f %5.3f %5.3f\n",
                activeItemColor[0], activeItemColor[1], activeItemColor[2], activeItemColor[3]);
        fprintf(fout, "HelpBackground\t %5.3f %5.3f %5.3f %5.3f\n",
                helpBackground[0], helpBackground[1], helpBackground[2], helpBackground[3]);
        fprintf(fout, "InfoBackground\t %5.3f %5.3f %5.3f %5.3f\n",
                infoBackground[0], infoBackground[1], infoBackground[2], infoBackground[3]);
        fprintf(fout, "ModelBackground\t %5.3f %5.3f %5.3f %5.3f\n",
                modelBackground[0], modelBackground[1], modelBackground[2], modelBackground[3]);
        fprintf(fout, "FogColor\t %5.3f %5.3f %5.3f %5.3f\n",
                fogColor[0], fogColor[1], fogColor[2], fogColor[3]);
        fclose(fout);
    }
}



// }}}
// arandom() {{{



// ----------------------------------------------------------------------------
// This function works as pseudo-random number generator.
// ----------------------------------------------------------------------------
float arandom(float max)
{
    return (float)rand()*max/RAND_MAX;
}



// }}}
// getMinMax() {{{



// ----------------------------------------------------------------------------
// This function returns minimums and maximums coordinates of surfel positions.
// ----------------------------------------------------------------------------
void getMinMax(int surfelList, float *minx, float *maxx,
                               float *miny, float *maxy,
                               float *minz, float *maxz)
{
    SurfelF surfel;
    *minx=*miny=*minz=1.0e9;
    *maxx=*maxy=*maxz=-1.0e9;
    surfelListFirst(surfelList);
    while (!surfelListIsLast(surfelList)) {
        surfelListGetSurfelF(surfelList, &surfel);
        *minx=MIN(*minx, surfel.position.x);
        *maxx=MAX(*maxx, surfel.position.x);
        *miny=MIN(*miny, surfel.position.y);
        *maxy=MAX(*maxy, surfel.position.y);
        *minz=MIN(*minz, surfel.position.z);
        *maxz=MAX(*maxz, surfel.position.z);
        surfelListNext(surfelList);
    }
}



// }}}
// getArray() {{{



// ----------------------------------------------------------------------------
// This function returns pointer to proper data of selected IFS system.
// ----------------------------------------------------------------------------
void getArray(int type, float (**p)[13])
{
    switch (type) {
        case CommandModelPyramid:       *p=ifs_pyramid;       break;
        case CommandModelTetrahedron1:  *p=ifs_tetrahedron1;  break;
        case CommandModelTetrahedron2:  *p=ifs_tetrahedron2;  break;
        case CommandModelHexahedron:    *p=ifs_hexahedron;    break;
        case CommandModelCube:          *p=ifs_cube;          break;
        case CommandModelOctahedron:    *p=ifs_octahedron;    break;
        case CommandModelDuodecahedron: *p=ifs_duodecahedron; break;
        case CommandModelFern:          *p=ifs_fern;          break;
        case CommandModelWing:          *p=ifs_wing;          break;
        case CommandModelLeaf:          *p=ifs_leaf;          break;
        case CommandModelBush:          *p=ifs_bush;          break;
        case CommandModelFlash:         *p=ifs_flash;         break;
        case CommandModelTower:         *p=ifs_tower;         break;
        case CommandModelArtefact1:     *p=ifs_artefact1;     break;
        case CommandModelArtefact2:     *p=ifs_artefact2;     break;
        case CommandModelArtefact3:     *p=ifs_artefact3;     break;
        case CommandModelArtefact4:     *p=ifs_artefact4;     break;
        case CommandModelArtefact5:     *p=ifs_artefact5;     break;
        case CommandModelArtefact6:     *p=ifs_artefact6;     break;
        case CommandModelArtefact7:     *p=ifs_artefact7;     break;
        default:                        *p=NULL;              break;
    }
}



// }}}
// centerSurfels() {{{



// ----------------------------------------------------------------------------
// This function centers whole set of surfels.
// ----------------------------------------------------------------------------
void centerSurfels(int surfelList, int midx, int midy, int midz)
{
    SurfelF surfel;
    surfelListFirst(surfelList);
    while (!surfelListIsLast(surfelList)) {         // center object around origin
        surfelListGetSurfelF(surfelList, &surfel);
        surfel.position.x+=ORIGIN_X-midx;
        surfel.position.y+=ORIGIN_Y-midy;
        surfel.position.z+=ORIGIN_Z-midz;
        surfelListSetSurfelF(surfelList, &surfel);
        surfelListNext(surfelList);
    }
}



// }}}
// scaleAndMoveSurfels() {{{



// ----------------------------------------------------------------------------
// This function scales and moves whole set of surfels to fit to an unit cube.
// ----------------------------------------------------------------------------
void scaleAndMoveSurfels(int surfelList, float scale)
{
    SurfelF surfel;
    surfelListFirst(surfelList);
    while (!surfelListIsLast(surfelList)) {         // scale object into unit cube
        surfelListGetSurfelF(surfelList, &surfel);
        surfel.position.x-=ORIGIN_X;
        surfel.position.y-=ORIGIN_Y;
        surfel.position.z-=ORIGIN_Z;
        surfel.position.x*=scale;
        surfel.position.y*=scale;
        surfel.position.z*=scale;
        surfel.position.x+=ORIGIN_X;
        surfel.position.y+=ORIGIN_Y;
        surfel.position.z+=ORIGIN_Z;
        surfelListSetSurfelF(surfelList, &surfel);
        surfelListNext(surfelList);
    }
}



// }}}
// objectInit() {{{



// ----------------------------------------------------------------------------
// This function initializes new surfel-based object.
// ----------------------------------------------------------------------------
void objectInit(Object *o)
{
    o->type=CommandModelFirst;
    o->colorR=1.0;                                  // set initial values
    o->colorG=1.0;                                  // for all color components
    o->colorB=1.0;
    o->blending=0;
    o->alpha=0.5f;
    o->surfelList=surfelListInit(SURFEL_LIST_FLOAT);
}



// }}}
// objectCreate() {{{



// ----------------------------------------------------------------------------
// This function creates new surfel-based object.
// ----------------------------------------------------------------------------
void objectCreate(Object *o, int type, int grid)
{
    int     subtype;
    int     pts=grid*grid;
    int     i, k;
    float   x,y,z,x1,y1,z1;
    float   pp, sum;
    SurfelF surfel;
    float   (*ifs)[13];

    o->type=type;
    o->size=grid;
    getArray(type, &ifs);
    if (!ifs) return;
    surfelListDestroy(o->surfelList);
    surfelListAlloc(o->surfelList, pts);

    if (ifs[0][12]<0) {                             // check object type
        subtype=1;
        ifs[0][12]=-ifs[0][12];
    }
    else {                                          // bad object type
        subtype=0;
    }

    x=y=z=0.0;
    i=0;
    surfelListFirst(o->surfelList);
    if (!subtype) {                                 // generate model as normal IFS system
        while (!surfelListIsLast(o->surfelList)) {
            pp=arandom(999.0)/1000.0;
            sum=0;
            for (k=0; sum<=pp; k++)
                sum+=ifs[k][12];
            k--;
            x1=ifs[k][0]*x+ifs[k][1]*y+ifs[k][2]*z+ifs[k][9];
            y1=ifs[k][3]*x+ifs[k][4]*y+ifs[k][5]*z+ifs[k][10];
            z1=ifs[k][6]*x+ifs[k][7]*y+ifs[k][8]*z+ifs[k][11];
            x=x1;y=y1;z=z1;
            if (i>=100) {                           // after starting iterations
                surfel.position.x=x*20.0;           // generate surfel
                surfel.position.y=y*20.0;
                surfel.position.z=z*20.0;
                surfel.size=1;
                surfelListSetSurfelF(o->surfelList, &surfel);
                surfelListNext(o->surfelList);
            }
            i++;
        }
    }
    else {                                          // generate model as IFS with alternating coordinates
        while (!surfelListIsLast(o->surfelList)) {
            pp=arandom(999.0)/1000.0;
            sum=0;
            for (k=0; sum<=pp; k++)
                sum+=ifs[k][12];
            k--;
            x1=ifs[k][0]*x+ifs[k][1]*y+ifs[k][2]*z+ifs[k][3];
            y1=ifs[k][4]*x+ifs[k][5]*y+ifs[k][6]*z+ifs[k][7];
            z1=ifs[k][8]*x+ifs[k][9]*y+ifs[k][10]*z+ifs[k][11];
            x=-x1;y=-y1;z=-z1;
            if (i>=100) {                           // after starting iterations
                surfel.position.x=x*20.0;           // generate surfel
                surfel.position.y=y*20.0;
                surfel.position.z=z*20.0;
                surfel.size=1;
                surfelListSetSurfelF(o->surfelList, &surfel);
                surfelListNext(o->surfelList);
            }
            i++;
        }
        ifs[0][12]=-ifs[0][12];
    }
}



// }}}
// objectSizeMove() {{{



// ----------------------------------------------------------------------------
// This function centers surfel-based object along origin.
// ----------------------------------------------------------------------------
void objectSizeMove(Object *o)
{
    float minx, miny, minz;
    float maxx, maxy, maxz;
    float midx, midy, midz;
    float scale;

    getMinMax(o->surfelList, &minx, &maxx, &miny, &maxy, &minz, &maxz);
    midx=(minx+maxx)/2.0;
    midy=(miny+maxy)/2.0;
    midz=(minz+maxz)/2.0;
    centerSurfels(o->surfelList, midx, midy, midz);
    scale=RANGE_X/(maxx-minx);
    scale=MIN(scale, RANGE_Y/(maxy-miny));
    scale=MIN(scale, RANGE_Z/(maxz-minz));
    scaleAndMoveSurfels(o->surfelList, scale);
}



// }}}
// objectSave() {{{



// ----------------------------------------------------------------------------
// This function saves surfel-based object to an external file.
// ----------------------------------------------------------------------------
void objectSave(Object *o)
{
    time_t timer;
    struct tm *t;
    char fileName[100];
    time(&timer);
    t=localtime(&timer);                            // get current date and time

    sprintf(fileName, "%04d%02d%02d-%02d%02d%02d.sfl", // prepare file name
            t->tm_year+1900, t->tm_mon+1, t->tm_mday,
            t->tm_hour, t->tm_min, t->tm_sec);
    surfelListSaveP(o->surfelList, fileName);       // write surfels to file
}



// }}}
// objectSaveSize() {{{



// ----------------------------------------------------------------------------
// This function saves surfel-based object to file, including surfel' sizes.
// ----------------------------------------------------------------------------
#ifdef __BORLANDC__
#pragma option -w-par
#endif
void objectSaveSize(Object *o, View *v)
{
    time_t timer;
    struct tm *t;
    char fileName[100];
    time(&timer);
    t=localtime(&timer);                            // get current date and time

    sprintf(fileName, "%04d%02d%02d-%02d%02d%02d.sfl", // prepare file name
            t->tm_year+1900, t->tm_mon+1, t->tm_mday,
            t->tm_hour, t->tm_min, t->tm_sec);
    surfelListSavePS(o->surfelList, fileName);      // write surfels to file
}
#ifdef __BORLANDC__
#pragma option -w+par
#endif



// }}}
// drawString() {{{



// ----------------------------------------------------------------------------
// This function draws string at world' specified co-ordinates
// ----------------------------------------------------------------------------
void drawString(char *string, int x, int y, int z)
{
    glRasterPos3i(x, y, z);
    while (*string)                                 // for all characters in string
        glutBitmapCharacter(FONT, *string++);       // draw each character
}



// }}}
// drawObjectNoShading() {{{



// ----------------------------------------------------------------------------
// This function draws the surfel object using no shading
// ----------------------------------------------------------------------------
void drawObjectNoShading(Object *o)
{
    SurfelF surfel;
    if (o->blending) {                              // if blending is enabled
        glEnable(GL_BLEND);                         // enable it
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }
    glColor4fv(getModelColor());                    // constant object color
    glBegin(GL_POINTS);
    surfelListFirst(o->surfelList);                 // rewind to first surfel in object
    while (!surfelListIsLast(o->surfelList)) {      // for all surfels in object
        surfelListGetSurfelF(o->surfelList, &surfel);// get surfel
        glVertex3f(surfel.position.x, surfel.position.y, surfel.position.z);
        surfelListNext(o->surfelList);              // move to next surfel in list
    }
    glEnd();
    if (o->blending) {                              // if blending is enabled
        glDisable(GL_BLEND);                        // disable it (for GUI)
    }
}



// }}}
// drawObjectXShading() {{{



// ----------------------------------------------------------------------------
// This function draws the surfel object using shading along x-axis
// ----------------------------------------------------------------------------
void drawObjectXShading(Object *o)
{
    GLfloat weight;
    GLfloat *color;
    GLfloat *fog;
    SurfelF surfel;

    color=getModelColor();
    fog=getFogColor();
    if (o->blending) {                              // if blending is enabled
        glEnable(GL_BLEND);                         // enable it
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }
    glBegin(GL_POINTS);
    surfelListFirst(o->surfelList);                 // rewind to first surfel in object
    while (!surfelListIsLast(o->surfelList)) {      // for all surfels in object
        surfelListGetSurfelF(o->surfelList, &surfel);
        weight=(float)surfel.position.x/RANGE_X+0.2;// calculate surfel color
        glColor4f(color[0]*weight+fog[0]*(1.0f-weight),
                  color[1]*weight+fog[1]*(1.0f-weight),
                  color[2]*weight+fog[2]*(1.0f-weight),
                  color[3]);                        // set color and draw surfel
        glVertex3f(surfel.position.x, surfel.position.y, surfel.position.z);
        surfelListNext(o->surfelList);              // goto next surfel in list
    }
    glEnd();
    if (o->blending) {                              // if blending is enabled
        glDisable(GL_BLEND);                        // disable it (for GUI)
    }
}



// }}}
// drawObjectYShading() {{{



// ----------------------------------------------------------------------------
// This function draws the surfel object using shading along y-axis
// ----------------------------------------------------------------------------
void drawObjectYShading(Object *o)
{
    GLfloat weight;
    GLfloat *color;
    GLfloat *fog;
    SurfelF surfel;

    color=getModelColor();
    fog=getFogColor();
    if (o->blending) {                              // if blending is enabled
        glEnable(GL_BLEND);                         // enable it
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }
    glBegin(GL_POINTS);
    surfelListFirst(o->surfelList);                 // rewind to first surfel in object
    while (!surfelListIsLast(o->surfelList)) {      // for all surfels in object
        surfelListGetSurfelF(o->surfelList, &surfel);
        weight=(float)surfel.position.y/RANGE_Y+0.2;// calculate surfel color
        glColor4f(color[0]*weight+fog[0]*(1.0f-weight),
                  color[1]*weight+fog[1]*(1.0f-weight),
                  color[2]*weight+fog[2]*(1.0f-weight),
                  color[3]);                        // set color and draw surfel
        glVertex3f(surfel.position.x, surfel.position.y, surfel.position.z);
        surfelListNext(o->surfelList);              // goto next surfel in list
    }
    glEnd();
    if (o->blending) {                              // if blending is enabled
        glDisable(GL_BLEND);                        // disable it (for GUI)
    }
}



// }}}
// drawObjectZShading() {{{



// ----------------------------------------------------------------------------
// This function draws the surfel object using shading along z-axis
// ----------------------------------------------------------------------------
void drawObjectZShading(Object *o)
{
    GLfloat weight;
    GLfloat *color;
    GLfloat *fog;
    SurfelF surfel;

    color=getModelColor();
    fog=getFogColor();
    if (o->blending) {                              // if blending is enabled
        glEnable(GL_BLEND);                         // enable it
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }
    glBegin(GL_POINTS);
    surfelListFirst(o->surfelList);                 // rewind to first surfel in object
    while (!surfelListIsLast(o->surfelList)) {      // for all surfels in object
        surfelListGetSurfelF(o->surfelList, &surfel);
        weight=(float)surfel.position.z/RANGE_Z+0.2;// calculate surfel color
        glColor4f(color[0]*weight+fog[0]*(1.0f-weight),
                  color[1]*weight+fog[1]*(1.0f-weight),
                  color[2]*weight+fog[2]*(1.0f-weight),
                  color[3]);                        // set color and draw surfel
        glVertex3f(surfel.position.x, surfel.position.y, surfel.position.z);
        surfelListNext(o->surfelList);              // goto next surfel in list
    }
    glEnd();
    if (o->blending) {                              // if blending is enabled
        glDisable(GL_BLEND);                        // disable it (for GUI)
    }
}



// }}}
// drawObjectFogShading() {{{



// ----------------------------------------------------------------------------
// This function draws the surfel object using fog
// ----------------------------------------------------------------------------
void drawObjectFogShading(Object *o)
{
    SurfelF surfel;
    GLfloat *fogColor=getFogColor();
    glEnable(GL_FOG);                               // set fog properties
    glFogi(GL_FOG_MODE, GL_LINEAR);
    glFogfv(GL_FOG_COLOR, fogColor);
    glFogf(GL_FOG_DENSITY, 0.1f);
    glHint(GL_FOG_HINT, GL_DONT_CARE);
    glFogf(GL_FOG_START, 220.0);                    // near and far planes
    glFogf(GL_FOG_END, 320.0);                      // fog increases inbetween these planes
    if (o->blending) {                              // if blending is enabled
        glEnable(GL_BLEND);                         // enable it
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }
    glColor4fv(getModelColor());
    glBegin(GL_POINTS);
    surfelListFirst(o->surfelList);
    while (!surfelListIsLast(o->surfelList)) {      // for all surfels in object
        surfelListGetSurfelF(o->surfelList, &surfel);
        glVertex3f(surfel.position.x, surfel.position.y, surfel.position.z);
        surfelListNext(o->surfelList);              // goto next surfel in list
    }
    glEnd();
    glDisable(GL_FOG);
    if (o->blending) {                              // if blending is enabled
        glDisable(GL_BLEND);                        // disable it (for GUI)
    }
}



// }}}
// drawObject() {{{



// ----------------------------------------------------------------------------
// This function draws the surfel object
// ----------------------------------------------------------------------------
void drawObject(Object *o, View *v)
{
    if (v->zBuffer)
        glEnable(GL_DEPTH_TEST);
    switch (v->shading) {
        case CommandShadingNone: drawObjectNoShading(o);  break;
        case CommandShadingX:    drawObjectXShading(o);   break;
        case CommandShadingY:    drawObjectYShading(o);   break;
        case CommandShadingZ:    drawObjectZShading(o);   break;
        case CommandShadingFog:  drawObjectFogShading(o); break;
        default:                                          break;
    }
    if (v->zBuffer)
        glDisable(GL_DEPTH_TEST);
}



// }}}
// drawAxis() {{{



// ----------------------------------------------------------------------------
// This function draws axes
// ----------------------------------------------------------------------------
void drawAxis(void)
{
    glEnable(GL_BLEND);
    glColor4fv(getAxisColor());
    drawString("origin", MINX+1, MINY+1, MINZ+1);   // text labels
    drawString("xmax", MAXX+1, MINY+1, MINZ+1);
    drawString("ymax", MINX+1, MAXY+1, MINZ+1);
    drawString("zmax", MINX+1, MINY+1, MAXZ+1);

    glBegin(GL_LINE_STRIP);                         // bottom side
        glVertex3f(MINX, MINY, MINZ);
        glVertex3f(MAXX, MINY, MINZ);
        glVertex3f(MAXX, MAXY, MINZ);
        glVertex3f(MINX, MAXY, MINZ);
        glVertex3f(MINX, MINY, MINZ);
    glEnd();
    glBegin(GL_LINE_STRIP);                         // top side
        glVertex3f(MINX, MINY, MAXZ);
        glVertex3f(MAXX, MINY, MAXZ);
        glVertex3f(MAXX, MAXY, MAXZ);
        glVertex3f(MINX, MAXY, MAXZ);
        glVertex3f(MINX, MINY, MAXZ);
    glEnd();
    glBegin(GL_LINES);                              // left, right, front and back side
        glVertex3f(MINX, MINY, MINZ);
        glVertex3f(MINX, MINY, MAXZ);
        glVertex3f(MAXX, MINY, MINZ);
        glVertex3f(MAXX, MINY, MAXZ);
        glVertex3f(MAXX, MAXY, MINZ);
        glVertex3f(MAXX, MAXY, MAXZ);
        glVertex3f(MINX, MAXY, MINZ);
        glVertex3f(MINX, MAXY, MAXZ);
        glVertex3f(MINX, MINY, MINZ);
        glVertex3f(MINX, MINY, MAXZ);
    glEnd();
    glDisable(GL_BLEND);
}



// }}}
// drawScrollbar() {{{



// ----------------------------------------------------------------------------
// This function draws scrollbar
// ----------------------------------------------------------------------------
void drawScrollbar(View *v, Scrollbar *s)
{
    if (v->windowHeight<120) return;                // ensure proper window size
    if (v->windowWidth<s->xmax+2) return;

    glEnable(GL_BLEND);
    glColor4fv(getInfoColor());                     // display border
    glBegin(GL_LINE_LOOP);
        glVertex2i(s->xmin, s->ymin);
        glVertex2i(s->xmin, s->ymax);
        glVertex2i(s->xmax, s->ymax);
        glVertex2i(s->xmax, s->ymin);
    glEnd();

    glColor4fv(getScrollBarColor());                // display slider
    glBegin(GL_QUADS);
        glVertex2i(s->xmin+1, s->ymin+2);
        glVertex2i(s->xmin+1, s->ymax-1);
        glVertex2i(s->xmin+s->position, s->ymax-1);
        glVertex2i(s->xmin+s->position, s->ymin+2);
    glEnd();
    glDisable(GL_BLEND);
}



// }}}
// drawInfo() {{{



// ----------------------------------------------------------------------------
// This function displays informations at window' bottom area
// ----------------------------------------------------------------------------
void drawInfo(Object *o, View *v, char *objectType, int activeItem)
{
    char *enableDisableStr[]={"disabled", "enabled"};
    char str[100];

    if (v->windowHeight<120) return;                // ensure proper window size
    if (v->windowWidth<189) return;

    glEnable(GL_BLEND);                             // draw background
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glColor4fv(getInfoBackground());
    glRecti(0, 0, v->windowWidth, 90);

    if (activeItem==modelMenu)                      // highlight menu if necessary
        glColor4fv(getActiveItemColor());
    else
        glColor4fv(getInfoColor());
    sprintf(str, "Object type:  %s", objectType);
    drawString(str, 10, 71, 0);
    if (activeItem==surfelMenu)                     // highlight menu if necessary
        glColor4fv(getActiveItemColor());
    else
        glColor4fv(getInfoColor());
    sprintf(str, "Surfels size: %d", v->surfelsSize);
    drawString(str, 10, 55, 0);
    if (activeItem==shadingMenu)
        glColor4fv(getActiveItemColor());
    else
        glColor4fv(getInfoColor());
    sprintf(str, "Shading:      %s", shadingString[v->shading-CommandShadingNone]);
    drawString(str, 10, 40, 0);
    if (activeItem==zBufferMenu)
        glColor4fv(getActiveItemColor());
    else
        glColor4fv(getInfoColor());
    sprintf(str, "Depth buffer: %s", enableDisableStr[v->zBuffer]);
    drawString(str, 10, 25, 0);
    if (activeItem==blendingMenu)
        glColor4fv(getActiveItemColor());
    else
        glColor4fv(getInfoColor());
    sprintf(str, "Blending:     %s", enableDisableStr[o->blending]);
    drawString(str, 10, 10, 0);
    glDisable(GL_BLEND);
}



// }}}
// drawSurfelsInfo() {{{



// ----------------------------------------------------------------------------
// This function displays surfels informations at window' bottom area
// ----------------------------------------------------------------------------
void drawSurfelsInfo(Object *o, View *v, Mouse *m, int activeItem)
{
    char str[100];

    if (v->windowHeight<120) return;                // ensure proper window size
    if (v->windowWidth<390) return;

    glEnable(GL_BLEND);
    if (activeItem==settingsMenu)                   // highlight menu item
        glColor4fv(getActiveItemColor());           // if necessary
    else
        glColor4fv(getInfoColor());
    drawString("Model settings", 230, 73, 0);
    if (activeItem==surfelCountMenu)
        glColor4fv(getActiveItemColor());
    else
        glColor4fv(getInfoColor());
    sprintf(str, "Surfels count: %d", surfelGetCount(o->surfelList));
    drawString(str, 230, 40, 0);
    glColor4fv(getInfoColor());
    sprintf(str, "Rotation:     %d %d", m->ynew, m->xnew);
    drawString(str, 230, 25, 0);
    sprintf(str, "Translation:  %d", m->znew);
    drawString(str, 230, 10, 0);
    if (v->windowWidth<556) return;                 // ensure proper window size
    if (activeItem==surfelCountMenu)
        glColor4fv(getActiveItemColor());
    else
        glColor4fv(getInfoColor());
    sprintf(str, "sqrt(surfels): %d", o->size);
    drawString(str, 410, 40, 0);
    glDisable(GL_BLEND);
}



// }}}
// drawColorScrollbars() {{{



// ----------------------------------------------------------------------------
// This function displays RGBA scrollbars
// ----------------------------------------------------------------------------
void drawColorScrollbars(View *v, int settings, int activeItem)
{
    char *prompt[]={
        "Model foreground",
        "Model background",
        "Fog color",
        "Axes color",
        "Active item",
        "Help foreground",
        "Help background",
        "Info foreground",
        "Info background",
        "Scroll bar color"
    };
    char str[100];
    GLfloat r, g, b, a;

    if (v->windowHeight<120) return;                // ensure proper window size
    if (v->windowWidth<350) return;

    glEnable(GL_BLEND);
    glColor4fv(getInfoColor());
    if (activeItem==settingsMenu)
        glColor4fv(getActiveItemColor());
    drawString(prompt[settings], 230, 73, 0);       // draw title
    getCurrentColor(&r, &g, &b, &a);

    glColor3f(0.9f, 0.5f, 0.5f);                    // red scrollbar stuff
    sprintf(str, "Red:   %3.0f%%", r*100.0f);
    drawString(str, 230, 55, 0);
    if (v->windowWidth>542) {
        glBegin(GL_QUADS);
            glVertex2i(340, 58);
            glVertex2f(340+r*200.0f, 58.0f);
            glVertex2f(340+r*200.0f, 61.0f);
            glVertex2i(340, 61);
        glEnd();
        glColor3f(0.8f, 0.4f, 0.4f);
        glBegin(GL_LINE_LOOP);
            glVertex2i(340, 56);
            glVertex2i(540, 56);
            glVertex2i(540, 62);
            glVertex2i(340, 62);
        glEnd();
    }

    glColor3f(0.5f, 0.9f, 0.5f);                    // green scrollbar stuff
    sprintf(str, "Green: %3.0f%%", g*100.0f);
    drawString(str, 230, 40, 0);
    if (v->windowWidth>542) {
        glBegin(GL_QUADS);
            glVertex2i(340, 43);
            glVertex2f(340+g*200.0f, 43.0f);
            glVertex2f(340+g*200.0f, 46.0f);
            glVertex2i(340, 46);
        glEnd();
        glColor3f(0.4f, 0.8f, 0.4f);
        glBegin(GL_LINE_LOOP);
            glVertex2i(340, 41);
            glVertex2i(540, 41);
            glVertex2i(540, 47);
            glVertex2i(340, 47);
        glEnd();
    }

    glColor3f(0.5f, 0.5f, 0.9f);                    // blue scrollbar stuff
    sprintf(str, "Blue:  %3.0f%%", b*100.0f);
    drawString(str, 230, 25, 0);
    if (v->windowWidth>542) {
        glBegin(GL_QUADS);
            glVertex2i(340, 28);
            glVertex2f(340+b*200.0f, 28.0f);
            glVertex2f(340+b*200.0f, 31.0f);
            glVertex2i(340, 31);
        glEnd();
        glColor3f(0.4f, 0.4f, 0.8f);
        glBegin(GL_LINE_LOOP);
            glVertex2i(340, 26);
            glVertex2i(540, 26);
            glVertex2i(540, 32);
            glVertex2i(340, 32);
        glEnd();
    }

    glColor3f(0.7f, 0.7f, 0.7f);                    // alpha scrollbar stuff
    sprintf(str, "Alpha: %3.0f%%", a*100.0f);
    drawString(str, 230, 10, 0);
    if (v->windowWidth>542) {
        glBegin(GL_QUADS);
            glVertex2i(340, 13);
            glVertex2f(340+a*200.0f, 13.0f);
            glVertex2f(340+a*200.0f, 16.0f);
            glVertex2i(340, 16);
        glEnd();
        glColor3f(0.6f, 0.6f, 0.6f);
        glBegin(GL_LINE_LOOP);
            glVertex2i(340, 11);
            glVertex2i(540, 11);
            glVertex2i(540, 17);
            glVertex2i(340, 17);
        glEnd();
    }
    glDisable(GL_BLEND);
}



// }}}
// drawHelp() {{{



// ----------------------------------------------------------------------------
// This function displays help at window' top area
// ----------------------------------------------------------------------------
void drawHelp(View *v)
{
    char *helpStr[]={
        "F-full screen",
        "W-window",
        "R-redraw",
        "A-axes on/off",
        "I-info on/off",
        "H-help on/off",
        "D-depth buffer",
        "B-blending",
        "N-none shading",
        "X-ax.x shading",
        "Y-ax.y shading",
        "Z-ax.z shading",
        "O-fog shading",
        "P-save pos.",
        "S-save size",
        "Q-quit",
        "ESC-quit",
        NULL
    };
    int i;

    if (v->windowHeight<396) return;                // ensure proper window size
    if (v->windowWidth<140) return;

    glEnable(GL_BLEND);                             // draw background
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glColor4fv(getHelpBackground());
    glRecti(0, v->info ? 90:0, 140, v->windowHeight-30);

    glColor4fv(getHelpColor());                     // get text color from configuration
    for (i=0; helpStr[i]; i++) {                    // print all help-lines
        drawString(helpStr[i], 10, v->windowHeight-50-i*15, 0);
    }
    glDisable(GL_BLEND);
}



// }}}
// drawMainMenu() {{{



// ----------------------------------------------------------------------------
// This function displays area and text for main menu rectangle
// ----------------------------------------------------------------------------
void drawMainMenu(View *v, int activeItem)
{
    if (v->windowWidth<230) return;                 // ensure proper window size

    glEnable(GL_BLEND);                             // draw background
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glColor4fv(getInfoBackground());
    glRecti(0, v->windowHeight, 230, v->windowHeight-30);

    if (activeItem==mainMenu)                       // highlight menu when necessary
        glColor4fv(getActiveItemColor());
    else
        glColor4fv(getInfoColor());
    drawString("Click here for main menu", 20, v->windowHeight-20, 0);
    glColor4fv(getAxisColor());
    glBegin(GL_LINE_LOOP);
        glVertex2i( 10, v->windowHeight-24);
        glVertex2i(220, v->windowHeight-24);
        glVertex2i(220, v->windowHeight-8);
        glVertex2i( 10, v->windowHeight-8);
    glEnd();
    glDisable(GL_BLEND);
}



// }}}
// buttonsInit() {{{



// ----------------------------------------------------------------------------
// This function initializes Button structure
// ----------------------------------------------------------------------------
void buttonsInit(void)
{
    buttons[0].menu=modelMenu;                      // fill in all fields
    buttons[1].menu=surfelMenu;
    buttons[2].menu=shadingMenu;
    buttons[3].menu=zBufferMenu;
    buttons[4].menu=surfelCountMenu;
    buttons[5].menu=blendingMenu;
    buttons[6].menu=settingsMenu;
}



// }}}
// buttonObjectClick() {{{



// ----------------------------------------------------------------------------
// This callback function is called when user click on [Object type] button
// ----------------------------------------------------------------------------
void buttonObjectClick(void)
{
    o.type++;
    if (o.type>CommandModelLast)                    // check type counter
        o.type=CommandModelFirst;
    if (o.type<CommandModelFirst)
        o.type=CommandModelLast;
    glutSetCursor(GLUT_CURSOR_WAIT);
    objectCreate(&o, o.type, s.position);           // create and align new object
    objectSizeMove(&o);
    glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
}



// }}}
// buttonSurfelsSizeClick() {{{



// ----------------------------------------------------------------------------
// This callback function is called when user click on [Surfel size] button
// ----------------------------------------------------------------------------
void buttonSurfelsSizeClick(void)
{
    v.surfelsSize++;                                // increase surfel size
    v.surfelsSize%=10;                              // and check ranges
    if (!v.surfelsSize)
        v.surfelsSize=1;
}



// }}}
// buttonShadingClick() {{{



// ----------------------------------------------------------------------------
// This callback function is called when user click on [Shading] button
// ----------------------------------------------------------------------------
void buttonShadingClick(void)
{
    v.shading++;
    if (v.shading>CommandShadingFog)
        v.shading=CommandShadingNone;
}



// }}}
// buttonZBufferClick() {{{



// ----------------------------------------------------------------------------
// This callback function is called when user click on [Z-buffer] button
// ----------------------------------------------------------------------------
void buttonZBufferClick(void)
{
    v.zBuffer=!v.zBuffer;                           // toggle z-buffer flag
}



// }}}
// buttonBlendingClick() {{{



// ----------------------------------------------------------------------------
// This callback function is called when user click on [Blending] button
// ----------------------------------------------------------------------------
void buttonBlendingClick(void)
{
    o.blending=!o.blending;                         // toggle blending flag
}



// }}}
// buttonSurfelsCountClick() {{{



// ----------------------------------------------------------------------------
// This callback function is called when user click on Surfels Count button
// ----------------------------------------------------------------------------
void buttonSurfelsCountClick(void)
{
    int sizes[]={10, 20, 25, 50, 100, 150, 200, 250};
    int size=s.position;
    int i;
    for (i=0; i<8; i++) {                           // find proper size
        if (size<sizes[i]) {
            s.position=sizes[i];
            break;
        }
        s.position=sizes[0];
    }
    glutSetCursor(GLUT_CURSOR_WAIT);
    objectCreate(&o, o.type, s.position);           // create and align new object
    objectSizeMove(&o);
    glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
    glutPostRedisplay();
}



// }}}
// buttonSettingsClick() {{{



// ----------------------------------------------------------------------------
// This callback function is called when user click on Settings button
// ----------------------------------------------------------------------------
void buttonSettingsClick(void)
{
    settings++;
    if (settings>CommandSettingsScrollBarColor)
        settings=CommandSettingsSurfelsCount;
    glutPostRedisplay();
}



// }}}
// updateScrollbar() {{{



// ----------------------------------------------------------------------------
// This function updates scrollbar
// ----------------------------------------------------------------------------
#ifdef __BORLANDC__
#pragma option -w-par
#endif
void updateScrollbar(Scrollbar *s, int x, int y)
{
    s->position=x-s->xmin+1;
}
#ifdef __BORLANDC__
#pragma option -w+par
#endif



// }}}
// viewInitMouse() {{{



// ----------------------------------------------------------------------------
// This function initializes mouse structure
// ----------------------------------------------------------------------------
void viewInitMouse(Mouse *m)
{
    m->xnew=m->ynew=m->znew=0;                      // initial mouse cursor
    m->xold=m->yold=m->zold=0;                      // position
    m->x1=m->y1=m->z1=0;                            // and derived values
    m->xtran0=m->ytran0=0;
    m->xtran1=m->ytran1=0;
    m->xtran2=m->ytran2=0;
}



// }}}
// viewInit() {{{



// ----------------------------------------------------------------------------
// This function initializes view structure
// ----------------------------------------------------------------------------
void viewInit(View *v)
{
    v->fov=45.0;
    v->nearPlane=0.1f;
    v->farPlane=1000.0;
    v->windowWidth=WINDOW_WIDTH;
    v->windowHeight=WINDOW_HEIGHT;
    v->surfelsSize=2.0;
    v->help=true;
    v->info=true;
    v->axis=true;
    v->shading=CommandShadingNone;
    v->zBuffer=false;
}



// }}}
// viewLeft() {{{



// ----------------------------------------------------------------------------
// This function changes camera to left-view
// ----------------------------------------------------------------------------
void viewLeft(Mouse *m)
{
    m->xnew=m->xold=m->x1=90;
    m->ynew=m->yold=m->y1=0;
    m->znew=m->zold=m->z1=0;
    glutPostRedisplay();
}



// }}}
// viewRight() {{{



// ----------------------------------------------------------------------------
// This function changes camera to right-view
// ----------------------------------------------------------------------------
void viewRight(Mouse *m)
{
    m->xnew=m->xold=m->x1=-90;
    m->ynew=m->yold=m->y1=0;
    m->znew=m->zold=m->z1=0;
    glutPostRedisplay();
}



// }}}
// viewTop() {{{



// ----------------------------------------------------------------------------
// This function changes camera to top-view
// ----------------------------------------------------------------------------
void viewTop(Mouse *m)
{
    m->xnew=m->xold=m->x1=0;
    m->ynew=m->yold=m->y1=90;
    m->znew=m->zold=m->z1=0;
    glutPostRedisplay();
}



// }}}
// viewBottom() {{{



// ----------------------------------------------------------------------------
// This function changes camera to bottom-view
// ----------------------------------------------------------------------------
void viewBottom(Mouse *m)
{
    m->xnew=m->xold=m->x1=0;
    m->ynew=m->yold=m->y1=-90;
    m->znew=m->zold=m->z1=0;
    glutPostRedisplay();
}



// }}}
// viewOriginal() {{{



// ----------------------------------------------------------------------------
// This function changes camera to original view
// ----------------------------------------------------------------------------
void viewOriginal(Mouse *m)
{
    m->xnew=m->xold=m->x1=0;
    m->ynew=m->yold=m->y1=0;
    m->znew=m->zold=m->z1=0;
    glutPostRedisplay();
}



// }}}
// viewRotateByMouse() {{{



// ----------------------------------------------------------------------------
// This function performs object rotation
// ----------------------------------------------------------------------------
void viewRotateByMouse(Mouse *m, int x, int y)
{
    m->xnew=m->xold+x-m->x1;
    m->ynew=m->yold+y-m->y1;
    glutPostRedisplay();
}



// }}}
// viewTranslateByMouse() {{{



// ----------------------------------------------------------------------------
// This function performs translation to back/forward
// ----------------------------------------------------------------------------
#ifdef __BORLANDC__
#pragma option -w-par
#endif
void viewTranslateByMouse(Mouse *m, int x, int y)
{
    m->znew=m->zold+y-m->z1;
    glutPostRedisplay();
}
#ifdef __BORLANDC__
#pragma option -w+par
#endif



// }}}
// onInit() {{{



// ----------------------------------------------------------------------------
// Callback function called before main loop begins
// ----------------------------------------------------------------------------
void onInit(void)
{
    viewInit(&v);                                   // initialize view and mouse
    viewInitMouse(&m);
    objectInit(&o);
    glutSetCursor(GLUT_CURSOR_WAIT);
    objectCreate(&o, o.type, 100);                  // create and align new object
    objectSizeMove(&o);
    glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
    glClearColor(0.0, 0.0, 0.0, 0.0);               // set OpenGL state
    glClearDepth(1.0f);
    glDepthFunc(GL_LESS);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
    glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
    glEnable(GL_POINT_SMOOTH);
    settingsReset();
}



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



// ----------------------------------------------------------------------------
// Callback function called when system needs to resize window
// ----------------------------------------------------------------------------
void onResize(int w, int h)
{
    glViewport(0, 0, w, h);
    v.windowWidth=w;
    v.windowHeight=h;
}



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



// ----------------------------------------------------------------------------
// Callback function called when system needs repaint window
// ----------------------------------------------------------------------------
void onDisplay(void)
{
    GLfloat *rgba;
    rgba=getModelBackground();
    glClearColor(rgba[0], rgba[1], rgba[2], rgba[3]);
    if (v.zBuffer)                                  // clear Z-buffer
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    else
        glClear(GL_COLOR_BUFFER_BIT);
    glPointSize(v.surfelsSize);

    glMatrixMode(GL_PROJECTION);                    // set projection matrix
    glLoadIdentity();
    gluPerspective(v.fov,(double)v.windowWidth/(double)v.windowHeight, v.nearPlane, v.farPlane);

    glMatrixMode(GL_MODELVIEW);                     // set modelview matrix
    glLoadIdentity();
    glViewport(m.xtran0, -m.ytran0, v.windowWidth, v.windowHeight);

    glTranslatef(v.help ? 15.0f:0.0f, v.info ? 15.0f:0.0f, -250.0f); // rotate and
    glTranslatef(0.0f,0.0f,m.znew);                                  // translate model
    glRotatef(m.ynew+rotX, 1.0, 0.0, 0.0);
    glRotatef(m.xnew+rotY, 0.0, 1.0, 0.0);
    glTranslatef(-(MINX+MAXX)/2.0, -(MINY+MAXY)/2.0, -(MINZ+MAXZ)/2.0);
    if (v.axis) drawAxis();
    drawObject(&o, &v);

    glViewport(0, 0, v.windowWidth, v.windowHeight);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, v.windowWidth, 0, v.windowHeight, -1, 1);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    o.size=s.position;
    drawMainMenu(&v, activeItem);
    if (v.info) {
        drawInfo(&o, &v, modelString[o.type-CommandModelFirst], activeItem);
        if (settings==CommandSettingsSurfelsCount) {
            drawSurfelsInfo(&o, &v, &m, activeItem);
            drawScrollbar(&v, &s);
        }
        else
            drawColorScrollbars(&v, settings-CommandSettingsSurfelsCount-1, activeItem);
    }
    if (v.help) drawHelp(&v);
    glFlush();
    glutSwapBuffers();
}



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



// ----------------------------------------------------------------------------
// Callback function called when user presses some ASCII key
// ----------------------------------------------------------------------------
#ifdef __BORLANDC__
#pragma option -w-par
#endif
void onKeyboard(unsigned char key, int x, int y)
{
    if (key>='A' && key<='Z')                       // change letter' case
        key+='a'-'A';

    if (key>='1' && key<='9') {                     // set surfel size
        v.surfelsSize=key-'0';
        glutPostRedisplay();
        return;
    }

    switch (key) {
        case 27:    exit(0);                                                 break;
        case 'q':   exit(0);                                                 break;
        case 'f':   glutFullScreen();                                        break;
        case 'w':   glutReshapeWindow(620, 460); glutPositionWindow(20, 20); break;
        case 's':   objectSaveSize(&o, &v);                                  break;
        case 'p':   objectSave(&o);                                          break;
        case 'r':   glutSetCursor(GLUT_CURSOR_WAIT);
                    objectCreate(&o, o.type, s.position);
                    objectSizeMove(&o);
                    glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
                    glutPostRedisplay();                                     break;
        case 'h':   v.help=!v.help;                glutPostRedisplay();      break;
        case 'i':   v.info=!v.info;                glutPostRedisplay();      break;
        case 'a':   v.axis=!v.axis;                glutPostRedisplay();      break;
        case 'n':   v.shading=CommandShadingNone;  glutPostRedisplay();      break;
        case 'x':   v.shading=CommandShadingX;     glutPostRedisplay();      break;
        case 'y':   v.shading=CommandShadingY;     glutPostRedisplay();      break;
        case 'z':   v.shading=CommandShadingZ;     glutPostRedisplay();      break;
        case 'o':   v.shading=CommandShadingFog;   glutPostRedisplay();      break;
        case 'd':   v.zBuffer=!v.zBuffer;          glutPostRedisplay();      break;
        case 'b':   o.blending=!o.blending;        glutPostRedisplay();      break;
        case 'c':   buttonSurfelsCountClick();                               break;
        // CTRL+letter
        case 'M'-64:rotXd=rotYd=0.0f;                                        break;
        case 'R'-64:settingsReset();               glutPostRedisplay();      break; // reset settings
        case 'L'-64:settingsLoad();                glutPostRedisplay();      break; // load settings
        case 'S'-64:settingsSave();                glutPostRedisplay();      break; // save settings
        // non-alphabet keys
        case '.':
        case '>':   buttonObjectClick();                                     break;
        case ',':
        case '<':   o.type-=2; buttonObjectClick();                          break;
        default:                                                             break;
    }
}
#ifdef __BORLANDC__
#pragma option -w+par
#endif



// }}}
// onSpecialKeyboard() {{{



// ----------------------------------------------------------------------------
// Callback function called when user presses some non-ASCII key
// ----------------------------------------------------------------------------
#ifdef __BORLANDC__
#pragma option -w-par
#endif
void onSpecialKeyboard(int key, int x, int y)
{
    switch (key) {
        case GLUT_KEY_F1:        setModelAlpha(0.1f); glutPostRedisplay();   break;
        case GLUT_KEY_F2:        setModelAlpha(0.2f); glutPostRedisplay();   break;
        case GLUT_KEY_F3:        setModelAlpha(0.3f); glutPostRedisplay();   break;
        case GLUT_KEY_F4:        setModelAlpha(0.4f); glutPostRedisplay();   break;
        case GLUT_KEY_F5:        setModelAlpha(0.5f); glutPostRedisplay();   break;
        case GLUT_KEY_F6:        setModelAlpha(0.6f); glutPostRedisplay();   break;
        case GLUT_KEY_F7:        setModelAlpha(0.7f); glutPostRedisplay();   break;
        case GLUT_KEY_F8:        setModelAlpha(0.8f); glutPostRedisplay();   break;
        case GLUT_KEY_F9:        setModelAlpha(0.9f); glutPostRedisplay();   break;
        case GLUT_KEY_F10:       setModelAlpha(1.0f); glutPostRedisplay();   break;
        case GLUT_KEY_HOME:      viewLeft(&m);                               break;
        case GLUT_KEY_END:       viewRight(&m);                              break;
        case GLUT_KEY_PAGE_UP:   viewTop(&m);                                break;
        case GLUT_KEY_PAGE_DOWN: viewBottom(&m);                             break;
        case GLUT_KEY_INSERT:    viewOriginal(&m);                           break;
        case GLUT_KEY_LEFT:      rotXd-=0.5f;                                break;
        case GLUT_KEY_RIGHT:     rotXd+=0.5f;                                break;
        case GLUT_KEY_UP:        rotYd-=0.5f;                                break;
        case GLUT_KEY_DOWN:      rotYd+=0.5f;                                break;
        default:                                                             break;
    }
}
#ifdef __BORLANDC__
#pragma option -w+par
#endif



// }}}
// onMouseButton() {{{



// ----------------------------------------------------------------------------
// Callback function called on mouse click
// ----------------------------------------------------------------------------
void onMouseButton(int button, int state, int x, int y)
{
    if (button==GLUT_LEFT_BUTTON) {                 // if left mouse button is pressed or released
        processLeftMouseButton(buttons, state, x, y);
        glutPostRedisplay();
    }
    if (button==GLUT_RIGHT_BUTTON) {                // if right mouse button is pressed or released
        processRightMouseButton(state, x, y);
        glutPostRedisplay();
    }
}



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



// ----------------------------------------------------------------------------
// Callback function called when user moves mouse pointer with button pressed
// ----------------------------------------------------------------------------
void onMouseMotion(int x, int y)
{
    switch (m.status) {
        case 1: viewRotateByMouse(&m, x, y); break; // rotate object
        case 2: viewTranslateByMouse(&m, x, y); break; // translate object
        case 3:                                     // translate camera
                m.xtran0=m.xtran2+x-m.xtran1;
                m.ytran0=m.ytran2+y-m.ytran1;
                glutPostRedisplay();
                break;
        default:
            processScrollbars(x, y);
            break;
    }
}



// }}}
// onPassiveMotion() {{{



// ----------------------------------------------------------------------------
// Callback function called when user moves mouse pointer without button pressed
// ----------------------------------------------------------------------------
void onPassiveMotion(int x, int y)
{
    glutDetachMenu(GLUT_LEFT_BUTTON);
    glutDetachMenu(GLUT_RIGHT_BUTTON);
    setMenuForButton(buttons, x, y);
    setMenuForMainButton(x, y);
}



// }}}
// onTimer() {{{



// ----------------------------------------------------------------------------
// Callback function triggered in a specified number of milliseconds
// ----------------------------------------------------------------------------
void onTimer(int value)
{
    rotX+=rotXd;                                    // update rotation angles
    rotY+=rotYd;
    if (fabs(rotXd)>0.1f || fabs(rotYd)>0.1f)
        glutPostRedisplay();
    glutTimerFunc(20, onTimer, value);
}



// }}}
// onMenu() {{{



// ----------------------------------------------------------------------------
// Callback function called when user selects command from main menu
// ----------------------------------------------------------------------------
void onMenu(int command)
{
    if (command>=CommandModelFirst && command<=CommandModelLast) {
        o.type=command;
        glutSetCursor(GLUT_CURSOR_WAIT);
        objectCreate(&o, o.type, s.position);
        objectSizeMove(&o);
        glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
        glutPostRedisplay();
        return;
    }
    if (command>=CommandSurfelCount100 && command<=CommandSurfelCount62500) {
        int sizes[]={10, 20, 25, 50, 100, 150, 200, 250};
        s.position=sizes[command-CommandSurfelCount100];
        glutSetCursor(GLUT_CURSOR_WAIT);
        objectCreate(&o, o.type, s.position);
        objectSizeMove(&o);
        glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
        glutPostRedisplay();
        return;
    }
    if (command>=CommandSurfelSize1 && command<=CommandSurfelSize9) {
        v.surfelsSize=command-CommandSurfelSize1+1;
        glutPostRedisplay();
        return;
    }
    if (command>=CommandBlending01 && command<=CommandBlending10) {
        setModelAlpha((command-CommandBlending01)/10.0f+0.1f);
        glutPostRedisplay();
    }
    switch (command) {
        case CommandSavePositions:          objectSave(&o);                         break;
        case CommandSavePositionsSize:      objectSaveSize(&o, &v);                 break;
        case CommandRedraw:                 glutPostRedisplay();                    break;
        case CommandViewLeft:               viewLeft(&m);                           break;
        case CommandViewRight:              viewRight(&m);                          break;
        case CommandViewTop:                viewTop(&m);                            break;
        case CommandViewBottom:             viewBottom(&m);                         break;
        case CommandViewOriginal:           viewOriginal(&m);                       break;
        case CommandShadingNone:
        case CommandShadingX:
        case CommandShadingY:
        case CommandShadingZ:
        case CommandShadingFog:             v.shading=command; glutPostRedisplay(); break;
        case CommandZBufferEnable:          v.zBuffer=true;    glutPostRedisplay(); break;
        case CommandZBufferDisable:         v.zBuffer=false;   glutPostRedisplay(); break;
        case CommandBlendingEnable:         o.blending=true;   glutPostRedisplay(); break;
        case CommandBlendingDisable:        o.blending=false;  glutPostRedisplay(); break;
        case CommandShowHelp:               v.help=true;       glutPostRedisplay(); break;
        case CommandHideHelp:               v.help=false;      glutPostRedisplay(); break;
        case CommandShowInfo:               v.info=true;       glutPostRedisplay(); break;
        case CommandHideInfo:               v.info=false;      glutPostRedisplay(); break;
        case CommandShowAxis:               v.axis=true;       glutPostRedisplay(); break;
        case CommandHideAxis:               v.axis=false;      glutPostRedisplay(); break;
        case CommandSettingsReset:          settingsReset();   glutPostRedisplay(); break;
        case CommandSettingsLoad:           settingsLoad();    glutPostRedisplay(); break;
        case CommandSettingsSave:           settingsSave();    glutPostRedisplay(); break;
        case CommandSettingsSurfelsCount:
        case CommandSettingsModelForeground:
        case CommandSettingsModelBackground:
        case CommandSettingsFogColor:
        case CommandSettingsAxesColor:
        case CommandSettingsActiveItemColor:
        case CommandSettingsHelpForeground:
        case CommandSettingsHelpBackground:
        case CommandSettingsInfoForeground:
        case CommandSettingsInfoBackground:
        case CommandSettingsScrollBarColor:
                                             settings=command; glutPostRedisplay(); break;
        case CommandQuitYes:                 exit(0);                               break;
        case CommandQuitNo:                                                         break;
        default:                                                                    break;
    }
}



// }}}
// createMainWindow() {{{



// ----------------------------------------------------------------------------
// This function creates main window
// ----------------------------------------------------------------------------
void createMainWindow(void)
{
    glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);// set window properties
    glutInitWindowPosition(WINDOW_LEFT, WINDOW_TOP);
    glutCreateWindow(WINDOW_TITLE);
    glutDisplayFunc(onDisplay);                     // register all callback functions
    glutReshapeFunc(onResize);
    glutKeyboardFunc(onKeyboard);
    glutSpecialFunc(onSpecialKeyboard);
    glutMouseFunc(onMouseButton);
    glutMotionFunc(onMouseMotion);
    glutPassiveMotionFunc(onPassiveMotion);
    glutTimerFunc(20, onTimer, 0x1234);
}



// }}}
// createMenu() {{{



// ----------------------------------------------------------------------------
// This function creates main menu and submenus
// ----------------------------------------------------------------------------
void createMenu(void)
{
    modelMenu=gpeCreateMenu(menuModel, onMenu);
    surfelMenu=gpeCreateMenu(menuSurfelSize, onMenu);
    shadingMenu=gpeCreateMenu(menuShading, onMenu);
    zBufferMenu=gpeCreateMenu(menuZBuffer, onMenu);
    surfelCountMenu=gpeCreateMenu(menuSurfelCount, onMenu);
    blendingMenu=gpeCreateMenu(menuBlending, onMenu);
    settingsMenu=gpeCreateMenu(menuSettings, onMenu);
    mainMenu=gpeCreateMenu(menuMain, onMenu);
}



// }}}
// gpeCreateMenuFromData() {{{



// ----------------------------------------------------------------------------
// This function creates menu from given data structure
// ----------------------------------------------------------------------------
int gpeCreateMenuFromData(GpeMenu *menu, void (*f)(int command))
{
    int i=0;
    int menuID;

    menuID=glutCreateMenu(f);
    glutSetMenu(menuID);
    while (menu[i].caption!=NULL) {
        if (menu[i].subMenu) {
            int subMenu=gpeCreateMenuFromData(menu[i].subMenu, f);
            glutSetMenu(menuID);
            glutAddSubMenu(menu[i].caption, subMenu);
        }
        else {
            glutAddMenuEntry(menu[i].caption, menu[i].id);
        }
        i++;
    }
    return menuID;
}



// }}}
// gpeCreateMenu() {{{



// ----------------------------------------------------------------------------
// This function creates context menu
// ----------------------------------------------------------------------------
int gpeCreateMenu(GpeMenu *menu, void (*f)(int command))
{
    int result=gpeCreateMenuFromData(menu, f);
    return result;
}



// }}}
// main() {{{



// ----------------------------------------------------------------------------
// Main function
// ----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
    glutInit(&argc, argv);                          // initialize glut
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    createMainWindow();                             // create main window
    createMenu();                                   // create main menu and all submenus
    buttonsInit();                                  // initialize buttons
    confInit(&v, &s, &m, &settings, &activeItem);   // initialize configuration module
    onInit();                                       // initialize OpenGL
    glutMainLoop();                                 // enter main loop
    return 0;                                       // return error code
}



// }}}



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

// vim:expandtab:foldenable:foldmethod=marker:foldclose=:foldmarker={{{,}}}
//