Musterlösung

7.Praktikum

Mandlbrot-Menge mandel1.gif

Hinweise zum Programmieren

Hier ist kein komplettes programm! Dies soll mit den Kenntnissen selbst zusammen gestellt werden.

// zum Abbrechen:
#define DO_STOP ((GetAsyncKeyState(VK_ESCAPE)<0))
// Weltgrösse
GLdouble x_min = -3.0; GLdouble x_max = +1.0;
GLdouble y_min = -2.0; GLdouble y_max = +2.0;
// ermittle die Anzahl der Iterationsschritte:
int count_mandel_schritte( int maxCount, double x0, double y0 ) 
{ int anz = 0;  double h, x = x0, y = y0, r = 0.0;

  while (( anz < maxCount ) && ( r < 4.0 )) { 
    h = x ;
    x = h * h - y * y + x0 ;  
    y = h * y; y += y + y0 ; 
    r = x * x + y * y ; 
    anz ++ ;
  } return anz ;
}

Anstelle von void draw_mandel( int di, int dj, int maxCount ) { ... } können auch globale "Paramter" verendet werden (siehe weiter unten ...)

Beispiele:







Hinweise zum Menu

Mit der folgenden Methode kann ein Menu erstellt werden. Zur Schreibvereinfachung werden (wie angegeben) die MACROS verwendet, wie z.B.:

  MENU_BEGIN()                                 // kein ;
    MENU_POPUP("Popup&1")                      // kein ;
      MENU_ITEM("Item &1 zu Popup1", show_1)   // kein ;
      MENU_ITEM("Item &2 zu Popup1", show_2)   // kein ;
    MENU_POPUP("&Fraktale ")                    // kein ;
      MENU_ITEM("...", draw_...1)  // kein ;
      MENU_ITEM("...", draw_...2)  // kein ;
  MENU_END()                                   // kein ;

Die Menu-Funktionen müssen vom Typ sein (sonst "Abstürze, Stacküberlauf, usw."):
typedef void (CALLBACK* SHOW_FUNC)();
SHOW_FUNC show_func;

Menu-Funktionen sehen dann z.B. so aus:
void CALLBACK draw_mandel1(void) { m_color_index = 1; draw_mandel(); }
void CALLBACK draw_mandel2(void) { m_color_index = 2; draw_mandel(); }
void CALLBACK draw_mandel3(void) { m_color_index = 3; draw_mandel(); }
void CALLBACK draw_mandel4(void) { m_color_index = 4; draw_mandel(); }
void CALLBACK set_org(void) { ...}
Als Menu-Item-Identifizierer (WM_COMMAND, wParam) wird der Funktionszeiger einer Menu-Funktion verwendet, was zwar einfach in der Benutzung ist, aber auch "gefährlich".

/* Rahmen-Quelltext: OpenGL mit Windows GLAUX.LIB OPENGL32.LIB GLU32.LIB */ 
#include <windows.h>
#include <gl\glaux.h>
#include <gl\gl.h>
#include <stdio.h>
#include <math.h>
#pragma comment(lib, "GLAUX.LIB")
#pragma comment(lib, "OPENGL32.LIB")
#pragma comment(lib, "GLU32.LIB")
#pragma comment(linker,"/defaultlib:kernel32.lib")
#pragma comment(linker,"/entry:mainCRTStartup")
#pragma comment(linker,"/subsystem:windows")

////////////////////////////////////////////////////////
// AB HIER NICHTS ÄNDERN BITTE BIS ...
#define err_if(e,errStr) if(e)if(IDOK!=MessageBox(0,TEXT(errStr),0,\
        MB_OKCANCEL|MB_DEFBUTTON2|MB_ICONSTOP|MB_SETFOREGROUND))exit(-1);
#define DO_STOP ((GetAsyncKeyState(VK_ESCAPE)<0))

void CALLBACK ChangeSize(int w, int h);
LONG old_wnd_proc;   // vorherige CALLBACK-Funktion
//Prototyp der CALLBACK-Funktion:
//LRESULT CALLBACK wnd_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); 
// MENU-Macros:
#define MENU_BEGIN()  {HMENU hPopup=0;TCHAR *pPopup=TEXT("");HMENU m_hMenu=CreateMenu();  
#define MENU_POPUP(text)   if(hPopup)AppendMenu(m_hMenu,MF_POPUP,(UINT_PTR)hPopup,pPopup);hPopup=CreatePopupMenu();pPopup=TEXT(text);
#define MENU_ITEM(text,func) AppendMenu(hPopup,MF_STRING,(UINT_PTR)(func),TEXT(text));
#define MENU_END()   AppendMenu(m_hMenu,MF_POPUP,(UINT_PTR)hPopup,pPopup);SetMenu(auxGetHWND(),m_hMenu);\
                     SetClassLong(auxGetHWND(),GCL_HICON,(LONG)ExtractIcon(GetModuleHandle(0),"Shell32.dll",43));\
                     old_wnd_proc = SetWindowLong(auxGetHWND(),GWL_WNDPROC,(LONG)wnd_proc);}

typedef void (CALLBACK* SHOW_FUNC)();
SHOW_FUNC show_func; // aktuelle Zeichenfunktion

LRESULT CALLBACK wnd_proc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{ if(WM_COMMAND == uMsg) {	//ist GetClassName(hwnd, buf, 512);//"tkLibWClass"
	if(!lParam ||(HIWORD(wnd_proc)==HIWORD(wParam))) { 
      show_func = (SHOW_FUNC)(wParam|(0xFFFF0000&(UINT)wnd_proc));
      InvalidateRect(hwnd,NULL,TRUE); UpdateWindow(hwnd);
	} 
  } //SetWindowPos(auxGetHWND(),HWND_TOP,0,0,0,0,(SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOSIZE|SWP_SHOWWINDOW));
  return CallWindowProc((WNDPROC)old_wnd_proc,hwnd,uMsg,wParam,lParam);
}
// ... BIS HIERHER
////////////////////////////////////////////////////////

So ist etwa die main-Funktion aufgebaut:

int main(int argc, char* argv[])
{
  auxInitDisplayMode(AUX_DOUBLE|AUX_RGBA|AUX_DEPTH);
  auxInitPosition(200,200, m_width, m_hight);
  auxInitWindow("GDV-Musterlösung (Fraktale)");
  
  // HIER WERDEN TASTEN EINGEFÜGT
  auxKeyFunc('1', set_1_aktiv);   // Taste 1
  auxKeyFunc('2', set_2_aktiv);   // Taste 2
  auxKeyFunc('3', set_3_aktiv);   // Taste 3
  
  // HIER WERDEN MAUS_CALLBACK-Funktionen hinterlegt
  auxMouseFunc(AUX_LEFTBUTTON,  AUX_MOUSEDOWN, mouse_ldown ); 
  auxMouseFunc(AUX_RIGHTBUTTON, AUX_MOUSEDOWN, mouse_rdown ); 
  auxReshapeFunc( ChangeSize );

/*
  // HIER WIRD DAS MENU EINGEFÜGT
  MENU_BEGIN()                                 // kein ;
    MENU_POPUP("Popup&1")                      // kein ;
      MENU_ITEM("Item &1 zu Popup1", show_1)   // kein ;
      MENU_ITEM("Item &2 zu Popup1", show_2)   // kein ;
    MENU_POPUP("&Fraktale ")                    // kein ;
      MENU_ITEM("draw_mandel (colortyp &1), Maus-Klick-Zoom on", draw_mandel1)  // kein ;
      MENU_ITEM("draw_mandel (Colortyp &2), Maus-Klick-Zoom on", draw_mandel2)  // kein ;
      MENU_ITEM("draw_mandel (Colortyp &3), Maus-Klick-Zoom on", draw_mandel3)  // kein ;
      MENU_ITEM("draw_mandel (Colortyp &4), Maus-Klick-Zoom on", draw_mandel4)  // kein ;
      MENU_ITEM("feine Rasterung und &langsam, Maus-Klick-Zoom on", set_fein)   // kein ;
      MENU_ITEM("mittlere Rasterung und &bunt, Maus-Klick-Zoom on", set_mittel) // kein ;
      MENU_ITEM("grobe Rasterung und &scnell, Maus-Klick-Zoom on",  set_grob)   // kein ;
      MENU_ITEM("&Reset (org. Weltgrösse), Maus-Klick-Zoom on",     set_org)    // kein ;
    MENU_POPUP("&Beispiele")                    // kein ;
      MENU_ITEM("beispiel &1, Maus-Klick-Zoom off", draw_mandel_a)  // kein ;
      MENU_ITEM("beispiel &2, Maus-Klick-Zoom off", draw_mandel_b)  // kein ;
      MENU_ITEM("beispiel &3, Maus-Klick-Zoom off", draw_mandel_c)  // kein ;
      MENU_ITEM("beispiel &4, Maus-Klick-Zoom off", draw_mandel_d)  // kein ;
    MENU_POPUP("&Help")                        // kein ;
      MENU_ITEM("&Info ", help1)               // kein ;
      MENU_ITEM("&About", help2)               // kein ;
  MENU_END()                                   // kein ;
*/
  auxMainLoop( RenderScene ); 
  return 0;
}

Es ist oft günstig, wenn die vielen möglichen Arten der Einstellungen und Auswahlmöglichkeiten des Menüs mit Hilfe von globale Konstanten gesteuert wird. Ein Menu-Item setzt dann lediglich diese globale Konstante(n), die oft an dem m_... erkennbar sind und später zu Member-Variablen werden können. Global ist z.B.:

// oben nach den #include ...
#define PI 3.14159265
GLdouble x_min = -3.0; GLdouble x_max = +1.0; // x-Weltgrösse
GLdouble y_min = -2.0; GLdouble y_max = +2.0; // y-Weltgrösse

GLint    m_width = 300;  GLint m_hight = 300; //Pixel-Breite,-Höhe
GLint    m_di = 2, m_dj = 2; // Vergröberungsfaktoren
GLint    m_color_index  = 1; // Art der farbgestaltung
GLdouble m_dx, m_dy;         // Welt-Schrittweite 
GLint    m_maxCount = 40;    // maximale Anzahl von Iterationen
GLint    m_anzCount;         // aktuelle Anzahl  
GLdouble m_rsum;             // Quadrat-Summe

Beispiele für Menu-Item-Funktionen sind:

void CALLBACK draw_mandel1(void) { m_color_index = 1; draw_mandel(); }
void CALLBACK draw_mandel2(void) { m_color_index = 2; draw_mandel(); }
void CALLBACK draw_mandel3(void) { m_color_index = 3; draw_mandel(); }
void CALLBACK draw_mandel4(void) { m_color_index = 4; draw_mandel(); }
void CALLBACK set_org(void)    {  
	 x_min = -3.0; x_max = +1.0; // x-Weltgrösse
    y_min = -2.0; y_max = +2.0; // y-Weltgrösse
	 m_di = 2;  m_dj = 2; m_maxCount = 40; 
	 ChangeSize(0,0); 
	 draw_mandel(); 
}

count_mandel_schritte(x0,y0) ermittelt an einem bestimmten Punkt (x0,y0) die Anzahl m_anzCount der Iterationen. m_anzCount entspricht (in irgend einer Art) der Farben des Punktes (x0,y0). Die Funktion set_color() benutzt den aktuell-eingestellten Wert m_color_index, um daraus in einer kreativen Art ein glColor3d(r(m_color_index),g(m_color_index),b(m_color_index)) zu machen.

void count_mandel_schritte(double x0,double y0) 
{ 
	GLdouble x,y, h, r = 0.0;
	m_anzCount = 0; m_rsum = 0.0; 
	x = x0; 
	y = y0; 
	
  while (( m_anzCount < m_maxCount ) && ( r < 4.0 )) { 
    h = x ;
    x = h * h - y * y + x0 ; 
    y = h * y; y += y + y0 ; 
	 r = x * x + y * y ;   
	 m_rsum += r;
	 m_anzCount++; 	
  } 
}

draw_mandel() ist eine Menu-Item-Funktion, die dann etwa wie folgt aussieht:

void CALLBACK draw_mandel(void)
{
  m_dx = (x_max - x_min)/m_width; m_dx *= m_di;
  m_dy = (y_max - y_min)/m_hight; m_dy *= m_dj;

  double x,y;
  for ( x = x_min; x < x_max; x += m_dx ) {
    for ( y = y_min; y < y_max; y += m_dy ) {
      if(DO_STOP) return; 
      count_mandel_schritte(x,y); 
      set_color();
	  if((m_di==1)&&(m_dj==1)){
        glBegin(GL_LINES);
          glVertex3d(x,        y,        0 );
          glVertex3d(x + m_dx, y + m_dy, 0 );
        glEnd();
	  } else {
	    err_if(1,"es ist noch was zu tun!!!");
	  }
    }
  }
}

Um Nachrichten-Rekurisonen zu vermeiden braucht eine MessageBox() das Zurücksetzen von show_func = 0;

void help1(void) { show_func = 0;
  MessageBox(auxGetHWND(),
      "1. Mit einem linken Mausklick kann in das Farktal hinein-gezoomt werden.\n" \
      "2. Mit einem rechten Mausklick kann in das Farktal hinein-gezoomt werden.\n" \
	  "3. Auc können die Taste '1' oder Alt+1 zum Zeichnen verwendet werden\n" 
      ,"Erklärungen",MB_OK);
}

Wie sieht ChangeSize() aus? Damit eigene Aufrufe von ChangeSize(0,0) den Viewport setzen, holt sich in diesem Fall glGetInteger() die aktuellen Viewport-Werte.

void CALLBACK ChangeSize(int w, int h) 
{ 
  if((w<1)||(h<1)) {
    int ijwh[4]; glGetIntegerv(GL_VIEWPORT, ijwh); 
    m_width = ijwh[2]; 
    m_hight = ijwh[3];
  } else {
    m_width = w; 
    m_hight = h;
  }
  glViewport( 0,0, m_width, m_hight ); 
  glMatrixMode( GL_MODELVIEW );glLoadIdentity();
  glOrtho(x_min,x_max,y_min,y_max,-100,100); // = volle Fläche
}

void CALLBACK RenderScene( void ) {
  glClear(GL_COLOR_BUFFER_BIT);
  if(show_func) show_func();
  auxSwapBuffers();
}

Wie kann durch einen Maus-Klick an der Maus-Position eine Ausschnittsvergrösserung (und ähnlich eine Ausschnittsverkleinerung) erfolgen?

// 
void CALLBACK mouse_rdown( AUX_EVENTREC *e )  
{     
  double x0,y0, dx,dy;
  int i0 = e -> data[AUX_MOUSEX];
  int j0 = e -> data[AUX_MOUSEY];
  int    di = m_width;
  int    dj = m_hight;
  dx = x_max - x_min; x0 = x_min + dx * i0/di;
  dy = y_max - y_min; y0 = y_max - dy * j0/dj; 
  x_min = x0 - dx*2; x_max = x0 + dx*2;
  y_min = y0 - dy*2; y_max = y0 + dy*2;
  //printf("\nx_min=%8.5f, x_max=%8.5f, y_min=%8.5f, y_max=%8.5f",x_min,x_max,y_min,y_max);
  ChangeSize(0,0);
}