/******************************************************************** * * * Joukowski Airfoil Generator with Streamline and Pressure * * Distribution Algorithms * * * * Written by: Russell Leighton 15 March 1987 * * Lancaster, CA * * Modified by: David Foster 19 June 1988 * * Rochester, MI * * To include effect of induced circulation * * by a first order approximation. * ********************************************************************/ #include "airfoil.h" main() { float rs,theta,h,vel,eta,S; int psi; ULONG MessageClass; open_things(); do_about(); /****************/ /* Loop forever */ /****************/ for(;;) { while (Continue) { /****************************************************/ /* Wait, initially and after each plot, for user to */ /* bring up the double menu requester */ /****************************************************/ Wait(1L<UserPort->mp_SigBit); if((message = (struct IntuiMessage *)GetMsg(w->UserPort)) != NULL) { MessageClass = message->Class; ReplyMsg(message); if (MessageClass == REQVERIFY) { do_request(); break; } } /* end if */ } /* end while */ do_init(); if(mode) { /********************/ /* Plot Streamlines */ /********************/ FILL = TRUE; for (psi = 12;psi > 0;--psi) { do_mess(); if(!Continue) break; PENUP; SetAPen(rp,(long)(psi+1)); SetBPen(rp,(long)(psi+1)); for (theta = 0.015;theta < TWO_PI;theta += PI/100) { vel = psi/(4.*velocity*r*sin(theta)); S = (1.+sin(alpha)/sin(theta)); vel = abs(S/(2.*vel)); eta = sqrt(vel*vel+1.0)-vel; rs = r*(1.+eta)/(1.-eta); transform(rs,theta); } /* end for */ AreaEnd(rp); } /* end for */ /*******************************/ /* Plot Stagnation Streamlines */ /*******************************/ do_mess(); if(Continue) { FILL = FALSE; PENUP; SetAPen(rp,1L); h = (r-4.0)/40.0; theta = -alpha; for (rs = 4;rs >= r;rs += h) { transform(rs,theta); } PENUP; theta = PI + alpha; for (rs = 4;rs > r;rs += h) { transform(rs,theta); } } /* end if */ } /* end if */ else { /******************************/ /* Plot Pressure Distribution */ /******************************/ do_mess(); if(Continue) { FILL = TRUE; PENUP; SetAPen(rp,2L); SetBPen(rp,2L); for (theta = 0.0;theta <= TWO_PI;theta += PI/100) { rs = r+(sin(theta)+sin(alpha))*(sin(theta)+sin(alpha)); transform(rs,theta); } /* end for */ AreaEnd(rp); } /* end if */ } /* end else */ /****************/ /* Plot Airfoil */ /****************/ do_mess(); if(Continue) { FILL = TRUE; PENUP; rs = r; SetAPen(rp,1L); SetBPen(rp,1L); for (theta = 0.0;theta <= TWO_PI;theta += PI/100) { transform(rs,theta); } AreaEnd(rp); } /* end if */ } /* end forever */ } /* end main */ do_init() { float a0; SetAPen(rp,0L); RectFill(rp,(long)(XMIN+1),(long)(YMIN+1),(long)(XMAX-1),(long)(YMAX-1)); SetOPen(rp,1L); /***********************************************************/ /* Calculate circle constants (circle center and radius) */ /* from airfoil constants through a reverse transformation */ /***********************************************************/ alpha = (float)angle*PI/180; c = (float)camber/25; t = (float)thickness/25; a = 0; b = c/2; do { a0 = a; a = t*(2*a0+1)/4/sqrt(b*b+2*a0+1); b = c*(1+2*a)/(2+2*a); } while(abs(a-a0) > TOL); r = sqrt(b*b+(a+1)*(a+1)); Continue = TRUE; } do_mess() { ULONG MessageClass; /******************************************************************/ /* Check for double menu requester. This can be brought up at any */ /* time but can not be displayed unless the message is replied */ /* to, therefore this routine must be called periodically. */ /******************************************************************/ if((message = (struct IntuiMessage *)GetMsg(w->UserPort)) != NULL) { MessageClass = message->Class; ReplyMsg(message); if(MessageClass == REQVERIFY) do_request(); } ixo = XCEN; iyo = YCEN; } transform(rs,theta) float rs,theta; { float x,y,z,u,v; int PLOT; long ix,iy,cx,cy; /********************************************************************/ /* This is the Joukowski transformation routine. This is also where */ /* the plotting is done. Plotting is usually done using the area */ /* fill routines (AreaDraw & AreaMove). If FILL is true then the */ /* points are used to build up the area shape to be filled. See the */ /* Rom Kernal manual containing the graphics primatives for more */ /* details. If FILL is false then normal plotting is done. */ /********************************************************************/ x = rs*cos(theta-alpha)+a; y = rs*sin(theta-alpha)+b; z = 1/(x*x+y*y); u = x*(1+z)*cos(alpha)-y*(1-z)*sin(alpha); v = y*(1-z)*cos(alpha)+x*(1+z)*sin(alpha); ix = (long)(SFAC*u+XCEN); iy = (long)(YCEN-SFAC*v); if(FILL) { PLOT = FALSE; cx = ix; cy = iy; /*************************************************************/ /* This subroutine also clips the display area, hence the */ /* following statements. Contrary to the what the Rom Kernal */ /* manual states, all clipping must be done by the program */ /* if the area fill routines are used otherwise a nasty */ /* crash will result if plotting takes place out of bounds. */ /* Only the x values are clipped accurately since, for this */ /* routine only the x values at the boundary need to be */ /* accurate. The PEN parameter indicates the pen status for */ /* moves and draws as set by either PENUP or PENDOWN. */ /*************************************************************/ if((ix <= XMIN) && (ixo > XMIN)) { cy += (float)(iyo-iy)*(XMIN-ix)/(ixo-ix); cx = XMIN; } else if((ixo <= XMIN) && (ix > XMIN)) { iyo += (float)(iy-iyo)*(XMIN-ixo)/(ix-ixo); ixo = XMIN; AreaDraw(rp,ixo,iyo); PLOT = TRUE; } else if((ix >= XMAX) && (ixo < XMAX)) { cy += (float)(iyo-iy)*(XMAX-ix)/(ixo-ix); cx = XMAX; } else if((ixo >= XMAX) && (ix < XMAX)) { iyo += (float)(iy-iyo)*(XMAX-ixo)/(ix-ixo); ixo = XMAX; AreaDraw(rp,ixo,iyo); PLOT = TRUE; } if((ixo > XMIN) && (ixo < XMAX)) PLOT = TRUE; if(cy < YMIN) cy = YMIN; if(cy > YMAX) cy = YMAX; if(PLOT) { if(PEN) AreaDraw(rp,cx,cy); else { AreaMove(rp,cx,cy); PENDOWN; } } ixo = ix; iyo = iy; } else { if(PEN) Draw(rp,ix,iy); else { Move(rp,ix,iy); PENDOWN; } } } do_request() { ULONG MessageClass; /***************************************************************/ /* This subroutine is activated when the double menu requester */ /* is brought up. It handles all input from this requester. */ /***************************************************************/ if(title) ShowTitle(s,FALSE); for (;;) { Wait(1L<UserPort->mp_SigBit); if((message = (struct IntuiMessage *)GetMsg(w->UserPort)) != NULL) { MessageClass = message->Class; ReplyMsg(message); switch (MessageClass) { case GADGETUP : case GADGETDOWN : if (do_gadgets(message) == CLOSE_GAD) { if(title) ShowTitle(s,TRUE); return; } break; } /* end switch */ } /* end if */ } /* end forever */ } int do_gadgets(mes) struct IntuiMessage *mes; { struct Gadget *igad; int gadgid; /*********************************************/ /* This subroutine handles all gadget input. */ /*********************************************/ igad = (struct Gadget *)mes->IAddress; gadgid = igad->GadgetID; switch(gadgid) { case CAMBER_GAD : camber = (ULONG)camber_string.LongInt; Continue = FALSE; break; case THICK_GAD : thickness = (ULONG)thick_string.LongInt; Continue = FALSE; break; case ANGLE_GAD : angle = (ULONG)angle_string.LongInt; Continue = FALSE; break; case VELOCITY_GAD : velocity = (ULONG)(16-(velocity_prop.HorizPot >> 12)); Continue = FALSE; break; case AIRFOIL_GAD : if(mode) mode = FALSE; else mode = TRUE; Continue = FALSE; break; case TITLE_GAD : if(title) title = FALSE; else title = TRUE; break; case CLOSE_GAD : break; case QUIT_GAD : close_things(); exit(0); break; } return gadgid; } do_about() { int i; /*****************************************************/ /* This subroutine displays the initial information. */ /*****************************************************/ SetAPen(rp,0L); RectFill(rp,(long)(XMIN+1),(long)(YMIN+1),(long)(XMAX-1),(long)(YMAX-1)); SetAPen(rp,1L); for(i = 0;i < 24;i++) { Move(rp,2L,(long)(8*i+8)); Text(rp,about[i],(long)strlen(about[i])); } } open_things() { struct Library *OpenLibrary(); struct Screen *OpenScreen(); struct Window *OpenWindow(); struct ViewPort *ViewPortAddress(); BYTE *AllocRaster(); if(!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0L))) { printf("no graphics library!!!\n"); close_things(); exit(1); } mask |= GRAPHICS; if(!(IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library",0L))) { printf("no intuition library!!!\n"); close_things(); exit(2); } mask |= INTUITION; if (!(s = OpenScreen(&ns))) { printf("could not open the screen\n"); close_things(); exit(3); } mask |= SCREEN; nw.Screen = s; if (!(w = OpenWindow(&nw))) { printf("could not open the window\n"); close_things(); exit(4); } mask |= WINDOW; rp = w->RPort; vp = ViewPortAddress(w); InitArea(&area,buffer,250L); rp->AreaInfo = &area; trp.Size = (long)RASSIZE(640L,400L); if (!(trp.RasPtr = (BYTE *)AllocRaster(640L,400L))) { printf("could not allocate memory for area fills\n"); close_things(); exit(5); } mask |= AREAFILL; rp->TmpRas = &trp; LoadRGB4(vp,colors,16L); InitRequester(&AirRequest); AirRequest.LeftEdge = 0L; AirRequest.TopEdge = 0L; AirRequest.Width = 200L; AirRequest.Height = 150L; AirRequest.RelLeft = -100L; AirRequest.RelTop = -75L; AirRequest.ReqGadget = &quit_gad; AirRequest.Flags = POINTREL; AirRequest.BackFill = 0; if (!(SetDMRequest(w,&AirRequest))) { printf("could not set Requester\n"); close_things(); exit(6); } mask |= DMREQUEST; ShowTitle(s,FALSE); } close_things() { if(mask & DMREQUEST) ClearDMRequest(w,&AirRequest); if(mask & AREAFILL) FreeRaster(trp.RasPtr,640L,400L); if(mask & WINDOW) CloseWindow(w); if(mask & SCREEN) CloseScreen(s); if(mask & GRAPHICS) CloseLibrary(GfxBase); if(mask & INTUITION) CloseLibrary(IntuitionBase); }