/* -*- Mode: C; tab-width: 4 -*- */ /* lightning --- fractal lightning bolds */ #if !defined( lint ) && !defined( SABER ) static const char sccsid[] = "@(#)lightning.c 5.00 2000/11/01 xlockmore"; #endif /*- * Copyright (c) 1996 by Keith Romberg * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * This file is provided AS IS with no warranties of any kind. The author * shall have no liability with respect to the infringement of copyrights, * trade secrets or any patents by this file or any part thereof. In no * event will the author be liable for any lost revenue or profits or * other special, indirect and consequential damages. * * Revision History: * 01-Nov-2000: Allocation checks * 10-May-1997: Compatible with xscreensaver * 14-Jul-1996: Cleaned up code. * 27-Jun-1996: Written and submitted by Keith Romberg . */ #ifdef STANDALONE #define MODE_lightning #define PROGCLASS "Lightning" #define HACK_INIT init_lightning #define HACK_DRAW draw_lightning #define lightning_opts xlockmore_opts #define DEFAULTS "*delay: 10000 \n" #define BRIGHT_COLORS #include "xlockmore.h" /* in xscreensaver distribution */ #else /* STANDALONE */ #include "xlock.h" /* in xlockmore distribution */ #endif /* STANDALONE */ #ifdef MODE_lightning ModeSpecOpt lightning_opts = {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL}; #ifdef USE_MODULES ModStruct lightning_description = {"lightning", "init_lightning", "draw_lightning", "release_lightning", "refresh_lightning", "init_lightning", (char *) NULL, &lightning_opts, 10000, 1, 1, 1, 64, 0.6, "", "Shows Keith's fractal lightning bolts", 0, NULL}; #endif #define BOLT_NUMBER 4 #define BOLT_ITERATION 4 #define LONG_FORK_ITERATION 3 #define MEDIUM_FORK_ITERATION 2 #define SMALL_FORK_ITERATION 1 #define WIDTH_VARIATION 30 #define HEIGHT_VARIATION 15 #define DELAY_TIME_AMOUNT 15 #define MULTI_DELAY_TIME_BASE 5 #define MAX_WIGGLES 16 #define WIGGLE_BASE 8 #define WIGGLE_AMOUNT 14 #define RANDOM_FORK_PROBILITY 4 #define FIRST_LEVEL_STRIKE 0 #define LEVEL_ONE_STRIKE 1 #define LEVEL_TWO_STRIKE 2 #define BOLT_VERTICIES ((1<= 51) && (multi_prob < 75)) result = 2; else if ((multi_prob >= 76) && (multi_prob < 92)) result = 3; else result = BOLT_NUMBER; /* 4 */ return (result); } /*-------------------------------------------------------------------------*/ static int flashing_strike(void) { int tmp = NRAND(FLASH_PROBILITY); if (tmp <= FLASH_PROBILITY) return (1); return (0); } /*-------------------------------------------------------------------------*/ static void flash_duration(int *start, int *end, int total_duration) { int mid, d; mid = total_duration / MAX_FLASH_AMOUNT; d = NRAND(total_duration / MAX_FLASH_AMOUNT) / 2; *start = mid - d; *end = mid + d; } /*-------------------------------------------------------------------------*/ static void random_storm(Storm * st) { int i, j, tmp; XPoint p; for (i = 0; i < st->multi_strike; i++) { st->bolts[i].end1.x = NRAND(st->scr_width); st->bolts[i].end1.y = 0; st->bolts[i].end2.x = NRAND(st->scr_width); st->bolts[i].end2.y = st->scr_height; st->bolts[i].wiggle_number = WIGGLE_BASE + NRAND(MAX_WIGGLES); if ((st->bolts[i].flash = flashing_strike())) flash_duration(&(st->bolts[i].flash_begin), &(st->bolts[i].flash_stop), st->bolts[i].wiggle_number); else st->bolts[i].flash_begin = st->bolts[i].flash_stop = 0; st->bolts[i].wiggle_amount = WIGGLE_AMOUNT; if (i == 0) st->bolts[i].delay_time = NRAND(DELAY_TIME_AMOUNT); else st->bolts[i].delay_time = NRAND(DELAY_TIME_AMOUNT) + (MULTI_DELAY_TIME_BASE * i); st->bolts[i].strike_level = FIRST_LEVEL_STRIKE; tmp = 0; generate(st->bolts[i].end1, st->bolts[i].end2, BOLT_ITERATION, st->bolts[i].middle, &tmp); st->bolts[i].fork_number = 0; st->bolts[i].visible = 0; for (j = 0; j < BOLT_VERTICIES; j++) { if (st->bolts[i].fork_number >= 2) break; if (NRAND(100) < RANDOM_FORK_PROBILITY) { p.x = NRAND(st->scr_width); p.y = st->scr_height; st->bolts[i].forks_start[st->bolts[i].fork_number] = j; create_fork(&(st->bolts[i].branch[st->bolts[i].fork_number]), st->bolts[i].middle[j], p, j); st->bolts[i].fork_number++; } } } } static void generate(XPoint A, XPoint B, int iter, XPoint * verts, int *vert_index) { XPoint mid; mid.x = (A.x + B.x) / 2 + NRAND(WIDTH_VARIATION) - WIDTH_VARIATION / 2; mid.y = (A.y + B.y) / 2 + NRAND(HEIGHT_VARIATION) - HEIGHT_VARIATION / 2; if (!iter) { verts[*vert_index].x = mid.x; verts[*vert_index].y = mid.y; (*vert_index)++; return; } generate(A, mid, iter - 1, verts, vert_index); generate(mid, B, iter - 1, verts, vert_index); } /*------------------------------------------------------------------------*/ static void create_fork(Fork * f, XPoint start, XPoint end, int level) { int tmp = 1; f->ForkVerticies[0].x = start.x; f->ForkVerticies[0].y = start.y; if (level <= 6) { generate(start, end, LONG_FORK_ITERATION, f->ForkVerticies, &tmp); f->num_used = 9; } else if ((level > 6) && (level <= 11)) { generate(start, end, MEDIUM_FORK_ITERATION, f->ForkVerticies, &tmp); f->num_used = 5; } else { if (distance(start, end) > 100) { generate(start, end, MEDIUM_FORK_ITERATION, f->ForkVerticies, &tmp); f->num_used = 5; } else { generate(start, end, SMALL_FORK_ITERATION, f->ForkVerticies, &tmp); f->num_used = 3; } } f->ForkVerticies[f->num_used - 1].x = end.x; f->ForkVerticies[f->num_used - 1].y = end.y; } /*------------------------------------------------------------------------*/ static void update_bolt(Lightning * bolt, int time_now) { wiggle_bolt(bolt); if ((bolt->wiggle_amount == 0) && (bolt->wiggle_number > 2)) bolt->wiggle_number = 0; if (((time_now % 3) == 0)) bolt->wiggle_amount++; if (((time_now >= bolt->delay_time) && (time_now < bolt->flash_begin)) || (time_now > bolt->flash_stop)) bolt->visible = 1; else bolt->visible = 0; if (time_now == bolt->delay_time) bolt->strike_level = FIRST_LEVEL_STRIKE; else if (time_now == (bolt->delay_time + 1)) bolt->strike_level = LEVEL_ONE_STRIKE; else if ((time_now > (bolt->delay_time + 1)) && (time_now <= (bolt->delay_time + bolt->flash_begin - 2))) bolt->strike_level = LEVEL_TWO_STRIKE; else if (time_now == (bolt->delay_time + bolt->flash_begin - 1)) bolt->strike_level = LEVEL_ONE_STRIKE; else if (time_now == (bolt->delay_time + bolt->flash_stop + 1)) bolt->strike_level = LEVEL_ONE_STRIKE; else bolt->strike_level = LEVEL_TWO_STRIKE; } /*------------------------------------------------------------------------*/ static void draw_bolt(Lightning * bolt, ModeInfo * mi) { if (bolt->visible) { if (bolt->strike_level == FIRST_LEVEL_STRIKE) first_strike(*bolt, mi); else if (bolt->strike_level == LEVEL_ONE_STRIKE) level1_strike(*bolt, mi); else level2_strike(*bolt, mi); } } /*------------------------------------------------------------------------*/ static void first_strike(Lightning bolt, ModeInfo * mi) { Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); GC gc = MI_GC(mi); int i; XSetForeground(display, gc, MI_WHITE_PIXEL(mi)); XDrawLine(display, window, gc, bolt.end1.x, bolt.end1.y, bolt.middle[0].x, bolt.middle[0].y); draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, 0); XDrawLine(display, window, gc, bolt.middle[BOLT_VERTICIES - 1].x, bolt.middle[BOLT_VERTICIES - 1].y, bolt.end2.x, bolt.end2.y); for (i = 0; i < bolt.fork_number; i++) draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used, gc, 0); } /*------------------------------------------------------------------------*/ static void draw_line(ModeInfo * mi, XPoint * points, int number, GC to_use, int offset) { int i; for (i = 0; i < number - 1; i++) { if (points[i].y <= points[i + 1].y) XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), to_use, points[i].x + offset, points[i].y, points[i + 1].x + offset, points[i + 1].y); else { if (points[i].x < points[i + 1].x) XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), to_use, points[i].x + offset, points[i].y + offset, points[i + 1].x + offset, points[i + 1].y + offset); else XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), to_use, points[i].x - offset, points[i].y + offset, points[i + 1].x - offset, points[i + 1].y + offset); } } } /*------------------------------------------------------------------------*/ static void level1_strike(Lightning bolt, ModeInfo * mi) { Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); Storm *st = &Helga[MI_SCREEN(mi)]; GC gc = MI_GC(mi); int i; if (MI_NPIXELS(mi) > 2) /* color */ XSetForeground(display, gc, MI_PIXEL(mi, st->color)); else XSetForeground(display, gc, MI_WHITE_PIXEL(mi)); XDrawLine(display, window, gc, bolt.end1.x - 1, bolt.end1.y, bolt.middle[0].x - 1, bolt.middle[0].y); draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, -1); XDrawLine(display, window, gc, bolt.middle[BOLT_VERTICIES - 1].x - 1, bolt.middle[BOLT_VERTICIES - 1].y, bolt.end2.x - 1, bolt.end2.y); XDrawLine(display, window, gc, bolt.end1.x + 1, bolt.end1.y, bolt.middle[0].x + 1, bolt.middle[0].y); draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, 1); XDrawLine(display, window, gc, bolt.middle[BOLT_VERTICIES - 1].x + 1, bolt.middle[BOLT_VERTICIES - 1].y, bolt.end2.x + 1, bolt.end2.y); for (i = 0; i < bolt.fork_number; i++) { draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used, gc, -1); draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used, gc, 1); } first_strike(bolt, mi); } /*------------------------------------------------------------------------*/ static int distance(XPoint a, XPoint b) { return ((int) sqrt((double) (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y))); } /*------------------------------------------------------------------------*/ static void level2_strike(Lightning bolt, ModeInfo * mi) { Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); Storm *st = &Helga[MI_SCREEN(mi)]; GC gc = MI_GC(mi); int i; /* This was originally designed to be a little darker then the level1 strike. This was changed to get it to work on multiscreens and to add more color variety. I tried stippling but it did not look good. */ if (MI_NPIXELS(mi) > 2) XSetForeground(display, gc, MI_PIXEL(mi, st->color)); else XSetForeground(display, gc, MI_WHITE_PIXEL(mi)); XDrawLine(display, window, gc, bolt.end1.x - 2, bolt.end1.y, bolt.middle[0].x - 2, bolt.middle[0].y); draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, -2); XDrawLine(display, window, gc, bolt.middle[BOLT_VERTICIES - 1].x - 2, bolt.middle[BOLT_VERTICIES - 1].y, bolt.end2.x - 2, bolt.end2.y); XDrawLine(display, window, gc, bolt.end1.x + 2, bolt.end1.y, bolt.middle[0].x + 2, bolt.middle[0].y); draw_line(mi, bolt.middle, BOLT_VERTICIES, gc, 2); XDrawLine(display, window, gc, bolt.middle[BOLT_VERTICIES - 1].x + 2, bolt.middle[BOLT_VERTICIES - 1].y, bolt.end2.x + 2, bolt.end2.y); for (i = 0; i < bolt.fork_number; i++) { draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used, gc, -2); draw_line(mi, bolt.branch[i].ForkVerticies, bolt.branch[i].num_used, gc, 2); } level1_strike(bolt, mi); } /*------------------------------------------------------------------------*/ static int storm_active(Storm * st) { int i, atleast_1 = 0; for (i = 0; i < st->multi_strike; i++) if (st->bolts[i].wiggle_number > 0) atleast_1++; return (atleast_1); } /*------------------------------------------------------------------------*/ static void wiggle_bolt(Lightning * bolt) { int i; wiggle_line(bolt->middle, BOLT_VERTICIES, bolt->wiggle_amount); bolt->end2.x += NRAND(bolt->wiggle_amount) - bolt->wiggle_amount / 2; bolt->end2.y += NRAND(bolt->wiggle_amount) - bolt->wiggle_amount / 2; for (i = 0; i < bolt->fork_number; i++) { wiggle_line(bolt->branch[i].ForkVerticies, bolt->branch[i].num_used, bolt->wiggle_amount); bolt->branch[i].ForkVerticies[0].x = bolt->middle[bolt->forks_start[i]].x; bolt->branch[i].ForkVerticies[0].y = bolt->middle[bolt->forks_start[i]].y; } if (bolt->wiggle_amount > 1) bolt->wiggle_amount -= 1; else bolt->wiggle_amount = 0; } /*------------------------------------------------------------------------*/ static void wiggle_line(XPoint * p, int number, int amount) { int i; for (i = 0; i < number; i++) { p[i].x += NRAND(amount) - amount / 2; p[i].y += NRAND(amount) - amount / 2; } } /*------------------------------------------------------------------------*/ void init_lightning(ModeInfo * mi) { Storm *st; if (Helga == NULL) { if ((Helga = (Storm *) calloc(MI_NUM_SCREENS(mi), sizeof (Storm))) == NULL) return; } st = &Helga[MI_SCREEN(mi)]; st->scr_width = MI_WIDTH(mi); st->scr_height = MI_HEIGHT(mi); st->multi_strike = setup_multi_strike(); random_storm(st); st->stage = 0; } /*------------------------------------------------------------------------*/ void draw_lightning(ModeInfo * mi) { int i; Storm *st; if (Helga == NULL) return; st = &Helga[MI_SCREEN(mi)]; MI_IS_DRAWN(mi) = True; switch (st->stage) { case 0: MI_IS_DRAWN(mi) = False; MI_CLEARWINDOW(mi); MI_IS_DRAWN(mi) = True; st->color = NRAND(MI_NPIXELS(mi)); st->draw_time = 0; if (storm_active(st)) st->stage++; else st->stage = 4; break; case 1: for (i = 0; i < st->multi_strike; i++) { if (st->bolts[i].visible) draw_bolt(&(st->bolts[i]), mi); update_bolt(&(st->bolts[i]), st->draw_time); } st->draw_time++; st->stage++; st->busyLoop = 0; break; case 2: if (++st->busyLoop > 6) { st->stage++; st->busyLoop = 0; } break; case 3: MI_IS_DRAWN(mi) = False; MI_CLEARWINDOW(mi); MI_IS_DRAWN(mi) = True; if (storm_active(st)) st->stage = 1; else st->stage++; break; case 4: if (++st->busyLoop > 100) { st->busyLoop = 0; } init_lightning(mi); break; } } void release_lightning(ModeInfo * mi) { if (Helga != NULL) { free(Helga); Helga = (Storm *) NULL; } } void refresh_lightning(ModeInfo * mi) { /* Do nothing, it will refresh by itself */ } #endif /* MODE_lightning */