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