|
|
1.1 ! root 1: ! 2: /************************************************************************ ! 3: * ! 4: * lfdraw.c ! 5: * ! 6: * Created by Microsoft Corp., 1988 ! 7: * ! 8: * This file contains the functions that handle drawing of a "line ! 9: * fractal". ! 10: * ! 11: * The fractal always starts out showing one application of the ! 12: * similarity transform to the unit interval, centered in the window. ! 13: * Clicking on the left mouse button causes the fractal to recurse ! 14: * deeper; on the right button, shallower. ! 15: * ! 16: * Ideas for the future: ! 17: * --------------------- ! 18: * ! 19: * 1) Add a help screen to explain how to use it. ! 20: * 2) Allow the user to create or edit a similarity transform. ! 21: * 3) Allow setting of line attributes per segment of transform. ! 22: * 4) Save transform to disk and read back, so user can remember the ! 23: * good ones (and justify the program!). ! 24: * 5) Display the level of recursion in a corner of the window. ! 25: * ! 26: * ! 27: * ! 28: * Background fractal drawing scheme: ! 29: * ! 30: * To enable the user to interact with the system during ! 31: * the drawing of a complicated fractal, the fractal is ! 32: * drawn into a bitmap by a background thread. This thread ! 33: * is started at the first WM_PAINT message received by the ! 34: * application, and never terminates. ! 35: * ! 36: * The thread's execution is controlled by a semaphore, ! 37: * lSemAccumulateFractal. The thread is initially blocked ! 38: * by its semaphore. When something changes in the environ- ! 39: * ment such that the fractal is to be redrawn, or drawn for ! 40: * the first time, the semaphore is cleared and the thread ! 41: * is off and running. Note that the environment must be ! 42: * set up BEFORE the semaphore is cleared, otherwise the ! 43: * fractal may be partially drawn with old parameters. ! 44: * The thread automatically resets the semaphore, so that ! 45: * as soon as it's done drawing, it has to wait for the signal ! 46: * to start again. ! 47: * ! 48: * The fractal is drawn in batches of 500 points per polyline. ! 49: * After each polyline is drawn, the background thread invalidates ! 50: * the main client rectangle to force a WM_PAINT message to be sent. ! 51: * All the paint procedure does is copy the bitmap, whatever ! 52: * it's current state, to the screen. The user therefore ! 53: * sees bursts of 500 points at a time as his fractal is drawn. ! 54: * ! 55: * The semaphore is controlled, in greater detail, as follows: ! 56: * ! 57: * Disable background drawing (set semaphore): ! 58: * ! 59: * LineFracInit ! 60: * Don't let second thread start working until a ! 61: * transform has been defined. ! 62: * ! 63: * AccumulateLineFractal ! 64: * Don't start the next one until the user asks for it. ! 65: * ! 66: * Enable background drawing (clear semaphore): ! 67: * ! 68: * WM_BUTTON1UP ! 69: * WM_BUTTON2UP ! 70: * WM_SIZE & fAutoScale ! 71: * The level of recursion or dimensions of bitmap have ! 72: * changed, so redraw the fractal. ! 73: * ! 74: * Change of transform ! 75: * The similarity transform has changed, so draw the ! 76: * new fractal with the current environment. ! 77: * ! 78: * ! 79: * Event AccumulateLineFractal ! 80: * ----- --------------------- ! 81: * ! 82: * WM_BUTTON1UP --------<-------- ! 83: * WM_BUTTON2UP / \ ! 84: * WM_SIZE & fAutoScale | | ! 85: * Change of transform | | ! 86: * | | | ! 87: * | V | ! 88: * +---------clear--------------> +----------------------+ | ! 89: * | lSemAccumulateFracal | | ! 90: * +----------set---------------> +----------------------+ | ! 91: * | | | ! 92: * | if semaphore is clear ^ ! 93: * initialization | | ! 94: * done with current fractal V | ! 95: * +----------------------+ | ! 96: * | Draw fractal | | ! 97: * WM_PAINT <---- | into bitmap | | ! 98: * +----------------------+ | ! 99: * | | ! 100: * | | ! 101: * \ / ! 102: * -------->-------- ! 103: * ! 104: ************************************************************************/ ! 105: ! 106: #define INCL_WIN ! 107: #define INCL_GPI ! 108: #define INCL_DOSSEMAPHORES ! 109: ! 110: #include <os2.h> ! 111: #include <math.h> ! 112: #include "linefrac.h" ! 113: ! 114: ! 115: ! 116: ! 117: /************************************************************************ ! 118: * ! 119: * Function prototypes. ! 120: * ! 121: * These add some error checking to function calls, and prevent ! 122: * forward references to functions of undefined return type. ! 123: * ! 124: ************************************************************************/ ! 125: ! 126: VOID LineFractal(int, double, double, BOOL, PLINEFRAC); ! 127: VOID AddFractalPoint(double, double); ! 128: VOID LineFracDraw(void); ! 129: ! 130: ! 131: ! 132: ! 133: /************************************************************************ ! 134: * ! 135: * Global Data ! 136: * ! 137: * ! 138: * These variables reduce the number of parameters passed to ! 139: * the recursive fractal drawing function, or to the various ! 140: * functions called by the window procedure. ! 141: * ! 142: * ! 143: * hwndLineFrac Window handle for the client area. ! 144: * LineFracRc The dimensions of the bitmap (drawing surface). ! 145: * LineFracPS The Presentation Space for drawing fractal. ! 146: * LineFracXform Points to currently selected similarity transform. ! 147: * usRecursion Number of levels of recursion in drawing. ! 148: * usPolygonSides Number of sides to the polygonal frame of "unit ! 149: * intervals". The fractal transformation is ! 150: * applied to each line segment of the frame. ! 151: * ! 152: * ptPolyLine Array for accumulating points for PolyLine. ! 153: * cptPoly The number of points in ptPolyLine. ! 154: * x, y Current position, i.e. the endpoint of the ! 155: * last line segment drawn/accumulated. ! 156: * lSemAccumulateFractal ! 157: * Semaphore controlling the background thread which ! 158: * accumulates points of fractal. Clear it to ! 159: * start new fractal. ! 160: * fInterrupted Indicates drawing should be aborted. ! 161: * fClearBetween Indicates that the drawing surface should be ! 162: * cleared to the background color before starting. ! 163: * ! 164: * xscale These transform each point accumulated, so ! 165: * yscale that the drawing fits into the window, ! 166: * xoff with the original unit interval at the center. ! 167: * yoff ! 168: * ! 169: * lColorBack Background color for the drawing. ! 170: * lColor Line foreground color. ! 171: * usStyle Line style. ! 172: * usMixMode Line foreground mix mode. ! 173: * ! 174: ************************************************************************/ ! 175: ! 176: extern HWND hwndLineFrac; ! 177: extern RECTL LineFracRc; ! 178: extern HPS LineFracPS; ! 179: extern PLINEFRAC LineFracXform; ! 180: extern USHORT usRecursion; ! 181: extern USHORT usPolygonSides; ! 182: ! 183: POINTL ptPolyLine[MAX_POINT_COUNT]; ! 184: ULONG cptPoly = 0L; ! 185: extern USHORT cptMax; ! 186: double x, y; ! 187: ! 188: extern LONG lSemAccumulateFractal; ! 189: extern BOOL fInterrupted; ! 190: extern BOOL fClearBetween; ! 191: ! 192: extern double xscale; ! 193: extern double yscale; ! 194: extern double xoff; ! 195: extern double yoff; ! 196: ! 197: extern LONG lColorBack; ! 198: extern LONG lColor; ! 199: extern USHORT usStyle; ! 200: extern USHORT usMixMode; ! 201: ! 202: ! 203: ! 204: ! 205: /************************************************************************ ! 206: * ! 207: * AccumulateLineFractal ! 208: * ! 209: * Organize the drawing of the fractal. Runs in an independent ! 210: * thread to accumulate the points of the fractal, then calls ! 211: * GpiPolyLine to draw into a bitmap. Each time a line is drawn ! 212: * (in batches of some appropriate number of points) the client ! 213: * rectangle of the application window is invalidated to cause a ! 214: * WM_PAINT message. When the paint message is processed, the ! 215: * bitmap will be copied to the display. ! 216: * ! 217: * This thread never terminates. It is blocked by the semaphore ! 218: * lSemAccumulateFractal when there is no work to be done. ! 219: * ! 220: ************************************************************************/ ! 221: ! 222: VOID FAR PASCAL ! 223: AccumulateLineFractal() ! 224: { ! 225: double initlength; ! 226: double initangle; ! 227: double angledecr; ! 228: int i; ! 229: ! 230: ! 231: WinInitialize(NULL); /* initialize ring 2 stack for thread */ ! 232: ! 233: while (1) ! 234: { ! 235: DosSemRequest(&lSemAccumulateFractal, -1L); ! 236: ! 237: ! 238: /* Initialize the fractal to the unit interval, with nothing ! 239: * in the point accumulation buffer. ! 240: */ ! 241: ! 242: fInterrupted = FALSE; ! 243: ! 244: x = 0.0; ! 245: y = 0.0; ! 246: ! 247: if (usPolygonSides == 1) ! 248: { ! 249: initlength = 1.0; ! 250: initangle = 0.0; ! 251: angledecr = 0.0; ! 252: } ! 253: else ! 254: { ! 255: initlength = sin(PI / usPolygonSides); ! 256: initangle = (PI * (usPolygonSides - 2)) / (2 * usPolygonSides); ! 257: angledecr = 2 * PI / usPolygonSides; ! 258: } ! 259: ! 260: ! 261: /* Clear the client rectangle to background color. */ ! 262: if (fClearBetween) ! 263: WinFillRect(LineFracPS, &LineFracRc, lColorBack); ! 264: ! 265: ! 266: for (i = 0; i < usPolygonSides; ++i) ! 267: { ! 268: ! 269: /* Anchor the fractal at the left endpoint of the unit interval, ! 270: * then draw it to the specified depth of recursion. If the point ! 271: * buffer is not empty afterwards, draw the last PolyLine. ! 272: */ ! 273: ! 274: cptPoly = 0L; ! 275: AddFractalPoint(x, y); ! 276: LineFractal(usRecursion, initlength, initangle, FALSE, LineFracXform); ! 277: ! 278: if (fInterrupted) ! 279: break; ! 280: ! 281: if (cptPoly > 1) ! 282: LineFracDraw(); ! 283: ! 284: initangle -= angledecr; ! 285: } ! 286: } ! 287: } ! 288: ! 289: ! 290: ! 291: ! 292: /************************************************************************ ! 293: * ! 294: * LineFractal ! 295: * ! 296: * Draw fractal with the given similarity transform. ! 297: * ! 298: * The general idea is to define a transformation to apply to the ! 299: * unit line segment, to get a new polyline. This same transformation ! 300: * is then applied to each line segment of the new polyline. The number ! 301: * of successive applications of the similarity transform is set by the ! 302: * user. It's known as the level of recursion of the fractal. ! 303: * ! 304: * Since this is where the point accumulation process will usually ! 305: * be, it recognizes the flag fInterrupted to allow the current ! 306: * work to be abandoned. ! 307: * ! 308: ************************************************************************/ ! 309: ! 310: VOID ! 311: LineFractal(depth, len, ang, flip, xform) ! 312: int depth; ! 313: double len; ! 314: double ang; ! 315: BOOL flip; ! 316: PLINEFRAC xform; ! 317: { ! 318: double newlen; ! 319: PLINEFRAC newseg; ! 320: ! 321: ! 322: if (fInterrupted) ! 323: return; ! 324: ! 325: if (depth) ! 326: { ! 327: /* ! 328: * We have not reached the maximum depth of recursion yet, ! 329: * so apply the similarity transform to the current line ! 330: * segment. ! 331: */ ! 332: ! 333: --depth; ! 334: newseg = xform; ! 335: do ! 336: { ! 337: newlen = len * newseg->length; ! 338: ang += newseg->angle * (flip ? -1 : 1); ! 339: LineFractal(depth, newlen, ang, flip ^ newseg->flip, xform); ! 340: } while ((newseg = newseg->next) != EOLIST); ! 341: } ! 342: else ! 343: { ! 344: /* ! 345: * We have reached the maximum depth of recursion, so ! 346: * draw a line segment. ! 347: */ ! 348: ! 349: x += len * cos(ang); ! 350: y += len * sin(ang); ! 351: AddFractalPoint(x, y); ! 352: ! 353: if (cptPoly >= (ULONG)cptMax) ! 354: { ! 355: LineFracDraw(); ! 356: ptPolyLine[0] = ptPolyLine[cptPoly-1]; ! 357: cptPoly = 1L; ! 358: } ! 359: } ! 360: } ! 361: ! 362: ! 363: ! 364: ! 365: /************************************************************************ ! 366: * ! 367: * AddFractalPoint ! 368: * ! 369: * Applies the global coordinate transform to the point (x, y), then ! 370: * stuffs it into the global PolyLine point array, and increments the ! 371: * counts of points in the array. ! 372: * ! 373: ************************************************************************/ ! 374: ! 375: void ! 376: AddFractalPoint(x, y) ! 377: double x,y; ! 378: { ! 379: ptPolyLine[cptPoly].x = (int)(x * xscale + xoff + 0.5); ! 380: ptPolyLine[cptPoly].y = (int)(y * yscale + yoff + 0.5); ! 381: ++cptPoly; ! 382: } ! 383: ! 384: ! 385: ! 386: ! 387: /************************************************************************ ! 388: * ! 389: * LineFracDraw ! 390: * ! 391: * Set line attributes and draw a batch of lines. Invalidate the client ! 392: * rectangle of the main window to cause our latest bitmap to be ! 393: * copied there. ! 394: * ! 395: ************************************************************************/ ! 396: ! 397: VOID ! 398: LineFracDraw() ! 399: { ! 400: LINEBUNDLE lb; ! 401: ! 402: lb.lColor = lColor; ! 403: lb.usMixMode = usMixMode; ! 404: lb.usType = usStyle; ! 405: GpiSetAttrs(LineFracPS, PRIM_LINE, LBB_COLOR|LBB_MIX_MODE|LBB_TYPE, ! 406: 0L, (PBUNDLE)&lb); ! 407: GpiSetCurrentPosition( LineFracPS, (PPOINTL)ptPolyLine ); ! 408: GpiPolyLine( LineFracPS, cptPoly-1, (PPOINTL)&ptPolyLine[1] ); ! 409: WinInvalidateRect(hwndLineFrac, &LineFracRc, FALSE); ! 410: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.