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 DEFAULTS "*delay: 10000 \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 */
44 ENTRYPOINT ModeSpecOpt lightning_opts =
45 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
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};
57 #define BOLT_ITERATION 4
58 #define LONG_FORK_ITERATION 3
59 #define MEDIUM_FORK_ITERATION 2
60 #define SMALL_FORK_ITERATION 1
62 #define WIDTH_VARIATION 30
63 #define HEIGHT_VARIATION 15
65 #define DELAY_TIME_AMOUNT 15
66 #define MULTI_DELAY_TIME_BASE 5
68 #define MAX_WIGGLES 16
70 #define WIGGLE_AMOUNT 14
72 #define RANDOM_FORK_PROBILITY 4
74 #define FIRST_LEVEL_STRIKE 0
75 #define LEVEL_ONE_STRIKE 1
76 #define LEVEL_TWO_STRIKE 2
78 #define BOLT_VERTICIES ((1<<BOLT_ITERATION)-1)
79 /* BOLT_ITERATION = 4. 2^(BOLT_ITERATION) - 1 = 15 */
81 #define NUMBER_FORK_VERTICIES 9
83 #define FLASH_PROBILITY 20
84 #define MAX_FLASH_AMOUNT 2 /* half the total duration of the bolt */
87 XPoint ForkVerticies[NUMBER_FORK_VERTICIES];
93 XPoint middle[BOLT_VERTICIES];
101 int flash_begin, flash_stop;
107 Lightning bolts[BOLT_NUMBER];
108 int scr_width, scr_height;
117 static Storm *Helga = (Storm *) NULL;
119 /*------------------- function prototypes ----------------------------*/
121 static int distance(XPoint a, XPoint b);
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);
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);
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);
141 /*------------------------- functions ---------------------------------*/
144 setup_multi_strike(void)
146 int result, multi_prob;
148 multi_prob = NRAND(100);
152 else if ((multi_prob >= 51) && (multi_prob < 75))
154 else if ((multi_prob >= 76) && (multi_prob < 92))
157 result = BOLT_NUMBER; /* 4 */
162 /*-------------------------------------------------------------------------*/
165 flashing_strike(void)
167 int tmp = NRAND(FLASH_PROBILITY);
169 if (tmp <= FLASH_PROBILITY)
174 /*-------------------------------------------------------------------------*/
177 flash_duration(int *start, int *end, int total_duration)
181 mid = total_duration / MAX_FLASH_AMOUNT;
182 d = NRAND(total_duration / MAX_FLASH_AMOUNT) / 2;
187 /*-------------------------------------------------------------------------*/
190 random_storm(Storm * st)
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);
205 st->bolts[i].flash_begin = st->bolts[i].flash_stop = 0;
206 st->bolts[i].wiggle_amount = WIGGLE_AMOUNT;
208 st->bolts[i].delay_time = NRAND(DELAY_TIME_AMOUNT);
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;
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)
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++;
234 generate(XPoint A, XPoint B, int iter, XPoint * verts, int *vert_index)
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;
242 verts[*vert_index].x = mid.x;
243 verts[*vert_index].y = mid.y;
247 generate(A, mid, iter - 1, verts, vert_index);
248 generate(mid, B, iter - 1, verts, vert_index);
251 /*------------------------------------------------------------------------*/
254 create_fork(Fork * f, XPoint start, XPoint end, int level)
258 f->ForkVerticies[0].x = start.x;
259 f->ForkVerticies[0].y = start.y;
262 generate(start, end, LONG_FORK_ITERATION, f->ForkVerticies, &tmp);
264 } else if ((level > 6) && (level <= 11)) {
265 generate(start, end, MEDIUM_FORK_ITERATION, f->ForkVerticies, &tmp);
268 if (distance(start, end) > 100) {
269 generate(start, end, MEDIUM_FORK_ITERATION, f->ForkVerticies, &tmp);
272 generate(start, end, SMALL_FORK_ITERATION, f->ForkVerticies, &tmp);
277 f->ForkVerticies[f->num_used - 1].x = end.x;
278 f->ForkVerticies[f->num_used - 1].y = end.y;
281 /*------------------------------------------------------------------------*/
284 update_bolt(Lightning * bolt, int time_now)
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++;
292 if (((time_now >= bolt->delay_time) && (time_now < bolt->flash_begin)) ||
293 (time_now > bolt->flash_stop))
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;
310 bolt->strike_level = LEVEL_TWO_STRIKE;
313 /*------------------------------------------------------------------------*/
316 draw_bolt(Lightning * bolt, ModeInfo * mi)
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);
324 level2_strike(*bolt, mi);
328 /*------------------------------------------------------------------------*/
331 first_strike(Lightning bolt, ModeInfo * mi)
333 Display *display = MI_DISPLAY(mi);
334 Window window = MI_WINDOW(mi);
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);
346 for (i = 0; i < bolt.fork_number; i++)
347 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
351 /*------------------------------------------------------------------------*/
354 draw_line(ModeInfo * mi, XPoint * points, int number, GC to_use, int offset)
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);
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);
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);
375 /*------------------------------------------------------------------------*/
378 level1_strike(Lightning bolt, ModeInfo * mi)
380 Display *display = MI_DISPLAY(mi);
381 Window window = MI_WINDOW(mi);
382 Storm *st = &Helga[MI_SCREEN(mi)];
386 if (MI_NPIXELS(mi) > 2) /* color */
387 XSetForeground(display, gc, MI_PIXEL(mi, st->color));
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);
403 for (i = 0; i < bolt.fork_number; i++) {
404 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
406 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
409 first_strike(bolt, mi);
412 /*------------------------------------------------------------------------*/
415 distance(XPoint a, XPoint b)
417 return ((int) sqrt((double) (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)));
420 /*------------------------------------------------------------------------*/
423 level2_strike(Lightning bolt, ModeInfo * mi)
425 Display *display = MI_DISPLAY(mi);
426 Window window = MI_WINDOW(mi);
427 Storm *st = &Helga[MI_SCREEN(mi)];
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));
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);
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);
453 for (i = 0; i < bolt.fork_number; i++) {
454 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
456 draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used,
459 level1_strike(bolt, mi);
462 /*------------------------------------------------------------------------*/
465 storm_active(Storm * st)
467 int i, atleast_1 = 0;
469 for (i = 0; i < st->multi_strike; i++)
470 if (st->bolts[i].wiggle_number > 0)
476 /*------------------------------------------------------------------------*/
479 wiggle_bolt(Lightning * bolt)
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;
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;
494 if (bolt->wiggle_amount > 1)
495 bolt->wiggle_amount -= 1;
497 bolt->wiggle_amount = 0;
500 /*------------------------------------------------------------------------*/
503 wiggle_line(XPoint * p, int number, int amount)
507 for (i = 0; i < number; i++) {
508 p[i].x += NRAND(amount) - amount / 2;
509 p[i].y += NRAND(amount) - amount / 2;
513 /*------------------------------------------------------------------------*/
516 init_lightning (ModeInfo * mi)
521 if ((Helga = (Storm *) calloc(MI_NUM_SCREENS(mi),
522 sizeof (Storm))) == NULL)
525 st = &Helga[MI_SCREEN(mi)];
527 st->scr_width = MI_WIDTH(mi);
528 st->scr_height = MI_HEIGHT(mi);
530 st->multi_strike = setup_multi_strike();
535 /*------------------------------------------------------------------------*/
538 draw_lightning (ModeInfo * mi)
545 st = &Helga[MI_SCREEN(mi)];
546 MI_IS_DRAWN(mi) = True;
549 MI_IS_DRAWN(mi) = False;
551 MI_IS_DRAWN(mi) = True;
553 st->color = NRAND(MI_NPIXELS(mi));
555 if (storm_active(st))
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);
571 if (++st->busyLoop > 6) {
577 MI_IS_DRAWN(mi) = False;
579 MI_IS_DRAWN(mi) = True;
581 if (storm_active(st))
587 if (++st->busyLoop > 100) {
596 release_lightning(ModeInfo * mi)
599 (void) free((void *) Helga);
600 Helga = (Storm *) NULL;
605 refresh_lightning(ModeInfo * mi)
607 /* Do nothing, it will refresh by itself */
610 XSCREENSAVER_MODULE ("Lightning", lightning)
613 #endif /* MODE_lightning */