QUADOBJ.CXX

//+--------------------------------------------------------------------------- 
//
// Microsoft Windows
// Copyright 1992 - 1998 Microsoft Corporation.
//
// File: mandelob.cxx
//
// Contents: Quadrant Engine object
//
// Classes: CQuadrantEngine
//
// Functions: StartGraphicsThread
//
// History: 4-14-94 stevebl Created
//
//----------------------------------------------------------------------------

#include <windows.h>
#include <ole2.h>
#include "quad.h"

//+---------------------------------------------------------------------------
//
// Function: StartGraphicsThread
//
// Synopsis: Global function used to bootstrap into the GraphicsThread
// member of the CQuadrantEngine class.
//
// Arguments: [lpdwParam] - this pointer to a CQuadrantEngine class object
//
// History: 4-15-94 stevebl Created
//
//----------------------------------------------------------------------------

DWORD StartGraphicsThread(LPDWORD lpdwParam)
{
return (((CQuadrantEngine *)lpdwParam)->GraphicsThread());
}

//+---------------------------------------------------------------------------
//
// Member: CQuadrantEngine::QueryInterface
//
// Synopsis: standard OLE method
//
// Arguments: [riid] - reference to the requested interface id
// [ppv] - pointer to recieve the interface
//
// Returns: S_OK on success
// E_NOINTERFACE if the requested interface isn't supported
//
// History: 4-14-94 stevebl Created
//
//----------------------------------------------------------------------------

HRESULT STDMETHODCALLTYPE CQuadrantEngine::QueryInterface(REFIID riid, LPVOID * ppv)
{
*ppv = NULL;
if (IsEqualGUID(IID_IQuadrantEngine, riid))
{
*ppv = (IQuadrantEngine *)this;
}
else if (IsEqualGUID(IID_IUnknown, riid))
{
*ppv = (IUnknown *)(IFractalEngine *)this;
}
if (*ppv)
{
((IUnknown *)*ppv)->AddRef();
return(S_OK);
}
return(E_NOINTERFACE);
}

//+---------------------------------------------------------------------------
//
// Member: CQuadrantEngine::AddRef
//
// Synopsis: increments the reference count
//
// Returns: new reference count
//
// History: 4-14-94 stevebl Created
//
//----------------------------------------------------------------------------

ULONG STDMETHODCALLTYPE CQuadrantEngine::AddRef(void)
{
return(++_cRef);
}

//+---------------------------------------------------------------------------
//
// Member: CQuadrantEngine::Release
//
// Synopsis: decrements the reference count
//
// Returns: new reference count
//
// History: 4-14-94 stevebl Created
//
//----------------------------------------------------------------------------

ULONG STDMETHODCALLTYPE CQuadrantEngine::Release(void)
{
ULONG cRef = --_cRef;
if (0 == cRef)
{
delete this;
gcRef--;
}
return(cRef);
}

// IQuadrantEngine methods

//+---------------------------------------------------------------------------
//
// Member: CQuadrantEngine::Init
//
// Synopsis: initializes the quadrant engine
//
// Arguments: [pqc] - pointer to an IQuadrantClient interface
// [pfh] - pointer to an IFractalHost interface
//
// Returns: S_OK
//
// History: 4-27-94 stevebl Created
//
//----------------------------------------------------------------------------

HRESULT STDMETHODCALLTYPE CQuadrantEngine::Init(
IQuadrantClient *pqc,
IFractalHost *pfh)
{
_pqc = pqc;
_pfh = pfh;
// NOTE that neither of these interface pointers are reference counted since
// I am depending on their owners to tell me when I can be unloaded.
return(S_OK);
}

//+---------------------------------------------------------------------------
//
// Member: CQuadrantEngine::UseBoundingBoxes
//
// Synopsis: tells the quadrant engine to use the bounding box optimization
//
// Arguments: [fFlag] - TRUE if the optimization is enabled (FALSE otherwise)
//
// Returns: S_OK
//
// History: 4-27-94 stevebl Created
//
//----------------------------------------------------------------------------

HRESULT STDMETHODCALLTYPE CQuadrantEngine::UseBoundingBoxes(BOOL fFlag)
{
_fUseBoundingBoxes = fFlag;
return(S_OK);
}

//+---------------------------------------------------------------------------
//
// Member: CQuadrantEngine::SetGraphSize
//
// Synopsis: tells the quadrant engine how big the graph is
//
// Arguments: [uWidth] - width of the graph in pixels
// [uHeight] - height of the graph in pixels
//
// Returns: S_OK on success
// E_FAIL if the graph size can't be altered at this time
//
// History: 4-27-94 stevebl Created
//
//----------------------------------------------------------------------------

HRESULT STDMETHODCALLTYPE CQuadrantEngine::SetGraphSize(
unsigned int uWidth,
unsigned int uHeight)
{
if (WAIT_OBJECT_0 == WaitForSingleObject(_hRunning, 0))
{
return(E_FAIL);
}
_uWidth = uWidth;
_uHeight = uHeight;
SetEvent(_hRestart);
return(S_OK);
}

//+---------------------------------------------------------------------------
//
// Member: CQuadrantEngine::Start
//
// Synopsis: Starts the graph engine
//
// Returns: S_OK on success
//
// History: 4-27-94 stevebl Created
//
//----------------------------------------------------------------------------

HRESULT STDMETHODCALLTYPE CQuadrantEngine::Start(void)
{
SetEvent(_hRunning);
return(S_OK);
}

//+---------------------------------------------------------------------------
//
// Member: CQuadrantEngine::Stop
//
// Synopsis: stops the graph engine
//
// Returns: S_OK on success
//
// History: 4-27-94 stevebl Created
//
//----------------------------------------------------------------------------

HRESULT STDMETHODCALLTYPE CQuadrantEngine::Stop(void)
{
ResetEvent(_hRunning);
return(S_OK);
}

//+---------------------------------------------------------------------------
//
// Member: CQuadrantEngine::Reset
//
// Synopsis: tells the graph engine to quit drawing the current graph
// (if it is drawing it) and start over again
//
// Returns: S_OK
//
// History: 4-27-94 stevebl Created
//
//----------------------------------------------------------------------------

HRESULT STDMETHODCALLTYPE CQuadrantEngine::Reset(void)
{
SetEvent(_hRestart);
return(S_OK);
}


//+---------------------------------------------------------------------------
//
// Member: CQuadrantEngine::Initialize
//
// Synopsis: private method used to initialize the c++ class object
//
// History: 4-27-94 stevebl Created
//
//----------------------------------------------------------------------------

BOOL CQuadrantEngine::Initialize(void)
{
_hRestart = CreateEvent(
NULL,
TRUE, // must be manually reset
TRUE, // initially signaled
NULL);
if (_hRestart == NULL)
{
return(FALSE); // couldn't create an event
}
_hRunning = CreateEvent(
NULL,
TRUE,
FALSE,
NULL);
if (_hRunning == NULL)
{
return(FALSE);
}
_hEngine = CreateThread(
(LPSECURITY_ATTRIBUTES)NULL,
0,
(LPTHREAD_START_ROUTINE) StartGraphicsThread,
this,
0,
&_dwThreadId);
if (_hEngine == NULL)
{
return(FALSE); // couldn't start the graphics thread
}
return(TRUE);
}

//+---------------------------------------------------------------------------
//
// Member: CQuadrantEngine::GraphicsThread
//
// Synopsis: main entry point for the engine's graphics thread
//
// History: 4-27-94 stevebl Created
//
// Notes: This routine enters into an eternal loop. It will continue
// looping until the class is destroyed at which point this
// thread will be simply terminated.
//
// Algorithm: Wait for the engine to be started.
// Clear the graph.
// Plot the points at the graph's edge and also plot two lines
// through the center of the graph, dividing it into fourths.
// For each quadrant, call Subdivide to begin the recursion.
// After falling out of the recursion, check to see if the
// graph has been aborted.
// If not, notify that the graph has been completed.
// Loop back around to the beginning.
//
//----------------------------------------------------------------------------

DWORD CQuadrantEngine::GraphicsThread(void)
{
do
{
// wait here until someone starts the graphics engine
WaitForSingleObject(_hRunning, INFINITE);
// reset the _hRestart event so that we won't immediately fall out
// of the engine
ResetEvent(_hRestart);
// Erase the graph
_pfh->Rect(0, 0, (int) _uWidth - 1, (int) _uHeight - 1, PALETTEINDEX(0));
unsigned uColor;
for (int x = 0;
x < (int)_uWidth
&& WAIT_TIMEOUT == WaitForSingleObject(_hRestart, 0)
; x++)
{
_pqc->ComputePoint(&uColor, x, 0);
_pfh->Point(x, 0, uColor);
_pqc->ComputePoint(&uColor, x, ((int) _uHeight - 1) / 2);
_pfh->Point(x, ((int) _uHeight - 1) / 2, uColor);
_pqc->ComputePoint(&uColor, x, (int) _uHeight - 1);
_pfh->Point(x, (int) _uHeight - 1, uColor);
WaitForSingleObject(_hRunning, INFINITE);
}
int y;
for (y = 1;
y < ((int) _uHeight - 1) / 2
&& WAIT_TIMEOUT == WaitForSingleObject(_hRestart, 0)
; y++)
{
_pqc->ComputePoint(&uColor, 0, y);
_pfh->Point(0, y, uColor);
_pqc->ComputePoint(&uColor, ((int) _uWidth - 1) / 2, y);
_pfh->Point(((int) _uWidth - 1) / 2, y, uColor);
_pqc->ComputePoint(&uColor, (int) _uWidth - 1, y);
_pfh->Point((int) _uWidth - 1, y, uColor);
WaitForSingleObject(_hRunning, INFINITE);
}
for (y = ((int) _uHeight - 1) / 2 + 1;
y < (int) _uHeight - 1
&& WAIT_TIMEOUT == WaitForSingleObject(_hRestart, 0)
; y++)
{
_pqc->ComputePoint(&uColor, 0, y);
_pfh->Point(0, y, uColor);
_pqc->ComputePoint(&uColor, ((int) _uWidth - 1) / 2, y);
_pfh->Point(((int) _uWidth - 1) / 2, y, uColor);
_pqc->ComputePoint(&uColor, (int) _uWidth - 1, y);
_pfh->Point((int) _uWidth - 1, y, uColor);
WaitForSingleObject(_hRunning, INFINITE);
}
Subdivide(0, 0, (int) (_uWidth - 1) / 2, (int) (_uHeight - 1) / 2);
Subdivide((int) (_uWidth - 1) / 2, 0, (int) _uWidth - 1, (int) (_uHeight - 1) / 2);
Subdivide(0, (int) (_uHeight - 1) / 2, (int) (_uWidth - 1) / 2, (int) _uHeight - 1);
Subdivide((int) (_uWidth - 1) / 2, (int) (_uHeight - 1) / 2, (int) _uWidth - 1, (int) _uHeight - 1);
if (WAIT_TIMEOUT == WaitForSingleObject(_hRestart, 0))
{
// Tell our clients that we're finished
_pqc->DoneDrawingGraph();
_pfh->DoneDrawingGraph();
ResetEvent(_hRunning);
}
} while (TRUE);
return(0);
}

//+---------------------------------------------------------------------------
//
// Member: CQuadrantEngine::PlotAreaSlowly
//
// Synopsis: plots each point in a rectangle by brute force: one pixel at a
// time
//
// Arguments: [iXLow] - left side of rectangle
// [iYLow] - top
// [iXHigh] - right side
// [iYHigh] - bottom
//
// History: 4-27-94 stevebl Created
//
// Notes: Used when a box is to small to be subdivided any farther.
//
//----------------------------------------------------------------------------

void CQuadrantEngine::PlotAreaSlowly(
int iXLow,
int iYLow,
int iXHigh,
int iYHigh)
{
for (int y = iYLow ; y <= iYHigh
&& WAIT_TIMEOUT == WaitForSingleObject(_hRestart, 0)
; y++)
{
for (int x = iXLow; x <= iXHigh
&& WAIT_TIMEOUT == WaitForSingleObject(_hRestart, 0)
; x++)
{
unsigned uColor;
_pqc->ComputePoint(&uColor, x, y);
_pfh->Point(x, y, uColor);
WaitForSingleObject(_hRunning, INFINITE);
}
}
}

// SUBDIVIDE determines at what point the Subdivide routine quits trying
// to draw ever-smaller boxes and just plots the pixels by brute force.
CONST unsigned SUBDIVIDE = 3;

//+---------------------------------------------------------------------------
//
// Member: CQuadrantEngine::Subdivide
//
// Synopsis: recursively plots all points within a rectangle
//
// Arguments: [iXLow] - left side of rectangle
// [iYLow] - top
// [iXHigh] - right side
// [iYHigh] - bottom
//
// History: 4-27-94 stevebl Created
//
// Algorithm: If the bounding box optimization is enabled, check to see if
// the perimiter is all the same color.
// If the perimiter is all one color, fill the entire rectangle
// with that color and fall out of the recursion.
// Otherwise, divide the rectangle into four quadrants and
// continue the recursion into each of them.
//
//----------------------------------------------------------------------------

void CQuadrantEngine::Subdivide(
int iXLow,
int iYLow,
int iXHigh,
int iYHigh)
{
if (WAIT_TIMEOUT == WaitForSingleObject(_hRestart, 0))
{
BOOL fDescend = !_fUseBoundingBoxes;
//
// If the bounding box optimization is enabled then fDescend
// will be FALSE until it finds two points on the perimiter
// that don't match.
//
// If the bounding box optimization is disabled then fDescend
// will be TRUE, causing the checking to be disabled altogether.
//

if (iXHigh - iXLow > SUBDIVIDE && iYHigh - iYLow > SUBDIVIDE)
{
unsigned uTest, uTemp;
_pfh->GetPoint(&uTest, iXLow, iYLow);
for (int x = iXLow;
(!fDescend) && x <= iXHigh;
x++)
{
_pfh->GetPoint(&uTemp, x, iYLow);
if (uTest != uTemp)
{
fDescend = TRUE;
}
else
{
_pfh->GetPoint(&uTemp, x, iYHigh);
if (uTest != uTemp)
{
fDescend = TRUE;
}
}
}
for (int y = iYLow + 1;
(!fDescend) && y < iYHigh;
y++)
{
_pfh->GetPoint(&uTemp, iXLow, y);
if (uTest != uTemp)
{
fDescend = TRUE;
}
else
{
_pfh->GetPoint(&uTemp, iXHigh, y);
if (uTest != uTemp)
{
fDescend = TRUE;
}
}
}
if (fDescend)
{
unsigned uColor;
int iXMid = (iXLow + iXHigh) / 2;
int iYMid = (iYLow + iYHigh) / 2;
for (int x = iXLow + 1;
x < iXHigh
&& WAIT_TIMEOUT == WaitForSingleObject(_hRestart, 0)
; x++)
{
_pqc->ComputePoint(&uColor, x, iYMid);
_pfh->Point(x, iYMid, uColor);
WaitForSingleObject(_hRunning, INFINITE);
}
int y;
for (y = iYLow + 1;
y < iYMid
&& WAIT_TIMEOUT == WaitForSingleObject(_hRestart, 0)
; y++)
{
_pqc->ComputePoint(&uColor, iXMid, y);
_pfh->Point(iXMid, y, uColor);
WaitForSingleObject(_hRunning, INFINITE);
}
for (y = iYMid + 1;
y < iYHigh
&& WAIT_TIMEOUT == WaitForSingleObject(_hRestart, 0)
; y++)
{
_pqc->ComputePoint(&uColor, iXMid, y);
_pfh->Point(iXMid, y, uColor);
WaitForSingleObject(_hRunning, INFINITE);
}
Subdivide(iXLow, iYLow, iXMid, iYMid);
Subdivide(iXMid, iYLow, iXHigh, iYMid);
Subdivide(iXLow, iYMid, iXMid, iYHigh);
Subdivide(iXMid, iYMid, iXHigh, iYHigh);
}
else
{
_pfh->Rect(iXLow + 1, iYLow + 1,
iXHigh - 1, iYHigh - 1,
uTest);
}
}
else
{
// do a simple iteration to plot the graph
PlotAreaSlowly(iXLow + 1, iYLow + 1, iXHigh - 1, iYHigh - 1);
}
}
}

//+---------------------------------------------------------------------------
//
// Member: CQuadrantEngine::CQuadrantEngine
//
// Synopsis: constructor
//
// History: 4-14-94 stevebl Created
//
//----------------------------------------------------------------------------

CQuadrantEngine::CQuadrantEngine()
{
_uWidth = 160;
_uHeight = 180;
_cRef = 0;
_pfh = NULL;
_pqc = NULL;
_hRunning = NULL;
_hRestart = NULL;
_hEngine = NULL;
_dwThreadId = 0;
_fUseBoundingBoxes = TRUE;
}

//+---------------------------------------------------------------------------
//
// Member: CQuadrantEngine::~CQuadrantEngine
//
// Synopsis: destructor
//
// History: 4-14-94 stevebl Created
//
//----------------------------------------------------------------------------

CQuadrantEngine::~CQuadrantEngine()
{
if (_hRestart)
{
CloseHandle(_hRestart);
}
if (_hRunning)
{
CloseHandle(_hRunning);
}
if (_hEngine)
{
TerminateThread(_hEngine, 0);
CloseHandle(_hEngine);
}
}