From http://www.jwz.org/xscreensaver/xscreensaver-5.38.tar.gz
[xscreensaver] / hacks / lightning.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* lightning --- fractal lightning bolds */
3
4 #if 0
5 static const char sccsid[] = "@(#)lightning.c   5.00 2000/11/01 xlockmore";
6 #endif
7
8 /*-
9  * Copyright (c) 1996 by Keith Romberg <kromberg@saxe.com>
10  *
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appear in all copies and that
14  * both that copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * This file is provided AS IS with no warranties of any kind.  The author
18  * shall have no liability with respect to the infringement of copyrights,
19  * trade secrets or any patents by this file or any part thereof.  In no
20  * event will the author be liable for any lost revenue or profits or
21  * other special, indirect and consequential damages.
22  *
23  * Revision History:
24  * 01-Nov-2000: Allocation checks
25  * 10-May-1997: Compatible with xscreensaver
26  * 14-Jul-1996: Cleaned up code.
27  * 27-Jun-1996: Written and submitted by Keith Romberg <kromberg@saxe.com>.
28  */
29
30 #ifdef STANDALONE
31 # define MODE_lightning
32 # define DEFAULTS "*delay: 10000 \n" \
33                   "*ncolors: 64  \n"
34
35 # define BRIGHT_COLORS
36 # define free_lightning 0
37 # define release_lightning 0
38 # define reshape_lightning 0
39 # define lightning_handle_event 0
40 # include "xlockmore.h"         /* in xscreensaver distribution */
41 #else /* STANDALONE */
42 # include "xlock.h"             /* in xlockmore distribution */
43 #endif /* STANDALONE */
44
45 #ifdef MODE_lightning
46
47 ENTRYPOINT ModeSpecOpt lightning_opts =
48 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
49
50 #ifdef USE_MODULES
51 ModStruct   lightning_description =
52 {"lightning", "init_lightning", "draw_lightning", (char *) NULL,
53  "refresh_lightning", "init_lightning", (char *) NULL, &lightning_opts,
54  10000, 1, 1, 1, 64, 0.6, "",
55  "Shows Keith's fractal lightning bolts", 0, NULL};
56
57 #endif
58
59 #define BOLT_NUMBER 4
60 #define BOLT_ITERATION 4
61 #define LONG_FORK_ITERATION 3
62 #define MEDIUM_FORK_ITERATION 2
63 #define SMALL_FORK_ITERATION 1
64
65 #define WIDTH_VARIATION 30
66 #define HEIGHT_VARIATION 15
67
68 #define DELAY_TIME_AMOUNT 15
69 #define MULTI_DELAY_TIME_BASE 5
70
71 #define MAX_WIGGLES 16
72 #define WIGGLE_BASE 8
73 #define WIGGLE_AMOUNT 14
74
75 #define RANDOM_FORK_PROBILITY   4
76
77 #define FIRST_LEVEL_STRIKE 0
78 #define LEVEL_ONE_STRIKE 1
79 #define LEVEL_TWO_STRIKE 2
80
81 #define BOLT_VERTICIES ((1<<BOLT_ITERATION)-1)
82   /* BOLT_ITERATION = 4. 2^(BOLT_ITERATION) - 1 = 15 */
83
84 #define NUMBER_FORK_VERTICIES 9
85
86 #define FLASH_PROBILITY 20
87 #define MAX_FLASH_AMOUNT 2      /*  half the total duration of the bolt  */
88
89 typedef struct {
90         XPoint      ForkVerticies[NUMBER_FORK_VERTICIES];
91         int         num_used;
92 } Fork;
93
94 typedef struct {
95         XPoint      end1, end2;
96         XPoint      middle[BOLT_VERTICIES];
97         int         fork_number;
98         int         forks_start[2];
99         Fork        branch[2];
100         int         wiggle_number;
101         int         wiggle_amount;
102         int         delay_time;
103         int         flash;
104         int         flash_begin, flash_stop;
105         int         visible;
106         int         strike_level;
107 } Lightning;
108
109 typedef struct {
110         Lightning   bolts[BOLT_NUMBER];
111         int         scr_width, scr_height;
112         int         multi_strike;
113         int         give_it_hell;
114         int         draw_time;
115         int         stage;
116         int         busyLoop;
117         unsigned long color;
118 } Storm;
119
120 static Storm *Helga = (Storm *) NULL;
121
122 /*-------------------   function prototypes  ----------------------------*/
123
124 static int  distance(XPoint a, XPoint b);
125
126 static int  setup_multi_strike(void);
127 static int  flashing_strike(void);
128 static void flash_duration(int *start, int *end, int total_duration);
129 static void random_storm(Storm * st);
130 static void generate(XPoint A, XPoint B, int iter, XPoint * verts, int *vert_index);
131 static void create_fork(Fork * f, XPoint start, XPoint end, int level);
132
133 static void first_strike(Lightning bolt, ModeInfo * mi);
134 static void draw_bolt(Lightning * bolt, ModeInfo * mi);
135 static void draw_line(ModeInfo * mi, XPoint * p, int number, GC use, int x_offset);
136 static void level1_strike(Lightning bolt, ModeInfo * mi);
137 static void level2_strike(Lightning bolt, ModeInfo * mi);
138
139 static int  storm_active(Storm * st);
140 static void update_bolt(Lightning * bolt, int time_now);
141 static void wiggle_bolt(Lightning * bolt);
142 static void wiggle_line(XPoint * p, int number, int wiggle_amount);
143
144 /*-------------------------  functions  ---------------------------------*/
145
146 static int
147 setup_multi_strike(void)
148 {
149         int         result, multi_prob;
150
151         multi_prob = NRAND(100);
152
153         if (multi_prob < 50)
154                 result = 1;
155         else if ((multi_prob >= 51) && (multi_prob < 75))
156                 result = 2;
157         else if ((multi_prob >= 76) && (multi_prob < 92))
158                 result = 3;
159         else
160                 result = BOLT_NUMBER;   /* 4 */
161
162         return (result);
163 }
164
165 /*-------------------------------------------------------------------------*/
166
167 static int
168 flashing_strike(void)
169 {
170         int         tmp = NRAND(FLASH_PROBILITY);
171
172         if (tmp <= FLASH_PROBILITY)
173                 return (1);
174         return (0);
175 }
176
177 /*-------------------------------------------------------------------------*/
178
179 static void
180 flash_duration(int *start, int *end, int total_duration)
181 {
182         int         mid, d;
183
184         mid = total_duration / MAX_FLASH_AMOUNT;
185         d = NRAND(total_duration / MAX_FLASH_AMOUNT) / 2;
186         *start = mid - d;
187         *end = mid + d;
188 }
189
190 /*-------------------------------------------------------------------------*/
191
192 static void
193 random_storm(Storm * st)
194 {
195         int         i, j, tmp;
196         XPoint      p;
197
198         for (i = 0; i < st->multi_strike; i++) {
199                 st->bolts[i].end1.x = NRAND(st->scr_width);
200                 st->bolts[i].end1.y = 0;
201                 st->bolts[i].end2.x = NRAND(st->scr_width);
202                 st->bolts[i].end2.y = st->scr_height;
203                 st->bolts[i].wiggle_number = WIGGLE_BASE + NRAND(MAX_WIGGLES);
204                 if ((st->bolts[i].flash = flashing_strike()))
205                         flash_duration(&(st->bolts[i].flash_begin), &(st->bolts[i].flash_stop),
206                                        st->bolts[i].wiggle_number);
207                 else
208                         st->bolts[i].flash_begin = st->bolts[i].flash_stop = 0;
209                 st->bolts[i].wiggle_amount = WIGGLE_AMOUNT;
210                 if (i == 0)
211                         st->bolts[i].delay_time = NRAND(DELAY_TIME_AMOUNT);
212                 else
213                         st->bolts[i].delay_time = NRAND(DELAY_TIME_AMOUNT) +
214                                 (MULTI_DELAY_TIME_BASE * i);
215                 st->bolts[i].strike_level = FIRST_LEVEL_STRIKE;
216                 tmp = 0;
217                 generate(st->bolts[i].end1, st->bolts[i].end2, BOLT_ITERATION,
218                          st->bolts[i].middle, &tmp);
219                 st->bolts[i].fork_number = 0;
220                 st->bolts[i].visible = 0;
221                 for (j = 0; j < BOLT_VERTICIES; j++) {
222                         if (st->bolts[i].fork_number >= 2)
223                                 break;
224                         if (NRAND(100) < RANDOM_FORK_PROBILITY) {
225                                 p.x = NRAND(st->scr_width);
226                                 p.y = st->scr_height;
227                                 st->bolts[i].forks_start[st->bolts[i].fork_number] = j;
228                                 create_fork(&(st->bolts[i].branch[st->bolts[i].fork_number]),
229                                             st->bolts[i].middle[j], p, j);
230                                 st->bolts[i].fork_number++;
231                         }
232                 }
233         }
234 }
235
236 static void
237 generate(XPoint A, XPoint B, int iter, XPoint * verts, int *vert_index)
238 {
239         XPoint      mid;
240
241         mid.x = (A.x + B.x) / 2 + NRAND(WIDTH_VARIATION) - WIDTH_VARIATION / 2;
242         mid.y = (A.y + B.y) / 2 + NRAND(HEIGHT_VARIATION) - HEIGHT_VARIATION / 2;
243
244         if (!iter) {
245                 verts[*vert_index].x = mid.x;
246                 verts[*vert_index].y = mid.y;
247                 (*vert_index)++;
248                 return;
249         }
250         generate(A, mid, iter - 1, verts, vert_index);
251         generate(mid, B, iter - 1, verts, vert_index);
252 }
253
254 /*------------------------------------------------------------------------*/
255
256 static void
257 create_fork(Fork * f, XPoint start, XPoint end, int level)
258 {
259         int         tmp = 1;
260
261         f->ForkVerticies[0].x = start.x;
262         f->ForkVerticies[0].y = start.y;
263
264         if (level <= 6) {
265                 generate(start, end, LONG_FORK_ITERATION, f->ForkVerticies, &tmp);
266                 f->num_used = 9;
267         } else if ((level > 6) && (level <= 11)) {
268                 generate(start, end, MEDIUM_FORK_ITERATION, f->ForkVerticies, &tmp);
269                 f->num_used = 5;
270         } else {
271                 if (distance(start, end) > 100) {
272                         generate(start, end, MEDIUM_FORK_ITERATION, f->ForkVerticies, &tmp);
273                         f->num_used = 5;
274                 } else {
275                         generate(start, end, SMALL_FORK_ITERATION, f->ForkVerticies, &tmp);
276                         f->num_used = 3;
277                 }
278         }
279
280         f->ForkVerticies[f->num_used - 1].x = end.x;
281         f->ForkVerticies[f->num_used - 1].y = end.y;
282 }
283
284 /*------------------------------------------------------------------------*/
285
286 static void
287 update_bolt(Lightning * bolt, int time_now)
288 {
289         wiggle_bolt(bolt);
290         if ((bolt->wiggle_amount == 0) && (bolt->wiggle_number > 2))
291                 bolt->wiggle_number = 0;
292         if (((time_now % 3) == 0))
293                 bolt->wiggle_amount++;
294
295         if (((time_now >= bolt->delay_time) && (time_now < bolt->flash_begin)) ||
296             (time_now > bolt->flash_stop))
297                 bolt->visible = 1;
298         else
299                 bolt->visible = 0;
300
301         if (time_now == bolt->delay_time)
302                 bolt->strike_level = FIRST_LEVEL_STRIKE;
303         else if (time_now == (bolt->delay_time + 1))
304                 bolt->strike_level = LEVEL_ONE_STRIKE;
305         else if ((time_now > (bolt->delay_time + 1)) &&
306                  (time_now <= (bolt->delay_time + bolt->flash_begin - 2)))
307                 bolt->strike_level = LEVEL_TWO_STRIKE;
308         else if (time_now == (bolt->delay_time + bolt->flash_begin - 1))
309                 bolt->strike_level = LEVEL_ONE_STRIKE;
310         else if (time_now == (bolt->delay_time + bolt->flash_stop + 1))
311                 bolt->strike_level = LEVEL_ONE_STRIKE;
312         else
313                 bolt->strike_level = LEVEL_TWO_STRIKE;
314 }
315
316 /*------------------------------------------------------------------------*/
317
318 static void
319 draw_bolt(Lightning * bolt, ModeInfo * mi)
320 {
321         if (bolt->visible) {
322                 if (bolt->strike_level == FIRST_LEVEL_STRIKE)
323                         first_strike(*bolt, mi);
324                 else if (bolt->strike_level == LEVEL_ONE_STRIKE)
325                         level1_strike(*bolt, mi);
326                 else
327                         level2_strike(*bolt, mi);
328         }
329 }
330
331 /*------------------------------------------------------------------------*/
332
333 static void
334 first_strike(Lightning bolt, ModeInfo * mi)
335 {
336         Display    *display = MI_DISPLAY(mi);
337         Window      window = MI_WINDOW(mi);
338         GC          gc = MI_GC(mi);
339         int         i;
340
341         XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
342         XDrawLine(display, window, gc,
343                bolt.end1.x, bolt.end1.y, bolt.middle[0].x, bolt.middle[0].y);
344         draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, 0);
345         XDrawLine(display, window, gc,
346         bolt.middle[BOLT_VERTICIES - 1].x, bolt.middle[BOLT_VERTICIES - 1].y,
347                   bolt.end2.x, bolt.end2.y);
348
349         for (i = 0; i < bolt.fork_number; i++)
350                 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
351                           gc, 0);
352 }
353
354 /*------------------------------------------------------------------------*/
355
356 static void
357 draw_line(ModeInfo * mi, XPoint * points, int number, GC to_use, int offset)
358 {
359         int         i;
360
361         for (i = 0; i < number - 1; i++) {
362                 if (points[i].y <= points[i + 1].y)
363                         XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), to_use, points[i].x + offset,
364                                   points[i].y, points[i + 1].x + offset, points[i + 1].y);
365                 else {
366                         if (points[i].x < points[i + 1].x)
367                                 XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), to_use, points[i].x +
368                                           offset, points[i].y + offset, points[i + 1].x + offset,
369                                           points[i + 1].y + offset);
370                         else
371                                 XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), to_use, points[i].x -
372                                           offset, points[i].y + offset, points[i + 1].x - offset,
373                                           points[i + 1].y + offset);
374                 }
375         }
376 }
377
378 /*------------------------------------------------------------------------*/
379
380 static void
381 level1_strike(Lightning bolt, ModeInfo * mi)
382 {
383         Display    *display = MI_DISPLAY(mi);
384         Window      window = MI_WINDOW(mi);
385         Storm      *st = &Helga[MI_SCREEN(mi)];
386         GC          gc = MI_GC(mi);
387         int         i;
388
389         if (MI_NPIXELS(mi) > 2) /* color */
390                 XSetForeground(display, gc, MI_PIXEL(mi, st->color));
391         else
392                 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
393         XDrawLine(display, window, gc,
394         bolt.end1.x - 1, bolt.end1.y, bolt.middle[0].x - 1, bolt.middle[0].y);
395         draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, -1);
396         XDrawLine(display, window, gc,
397                   bolt.middle[BOLT_VERTICIES - 1].x - 1,
398             bolt.middle[BOLT_VERTICIES - 1].y, bolt.end2.x - 1, bolt.end2.y);
399         XDrawLine(display, window, gc,
400         bolt.end1.x + 1, bolt.end1.y, bolt.middle[0].x + 1, bolt.middle[0].y);
401         draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, 1);
402         XDrawLine(display, window, gc,
403                   bolt.middle[BOLT_VERTICIES - 1].x + 1,
404             bolt.middle[BOLT_VERTICIES - 1].y, bolt.end2.x + 1, bolt.end2.y);
405
406         for (i = 0; i < bolt.fork_number; i++) {
407                 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
408                           gc, -1);
409                 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
410                           gc, 1);
411         }
412         first_strike(bolt, mi);
413 }
414
415 /*------------------------------------------------------------------------*/
416
417 static int
418 distance(XPoint a, XPoint b)
419 {
420         return ((int) sqrt((double) (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)));
421 }
422
423 /*------------------------------------------------------------------------*/
424
425 static void
426 level2_strike(Lightning bolt, ModeInfo * mi)
427 {
428         Display    *display = MI_DISPLAY(mi);
429         Window      window = MI_WINDOW(mi);
430         Storm      *st = &Helga[MI_SCREEN(mi)];
431         GC          gc = MI_GC(mi);
432         int         i;
433
434         /* This was originally designed to be a little darker then the
435            level1 strike.  This was changed to get it to work on
436            multiscreens and to add more color variety.   I tried
437            stippling but it did not look good. */
438         if (MI_NPIXELS(mi) > 2)
439                 XSetForeground(display, gc, MI_PIXEL(mi, st->color));
440         else
441                 XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
442         XDrawLine(display, window, gc,
443         bolt.end1.x - 2, bolt.end1.y, bolt.middle[0].x - 2, bolt.middle[0].y);
444         draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, -2);
445         XDrawLine(display, window, gc,
446                   bolt.middle[BOLT_VERTICIES - 1].x - 2,
447             bolt.middle[BOLT_VERTICIES - 1].y, bolt.end2.x - 2, bolt.end2.y);
448
449         XDrawLine(display, window, gc,
450         bolt.end1.x + 2, bolt.end1.y, bolt.middle[0].x + 2, bolt.middle[0].y);
451         draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, 2);
452         XDrawLine(display, window, gc,
453                   bolt.middle[BOLT_VERTICIES - 1].x + 2,
454             bolt.middle[BOLT_VERTICIES - 1].y, bolt.end2.x + 2, bolt.end2.y);
455
456         for (i = 0; i < bolt.fork_number; i++) {
457                 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
458                           gc, -2);
459                 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
460                           gc, 2);
461         }
462         level1_strike(bolt, mi);
463 }
464
465 /*------------------------------------------------------------------------*/
466
467 static int
468 storm_active(Storm * st)
469 {
470         int         i, atleast_1 = 0;
471
472         for (i = 0; i < st->multi_strike; i++)
473                 if (st->bolts[i].wiggle_number > 0)
474                         atleast_1++;
475
476         return (atleast_1);
477 }
478
479 /*------------------------------------------------------------------------*/
480
481 static void
482 wiggle_bolt(Lightning * bolt)
483 {
484         int         i;
485
486         wiggle_line(bolt->middle, BOLT_VERTICIES, bolt->wiggle_amount);
487         bolt->end2.x += NRAND(bolt->wiggle_amount) - bolt->wiggle_amount / 2;
488         bolt->end2.y += NRAND(bolt->wiggle_amount) - bolt->wiggle_amount / 2;
489
490         for (i = 0; i < bolt->fork_number; i++) {
491                 wiggle_line(bolt->branch[i].ForkVerticies, bolt->branch[i].num_used,
492                             bolt->wiggle_amount);
493                 bolt->branch[i].ForkVerticies[0].x = bolt->middle[bolt->forks_start[i]].x;
494                 bolt->branch[i].ForkVerticies[0].y = bolt->middle[bolt->forks_start[i]].y;
495         }
496
497         if (bolt->wiggle_amount > 1)
498                 bolt->wiggle_amount -= 1;
499         else
500                 bolt->wiggle_amount = 0;
501 }
502
503 /*------------------------------------------------------------------------*/
504
505 static void
506 wiggle_line(XPoint * p, int number, int amount)
507 {
508         int         i;
509
510         for (i = 0; i < number; i++) {
511                 p[i].x += NRAND(amount) - amount / 2;
512                 p[i].y += NRAND(amount) - amount / 2;
513         }
514 }
515
516 /*------------------------------------------------------------------------*/
517
518 ENTRYPOINT void
519 init_lightning (ModeInfo * mi)
520 {
521         Storm      *st;
522
523         MI_INIT (mi, Helga);
524         st = &Helga[MI_SCREEN(mi)];
525
526         st->scr_width = MI_WIDTH(mi);
527         st->scr_height = MI_HEIGHT(mi);
528
529         st->multi_strike = setup_multi_strike();
530         random_storm(st);
531         st->stage = 0;
532 }
533
534 /*------------------------------------------------------------------------*/
535
536 ENTRYPOINT void
537 draw_lightning (ModeInfo * mi)
538 {
539         int         i;
540         Storm      *st;
541
542         if (Helga == NULL)
543                 return;
544         st = &Helga[MI_SCREEN(mi)];
545         MI_IS_DRAWN(mi) = True;
546         switch (st->stage) {
547                 case 0:
548                         XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
549
550                         st->color = NRAND(MI_NPIXELS(mi));
551                         st->draw_time = 0;
552                         if (storm_active(st))
553                                 st->stage++;
554                         else
555                                 st->stage = 4;
556                         break;
557                 case 1:
558                         for (i = 0; i < st->multi_strike; i++) {
559                                 if (st->bolts[i].visible)
560                                         draw_bolt(&(st->bolts[i]), mi);
561                                 update_bolt(&(st->bolts[i]), st->draw_time);
562                         }
563                         st->draw_time++;
564                         st->stage++;
565                         st->busyLoop = 0;
566                         break;
567                 case 2:
568                         if (++st->busyLoop > 6) {
569                                 st->stage++;
570                                 st->busyLoop = 0;
571                         }
572                         break;
573                 case 3:
574                         XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
575
576                         if (storm_active(st))
577                                 st->stage = 1;
578                         else
579                                 st->stage++;
580                         break;
581                 case 4:
582                         if (++st->busyLoop > 100) {
583                                 st->busyLoop = 0;
584                         }
585                         init_lightning(mi);
586                         break;
587         }
588 }
589
590 #ifndef STANDALONE
591 ENTRYPOINT void
592 refresh_lightning(ModeInfo * mi)
593 {
594         /* Do nothing, it will refresh by itself */
595 }
596 #endif
597
598 XSCREENSAVER_MODULE ("Lightning", lightning)
599
600
601 #endif /* MODE_lightning */