1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* lightning --- fractal lightning bolds */
5 static const char sccsid[] = "@(#)lightning.c 5.00 2000/11/01 xlockmore";
9 * Copyright (c) 1996 by Keith Romberg <kromberg@saxe.com>
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.
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.
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>.
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" \
39 #include "xlockmore.h" /* in xscreensaver distribution */
40 #else /* STANDALONE */
41 #include "xlock.h" /* in xlockmore distribution */
42 #endif /* STANDALONE */
46 ModeSpecOpt lightning_opts =
47 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
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};
59 #define BOLT_ITERATION 4
60 #define LONG_FORK_ITERATION 3
61 #define MEDIUM_FORK_ITERATION 2
62 #define SMALL_FORK_ITERATION 1
64 #define WIDTH_VARIATION 30
65 #define HEIGHT_VARIATION 15
67 #define DELAY_TIME_AMOUNT 15
68 #define MULTI_DELAY_TIME_BASE 5
70 #define MAX_WIGGLES 16
72 #define WIGGLE_AMOUNT 14
74 #define RANDOM_FORK_PROBILITY 4
76 #define FIRST_LEVEL_STRIKE 0
77 #define LEVEL_ONE_STRIKE 1
78 #define LEVEL_TWO_STRIKE 2
80 #define BOLT_VERTICIES ((1<<BOLT_ITERATION)-1)
81 /* BOLT_ITERATION = 4. 2^(BOLT_ITERATION) - 1 = 15 */
83 #define NUMBER_FORK_VERTICIES 9
85 #define FLASH_PROBILITY 20
86 #define MAX_FLASH_AMOUNT 2 /* half the total duration of the bolt */
89 XPoint ForkVerticies[NUMBER_FORK_VERTICIES];
95 XPoint middle[BOLT_VERTICIES];
103 int flash_begin, flash_stop;
109 Lightning bolts[BOLT_NUMBER];
110 int scr_width, scr_height;
119 static Storm *Helga = (Storm *) NULL;
121 /*------------------- function prototypes ----------------------------*/
123 static int distance(XPoint a, XPoint b);
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);
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);
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);
143 /*------------------------- functions ---------------------------------*/
146 setup_multi_strike(void)
148 int result, multi_prob;
150 multi_prob = NRAND(100);
154 else if ((multi_prob >= 51) && (multi_prob < 75))
156 else if ((multi_prob >= 76) && (multi_prob < 92))
159 result = BOLT_NUMBER; /* 4 */
164 /*-------------------------------------------------------------------------*/
167 flashing_strike(void)
169 int tmp = NRAND(FLASH_PROBILITY);
171 if (tmp <= FLASH_PROBILITY)
176 /*-------------------------------------------------------------------------*/
179 flash_duration(int *start, int *end, int total_duration)
183 mid = total_duration / MAX_FLASH_AMOUNT;
184 d = NRAND(total_duration / MAX_FLASH_AMOUNT) / 2;
189 /*-------------------------------------------------------------------------*/
192 random_storm(Storm * st)
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);
207 st->bolts[i].flash_begin = st->bolts[i].flash_stop = 0;
208 st->bolts[i].wiggle_amount = WIGGLE_AMOUNT;
210 st->bolts[i].delay_time = NRAND(DELAY_TIME_AMOUNT);
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;
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)
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++;
236 generate(XPoint A, XPoint B, int iter, XPoint * verts, int *vert_index)
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;
244 verts[*vert_index].x = mid.x;
245 verts[*vert_index].y = mid.y;
249 generate(A, mid, iter - 1, verts, vert_index);
250 generate(mid, B, iter - 1, verts, vert_index);
253 /*------------------------------------------------------------------------*/
256 create_fork(Fork * f, XPoint start, XPoint end, int level)
260 f->ForkVerticies[0].x = start.x;
261 f->ForkVerticies[0].y = start.y;
264 generate(start, end, LONG_FORK_ITERATION, f->ForkVerticies, &tmp);
266 } else if ((level > 6) && (level <= 11)) {
267 generate(start, end, MEDIUM_FORK_ITERATION, f->ForkVerticies, &tmp);
270 if (distance(start, end) > 100) {
271 generate(start, end, MEDIUM_FORK_ITERATION, f->ForkVerticies, &tmp);
274 generate(start, end, SMALL_FORK_ITERATION, f->ForkVerticies, &tmp);
279 f->ForkVerticies[f->num_used - 1].x = end.x;
280 f->ForkVerticies[f->num_used - 1].y = end.y;
283 /*------------------------------------------------------------------------*/
286 update_bolt(Lightning * bolt, int time_now)
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++;
294 if (((time_now >= bolt->delay_time) && (time_now < bolt->flash_begin)) ||
295 (time_now > bolt->flash_stop))
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;
312 bolt->strike_level = LEVEL_TWO_STRIKE;
315 /*------------------------------------------------------------------------*/
318 draw_bolt(Lightning * bolt, ModeInfo * mi)
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);
326 level2_strike(*bolt, mi);
330 /*------------------------------------------------------------------------*/
333 first_strike(Lightning bolt, ModeInfo * mi)
335 Display *display = MI_DISPLAY(mi);
336 Window window = MI_WINDOW(mi);
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);
348 for (i = 0; i < bolt.fork_number; i++)
349 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
353 /*------------------------------------------------------------------------*/
356 draw_line(ModeInfo * mi, XPoint * points, int number, GC to_use, int offset)
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);
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);
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);
377 /*------------------------------------------------------------------------*/
380 level1_strike(Lightning bolt, ModeInfo * mi)
382 Display *display = MI_DISPLAY(mi);
383 Window window = MI_WINDOW(mi);
384 Storm *st = &Helga[MI_SCREEN(mi)];
388 if (MI_NPIXELS(mi) > 2) /* color */
389 XSetForeground(display, gc, MI_PIXEL(mi, st->color));
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);
405 for (i = 0; i < bolt.fork_number; i++) {
406 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
408 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
411 first_strike(bolt, mi);
414 /*------------------------------------------------------------------------*/
417 distance(XPoint a, XPoint b)
419 return ((int) sqrt((double) (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)));
422 /*------------------------------------------------------------------------*/
425 level2_strike(Lightning bolt, ModeInfo * mi)
427 Display *display = MI_DISPLAY(mi);
428 Window window = MI_WINDOW(mi);
429 Storm *st = &Helga[MI_SCREEN(mi)];
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));
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);
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);
455 for (i = 0; i < bolt.fork_number; i++) {
456 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
458 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
461 level1_strike(bolt, mi);
464 /*------------------------------------------------------------------------*/
467 storm_active(Storm * st)
469 int i, atleast_1 = 0;
471 for (i = 0; i < st->multi_strike; i++)
472 if (st->bolts[i].wiggle_number > 0)
478 /*------------------------------------------------------------------------*/
481 wiggle_bolt(Lightning * bolt)
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;
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;
496 if (bolt->wiggle_amount > 1)
497 bolt->wiggle_amount -= 1;
499 bolt->wiggle_amount = 0;
502 /*------------------------------------------------------------------------*/
505 wiggle_line(XPoint * p, int number, int amount)
509 for (i = 0; i < number; i++) {
510 p[i].x += NRAND(amount) - amount / 2;
511 p[i].y += NRAND(amount) - amount / 2;
515 /*------------------------------------------------------------------------*/
518 init_lightning(ModeInfo * mi)
523 if ((Helga = (Storm *) calloc(MI_NUM_SCREENS(mi),
524 sizeof (Storm))) == NULL)
527 st = &Helga[MI_SCREEN(mi)];
529 st->scr_width = MI_WIDTH(mi);
530 st->scr_height = MI_HEIGHT(mi);
532 st->multi_strike = setup_multi_strike();
537 /*------------------------------------------------------------------------*/
540 draw_lightning(ModeInfo * mi)
547 st = &Helga[MI_SCREEN(mi)];
548 MI_IS_DRAWN(mi) = True;
551 MI_IS_DRAWN(mi) = False;
553 MI_IS_DRAWN(mi) = True;
555 st->color = NRAND(MI_NPIXELS(mi));
557 if (storm_active(st))
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);
573 if (++st->busyLoop > 6) {
579 MI_IS_DRAWN(mi) = False;
581 MI_IS_DRAWN(mi) = True;
583 if (storm_active(st))
589 if (++st->busyLoop > 100) {
598 release_lightning(ModeInfo * mi)
601 (void) free((void *) Helga);
602 Helga = (Storm *) NULL;
607 refresh_lightning(ModeInfo * mi)
609 /* Do nothing, it will refresh by itself */
612 #endif /* MODE_lightning */