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