BOUNCE.C


/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1993 - 1998 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\******************************************************************************/

/*---------------------------------------------------------------------------*\
| BOUNCING BALL MODULE
\*---------------------------------------------------------------------------*/

#include <windows.h>
#include "gdidemo.h"
#include "bounce.h"


VOID XFormClear(PMYXFORM);
VOID XFormScale(PMYXFORM,int,int);
VOID XFormTrans(PMYXFORM,int,int);
VOID XFormPoint(PMYXFORM,LPPOINT);


/*---------------------------------------------------------------------------*\
| CREATE BOUNCE WINDOW PROCEDURE
|
\*---------------------------------------------------------------------------*/
HWND FAR CreateBounceWindow(HWND hWndClient, int nItem)
{
HANDLE hInstance;
MDICREATESTRUCT mcs;
static char buffer[256];


hInstance = GETINSTANCE(hWndClient);
LoadString (hInstance, BOUNCETITLE, buffer, 256);

/*
** Initialize the MDI create struct for creation of the
** test window.
*/
mcs.szClass = BOUNCECLASS;
mcs.szTitle = buffer;
mcs.hOwner = hInstance;
mcs.x = CW_USEDEFAULT;
mcs.y = CW_USEDEFAULT;
mcs.cx = CW_USEDEFAULT;
mcs.cy = CW_USEDEFAULT;
mcs.style = 0l;
mcs.lParam = (LONG)nItem;

return((HWND)SendMessage(hWndClient,WM_MDICREATE,0,(LONG)(LPMDICREATESTRUCT)&mcs));
}


/*---------------------------------------------------------------------------*\
| BOUNCE WINDOW PROCEDURE
|
\*---------------------------------------------------------------------------*/
LONG APIENTRY BounceProc(HWND hWnd, UINT wMsg, WPARAM wParam, LONG lParam)
{
switch(wMsg)
{
case WM_CREATE:
BounceCreateProc(hWnd);
break;

case WM_MOVE:
BounceRefresh(hWnd);
break;

case WM_COMMAND:
BounceCommandProc(hWnd,wParam,lParam);
break;

case WM_TIMER:
BounceObjects(hWnd);
break;

case WM_PAINT:
BouncePaintProc(hWnd);
break;

case WM_DESTROY:
BounceDestroyProc(hWnd);
break;


default:
return(DefMDIChildProc(hWnd,wMsg,wParam,lParam));
}
return(0l);
}


/*---------------------------------------------------------------------------*\
| BOUNCE CREATE PROCEDURE
|
\*---------------------------------------------------------------------------*/
BOOL BounceCreateProc(HWND hWnd)
{
PBOUNCEDATA pbd;


if(AllocWindowInfo(hWnd,sizeof(BOUNCEDATA)))
{
if(pbd = (PBOUNCEDATA)LockWindowInfo(hWnd))
{
pbd->hBall1 = ballCreate(hWnd,20,20,RGB(255,0,0));
pbd->hBall2 = ballCreate(hWnd,20,20,RGB(0,0,255));
pbd->hBall3 = ballCreate(hWnd,20,20,RGB(0,255,0));
pbd->hBall4 = ballCreate(hWnd,20,20,RGB(255,0,255));

UnlockWindowInfo(hWnd);

SetTimer(hWnd,1,50,NULL);
return(TRUE);
}
FreeWindowInfo(hWnd);
}
return(FALSE);
}


/*---------------------------------------------------------------------------*\
| BOUNCE COMMAND PROCEDURE
|
\*---------------------------------------------------------------------------*/
BOOL BounceCommandProc(HWND hWnd, WPARAM wParam, LONG lParam)
{
hWnd = hWnd;
wParam = wParam;
lParam = lParam;

return(TRUE);
}


/*---------------------------------------------------------------------------*\
| BOUNCE PAINT PROCEDURE
|
\*---------------------------------------------------------------------------*/
VOID BouncePaintProc(HWND hWnd)
{
HDC hDC;
PAINTSTRUCT ps;


if(hDC = BeginPaint(hWnd,&ps))
EndPaint(hWnd,&ps);

BounceRefresh(hWnd);

return;
}


/*---------------------------------------------------------------------------*\
| BOUNCE DESTROY PROCEDURE
|
\*---------------------------------------------------------------------------*/
VOID BounceDestroyProc(HWND hWnd)
{
PBOUNCEDATA pbd;


KillTimer(hWnd,1);
if(pbd = (PBOUNCEDATA)LockWindowInfo(hWnd))
{
ballDestroy(pbd->hBall1);
ballDestroy(pbd->hBall2);
ballDestroy(pbd->hBall3);
ballDestroy(pbd->hBall4);

UnlockWindowInfo(hWnd);
}
FreeWindowInfo(hWnd);
return;
}


/*---------------------------------------------------------------------------*\
| BOUNCE REFRESH
|
\*---------------------------------------------------------------------------*/
VOID BounceRefresh(HWND hWnd)
{
PBOUNCEDATA pbd;
RECT rect;
HDC hDC;
POINT pDimensions;
int xDirection,yDirection;


if(pbd = (PBOUNCEDATA)LockWindowInfo(hWnd))
{
if(hDC = GetDC(hWnd))
{
GetClientRect(hWnd,&rect);
FillRect(hDC,&rect,GETCLASSBRUSH(hWnd));

xDirection = ((lRandom() % 5)+5);
yDirection = ((lRandom() % 5)+5);
ballSetPosition(pbd->hBall1,0,0);
ballSetDirection(pbd->hBall1,xDirection,yDirection);


ballGetDimensions(pbd->hBall2,&pDimensions);
ballSetPosition(pbd->hBall2,rect.right-pDimensions.x,0);
xDirection = -(((int)lRandom() % 5)+5);
yDirection = ((lRandom() % 5)+5);
ballSetDirection(pbd->hBall2,xDirection,yDirection);


ballGetDimensions(pbd->hBall3,&pDimensions);
ballSetPosition(pbd->hBall3,0,rect.bottom-pDimensions.y);
xDirection = ((lRandom() % 5)+5);
yDirection = -((int)(lRandom() % 5)+5);
ballSetDirection(pbd->hBall3,xDirection,yDirection);


ballGetDimensions(pbd->hBall4,&pDimensions);
ballSetPosition(pbd->hBall4,rect.right-pDimensions.x,rect.bottom-pDimensions.y);
xDirection = -((int)(lRandom() % 5)+5);
yDirection = -((int)(lRandom() % 5)+5);
ballSetDirection(pbd->hBall4,xDirection,yDirection);


ballBounce(pbd->hBall1);
ballBounce(pbd->hBall2);
ballBounce(pbd->hBall3);
ballBounce(pbd->hBall4);

ReleaseDC(hWnd,hDC);
}
UnlockWindowInfo(hWnd);
}
return;
}



VOID BounceObjects(HWND hWnd)
{
PBOUNCEDATA pbd;


if(pbd = (PBOUNCEDATA)LockWindowInfo(hWnd))
{
/*
** Determine if the balls hit any of the edges of the display. If
** they do, then these functions reset their position to something
** more desireable and re-do their direction.
*/
CheckEdgePosition(hWnd,pbd->hBall1);
CheckEdgePosition(hWnd,pbd->hBall2);
CheckEdgePosition(hWnd,pbd->hBall3);
CheckEdgePosition(hWnd,pbd->hBall4);

ballBounce(pbd->hBall1);
ballBounce(pbd->hBall2);
ballBounce(pbd->hBall3);
ballBounce(pbd->hBall4);


UnlockWindowInfo(hWnd);
}
return;
}


VOID CheckEdgePosition(HWND hWnd, HANDLE hBall)
{
POINT pPos,pDir,pDim;
int xNewPos,yNewPos,xNewDir,yNewDir;
RECT rect;



ballGetPosition(hBall,&pPos);
ballGetDirection(hBall,&pDir);
ballGetDimensions(hBall,&pDim);

GetClientRect(hWnd,&rect);


/*
** Check each edge of the client rectagle. If the ball goes past the
** boundries, reset the position and give it a new direction.
*/
xNewDir = pDir.x;
yNewDir = pDir.y;
xNewPos = pPos.x+pDir.x;
yNewPos = pPos.y+pDir.y;

if(xNewPos < rect.left)
{
xNewDir = ((lRandom() % 5)+5);
ballSetPosition(hBall,rect.left,pPos.y);
}
if((xNewPos+pDim.x) > rect.right)
{
xNewDir = -(((int)lRandom() % 5)+5);
ballSetPosition(hBall,rect.right-pDim.x,pPos.y);
}
if(yNewPos < rect.top)
{
yNewDir = ((lRandom() % 5)+5);
ballSetPosition(hBall,pPos.x,rect.top);
}
if((yNewPos+pDim.y) > rect.bottom)
{
yNewDir = -(((int)lRandom() % 5)+5);
ballSetPosition(hBall,pPos.x,rect.bottom-pDim.y);
}
ballSetDirection(hBall,xNewDir,yNewDir);

return;
}


/*---------------------------------------------------------------------------*\
| CLEAR XFORM
|
\*---------------------------------------------------------------------------*/
VOID XFormClear(PMYXFORM pXForm)
{
int Row,Col;


for(Row=0; Row < 3; Row++)
for(Col=0; Col < 3; Col++)
if(Row == Col)
pXForm->xForm[Row][Col] = 1;
else
pXForm->xForm[Row][Col] = 0;
return;
}


/*---------------------------------------------------------------------------*\
| XFORM SCALE
|
\*---------------------------------------------------------------------------*/
VOID XFormScale(PMYXFORM pXForm, int xScale, int yScale)
{
int idx;


for(idx=0; idx < 3; idx++)
{
pXForm->xForm[idx][0] = pXForm->xForm[idx][0] * xScale;
pXForm->xForm[idx][1] = pXForm->xForm[idx][1] * yScale;
}
return;
}



VOID XFormTrans(PMYXFORM pXForm, int xTrans, int yTrans)
{
pXForm->xForm[2][0] = pXForm->xForm[2][0] + xTrans;
pXForm->xForm[2][1] = pXForm->xForm[2][1] + yTrans;

return;
}



VOID XFormPoint(PMYXFORM pXForm, LPPOINT pPoint)
{
int x,y;


x = (pXForm->xForm[0][0] * pPoint->x) + (pXForm->xForm[1][0] * pPoint->y) + pXForm->xForm[2][0];
y = (pXForm->xForm[0][1] * pPoint->x) + (pXForm->xForm[1][1] * pPoint->y) + pXForm->xForm[2][1];

pPoint->x = x;
pPoint->y = y;

return;
}


/*
** -------------------------------
** -------------------------------
** -------------------------------
*/

HANDLE ballCreate(HWND hWnd, int nWidth, int nHeight, COLORREF crColor)
{
HANDLE hBall;
PBALLDATA pbd;

if(hBall = LocalAlloc(LHND,sizeof(BALLDATA)))
{
if(pbd = (PBALLDATA)LocalLock(hBall))
{
pbd->hWnd = hWnd;
pbd->nWidth = nWidth;
pbd->nHeight = nHeight;
pbd->xDirection = 0;
pbd->yDirection = 0;
pbd->bNewPosition = FALSE;
pbd->xPosition = 0;
pbd->yPosition = 0;
pbd->crColor = crColor;
XFormClear(&pbd->xForm);

LocalUnlock(hBall);
return(hBall);
}
LocalFree(hBall);
}
return(NULL);
}


BOOL ballDestroy(HANDLE hBall)
{
if(LocalFree(hBall))
return(FALSE);
return(TRUE);
}


VOID ballBounce(HANDLE hBall)
{
PBALLDATA pbd;
HDC hDC;
HBRUSH hBrush;
POINT pOrg,pExt;
HRGN hTmp,hOld,hNew;


if(pbd = (PBALLDATA)LocalLock(hBall))
{
/*
** Create old ball object.
*/
pOrg.x = 0;
pOrg.y = 0;
pExt.x = pbd->nWidth;
pExt.y = pbd->nHeight;
XFormPoint(&pbd->xForm,&pOrg);
XFormPoint(&pbd->xForm,&pExt);
hOld = CreateEllipticRgn(pOrg.x,pOrg.y,pExt.x,pExt.y);


/*
** Create new ball object.
*/
if(pbd->bNewPosition)
{
pbd->bNewPosition = FALSE;
XFormClear(&pbd->xForm);
XFormTrans(&pbd->xForm,pbd->xPosition,pbd->yPosition);
}
else
XFormTrans(&pbd->xForm,pbd->xDirection,pbd->yDirection);
pOrg.x = 0;
pOrg.y = 0;
pExt.x = pbd->nWidth;
pExt.y = pbd->nHeight;
XFormPoint(&pbd->xForm,&pOrg);
XFormPoint(&pbd->xForm,&pExt);
hNew = CreateEllipticRgn(pOrg.x,pOrg.y,pExt.x,pExt.y);



if(hDC = GetDC(pbd->hWnd))
{
hTmp = CreateRectRgn(1,1,2,2);
CombineRgn(hTmp,hOld,hNew,RGN_DIFF);
FillRgn(hDC,hTmp,GETCLASSBRUSH(pbd->hWnd));

hBrush = CreateSolidBrush(pbd->crColor);
FillRgn(hDC,hNew,hBrush);
DeleteObject(hBrush);

ReleaseDC(pbd->hWnd,hDC);

DeleteObject(hTmp);
}

DeleteObject(hOld);
DeleteObject(hNew);

LocalUnlock(hBall);
}
return;
}


BOOL ballGetDimensions(HANDLE hBall, LPPOINT pDimension)
{
PBALLDATA pbd;
BOOL bOK;


bOK = FALSE;
if(pbd = (PBALLDATA)LocalLock(hBall))
{
pDimension->x = pbd->nWidth;
pDimension->y = pbd->nHeight;

bOK = TRUE;
LocalUnlock(hBall);
}
return(bOK);
}


BOOL ballSetDimensions(HANDLE hBall, int nWidth, int nHeight)
{
PBALLDATA pbd;
BOOL bOK;


bOK = FALSE;
if(pbd = (PBALLDATA)LocalLock(hBall))
{
pbd->nWidth = nWidth;
pbd->nHeight = nHeight;

bOK = TRUE;
LocalUnlock(hBall);
}
return(bOK);
}


BOOL ballGetDirection(HANDLE hBall, LPPOINT pDirection)
{
PBALLDATA pbd;
BOOL bOK;


bOK = FALSE;
if(pbd = (PBALLDATA)LocalLock(hBall))
{
pDirection->x = pbd->xDirection;
pDirection->y = pbd->yDirection;

bOK = TRUE;
LocalUnlock(hBall);
}
return(bOK);
}


BOOL ballSetDirection(HANDLE hBall, int xDirection, int yDirection)
{
PBALLDATA pbd;
BOOL bOK;


bOK = FALSE;
if(pbd = (PBALLDATA)LocalLock(hBall))
{
pbd->xDirection = xDirection;
pbd->yDirection = yDirection;

bOK = TRUE;
LocalUnlock(hBall);
}
return(bOK);
}


BOOL ballGetPosition(HANDLE hBall, LPPOINT pPosition)
{
PBALLDATA pbd;
BOOL bOK;
POINT pOrg;


bOK = FALSE;
if(pbd = (PBALLDATA)LocalLock(hBall))
{
pOrg.x = 0;
pOrg.y = 0;
XFormPoint(&pbd->xForm,&pOrg);

pPosition->x = pOrg.x;
pPosition->y = pOrg.y;

bOK = TRUE;
LocalUnlock(hBall);
}
return(bOK);
}


BOOL ballSetPosition(HANDLE hBall, int x, int y)
{
PBALLDATA pbd;
BOOL bOK;


bOK = FALSE;
if(pbd = (PBALLDATA)LocalLock(hBall))
{
pbd->bNewPosition = TRUE;
pbd->xPosition = x;
pbd->yPosition = y;

bOK = TRUE;
LocalUnlock(hBall);
}
return(bOK);
}