|
|
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.