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