ftp://ftp.demon.nl/disk1/redhat-contrib/libc5/SRPMS/xscreensaver-2.14-1.src.rpm
[xscreensaver] / hacks / forest.c
1 /* -*- Mode: C; tab-width: 4 -*-
2  * forest.c --- draw a fractal forest.
3  */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)forest.c      4.03 97/05/10 xlockmore";
6 #endif
7
8 /* Copyright (c) 1995 Pascal Pensa <pensa@aurora.unice.fr>
9  *
10  * Original idea : Guillaume Ramey <ramey@aurora.unice.fr>
11  *
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * Revision History:
25  * 10-May-97: Compatible with xscreensaver
26  */
27
28 #ifdef STANDALONE
29 # define PROGCLASS                                      "Forest"
30 # define HACK_INIT                                      init_forest
31 # define HACK_DRAW                                      draw_forest
32 # define forest_opts                            xlockmore_opts
33 # define DEFAULTS       "*count:                100     \n"                     \
34                                         "*cycles:               200     \n"                     \
35                                         "*delay:                400000  \n"                     \
36                                         "*ncolors:              100     \n"                     \
37                                         "*eraseSpeed:   400 \n"                         \
38                                         "*eraseMode:    -1 \n"
39 # define UNIFORM_COLORS
40 # include "xlockmore.h"                         /* from the xscreensaver distribution */
41 # include "erase.h"
42 #else  /* !STANDALONE */
43 # include "xlock.h"                                     /* from the xlockmore distribution */
44 #endif /* !STANDALONE */
45
46 ModeSpecOpt forest_opts = {
47   0, NULL, 0, NULL, NULL };
48
49
50 #define MINTREES   1
51
52 #define MINHEIGHT  20           /* Tree height range */
53 #define MAXHEIGHT  40
54
55 #define MINANGLE   15           /* (degree) angle between soon */
56 #define MAXANGLE   35
57 #define RANDANGLE  15           /* (degree) Max random angle from default */
58
59 #define REDUCE     90           /* Height % from father */
60
61 #define ITERLEVEL  10           /* Tree iteration */
62
63 #define COLORSPEED  2           /* Color increment */
64
65 /* degree to radian */
66 #define DEGTORAD(x) (((float)(x)) * M_PI / 180.0)
67
68 #define RANGE_RAND(min,max) ((min) + NRAND((max) - (min)))
69
70 typedef struct {
71         int         width;
72         int         height;
73         int         time;       /* up time */
74         int         ntrees;
75 } foreststruct;
76
77 static foreststruct *forests = NULL;
78
79 static void
80 draw_tree(ModeInfo * mi,
81           short int x, short int y, short int len,
82           float a, float as, short int c, short int level)
83                                 /* Father's end */
84                                 /* Length */
85                                 /* color */
86                                 /* Height level */
87                                 /* Father's angle */
88                                 /* Father's angle step */
89 {
90         Display    *display = MI_DISPLAY(mi);
91         Window      window = MI_WINDOW(mi);
92         GC          gc = MI_GC(mi);
93         short       x_1, y_1, x_2, y_2;
94         float       a1, a2;
95
96         /* left */
97
98         a1 = a + as + DEGTORAD(NRAND(2 * RANDANGLE) - RANDANGLE);
99
100         x_1 = x + (short) (COSF(a1) * ((float) len));
101         y_1 = y + (short) (SINF(a1) * ((float) len));
102
103         /* right */
104
105         a2 = a - as + DEGTORAD(NRAND(2 * RANDANGLE) - RANDANGLE);
106
107         x_2 = x + (short) (COSF(a2) * ((float) len));
108         y_2 = y + (short) (SINF(a2) * ((float) len));
109
110         if (MI_NPIXELS(mi) > 2) {
111                 XSetForeground(display, gc, MI_PIXEL(mi, c));
112                 c = (c + COLORSPEED) % MI_NPIXELS(mi);
113         } else
114                 XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
115
116         XDrawLine(display, window, gc, x, y, x_1, y_1);
117         XDrawLine(display, window, gc, x, y, x_2, y_2);
118
119         if (level < 2) {
120                 XDrawLine(display, window, gc, x + 1, y, x_1 + 1, y_1);
121                 XDrawLine(display, window, gc, x + 1, y, x_2 + 1, y_2);
122         }
123         len = (len * REDUCE * 10) / 1000;
124
125         if (level < ITERLEVEL) {
126                 draw_tree(mi, x_1, y_1, len, a1, as, c, level + 1);
127                 draw_tree(mi, x_2, y_2, len, a2, as, c, level + 1);
128         }
129 }
130
131 void
132 init_forest(ModeInfo * mi)
133 {
134         foreststruct *fp;
135
136         if (forests == NULL) {
137                 if ((forests = (foreststruct *) calloc(MI_NUM_SCREENS(mi),
138                                              sizeof (foreststruct))) == NULL)
139                         return;
140         }
141         fp = &forests[MI_SCREEN(mi)];
142
143         fp->width = MI_WIN_WIDTH(mi);
144         fp->height = MI_WIN_HEIGHT(mi);
145         fp->time = 0;
146
147         fp->ntrees = MI_BATCHCOUNT(mi);
148         if (fp->ntrees < -MINTREES)
149                 fp->ntrees = NRAND(-fp->ntrees - MINTREES + 1) + MINTREES;
150         else if (fp->ntrees < MINTREES)
151                 fp->ntrees = MINTREES;
152         XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
153 }
154
155 void
156 draw_forest(ModeInfo * mi)
157 {
158         Display    *display = MI_DISPLAY(mi);
159         GC          gc = MI_GC(mi);
160         foreststruct *fp = &forests[MI_SCREEN(mi)];
161         short       x, y, x_2, y_2, len, c = 0;
162         float       a, as;
163
164         if (fp->time < fp->ntrees) {
165
166                 x = RANGE_RAND(0, fp->width);
167                 y = RANGE_RAND(0, fp->height + MAXHEIGHT);
168                 a = -M_PI / 2.0 + DEGTORAD(NRAND(2 * RANDANGLE) - RANDANGLE);
169                 as = DEGTORAD(RANGE_RAND(MINANGLE, MAXANGLE));
170                 len = ((RANGE_RAND(MINHEIGHT, MAXHEIGHT) * (fp->width / 20)) / 50) + 2;
171
172                 if (MI_NPIXELS(mi) > 2) {
173                         c = NRAND(MI_NPIXELS(mi));
174                         XSetForeground(display, gc, MI_PIXEL(mi, c));
175                         c = (c + COLORSPEED) % MI_NPIXELS(mi);
176                 } else
177                         XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
178
179                 x_2 = x + (short) (COSF(a) * ((float) len));
180                 y_2 = y + (short) (SINF(a) * ((float) len));
181
182                 XDrawLine(display, MI_WINDOW(mi), gc, x, y, x_2, y_2);
183                 XDrawLine(display, MI_WINDOW(mi), gc, x + 1, y, x_2 + 1, y_2);
184
185                 draw_tree(mi, x_2, y_2, (len * REDUCE) / 100, a, as, c, 1);
186         }
187         if (++fp->time > MI_CYCLES(mi)) {
188 #ifdef STANDALONE
189           erase_full_window(MI_DISPLAY(mi), MI_WINDOW(mi));
190 #endif /* STANDALONE */
191           init_forest(mi);
192         }
193 }
194
195 void
196 release_forest(ModeInfo * mi)
197 {
198         if (forests != NULL) {
199                 (void) free((void *) forests);
200                 forests = NULL;
201         }
202 }
203
204 void
205 refresh_forest(ModeInfo * mi)
206 {
207         foreststruct *fp = &forests[MI_SCREEN(mi)];
208
209         if (fp->time < fp->ntrees)
210                 XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
211         else
212                 init_forest(mi);
213 }