/********************************************************************** * SIMULATION OF AN ELLIPTICAL BILLARD * * ------------------------------------------------------------------- * * In one of his books (*), the Polish mathematician Hugo Steinhaus * * outlines the idea of an elliptical billard table! He distinguishes * * three cases of trajectory, depending on throwing the ball: * * 1) between the two focuses of the ellipse 2) between one focus and * * the edge of the billard 3) passing through a focus. This program * * allows to visualize the envelope of trajectories in the three cases * * with a limitation of 100 rebounds. * * ------------------------------------------------------------------- * * From "Graphisme dans le plan et dans l'espace avec Turbo Pascal 4.0 * * By R. Dony - MASSON 1990, page 113" [BIBLI 12]. * * * * Microsoft Visual C++ version in API style by J-P Moreau * * (Program to use with Billard.mak and Gr2D.cpp) * * (www.jpmoreau.fr) * * ------------------------------------------------------------------- * * (*) Mathématiques en instantanés by Hugo Steinhaus, Flammarion 1964 * **********************************************************************/ #include #include #include HDC hdc; RECT rect; //Functions used here of module Gr2D.cpp void Fenetre(double,double,double,double); void Cloture(int,int,int,int); void Bordure(); void MoveXY(double,double); void LineXY(double,double); //global variables of billard const int bord = 10, limite = 100; const double a = 5.0, b = 3.0, increment = 0.1; double f1,f2,f3,f4,m,m0,n,n0,xx1,yy1,xx2,yy2; int nbrerebonds,intersection; char rep; extern MaxX, MaxY, xgclot, yhclot; //see Gr2D.h //"home made" graphic commands for hdc environment used by above functions void DrawPixel(int ix,int iy) { //sorry, no other available command found Rectangle(hdc,rect.left+ix,rect.top+iy, rect.left+ix+2,rect.top+iy+1); } void Swap(int *i1,int *i2) { int it; it=*i1; *i1=*i2; *i2=it; } void DrawLine(int ix1,int iy1,int ix2,int iy2) { int i,il,ix,iy; float dx,dy; if (ix20.0) intersection=1; else intersection=0; } //draw elliptical border of billard void DrawEllipse() { double angle,x1,y1,x2,y2,pi=3.1415927; Cloture(bord,MaxX-bord,2*bord,MaxY-25); Bordure(); angle=0.0; x1=a; y1=0.0; MoveXY(x1,y1); while (angle < 2.0*pi) { angle=angle+increment; x2=a*cos(angle); y2=b*sin(angle); LineXY(x2,y2); } } //draw a cross at point (x,y) void croixaufoyer(double x, double y) { double l; l=0.03*a; MoveXY(x-l,y); LineXY(x+l,y); MoveXY(x,y-l); LineXY(x,y+l); } //draw focuses of ellipse void TraceFoyers() { double xf; xf=sqrt(a*a-b*b); croixaufoyer(-xf,0); croixaufoyer(xf,0); } //swap coordinates void permute() { double aux; aux=xx1; xx1=xx2; xx2=aux; aux=yy1; yy1=yy2; yy2=aux; } //find intersection between line and ellipse void SeekIntersection() { double delta,denom; delta=a*a*b*b*(a*a*m*m+b*b-n*n); denom=a*a*m*m+b*b; if (delta>0) xx1=(-a*a*m*n+sqrt(delta))/denom; yy1=m*xx1+n; if (delta>0) xx2=(-a*a*m*n-sqrt(delta))/denom; yy2=m*xx2+n; if (m>0) {if (yy1>yy2) permute();} else if (m<0) {if (yy1xx2) permute(); } //draw trajectories with a maximum of 100 rebounds void Trajectory() { double xprec,yprec,tgphi,tgteta,angleinc,anglephi; nbrerebonds=0; MoveXY(f1,m*f1+n); LineXY(xx2,yy2); nbrerebonds++; do { if (xx2!=0.0) tgphi=a*a*yy2/(b*b*xx2); else tgphi=1E15; tgteta=m; angleinc=atan((tgphi-tgteta)/(1+tgphi*tgteta)); anglephi=atan(tgphi); m=sin(angleinc+anglephi)/cos(angleinc+anglephi); if (fabs(n) < 1E-3) { m=0; n=0; } else n=yy2-m*xx2; xprec=xx2; yprec=yy2; SeekIntersection(); if (fabs(xprec-xx1) > 1E-6) permute(); MoveXY(xprec,yprec); LineXY(xx2,yy2); nbrerebonds++; } while (nbrerebonds <= limite); } // bip if line does not meet the ellipse void Bip() { MessageBeep(1); } //The initial line has for equation y = mx + n, with m=1.5 and n=3.5 //These values may be changed by the program user void Draw_Billard() { char s[20]; MaxX=(rect.right-rect.left); MaxY=(rect.bottom-rect.top); //set initial values m0=1.5; n0=3.5; m=m0; n=n0; f1=-1.1*a; f2=-f1; f3=-1.1*b; f4=-f3; Fenetre(f1,f2,f3,f4); Test(); if (intersection) { OutText((MaxX/2)-90,5,"ELLIPTICAL BILLARD"); DrawEllipse(); TraceFoyers(); SeekIntersection(); Trajectory(); sprintf(s,"M=%4.1f N=%4.1f",m0,n0); OutText(xgclot+10,MaxY-yhclot+25,s); } else { Bip(); OutText(25,25,"The line does not meet the ellipse !"); } } //Handle window Paint and Destroy messages long FAR PASCAL /*_export*/ WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam) { HPEN hpen, hpenOld; PAINTSTRUCT ps; switch (message) { case WM_PAINT: hdc = BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rect); hpen = CreatePen(PS_SOLID, 1, RGB(0, 0, 255)); hpenOld = SelectObject(hdc,hpen); Draw_Billard(); SelectObject(hdc,hpenOld); DeleteObject(hpen); EndPaint(hwnd, &ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); } // main program int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow) { static char szAppName[] = "Billard"; HWND hwnd; MSG msg; WNDCLASS wndclass; if (!hPrevInstance) { wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; RegisterClass(&wndclass); } hwnd = CreateWindow(szAppName, // window class name "BILLARD", // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL); // creation parameters ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //end of file billard.cpp