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