|
|
Microsoft OS/2 SDK PM 08-08-1988
/************************************************************************
*
* lfmsg.c
*
* Message handling subroutines for the LineFractal window class.
*
* Created by Microsoft Corp., 1988
************************************************************************/
#define INCL_WIN
#define INCL_GPI
#define INCL_DOSPROCESS
#define INCL_DOSSEMAPHORES
#include <os2.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "linefrac.h"
/************************************************************************
*
* Function prototypes.
*
* These add some error checking to function calls, and prevent
* forward references to functions of undefined return type.
*
************************************************************************/
VOID MySetWindowUShort(HWND, USHORT, USHORT);
VOID MySetWindowLong(HWND, USHORT, LONG);
USHORT MyGetWindowUShort(HWND, USHORT);
LONG MyGetWindowLong(HWND, USHORT);
/************************************************************************
*
* Global Variables
*
*
* hAB Anchor block handle. Needed only for some obscure mainframe
* compatability issue.
* hwndLineFrac
* Our main window handle.
* hwndLineFracFrame
* The handle to our window frame (to get at menus, etc.).
*
* LineFracPS Presentation Space for the background accumulation thread.
* LineFracRc Holds the dimensions of the bitmap we draw into.
* LineFracBM Handle to our bitmap. Needed only during creation and
* destruction.
* LineFracDC Handle to the DC we've selected our bitmap into. Needed
* only during creation and destruction.
* fHaveBitmap TRUE ==> a bitmap was successfully created for the second
* thread to draw into.
* fHaveAccumThread
* TRUE ==> the point accumulation thread was successfully
* created.
*
* tidAccumFrac
* Thread ID for the point accumulation thread.
* abAccumStack
* Stack for the ...
*
* usCurrentXform
* The menu command id of the currently selected transform.
* LineFracXform
* The current similarity transform used when drawing.
* usRecursion The currently selected depth of recursion.
* usPolygonSides
* Number of sides to the polygonal frame of "unit
* intervals". The fractal transformation is
* applied to each line segment of the frame.
*
* lSemAccumulateFractal
* Semaphore controlling the point accumulation thread.
* Clear it to start another fractal.
* fInterrupted
* TRUE ==> abort current fractal.
* fClearBetween
* TRUE ==> clear drawing surface (bitmap) between fractals.
* fAutoScale TRUE ==> resize our bitmap to match the client rectangle.
*
* xscale These transform each point accumulated, so
* yscale that the drawing fits into the window,
* xoff with the original unit interval at the center.
* yoff
*
* lColorBack Background color for fractal.
* lColor Line foreground color.
* usStyle Line style.
* usMixMode Line foreground mix mode.
*
* XformFuncs Array of built-in transform-defining functions.
*
************************************************************************/
#define CCHSTR 12 /* work buffer size for MyGetWindowLong, etc */
HAB hAB;
HWND hwndLineFrac;
HWND hwndLineFracFrame;
HPS LineFracPS = NULL;
RECTL LineFracRc = { 0L, 0L, 0L, 0L };
HBITMAP LineFracBM = NULL;
HDC LineFracDC = NULL;
BOOL fHaveBitmap = FALSE;
BOOL fHaveAccumThread = FALSE;
#define SIZE_ACCUM_STACK 2048
TID tidAccumFrac;
CHAR abAccumStack[SIZE_ACCUM_STACK];
USHORT usCurrentXform = IDMSAWTOOTH;
PLINEFRAC LineFracXform = NULL;
USHORT usRecursion = 1;
USHORT usPolygonSides = 3;
USHORT cptMax = MAX_POINT_COUNT;
LONG lSemAccumulateFractal = NULL;
BOOL fInterrupted = FALSE;
BOOL fClearBetween = TRUE;
BOOL fAutoScale = TRUE;
double xscale = 250.0;
double yscale = 220.0;
double xoff = 30.0;
double yoff = 30.0;
LONG lColorBack = CLR_BLACK;
LONG lColor = CLR_BLUE;
USHORT usStyle = LINETYPE_SOLID;
USHORT usMixMode = FM_OVERPAINT;
typedef PLINEFRAC (* PFPLF)(); /* "ptr to func returning ptr to LINEFRAC" */
PFPLF XformFuncs[] =
{
DefineSharkTooth,
DefineSawTooth,
DefineKochIsland,
DefineStovePipe,
DefineEsses
};
/************************************************************************
*
* LineFracInit
*
* Set up a bitmap for the second thread to draw into. Then set
* up the second thread itself. Set the flag so we don't do this
* twice. Lower the priority of the background thread to IDLETIME,
* so that it won't slow down user-interface response.
*
* Initialize the current transform. Post a message to cause the
* transform to actually be defined.
*
************************************************************************/
VOID
LineFracInit(hWnd)
HWND hWnd;
{
if (fHaveBitmap = ResizeBitmap(hWnd))
{
DosSemSet( &lSemAccumulateFractal );
if (fHaveAccumThread = !DosCreateThread( AccumulateLineFractal,
&tidAccumFrac,
abAccumStack + sizeof(abAccumStack)));
DosSetPrty(PRTYS_THREAD, PRTYC_IDLETIME, 0, tidAccumFrac);
WinPostMsg(hWnd, WM_COMMAND, (MPARAM)usCurrentXform, (MPARAM)0L);
}
}
/************************************************************************
*
* LineFracExit
*
* If we have a bitmap lying around, delete it.
*
************************************************************************/
VOID
LineFracExit()
{
if (fHaveBitmap)
{
GpiSetBitmap(LineFracPS, NULL);
GpiDeleteBitmap(LineFracBM);
GpiDestroyPS(LineFracPS);
DevCloseDC(LineFracDC);
}
}
/************************************************************************
*
* ResizeBitmap
*
* Destroy the current bitmap and create a new one the size of the
* current client rectangle.
*
************************************************************************/
BOOL
ResizeBitmap(hWnd)
HWND hWnd;
{
SIZEL size;
BITMAPINFOHEADER bminfo;
if (fHaveBitmap)
{
GpiSetBitmap(LineFracPS, NULL);
GpiDeleteBitmap(LineFracBM);
GpiDestroyPS(LineFracPS);
DevCloseDC(LineFracDC);
}
LineFracDC = DevOpenDC(hAB, OD_MEMORY, "*", 0L, NULL, NULL);
if (!LineFracDC)
return FALSE;
size.cx = 0L;
size.cy = 0L;
LineFracPS = GpiCreatePS(hAB, LineFracDC, &size,
PU_PELS|GPIT_MICRO|GPIA_ASSOC);
if (!LineFracPS)
{
DevCloseDC(LineFracDC);
return FALSE;
}
bminfo.cbFix = sizeof(BITMAPINFOHEADER);
WinQueryWindowRect(hWnd, &LineFracRc);
bminfo.cx = (USHORT) (LineFracRc.xRight - LineFracRc.xLeft);
bminfo.cy = (USHORT) (LineFracRc.yTop - LineFracRc.yBottom);
bminfo.cPlanes = 1;
bminfo.cBitCount = 4;
LineFracBM = GpiCreateBitmap(LineFracPS, &bminfo, 0L, 0L, 0L);
if (!LineFracBM)
{
GpiDestroyPS(LineFracPS);
DevCloseDC(LineFracDC);
return FALSE;
}
GpiSetBitmap(LineFracPS, LineFracBM);
WinFillRect(LineFracPS, &LineFracRc, lColorBack);
/* Compute the scale and translation parameters from the
* current client rectangle.
*/
xoff = (LineFracRc.xRight - LineFracRc.xLeft) * 0.125;
yoff = (LineFracRc.yTop - LineFracRc.yBottom) * 0.5;
xscale = xoff * 6.0;
yscale = (LineFracRc.yTop - LineFracRc.yBottom) * 0.75;
return TRUE;
}
/************************************************************************
*
* LineFracCommand
*
* Process menu commands related to line fractals.
*
************************************************************************/
VOID
LineFracCommand(hWnd, id)
HWND hWnd;
USHORT id;
{
switch (id)
{
case IDMABOUT:
WinDlgBox( HWND_DESKTOP, hWnd, (PFNWP)AboutDlg, NULL,
IDD_ABOUT, NULL );
break;
case IDMFRACATTRS:
WinDlgBox( HWND_DESKTOP, hWnd, (PFNWP)LineFracDlg, NULL,
IDD_LINEFRAC, NULL );
break;
case IDMCLEARBITMAP:
if (fHaveBitmap)
{
WinFillRect(LineFracPS, &LineFracRc, lColorBack);
WinInvalidateRect(hwndLineFrac, &LineFracRc, FALSE);
}
break;
case IDMAUTOSCALE:
fAutoScale = !fAutoScale;
WinSendDlgItemMsg( hwndLineFracFrame, FID_MENU,
MM_SETITEMATTR, MPFROM2SHORT(IDMAUTOSCALE, TRUE),
MPFROM2SHORT(MIA_CHECKED, fAutoScale ? MIA_CHECKED : NULL));
break;
case IDMSHARKTOOTH:
case IDMSAWTOOTH:
case IDMKOCH:
case IDMSTOVE:
case IDMESSES:
LineFracXform = (*(XformFuncs[id - IDMSHARKTOOTH]))();
WinSendDlgItemMsg( hwndLineFracFrame, FID_MENU,
MM_SETITEMATTR, MPFROM2SHORT(usCurrentXform, TRUE),
MPFROM2SHORT(MIA_CHECKED, NULL));
usCurrentXform = id;
WinSendDlgItemMsg( hwndLineFracFrame, FID_MENU,
MM_SETITEMATTR, MPFROM2SHORT(usCurrentXform, TRUE),
MPFROM2SHORT(MIA_CHECKED, MIA_CHECKED));
case IDMREDRAW:
DosSemClear(&lSemAccumulateFractal);
case IDMABORT:
fInterrupted = TRUE; /* suicide flag for pt accumulation */
break;
}
}
/************************************************************************
*
* LineFracPaint
*
* If we have a bitmap, blit it to the screen, no matter what state
* it's in.
*
************************************************************************/
VOID
LineFracPaint(hPS)
HPS hPS;
{
POINTL bbp[4];
if (fHaveBitmap)
{
bbp[0].x = 0L; bbp[0].y = 0L;
bbp[1].x = LineFracRc.xRight; bbp[1].y = LineFracRc.yTop;
bbp[2].x = 0L; bbp[2].y = 0L;
GpiBitBlt(hPS, LineFracPS, 3L, bbp, ROP_SRCCOPY, (LONG)NULL);
}
}
/************************************************************************
*
* DefineSharkTooth
*
* Set up the similarity transform for the following linefractal,
* which looks roughly like:
*
*
* *
* ===> * *
* * *
* *************** * *
*
************************************************************************/
PLINEFRAC
DefineSharkTooth()
{
double ang;
double len;
static LINEFRAC shark[3];
ang = PI / 6.0;
len = 1.0 / sqrt(3.0);
shark[0].angle = ang;
shark[0].length = len;
shark[0].flip = FALSE;
shark[0].next = &(shark[1]);
shark[1].angle = -ang * 2.0;
shark[1].length = len;
shark[1].flip = FALSE;
shark[1].next = EOLIST;
return &(shark[0]);
}
/************************************************************************
*
* DefineSawTooth
*
* Set up the similarity transform for the following linefractal,
* which looks roughly like:
*
*
*
* ===> *
* * *
* *************** * * *
* * *
* *
*
************************************************************************/
PLINEFRAC
DefineSawTooth()
{
double ang;
double len;
static LINEFRAC saw[3];
ang = PI / 4.0;
len = 1.0 / sqrt(2.0);
saw[0].angle = ang;
saw[0].length = len / 2.0;
saw[0].flip = FALSE;
saw[0].next = &(saw[1]);
saw[1].angle = -ang * 2.0;
saw[1].length = len;
saw[1].flip = FALSE;
saw[1].next = &(saw[2]);
saw[2].angle = ang * 2.0;
saw[2].length = len / 2.0;
saw[2].flip = FALSE;
saw[2].next = EOLIST;
return &(saw[0]);
}
/************************************************************************
*
* DefineKochIsland
*
* Set up the similarity transform for the following linefractal,
* which looks roughly like:
*
* This is known as the Koch, or snowflake, transform.
*
*
* *
* ===> * *
* * *
* *************** ***** *****
*
************************************************************************/
PLINEFRAC
DefineKochIsland()
{
double ang;
double len;
static LINEFRAC koch[4];
ang = PI / 3.0;
len = 1.0 / 3.0;
koch[0].angle = 0.0;
koch[0].length = len;
koch[0].flip = FALSE;
koch[0].next = &(koch[1]);
koch[1].angle = ang;
koch[1].length = len;
koch[1].flip = FALSE;
koch[1].next = &(koch[2]);
koch[2].angle = -2.0 * ang;
koch[2].length = len;
koch[2].flip = FALSE;
koch[2].next = &(koch[3]);
koch[3].angle = ang;
koch[3].length = len;
koch[3].flip = FALSE;
koch[3].next = EOLIST;
return &(koch[0]);
}
/************************************************************************
*
* DefineStovePipe
*
* Set up the similarity transform for the following linefractal,
* which looks roughly like:
*
* This is sometimes known as the stovepipe transform.
*
*
* ******
* * *
* ===> * *
* * *
* ************** ***** *****
*
************************************************************************/
PLINEFRAC
DefineStovePipe()
{
double ang;
double len;
static LINEFRAC Stove[5];
ang = PI / 2.0;
len = 1.0 / 3.0;
Stove[0].angle = 0.0;
Stove[0].length = len;
Stove[0].flip = FALSE;
Stove[0].next = &(Stove[1]);
Stove[1].angle = ang;
Stove[1].length = len;
Stove[1].flip = FALSE;
Stove[1].next = &(Stove[2]);
Stove[2].angle = -ang;
Stove[2].length = len;
Stove[2].flip = FALSE;
Stove[2].next = &(Stove[3]);
Stove[3].angle = -ang;
Stove[3].length = len;
Stove[3].flip = FALSE;
Stove[3].next = &(Stove[4]);
Stove[4].angle = ang;
Stove[4].length = len;
Stove[4].flip = FALSE;
Stove[4].next = EOLIST;
return &(Stove[0]);
}
/************************************************************************
*
* DefineEsses
*
* Set up the similarity transform for the following linefractal,
* which looks roughly like:
*
*
* ******
* * *
* ************** ===> * * *
* * *
* ******
*
************************************************************************/
PLINEFRAC
DefineEsses()
{
double ang;
static LINEFRAC Esses[5];
ang = PI / 2.0;
Esses[0].angle = ang;
Esses[0].length = 0.25;
Esses[0].flip = FALSE;
Esses[0].next = &(Esses[1]);
Esses[1].angle = -ang;
Esses[1].length = 0.5;
Esses[1].flip = FALSE;
Esses[1].next = &(Esses[2]);
Esses[2].angle = -ang;
Esses[2].length = 0.5;
Esses[2].flip = FALSE;
Esses[2].next = &(Esses[3]);
Esses[3].angle = ang;
Esses[3].length = 0.5;
Esses[3].flip = FALSE;
Esses[3].next = &(Esses[4]);
Esses[4].angle = ang;
Esses[4].length = 0.25;
Esses[4].flip = FALSE;
Esses[4].next = EOLIST;
return &(Esses[0]);
}
/************************************************************************
*
* LineFracDlg
*
* Process messages for the fractal attributes dialog box.
*
************************************************************************/
ULONG FAR PASCAL
LineFracDlg( hWndDlg, message, mp1, mp2 )
HWND hWndDlg;
USHORT message;
MPARAM mp1;
MPARAM mp2;
{
switch (message)
{
case WM_INITDLG:
MySetWindowLong (hWndDlg, IDDCOLORBK, lColorBack);
MySetWindowLong (hWndDlg, IDDCOLOR, lColor);
MySetWindowUShort(hWndDlg, IDDSTYLE, usStyle);
MySetWindowUShort(hWndDlg, IDDMIX, usMixMode);
MySetWindowUShort(hWndDlg, IDDNUMSIDES, usPolygonSides);
MySetWindowUShort(hWndDlg, IDDCPTMAX, cptMax);
MySetWindowUShort(hWndDlg, IDDRECURSION, usRecursion);
WinSendDlgItemMsg( hWndDlg, IDDCLEARBETWEEN, BM_SETCHECK,
MPFROM2SHORT(fClearBetween, 0), 0L );
return FALSE;
case WM_COMMAND:
switch (SHORT1FROMMP(mp1))
{
case IDDOK:
lColorBack = MyGetWindowLong (hWndDlg, IDDCOLORBK);
lColor = MyGetWindowLong (hWndDlg, IDDCOLOR);
usStyle = MyGetWindowUShort(hWndDlg, IDDSTYLE);
usMixMode = MyGetWindowUShort(hWndDlg, IDDMIX);
usPolygonSides = MyGetWindowUShort(hWndDlg, IDDNUMSIDES);
cptMax = MyGetWindowUShort(hWndDlg, IDDCPTMAX);
usRecursion = MyGetWindowUShort(hWndDlg, IDDRECURSION);
fClearBetween = (SHORT)WinSendDlgItemMsg( hWndDlg, IDDCLEARBETWEEN,
BM_QUERYCHECK, 0L, 0L );
if (usPolygonSides == 0)
usPolygonSides = 2;
if (cptMax == 0)
cptMax = 1;
else if (cptMax > MAX_POINT_COUNT)
cptMax = MAX_POINT_COUNT;
WinDismissDlg( hWndDlg, TRUE );
break;
case IDDCANCEL:
WinDismissDlg( hWndDlg, TRUE );
break;
default:
return FALSE;
}
break;
default:
return (ULONG) WinDefDlgProc( hWndDlg, message, mp1, mp2 );
}
return FALSE;
}
/************************************************************************
*
* AboutDlg
*
* Process messages for the About box.
*
************************************************************************/
ULONG FAR PASCAL
AboutDlg( hWndDlg, message, mp1, mp2 )
HWND hWndDlg;
USHORT message;
MPARAM mp1;
MPARAM mp2;
{
switch (message)
{
case WM_COMMAND: /* the user has pressed a button */
switch (SHORT1FROMMP(mp1)) /* which button? */
{
case DID_OK:
case DID_CANCEL:
WinDismissDlg( hWndDlg, TRUE );
break;
default:
return FALSE;
}
break;
default:
return (ULONG) WinDefDlgProc( hWndDlg, message, mp1, mp2 );
}
return FALSE;
}
/************************************************************************
*
* MySetWindowUShort
*
* Sets the given control id to the value specified.
*
* Be careful! The call to sprintf only works because our
* stack segment is the same as our data segment.
*
************************************************************************/
VOID
MySetWindowUShort(hWnd, id, num)
HWND hWnd;
USHORT id;
USHORT num;
{
char szStr[CCHSTR];
sprintf((NPCH)szStr, "%u", num);
WinSetWindowText(WinWindowFromID(hWnd, id), (PCH)szStr);
}
/************************************************************************
*
* MySetWindowLong
*
* Sets the given control id to the value specified.
*
* Be careful! The call to sprintf only works because our
* stack segment is the same as our data segment.
*
************************************************************************/
VOID
MySetWindowLong(hWnd, id, num)
HWND hWnd;
USHORT id;
LONG num;
{
char szStr[CCHSTR];
sprintf((NPCH)szStr, "%ld", num);
WinSetWindowText(WinWindowFromID(hWnd, id), (PCH)szStr);
}
/************************************************************************
*
* MyGetWindowUShort
*
* Returns the value from the given control id.
*
* Be careful! The call to atoi only works because our
* stack segment is the same as our data segment.
*
************************************************************************/
USHORT
MyGetWindowUShort(hWnd, id)
HWND hWnd;
USHORT id;
{
char szStr[CCHSTR];
WinQueryWindowText(WinWindowFromID(hWnd, id), CCHSTR, (PCH)szStr);
return atoi(szStr);
}
/************************************************************************
*
* MyGetWindowLong
*
* Returns the value from the given control id.
*
* Be careful! The call to atol only works because our
* stack segment is the same as our data segment.
*
************************************************************************/
LONG
MyGetWindowLong(hWnd, id)
HWND hWnd;
USHORT id;
{
char szStr[CCHSTR];
WinQueryWindowText(WinWindowFromID(hWnd, id), CCHSTR, (PCH)szStr);
return atol(szStr);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.