xenocara/app/xlockmore/modes/triangle.c
2006-11-26 11:07:42 +00:00

378 lines
10 KiB
C

/* -*- Mode: C; tab-width: 4 -*- */
/* triangle --- create a triangle-mountain */
#if !defined( lint ) && !defined( SABER )
static const char sccsid[] = "@(#)triangle.c 5.00 2000/11/01 xlockmore";
#endif
/*-
* Copyright (c) 1995 by Tobias Gloth
*
* 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
* 22-Dec-1997: Removed MI_PAUSE since it does not work on multiscreens.
* 10-May-1997: Compatible with xscreensaver
* 10-Mar-1996: re-arranged and re-formatted the code for appearance and
* to make common subroutines. Simplified.
* Ron Hitchens <ron@idiom.com>
* 07-Mar-1996: Removed internal delay code, set MI_PAUSE(mi) for inter-scene
* delays. No other delays are needed here.
* Made pause time sensitive to value of cycles (in 10ths of a
* second). Removed (hopefully) all references to globals.
* Ron Hitchens <ron@idiom.com>
* 27-Feb-1996: Undid the changes listed below. Added ModeInfo argument.
* Implemented delay between scenes using the MI_PAUSE(mi)
* scheme. Ron Hitchens <ron@idiom.com>
* 27-Dec-1995: Ron Hitchens <ron@idiom.com>
* Modified logic of draw_triangle() to provide a delay
* (sensitive to the value of cycles) between each iteration.
* Because this mode is so compute intensive, when the new
* event loop adjusted the delay to compensate, this mode had
* almost no delay time left. This change pauses between each
* new landscape, but could still be done better (it is not
* sensitive to input events while drawing, for example).
* 03-Nov-1995: Many changes (hopefully some good ones) by David Bagley
* 01-Oct-1995: Written by Tobias Gloth
*/
#ifdef STANDALONE
#define MODE_triangle
#define PROGCLASS "Triangle"
#define HACK_INIT init_triangle
#define HACK_DRAW draw_triangle
#define triangle_opts xlockmore_opts
#define DEFAULTS "*delay: 10000 \n" \
"*ncolors: 128 \n " \
"*wireframe: False \n" \
"*fullrandom: False \n"
#define SMOOTH_COLORS
#if 0
#define UNIFORM_COLORS /* To get blue water uncomment, but ... */
#endif
#include "xlockmore.h" /* in xscreensaver distribution */
#else /* STANDALONE */
#include "xlock.h" /* in xlockmore distribution */
#endif /* STANDALONE */
#ifdef MODE_triangle
ModeSpecOpt triangle_opts =
{0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
#ifdef USE_MODULES
ModStruct triangle_description =
{"triangle", "init_triangle", "draw_triangle", "release_triangle",
"refresh_triangle", "init_triangle", (char *) NULL, &triangle_opts,
10000, 1, 1, 1, 64, 1.0, "",
"Shows a triangle mountain range", 0, NULL};
#endif
#define MAX_STEPS 8
#define MAX_SIZE (1<<MAX_STEPS)
#define MAX_LEVELS 1000
#define DELTA 0.4
#define LEFT -0.25
#define RIGHT 1.25
#define TOP 0.3
#define BOTTOM 1.0
#define BLUE (45 * MI_NPIXELS(mi) / 64) /* Just the right shade of blue */
#define BACKFACE_REMOVAL
#define DISPLACE(h,d) ((h)/2+LRAND()/(MAXRAND/(2*(d)+1))-d)
typedef struct {
int width;
int height;
int size;
int steps;
int stage;
int busyLoop;
int fast;
int i;
int j;
int d;
short level[MAX_LEVELS];
int xpos[2 * MAX_SIZE + 1];
int ypos[MAX_SIZE + 1];
short H[(MAX_SIZE + 1) * (MAX_SIZE + 2) / 2];
short *h[MAX_SIZE + 1];
short delta[MAX_STEPS];
Bool wireframe;
Bool joke;
} trianglestruct;
static trianglestruct *triangles = (trianglestruct *) NULL;
static
void
draw_atriangle(ModeInfo * mi, XPoint * p, int y_0, int y_1, int y_2, double dinv)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
GC gc = MI_GC(mi);
trianglestruct *tp = &triangles[MI_SCREEN(mi)];
if (MI_NPIXELS(mi) > 2) { /* color */
int dmax, dmin;
long color;
dmin = MIN(y_0, y_1);
dmin = MIN(dmin, y_2);
dmax = MAX(y_0, y_1);
dmax = MAX(dmax, y_2);
if (dmax == 0) {
color = BLUE;
} else {
color = MI_NPIXELS(mi) -
(int) ((double) MI_NPIXELS(mi) / M_PI_2 * atan(dinv * (dmax - dmin)));
}
XSetForeground(display, gc, MI_PIXEL(mi, color % MI_NPIXELS(mi)));
if (tp->joke) {
if ((Bool) (LRAND() & 1)) {
XDrawLines(display, window, gc, p, 4, CoordModeOrigin);
} else {
XFillPolygon(display, window, gc, p, 3, Convex, CoordModeOrigin);
}
} else if (tp->wireframe) {
XDrawLines(display, window, gc, p, 4, CoordModeOrigin);
} else {
/* dieing on my Sun here with gcc -g -O2, flakey */
XFillPolygon(display, window, gc, p, 3, Convex, CoordModeOrigin);
}
} else {
/* mono */
#ifdef BACKFACE_REMOVAL
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
XFillPolygon(display, window, gc, p, 3, Convex, CoordModeOrigin);
#endif
XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
XDrawLines(display, window, gc, p, 4, CoordModeOrigin);
}
}
static
void
calc_points1(trianglestruct * tp, int d, int *y0_p, int *y1_p, int *y2_p, XPoint * p)
{
*y0_p = tp->level[MAX(tp->h[tp->i][tp->j], 0)];
*y1_p = tp->level[MAX(tp->h[tp->i + d][tp->j], 0)];
*y2_p = tp->level[MAX(tp->h[tp->i][tp->j + d], 0)];
p[0].x = tp->xpos[2 * tp->i + tp->j];
p[1].x = tp->xpos[2 * (tp->i + d) + tp->j];
p[2].x = tp->xpos[2 * tp->i + (tp->j + d)];
p[0].y = tp->ypos[tp->j] - *y0_p;
p[1].y = tp->ypos[tp->j] - *y1_p;
p[2].y = tp->ypos[tp->j + d] - *y2_p;
p[3] = p[0];
}
static
void
calc_points2(trianglestruct * tp, int d, int *y0_p, int *y1_p, int *y2_p, XPoint * p)
{
*y0_p = tp->level[MAX(tp->h[tp->i + d][tp->j], 0)];
*y1_p = tp->level[MAX(tp->h[tp->i + d][tp->j + d], 0)];
*y2_p = tp->level[MAX(tp->h[tp->i][tp->j + d], 0)];
p[0].x = tp->xpos[2 * (tp->i + d) + tp->j];
p[1].x = tp->xpos[2 * (tp->i + d) + (tp->j + d)];
p[2].x = tp->xpos[2 * tp->i + (tp->j + d)];
p[0].y = tp->ypos[tp->j] - *y0_p;
p[1].y = tp->ypos[tp->j + d] - *y1_p;
p[2].y = tp->ypos[tp->j + d] - *y2_p;
p[3]= p[0];
}
static
void
draw_mesh(ModeInfo * mi, trianglestruct * tp, int d, int count)
{
XPoint p[4];
int first = 1;
int y_0, y_1, y_2;
double dinv = 0.2 / d;
if ((tp->j == 0) && (tp->i == 0)) {
MI_CLEARWINDOWCOLORMAPFAST(mi, MI_GC(mi), MI_BLACK_PIXEL(mi));
}
for (; (tp->j < tp->size) && (count > 0); tp->j += ((count) ? d : 0)) {
for (tp->i = (first) ? tp->i : 0, first = 0;
(tp->i < MAX_SIZE - tp->j) && (count > 0);
tp->i += d, count--) {
if (tp->i + tp->j < tp->size) {
calc_points1(tp, d, &y_0, &y_1, &y_2, p);
draw_atriangle(mi, p, y_0, y_1, y_2, dinv);
}
if (tp->i + tp->j + d < tp->size) {
calc_points2(tp, d, &y_0, &y_1, &y_2, p);
draw_atriangle(mi, p, y_0, y_1, y_2, dinv);
}
}
}
if (tp->j == tp->size) {
tp->busyLoop = 1;
}
}
void
init_triangle(ModeInfo * mi)
{
short *tmp;
int i, dim, one;
trianglestruct *tp;
if (triangles == NULL) {
if ((triangles = (trianglestruct *) calloc(MI_NUM_SCREENS(mi),
sizeof (trianglestruct))) == NULL)
return;
}
tp = &triangles[MI_SCREEN(mi)];
tp->width = MI_WIDTH(mi);
tp->height = MI_HEIGHT(mi);
tp->busyLoop = -1;
tp->fast = 2;
if (MI_IS_FULLRANDOM(mi)) {
tp->joke = (Bool) (NRAND(10) == 0);
tp->wireframe = (Bool) (LRAND() & 1);
} else
tp->wireframe = MI_IS_WIREFRAME(mi);
MI_CLEARWINDOW(mi);
tp->steps = MAX_STEPS;
do {
tp->size = 1 << --tp->steps;
} while (tp->size * 5 > tp->width);
tmp = tp->H;
for (i = 0; i < tp->size + 1; i++) {
tp->h[i] = tmp;
tmp += (tp->size) + 1 - i;
}
tp->stage = -1;
dim = MIN(tp->width, tp->height);
for (i = 0; i < 2 * tp->size + 1; i++) {
tp->xpos[i] = (short) ((((double) i)
/ ((double) (2 * tp->size)) * (RIGHT - LEFT) + LEFT)
* dim) + (tp->width - dim) / 2;
}
for (i = 0; i < (tp->size + 1); i++) {
tp->ypos[i] = (short) ((((double) i)
/ ((double) tp->size) * (BOTTOM - TOP) + TOP) * dim)
+ (tp->height - dim) / 2;
}
for (i = 0; i < tp->steps; i++) {
tp->delta[i] = ((short) (DELTA * dim)) >> i;
}
one = tp->delta[0];
if (one > 0)
for (i = 0; i < MAX_LEVELS; i++) {
tp->level[i] = (i * i) / one;
}
}
void
draw_triangle(ModeInfo * mi)
{
int d, d2, i, j, delta;
trianglestruct *tp;
if (triangles == NULL)
return;
tp = &triangles[MI_SCREEN(mi)];
MI_IS_DRAWN(mi) = True;
if (tp->busyLoop > 0) {
if (tp->busyLoop >= 100)
tp->busyLoop = -1;
else
tp->busyLoop++;
return;
}
if (!tp->busyLoop) {
draw_mesh(mi, tp, tp->d / 2, MAX_SIZE / tp->d);
return;
}
if (tp->delta[0] > 0) {
if (!(++tp->stage)) {
tp->h[0][0] = (short int) MAX(0, DISPLACE(0, tp->delta[0]));
tp->h[tp->size][0] = (short int) MAX(0, DISPLACE(0, tp->delta[0]));
tp->h[0][tp->size] = (short int) MAX(0, DISPLACE(0, tp->delta[0]));
} else {
d = 2 << (tp->steps - tp->stage);
d2 = d / 2;
delta = tp->delta[tp->stage - 1];
for (i = 0; i < tp->size; i += d) {
for (j = 0; j < (tp->size - i); j += d) {
tp->h[i + d2][j] = (short int) DISPLACE(tp->h[i][j] +
tp->h[i + d][j], delta);
tp->h[i][j + d2] = (short int) DISPLACE(tp->h[i][j] +
tp->h[i][j + d], delta);
tp->h[i + d2][j + d2] = (short int) DISPLACE(tp->h[i + d][j] +
tp->h[i][j + d], delta);
}
tp->busyLoop = 0;
tp->i = 0;
tp->j = 0;
tp->d = d;
}
}
}
if (tp->stage == tp->steps) {
#ifdef STANDALONE
erase_full_window(MI_DISPLAY(mi), MI_WINDOW(mi));
#endif
init_triangle(mi);
}
}
void
release_triangle(ModeInfo * mi)
{
if (triangles != NULL) {
free(triangles);
triangles = (trianglestruct *) NULL;
}
}
void
refresh_triangle(ModeInfo * mi)
{
MI_CLEARWINDOW(mi);
}
#endif /* MODE_triangle */