--- pmsdk/samples/linefrac/lfdraw.c 2018/08/09 12:28:13 1.1 +++ pmsdk/samples/linefrac/lfdraw.c 2018/08/09 12:28:32 1.1.1.2 @@ -1,28 +1,8 @@ - /************************************************************************ * -* lfdraw.c -* -* Created by Microsoft Corp., 1988 -* -* This file contains the functions that handle drawing of a "line -* fractal". -* -* The fractal always starts out showing one application of the -* similarity transform to the unit interval, centered in the window. -* Clicking on the left mouse button causes the fractal to recurse -* deeper; on the right button, shallower. -* -* Ideas for the future: -* --------------------- -* -* 1) Add a help screen to explain how to use it. -* 2) Allow the user to create or edit a similarity transform. -* 3) Allow setting of line attributes per segment of transform. -* 4) Save transform to disk and read back, so user can remember the -* good ones (and justify the program!). -* 5) Display the level of recursion in a corner of the window. +* lfdraw.c -- This file contains "line fractal" drawing routines * +* Created by Microsoft Corporation, 1989 * * * Background fractal drawing scheme: @@ -34,7 +14,7 @@ * application, and never terminates. * * The thread's execution is controlled by a semaphore, -* lSemAccumulateFractal. The thread is initially blocked +* lSemRedraw. The thread is initially blocked * by its semaphore. When something changes in the environ- * ment such that the fractal is to be redrawn, or drawn for * the first time, the semaphore is cleared and the thread @@ -45,244 +25,552 @@ * as soon as it's done drawing, it has to wait for the signal * to start again. * -* The fractal is drawn in batches of 500 points per polyline. +* The fractal is drawn in batches of up to 8196 points per polyline. * After each polyline is drawn, the background thread invalidates * the main client rectangle to force a WM_PAINT message to be sent. * All the paint procedure does is copy the bitmap, whatever * it's current state, to the screen. The user therefore -* sees bursts of 500 points at a time as his fractal is drawn. +* sees bursts of n points at a time as his fractal is drawn. * * The semaphore is controlled, in greater detail, as follows: * * Disable background drawing (set semaphore): * -* LineFracInit +* LfInit * Don't let second thread start working until a * transform has been defined. * -* AccumulateLineFractal +* LineFractalThread * Don't start the next one until the user asks for it. * * Enable background drawing (clear semaphore): * * WM_BUTTON1UP * WM_BUTTON2UP -* WM_SIZE & fAutoScale +* WM_SIZE & fAutoSizePS * The level of recursion or dimensions of bitmap have * changed, so redraw the fractal. * -* Change of transform -* The similarity transform has changed, so draw the -* new fractal with the current environment. -* -* -* Event AccumulateLineFractal -* ----- --------------------- -* -* WM_BUTTON1UP --------<-------- -* WM_BUTTON2UP / \ -* WM_SIZE & fAutoScale | | -* Change of transform | | -* | | | -* | V | -* +---------clear--------------> +----------------------+ | -* | lSemAccumulateFracal | | -* +----------set---------------> +----------------------+ | -* | | | -* | if semaphore is clear ^ -* initialization | | -* done with current fractal V | -* +----------------------+ | -* | Draw fractal | | -* WM_PAINT <---- | into bitmap | | -* +----------------------+ | -* | | -* | | -* \ / -* -------->-------- +* Change of fractal +* Change of drawing primitive +* Change of attributes +* If the corresponding redraw flag is enabled for one +* of these events, then the semaphore is cleared. +* +* +* Event LineFractalThread +* ----- ----------------- +* +* WM_BUTTON1UP --------<-------- +* WM_BUTTON2UP / \ +* WM_SIZE & fAutoSizePS | | +* Change of fractal/primitive/attributes | | +* | | | +* | V | +* +---------clear----------> +----------------------+ | +* | lSemRedraw | | +* +----------set-----------> +----------------------+ | +* | | | +* | if semaphore is clear ^ +* initialization | | +* done with current fractal V | +* +----------------------+ | +* | Draw fractal | | +* WM_PAINT <---- | into bitmap | | +* +----------------------+ | +* | | +* | | +* \ / +* -------->-------- * ************************************************************************/ #define INCL_WIN #define INCL_GPI #define INCL_DOSSEMAPHORES +#define INCL_DOSPROCESS #include -#include +#include + +#define INCL_GLOBALS +#define INCL_THREADS #include "linefrac.h" +#define INCL_LFTHREAD +#define INCL_LFDRAW +#include "lffuncs.h" + /************************************************************************ * -* Function prototypes. -* -* These add some error checking to function calls, and prevent -* forward references to functions of undefined return type. +* Global Variables * ************************************************************************/ -VOID LineFractal(int, double, double, BOOL, PLINEFRAC); -VOID AddFractalPoint(double, double); -VOID LineFracDraw(void); +extern GLOBALDATA global; +extern XFORMDATA aXform[]; /************************************************************************ * -* Global Data -* -* -* These variables reduce the number of parameters passed to -* the recursive fractal drawing function, or to the various -* functions called by the window procedure. +* LineFractalThread * +* Organize the drawing of the fractal. Runs in an independent +* thread to accumulate the points of the fractal, then calls +* LfDraw to draw with the selected primitive onto the surface in +* batches of a size selected by the user. If this thread is the +* top, then all or part of the client rectangle is invalidated +* after drawing to force a WM_PAINT message. When the paint message +* is processed, the image will be copied to the display. * -* hwndLineFrac Window handle for the client area. -* LineFracRc The dimensions of the bitmap (drawing surface). -* LineFracPS The Presentation Space for drawing fractal. -* LineFracXform Points to currently selected similarity transform. -* usRecursion Number of levels of recursion in drawing. -* usPolygonSides Number of sides to the polygonal frame of "unit -* intervals". The fractal transformation is -* applied to each line segment of the frame. +* This function is entered via _beginthread, which takes care of +* putting the parameter on the stack. * -* ptPolyLine Array for accumulating points for PolyLine. -* cptPoly The number of points in ptPolyLine. -* x, y Current position, i.e. the endpoint of the -* last line segment drawn/accumulated. -* lSemAccumulateFractal -* Semaphore controlling the background thread which -* accumulates points of fractal. Clear it to -* start new fractal. -* fInterrupted Indicates drawing should be aborted. -* fClearBetween Indicates that the drawing surface should be -* cleared to the background color before starting. +************************************************************************/ + +VOID FAR cdecl +LineFractalThread(pthr) +PTHR pthr; +{ + HAB hab; + int cFracSegs; + ULONG cptReq; + BOOL fCached; + BOOL fCacheable; + BOOL fModelXformsValid; + PLINEFRAC pXform; /* linked list of fractal segments */ + + + hab = WinInitialize(NULL); /* initialize ring 2 stack for thread */ + + fModelXformsValid = FALSE; + fCached = FALSE; + fCacheable = FALSE; + + pthr->pptl = NULL; + pthr->pmatlf = NULL; + + while (!pthr->fTimeToDie) + { + /**************************************************************** + * + * Clear the busy flag to indicate we're at the semaphore. + * If we happen to be the top thread, then force the pointer + * to be what we expect to see (if we didn't do this, it + * might stay as an hour glass until the user moves the mouse). + * + ****************************************************************/ + + pthr->fBusy = FALSE; + if (LfIsThreadTop(pthr)) + if ((global.hptr)[global.usCurPtr]) + WinSetPointer(HWND_DESKTOP,(global.hptr)[global.usCurPtr]); + + + /**************************************************************** + * + * Wait for permission to redraw. + * See if we're supposed to exit. If not, clear the suicide + * flag, and set the busy flag. If we're the top thread, then + * set the pointer to an hour glass to let the user know we're + * working on something. + * + ****************************************************************/ + + DosSemRequest(&pthr->lSemRedraw, -1L); + + if (pthr->fTimeToDie) + goto lfthread_exit; + pthr->fInterrupted = FALSE; + pthr->fBusy = TRUE; + if (LfIsThreadTop(pthr)) + if (global.hptrWait) + WinSetPointer(HWND_DESKTOP,global.hptrWait); + + /**************************************************************** + * + * Check for changes of attributes. If anything has changed, + * this subroutine copies the new stuff over within a critical + * section. + * + ****************************************************************/ + + LfUpdateAttrs(pthr); + + + /**************************************************************** + * + * Check the buffers for points and model transforms. If we + * don't have them, or the appropriate attributes have changed + * such that the ones we have are invalid, allocate for them. + * + ****************************************************************/ + + if ((pthr->pptl == NULL) || (pthr->flMiscAttrs & LFA_CPTMAX)) + { + if (pthr->pptl != NULL) + DosFreeSeg(*(((PUSHORT)&pthr->pptl)+1)); + if (DosAllocSeg(pthr->cptMax * sizeof(POINTL), + ((PUSHORT)&pthr->pptl)+1, 0)) + goto lfthread_exit; + } + + if ((pthr->pmatlf == NULL) || (pthr->flMiscAttrs & LFA_POLYGONSIDES)) + { + if (pthr->pmatlf != NULL) + DosFreeSeg(*(((PUSHORT)&pthr->pmatlf)+1)); + if (DosAllocSeg(pthr->usPolygonSides * sizeof(MATRIXLF), + ((PUSHORT)&pthr->pmatlf)+1, 0)) + goto lfthread_exit; + fModelXformsValid = FALSE; + } + + + /**************************************************************** + * + * See if we can cache the whole lot of points. This depends + * on the fractal, the size of the point cache, and the level + * of recursion. Note that we must execute this code the first + * time through, or fCacheable will be undefined. We're sure + * to come here, though, because threads are initialized with + * all attributes "changed". + * + ****************************************************************/ + + if (pthr->flMiscAttrs & (LFA_CPTMAX | LFA_RECURSION | LFA_CURXFORM)) + { + if (pthr->flMiscAttrs & LFA_CURXFORM) + { + PLINEFRAC p; + + cFracSegs = 0; + pXform = aXform[pthr->usCurXform - IDM_SHARKTOOTH].pXform; + p = pXform; + while (p != EOLIST) + { + ++cFracSegs; + p = p->next; + } + } + + cptReq = (ULONG) exp((double)pthr->usRecursion * + log((double)cFracSegs)); + + if ((ULONG)pthr->cptMax > cptReq) + fCacheable = TRUE; + else + fCacheable = FALSE; + + fCached = FALSE; + } + + + /**************************************************************** + * + * If the model transforms are invalid, then recompute them. + * Check first to see if any attributes have changed that + * would invalidate the transforms. + * + ****************************************************************/ + + if (fModelXformsValid) + if (pthr->flMiscAttrs & (LFA_ROTATION | LFA_POLYGONSIDES | + LFA_XSCALE | LFA_YSCALE | LFA_XOFF | LFA_YOFF)) + fModelXformsValid = FALSE; + + if (!fModelXformsValid) + { + LfComputeModelXforms(pthr); + fModelXformsValid = TRUE; + } + + + /**************************************************************** + * + * Clear the change-of-attributes flags that have been examined + * by the time we get here. + * + ****************************************************************/ + + pthr->flMiscAttrs &= + ~( + LFA_CPTMAX | LFA_RECURSION | LFA_POLYGONSIDES | LFA_CURXFORM | + LFA_XSCALE | LFA_YSCALE | LFA_XOFF | LFA_YOFF | + LFA_ROTATION + ); + + + /**************************************************************** + * + * Clear the surface if fClearOnRedraw is enabled. + * If the points are cached, then redraw straight from the cache. + * Otherwise, anchor the fractal at the left endpoint of the + * unit interval, then draw it to the specified depth of recursion. + * If we are able to cache all the points, then nothing will have + * been drawn when LineFractal returns, so draw the fractal without + * flushing the cache. If we were not able to cache all the + * points, and the buffer is not empty, flush the last batch. + * + ****************************************************************/ + + if (pthr->fClearOnRedraw) + LfClearRect(pthr, NULL); + + if (fCached) + LfDraw(pthr, FALSE); + else + { + pthr->x = 0.0; + pthr->y = 0.0; + + pthr->cptl = 0L; + LfAddPoint(pthr); + + LineFractal(pthr, pthr->usRecursion, (double)pthr->cxWCS, + 0.0, FALSE, pXform); + + if (!pthr->fInterrupted) + { + if (fCacheable) + { + LfDraw(pthr, FALSE); + fCached = TRUE; + } + else + { + fCached = FALSE; + if (pthr->cptl > 1L) + LfDraw(pthr, TRUE); + } + } + } + } + + + /**************************************************************** + * + * Common exit point for thread. Free up memory allocated + * by this thread. + * + ****************************************************************/ + +lfthread_exit: + + if (pthr->pmatlf != NULL) + DosFreeSeg(*(((PUSHORT)&pthr->pmatlf)+1)); + if (pthr->pptl != NULL) + DosFreeSeg(*(((PUSHORT)&pthr->pptl)+1)); +} + + + + +/************************************************************************ * -* 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 +* LfUpdateAttrs * -* lColorBack Background color for the drawing. -* lColor Line foreground color. -* usStyle Line style. -* usMixMode Line foreground mix mode. +* Update any changed attributes from the global attributes. * ************************************************************************/ -extern HWND hwndLineFrac; -extern RECTL LineFracRc; -extern HPS LineFracPS; -extern PLINEFRAC LineFracXform; -extern USHORT usRecursion; -extern USHORT usPolygonSides; +VOID +LfUpdateAttrs(pthr) +PTHR pthr; +{ + + DosEnterCritSec(); + if (pthr->fUpdateAttrs) + { + if (global.flLineAttrs & LFA_LINEALL) + { + pthr->lb = global.lb; + pthr->flLineAttrs |= global.flLineAttrs; + global.flLineAttrs = 0L; + } + if (global.flMarkerAttrs & LFA_MARKALL) + { + pthr->mb = global.mb; + pthr->flMarkerAttrs |= global.flMarkerAttrs; + global.flMarkerAttrs = 0L; + } + if (global.flAreaAttrs & LFA_AREAALL) + { + pthr->ab = global.ab; + pthr->flAreaAttrs |= global.flAreaAttrs; + global.flAreaAttrs = 0L; + } + if (global.flImageAttrs & LFA_IMAGEALL) + { + pthr->ib = global.ib; + pthr->flImageAttrs |= global.flImageAttrs; + global.flImageAttrs = 0L; + } + if (global.flMiscAttrs & LFA_MISCALL) + { + if (global.flMiscAttrs & LFA_CURPRIM) + pthr->usCurPrim = global.usCurPrim; + if (global.flMiscAttrs & LFA_CURXFORM) + pthr->usCurXform = global.usCurXform; + if (global.flMiscAttrs & LFA_RECURSION) + pthr->usRecursion = global.usRecursion; + if (global.flMiscAttrs & LFA_POLYGONSIDES) + pthr->usPolygonSides = global.usPolygonSides; + if (global.flMiscAttrs & LFA_CPTMAX) + pthr->cptMax = global.cptMax; + if (global.flMiscAttrs & LFA_XOFF) + pthr->dblXOff = global.dblXOff; + if (global.flMiscAttrs & LFA_YOFF) + pthr->dblYOff = global.dblYOff; + if (global.flMiscAttrs & LFA_XSCALE) + pthr->dblXScale = global.dblXScale; + if (global.flMiscAttrs & LFA_YSCALE) + pthr->dblYScale = global.dblYScale; + if (global.flMiscAttrs & LFA_ROTATION) + pthr->dblRotation = global.dblRotation; + if (global.flMiscAttrs & LFA_CXWCS) + pthr->cxWCS = global.cxWCS; + if (global.flMiscAttrs & LFA_CYWCS) + pthr->cyWCS = global.cyWCS; + pthr->flMiscAttrs |= global.flMiscAttrs; + global.flMiscAttrs = 0L; + } - POINTL ptPolyLine[MAX_POINT_COUNT]; - ULONG cptPoly = 0L; -extern USHORT cptMax; - double x, y; + pthr->fUpdateAttrs = FALSE; + global.fUpdateAttrs = FALSE; -extern LONG lSemAccumulateFractal; -extern BOOL fInterrupted; -extern BOOL fClearBetween; + } + DosExitCritSec(); -extern double xscale; -extern double yscale; -extern double xoff; -extern double yoff; -extern LONG lColorBack; -extern LONG lColor; -extern USHORT usStyle; -extern USHORT usMixMode; + /* Take care of the attribute bundles now. The miscellaneous attributes + * require more processing, so don't clear their flags yet. + */ + + if (pthr->flLineAttrs & LFA_LINEALL) + { + GpiSetAttrs(pthr->hps, PRIM_LINE, pthr->flLineAttrs, 0L, &pthr->lb); + pthr->flLineAttrs = 0L; + } + if (pthr->flMarkerAttrs & LFA_MARKALL) + { + GpiSetAttrs(pthr->hps, PRIM_MARKER, pthr->flMarkerAttrs, 0L, &pthr->mb); + pthr->flMarkerAttrs = 0L; + } + if (pthr->flAreaAttrs & LFA_AREAALL) + { + GpiSetAttrs(pthr->hps, PRIM_AREA, pthr->flAreaAttrs, 0L, &pthr->ab); + pthr->flAreaAttrs = 0L; + } + if (pthr->flImageAttrs) + { + GpiSetAttrs(pthr->hps, PRIM_IMAGE, pthr->flImageAttrs, 0L, &pthr->ib); + pthr->flImageAttrs = 0L; + } +} /************************************************************************ * -* AccumulateLineFractal +* LfComputeModelXforms * -* Organize the drawing of the fractal. Runs in an independent -* thread to accumulate the points of the fractal, then calls -* GpiPolyLine to draw into a bitmap. Each time a line is drawn -* (in batches of some appropriate number of points) the client -* rectangle of the application window is invalidated to cause a -* WM_PAINT message. When the paint message is processed, the -* bitmap will be copied to the display. -* -* This thread never terminates. It is blocked by the semaphore -* lSemAccumulateFractal when there is no work to be done. +* Compute the model transform matrices necessary to draw the fractal +* on each side of the polygonal frame. The rotation, scaling, and +* translation are all rolled into one matrix for simplicity. * ************************************************************************/ -VOID FAR PASCAL -AccumulateLineFractal() +VOID +LfComputeModelXforms(pthr) +PTHR pthr; { - double initlength; - double initangle; - double angledecr; + double dblXScale, dblYScale; + double dblXOff, dblYOff; + double dblTheta; + double dblRotation, dblSinRotation, dblCosRotation; + double dblSideLen, dblAngleDecr; + double dblXExtDims, dblYExtDims; + double dblHalfXDims, dblHalfYDims; + double dx, dy; + PMATRIXLF pmatlf; int i; - WinInitialize(NULL); /* initialize ring 2 stack for thread */ + dblAngleDecr = TWO_PI / (double)pthr->usPolygonSides; - while (1) + if (pthr->usPolygonSides == 1) { - DosSemRequest(&lSemAccumulateFractal, -1L); - - - /* Initialize the fractal to the unit interval, with nothing - * in the point accumulation buffer. + dblSideLen = 1.0; + dblRotation = (double) pthr->dblRotation; + } + else + { + /* C 5.1 incorrectly compiles sin(temp_dbl2) in large + * model, where temp_dbl2 is expanded to eliminate all + * temporary variables, therefore I DO use the temp vars. */ - fInterrupted = FALSE; - - x = 0.0; - y = 0.0; + double temp_dbl1, temp_dbl2; - if (usPolygonSides == 1) - { - initlength = 1.0; - initangle = 0.0; - angledecr = 0.0; - } - else - { - initlength = sin(PI / usPolygonSides); - initangle = (PI * (usPolygonSides - 2)) / (2 * usPolygonSides); - angledecr = 2 * PI / usPolygonSides; - } + temp_dbl1 = (double)pthr->usPolygonSides; + temp_dbl2 = PI / temp_dbl1; + dblSideLen = sin(temp_dbl2); + dblRotation = PI - dblAngleDecr; + dblRotation = 0.5 * dblRotation + pthr->dblRotation; + } + { + double temp_dbl1, temp_dbl2; - /* Clear the client rectangle to background color. */ - if (fClearBetween) - WinFillRect(LineFracPS, &LineFracRc, lColorBack); + temp_dbl1 = (double) pthr->rcl.xRight; + temp_dbl2 = (double) pthr->cxWCS; + dblXScale = temp_dbl1 / temp_dbl2; + dblXScale *= pthr->dblXScale * dblSideLen; + + temp_dbl1 = (double) pthr->rcl.yTop; + temp_dbl2 = (double) pthr->cyWCS; + dblYScale = temp_dbl1 / temp_dbl2; + dblYScale *= pthr->dblYScale * dblSideLen; + } + dblXExtDims = (double) pthr->rcl.xRight * pthr->dblXScale; + dblYExtDims = (double) pthr->rcl.yTop * pthr->dblYScale; + dblHalfXDims = 0.5 * dblXExtDims; + dblHalfYDims = 0.5 * dblYExtDims; + dblXOff = (double) pthr->rcl.xRight * pthr->dblXOff + dblHalfXDims; + dblYOff = (double) pthr->rcl.yTop * pthr->dblYOff + dblHalfYDims; - for (i = 0; i < usPolygonSides; ++i) - { + dblTheta = PI + pthr->dblRotation; - /* Anchor the fractal at the left endpoint of the unit interval, - * then draw it to the specified depth of recursion. If the point - * buffer is not empty afterwards, draw the last PolyLine. - */ + for (i = 0; i < pthr->usPolygonSides; ++i) + { + dblCosRotation = cos(dblRotation); + dblSinRotation = sin(dblRotation); - cptPoly = 0L; - AddFractalPoint(x, y); - LineFractal(usRecursion, initlength, initangle, FALSE, LineFracXform); + dx = dblHalfXDims * cos(dblTheta); + dy = dblHalfYDims * sin(dblTheta); - if (fInterrupted) - break; + /* 0.000015 = about 1/65536 */ - if (cptPoly > 1) - LineFracDraw(); + pmatlf = pthr->pmatlf + i; + pmatlf->fxM11 = (FIXED)(( dblCosRotation * dblXScale + 0.000015) * (double) 0x10000L); + pmatlf->fxM12 = (FIXED)(( dblSinRotation * dblYScale + 0.000015) * (double) 0x10000L); + pmatlf-> lM13 = 0L; + pmatlf->fxM21 = (FIXED)((-dblSinRotation * dblXScale + 0.000015) * (double) 0x10000L); + pmatlf->fxM22 = (FIXED)(( dblCosRotation * dblYScale + 0.000015) * (double) 0x10000L); + pmatlf-> lM23 = 0L; + pmatlf-> lM31 = (LONG) (dblXOff + dx + 0.5); + pmatlf-> lM32 = (LONG) (dblYOff + dy + 0.5); + pmatlf-> lM33 = 1L; - initangle -= angledecr; - } + dblRotation -= dblAngleDecr; + dblTheta -= dblAngleDecr; } } @@ -308,7 +596,8 @@ AccumulateLineFractal() ************************************************************************/ VOID -LineFractal(depth, len, ang, flip, xform) +LineFractal(pthr, depth, len, ang, flip, xform) +PTHR pthr; int depth; double len; double ang; @@ -319,7 +608,7 @@ PLINEFRAC xform; PLINEFRAC newseg; - if (fInterrupted) + if (pthr->fInterrupted) return; if (depth) @@ -336,7 +625,7 @@ PLINEFRAC xform; { newlen = len * newseg->length; ang += newseg->angle * (flip ? -1 : 1); - LineFractal(depth, newlen, ang, flip ^ newseg->flip, xform); + LineFractal(pthr, depth, newlen, ang, (flip ^ newseg->flip), xform); } while ((newseg = newseg->next) != EOLIST); } else @@ -346,16 +635,132 @@ PLINEFRAC xform; * draw a line segment. */ - x += len * cos(ang); - y += len * sin(ang); - AddFractalPoint(x, y); + pthr->x += len * cos(ang); + pthr->y += len * sin(ang); + LfAddPoint(pthr); + } +} + + + + +/************************************************************************ +* +* LfAddPoint +* +* Applies the global coordinate transform to the point (x, y), then +* stuffs it into the global PolyLine point array, and increments the +* count of points in the array. +* +************************************************************************/ + +VOID +LfAddPoint(pthr) +PTHR pthr; +{ + if (pthr->cptl == (ULONG)pthr->cptMax) + LfDraw(pthr, TRUE); + + if (pthr->cptl < (ULONG)pthr->cptMax) + { + (pthr->pptl + pthr->cptl)->x = + (int)(pthr->x + 0.5); + (pthr->pptl + pthr->cptl)->y = + (int)(pthr->y + 0.5); + ++pthr->cptl; + } +} + + + + +/************************************************************************ +* +* LfDraw +* +* For each segment of the frame, set the model transform and draw the +* cache of points in the current primitive. +* +* Invalidate the client rectangle of the main window in case this is +* the top thread, to cause our latest bitmap to be copied there. +* +************************************************************************/ + +VOID +LfDraw(pthr, fFlush) +PTHR pthr; +BOOL fFlush; +{ + int i; + BOOL myFlush; + + + /* If this is a direct DC, but is not the top thread, then + * don't draw anything. + */ + if (pthr->dcType == IDM_DCDIRECT) + if (!LfIsThreadTop(pthr)) + { + if (fFlush) + pthr->cptl = 0L; + return; + } + + + if (pthr->fCollectBounds) + GpiResetBoundaryData(pthr->hps); + + + myFlush = FALSE; + + for (i = 0; i < pthr->usPolygonSides; ++i) + { + if (pthr->fInterrupted) + return; + + /* set model transform */ + GpiSetModelTransformMatrix(pthr->hps, 9L, pthr->pmatlf+i, TRANSFORM_REPLACE); + + /* we only really flush the last time we use the cache */ + if (i == pthr->usPolygonSides - 1) + myFlush = fFlush; - if (cptPoly >= (ULONG)cptMax) + switch ( pthr->usCurPrim ) { - LineFracDraw(); - ptPolyLine[0] = ptPolyLine[cptPoly-1]; - cptPoly = 1L; + case IDM_POLYLINE: + LfDrawPolyLine(pthr, myFlush); + break; + + case IDM_POLYFILLET: + LfDrawPolyFillet(pthr, myFlush); + break; + + case IDM_POLYSPLINE: + LfDrawPolySpline(pthr, myFlush); + break; + + case IDM_PEANO: + LfDrawPolyPeano(pthr, myFlush); + break; + + case IDM_POLYMARKER: + LfDrawPolyMarker(pthr, myFlush); + break; } + + if (pthr->dcType != IDM_DCDIRECT) + if (LfIsThreadTop(pthr)) + { + if (pthr->fCollectBounds) + { + GpiQueryBoundaryData(pthr->hps, &pthr->rclBounds); + ++(pthr->rclBounds).xRight; + ++(pthr->rclBounds).yTop; + WinInvalidateRect(global.hwnd, &pthr->rclBounds, FALSE); + } + else + WinInvalidateRect(global.hwnd, &pthr->rcl, FALSE); + } } } @@ -364,21 +769,95 @@ PLINEFRAC xform; /************************************************************************ * -* AddFractalPoint +* LfDrawPolyLine * -* Applies the global coordinate transform to the point (x, y), then -* stuffs it into the global PolyLine point array, and increments the -* counts of points in the array. +* Draw a polyline using the thread's point buffer. +* +************************************************************************/ + +VOID +LfDrawPolyLine(pthr, fFlush) +PTHR pthr; +BOOL fFlush; +{ + /* After drawing the line, save the last point to set the + current position before the next call. */ + + GpiSetCurrentPosition( pthr->hps, pthr->pptl ); + GpiPolyLine( pthr->hps, pthr->cptl-1L, pthr->pptl+1 ); + if (fFlush) + { + *pthr->pptl = *(pthr->pptl + pthr->cptl-1); + pthr->cptl = 1L; + } +} + + + + +/************************************************************************ +* +* LfDrawPolyFillet +* +* Draw a polyfillet using the thread's point buffer. +* +************************************************************************/ + +VOID +LfDrawPolyFillet(pthr, fFlush) +PTHR pthr; +BOOL fFlush; +{ + /* After drawing the curve, save the last point to set the + current position before the next call. */ + + if (pthr->cptl > 2) + { + GpiSetCurrentPosition( pthr->hps, pthr->pptl ); + GpiPolyFillet( pthr->hps, pthr->cptl-1L, pthr->pptl+1 ); + if (fFlush) + { + *pthr->pptl = *(pthr->pptl + pthr->cptl-1); + pthr->cptl = 1L; + } + } +} + + + + +/************************************************************************ +* +* LfDrawPolySpline +* +* Draw a polyspline using the thread's point buffer. * ************************************************************************/ -void -AddFractalPoint(x, y) -double x,y; +VOID +LfDrawPolySpline(pthr, fFlush) +PTHR pthr; +BOOL fFlush; { - ptPolyLine[cptPoly].x = (int)(x * xscale + xoff + 0.5); - ptPolyLine[cptPoly].y = (int)(y * yscale + yoff + 0.5); - ++cptPoly; + int i; /* loop counter */ + USHORT cptSlack; /* # points in pptl not usable by PolySpline */ + + /* GpiPolySpline expects the number of points to be a + multiple of 3. If we have a non-multiple of three, + (excluding the first point, which we've used to set + the current position), only pass the largest multiple + of three, saving the rest for the next go-round. */ + + cptSlack = (int)((pthr->cptl-1L) % 3) + 1; + GpiSetCurrentPosition( pthr->hps, pthr->pptl ); + GpiPolySpline( pthr->hps, pthr->cptl-cptSlack, + pthr->pptl+1 ); + if (fFlush) + { + for (i = 0; i < cptSlack; ++i) + *(pthr->pptl + i) = *(pthr->pptl + pthr->cptl-cptSlack+i); + pthr->cptl = cptSlack; + } } @@ -386,25 +865,114 @@ double x,y; /************************************************************************ * -* LineFracDraw +* LfDrawPolyPeano * -* Set line attributes and draw a batch of lines. Invalidate the client -* rectangle of the main window to cause our latest bitmap to be -* copied there. +* Draw a chain of Peano primitives using the thread's point buffer. * ************************************************************************/ VOID -LineFracDraw() +LfDrawPolyPeano(pthr, fFlush) +PTHR pthr; +BOOL fFlush; { - LINEBUNDLE lb; + LONG a, b, c, d; /* temporary vars for Peano curvelet calculations */ + int cptPeano; /* current point in pptl in use by Peano curve */ + POINTL ptPeano[2]; /* Peano curvelet array to pass to PolyLine */ + + for (cptPeano = 0; cptPeano < (int)(pthr->cptl-1L); ++cptPeano) + { + ptPeano[0] = *(pthr->pptl + cptPeano); + ptPeano[1] = *(pthr->pptl + cptPeano+1); + a = (pthr->pptl + cptPeano+1)->x - (pthr->pptl + cptPeano)->x; + b = (pthr->pptl + cptPeano+1)->y - (pthr->pptl + cptPeano)->y; + c = (a + b)/2; + d = (a - b)/2; + if (labs(a) > labs(b)) + { + ptPeano[0].x += d; + ptPeano[0].y += c; + } + else + { + ptPeano[0].x += c; + ptPeano[0].y += -d; + } - lb.lColor = lColor; - lb.usMixMode = usMixMode; - lb.usType = usStyle; - GpiSetAttrs(LineFracPS, PRIM_LINE, LBB_COLOR|LBB_MIX_MODE|LBB_TYPE, - 0L, (PBUNDLE)&lb); - GpiSetCurrentPosition( LineFracPS, (PPOINTL)ptPolyLine ); - GpiPolyLine( LineFracPS, cptPoly-1, (PPOINTL)&ptPolyLine[1] ); - WinInvalidateRect(hwndLineFrac, &LineFracRc, FALSE); + GpiSetCurrentPosition( pthr->hps, pthr->pptl + cptPeano); + GpiPolyLine( pthr->hps, 2L, (PPOINTL)ptPeano); + } + if (fFlush) + { + *pthr->pptl = *(pthr->pptl + pthr->cptl-1); + pthr->cptl = 1L; + } +} + + + + +/************************************************************************ +* +* LfDrawPolyMarker +* +* Draw a list of markers using the thread's point buffer. +* +************************************************************************/ + +VOID +LfDrawPolyMarker(pthr, fFlush) +PTHR pthr; +BOOL fFlush; +{ + /* I want to draw markers at every point in the array, but + GpiPolyMarker won't draw at the last point! So, GpiMarker + does the job instead. */ + + GpiSetCurrentPosition( pthr->hps, pthr->pptl + pthr->cptl-1); + GpiPolyMarker( pthr->hps, pthr->cptl, pthr->pptl ); + GpiMarker ( pthr->hps, pthr->pptl + pthr->cptl-1 ); + + if (fFlush) + pthr->cptl = 0L; +} + + + + +/************************************************************************ +* +* LfClearRect +* +* Set the area attributes if needed and fill the bitmap with them. +* +************************************************************************/ + +VOID +LfClearRect(pthr, prcl) +PTHR pthr; +PRECTL prcl; +{ + PRECTL prclT; + + if (pthr->hps) + { + if (prcl) + prclT = prcl; + else + prclT = &pthr->rcl; + + if (pthr->dcType == IDM_DCDIRECT) + { + /* If direct DC, only blt if top thread. */ + if (LfIsThreadTop(pthr)) + GpiBitBlt(pthr->hps, NULL, 2L, (PPOINTL)prclT, ROP_PATCOPY, NULL); + } + else + { + GpiBitBlt(pthr->hps, NULL, 2L, (PPOINTL)prclT, ROP_PATCOPY, NULL); + if (LfIsThreadTop(pthr)) + WinInvalidateRect(global.hwnd, prclT, FALSE); + } + } }