1 /* -*- Mode: C; tab-width: 4 -*-
2 * lightning --- fractal lightning bolts.
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)lightning.c 4.00 97/01/01 xlockmore";
8 /* Copyright (c) 1996 by Keith Romberg <kromberg@saxe.com>.
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.
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.
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>.
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" \
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 */
41 ModeSpecOpt lightning_opts = {
42 0, NULL, 0, NULL, NULL };
44 /*---------------------------- defines -------------------------------*/
47 #define BOLT_ITERATION 4
48 #define LONG_FORK_ITERATION 3
49 #define MEDIUM_FORK_ITERATION 2
50 #define SMALL_FORK_ITERATION 1
52 #define WIDTH_VARIATION 30
53 #define HEIGHT_VARIATION 15
55 #define DELAY_TIME_AMOUNT 15
56 #define MULTI_DELAY_TIME_BASE 5
58 #define MAX_WIGGLES 16
60 #define WIGGLE_AMOUNT 14
62 #define RANDOM_FORK_PROBILITY 4
64 #define FIRST_LEVEL_STRIKE 0
65 #define LEVEL_ONE_STRIKE 1
66 #define LEVEL_TWO_STRIKE 2
68 #define BOLT_VERTICIES ((1<<BOLT_ITERATION)-1)
69 /* BOLT_ITERATION = 4. 2^(BOLT_ITERATION) - 1 = 15 */
71 #define NUMBER_FORK_VERTICIES 9
73 #define FLASH_PROBILITY 20
74 #define MAX_FLASH_AMOUNT 2 /* half the total duration of the bolt */
77 XPoint ForkVerticies[NUMBER_FORK_VERTICIES];
83 XPoint middle[BOLT_VERTICIES];
91 int flash_begin, flash_stop;
97 Lightning bolts[BOLT_NUMBER];
98 int scr_width, scr_height;
106 static Storm *Helga = NULL;
108 /*------------------- function prototypes ----------------------------*/
110 static int distance(XPoint a, XPoint b);
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);
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);
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);
130 /*------------------------- functions ---------------------------------*/
133 setup_multi_strike(void)
135 int result, multi_prob;
137 multi_prob = NRAND(100);
141 else if ((multi_prob >= 51) && (multi_prob < 75))
143 else if ((multi_prob >= 76) && (multi_prob < 92))
146 result = BOLT_NUMBER; /* 4 */
151 /*-------------------------------------------------------------------------*/
154 flashing_strike(void)
156 int tmp = NRAND(FLASH_PROBILITY);
158 if (tmp <= FLASH_PROBILITY)
163 /*-------------------------------------------------------------------------*/
166 flash_duration(int *start, int *end, int total_duration)
170 mid = total_duration / MAX_FLASH_AMOUNT;
171 d = NRAND(total_duration / MAX_FLASH_AMOUNT) / 2;
176 /*-------------------------------------------------------------------------*/
179 random_storm(Storm * st)
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);
194 st->bolts[i].flash_begin = st->bolts[i].flash_stop = 0;
195 st->bolts[i].wiggle_amount = WIGGLE_AMOUNT;
197 st->bolts[i].delay_time = NRAND(DELAY_TIME_AMOUNT);
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;
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)
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++;
223 generate(XPoint A, XPoint B, int iter, XPoint * verts, int *index)
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;
231 verts[*index].x = mid.x;
232 verts[*index].y = mid.y;
236 generate(A, mid, iter - 1, verts, index);
237 generate(mid, B, iter - 1, verts, index);
240 /*------------------------------------------------------------------------*/
243 create_fork(Fork * f, XPoint start, XPoint end, int level)
247 f->ForkVerticies[0].x = start.x;
248 f->ForkVerticies[0].y = start.y;
251 generate(start, end, LONG_FORK_ITERATION, f->ForkVerticies, &tmp);
253 } else if ((level > 6) && (level <= 11)) {
254 generate(start, end, MEDIUM_FORK_ITERATION, f->ForkVerticies, &tmp);
257 if (distance(start, end) > 100) {
258 generate(start, end, MEDIUM_FORK_ITERATION, f->ForkVerticies, &tmp);
261 generate(start, end, SMALL_FORK_ITERATION, f->ForkVerticies, &tmp);
266 f->ForkVerticies[f->num_used - 1].x = end.x;
267 f->ForkVerticies[f->num_used - 1].y = end.y;
270 /*------------------------------------------------------------------------*/
273 update_bolt(Lightning * bolt, int time)
276 if ((bolt->wiggle_amount == 0) && (bolt->wiggle_number > 2))
277 bolt->wiggle_number = 0;
278 if (((time % 3) == 0))
279 bolt->wiggle_amount++;
281 if (((time >= bolt->delay_time) && (time < bolt->flash_begin)) ||
282 (time > bolt->flash_stop))
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;
299 bolt->strike_level = LEVEL_TWO_STRIKE;
302 /*------------------------------------------------------------------------*/
305 draw_bolt(Lightning * bolt, ModeInfo * mi)
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);
313 level2_strike(*bolt, mi);
317 /*------------------------------------------------------------------------*/
320 first_strike(Lightning bolt, ModeInfo * mi)
322 Display *display = MI_DISPLAY(mi);
323 Window window = MI_WINDOW(mi);
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);
335 for (i = 0; i < bolt.fork_number; i++)
336 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
340 /*------------------------------------------------------------------------*/
343 draw_line(ModeInfo * mi, XPoint * points, int number, GC to_use, int offset)
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);
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);
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);
364 /*------------------------------------------------------------------------*/
367 level1_strike(Lightning bolt, ModeInfo * mi)
369 Display *display = MI_DISPLAY(mi);
370 Window window = MI_WINDOW(mi);
371 Storm *st = &Helga[MI_SCREEN(mi)];
375 if (MI_NPIXELS(mi) > 2) /* color */
376 XSetForeground(display, gc, MI_PIXEL(mi, st->color));
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);
392 for (i = 0; i < bolt.fork_number; i++) {
393 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
395 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
398 first_strike(bolt, mi);
401 /*------------------------------------------------------------------------*/
404 distance(XPoint a, XPoint b)
406 return ((int) sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)));
409 /*------------------------------------------------------------------------*/
412 level2_strike(Lightning bolt, ModeInfo * mi)
414 Display *display = MI_DISPLAY(mi);
415 Window window = MI_WINDOW(mi);
416 Storm *st = &Helga[MI_SCREEN(mi)];
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));
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);
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);
442 for (i = 0; i < bolt.fork_number; i++) {
443 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
445 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
448 level1_strike(bolt, mi);
451 /*------------------------------------------------------------------------*/
454 storm_active(Storm * st)
456 int i, atleast_1 = 0;
458 for (i = 0; i < st->multi_strike; i++)
459 if (st->bolts[i].wiggle_number > 0)
465 /*------------------------------------------------------------------------*/
468 wiggle_bolt(Lightning * bolt)
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;
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;
483 if (bolt->wiggle_amount > 1)
484 bolt->wiggle_amount -= 1;
486 bolt->wiggle_amount = 0;
489 /*------------------------------------------------------------------------*/
492 wiggle_line(XPoint * p, int number, int amount)
496 for (i = 0; i < number; i++) {
497 p[i].x += NRAND(amount) - amount / 2;
498 p[i].y += NRAND(amount) - amount / 2;
502 /*------------------------------------------------------------------------*/
505 init_lightning(ModeInfo * mi)
510 if ((Helga = (Storm *) calloc(MI_NUM_SCREENS(mi),
511 sizeof (Storm))) == NULL)
514 st = &Helga[MI_SCREEN(mi)];
516 st->scr_width = MI_WIN_WIDTH(mi);
517 st->scr_height = MI_WIN_HEIGHT(mi);
519 st->multi_strike = setup_multi_strike();
524 /*------------------------------------------------------------------------*/
527 draw_lightning(ModeInfo * mi)
529 Storm *st = &Helga[MI_SCREEN(mi)];
534 XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
535 st->color = NRAND(MI_NPIXELS(mi));
537 if (storm_active(st))
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);
549 XFlush(MI_DISPLAY(mi));
550 MI_PAUSE(mi) = 60000;
554 XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
555 if (storm_active(st))
561 MI_PAUSE(mi) = 1000000;
568 release_lightning(ModeInfo * mi)
571 (void) free((void *) Helga);
577 refresh_lightning(ModeInfo * mi)
579 /* Do nothing, it will refresh by itself */