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

1262 lines
33 KiB
C

/* -*- Mode: C; tab-width: 4 -*- */
/* hyper --- spinning hypercubes, not just for tesseracts any more */
#if !defined( lint ) && !defined( SABER )
static const char sccsid[] = "@(#)hyper.c 5.00 2000/11/01 xlockmore";
#endif
/*-
* hyper.c (nee multidico)
* Copyright (C) 1992,1998 John Heidemann <johnh@isi.edu>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*
***
*
* hyper (then called multidico) was originally written for
* xscreensaver in 1992. Unfortunately it didn't make it into that
* code because Jamie Zawinski was implementing his own tesseract and
* preferred his version (optimized for speed) to my version
* (with support for more than 4 dimesnions).
*
* In 1998 I finally got around to porting it to xlockmore and sending
* it off.
*
* (The implementation is independent of jwz's---I started with ico.)
*
* Editor's Note... (DAB)
* Removed all original Color stuff. Replaced it so it could do xoring
* effectively.... Took move_line from jwz's version.... to make it legal...
*
* Copyright (c) 1992 by Jamie Zawinski
*
* 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.
*
* Check out A.K. Dewdney's "Computer Recreations", Scientific American
* Magazine" Apr 1986 pp 14-25 for more info.
* Idea on 3d taken from there but does not work yet. Also a small number of
* random chosen planes drawn may be nice.
*
* Revision History:
* 01-Nov-2000: Added "3d" support from Scientific American
* 04-Aug-1998: Added "3d" support from Scientific American
*/
#ifdef STANDALONE
#define MODE_hyper
#define PROGCLASS "Hyper"
#define HACK_INIT init_hyper
#define HACK_DRAW draw_hyper
#define HACK_CHANGE change_hyper
#define hyper_opts xlockmore_opts
#define DEFAULTS "*delay: 100000 \n" \
"*count: -6 \n" \
"*cycles: 300 \n" \
"*ncolors: 200 \n" \
"*use3d: False \n" \
"*delta3d: 1.5 \n" \
"*right3d: red \n" \
"*left3d: blue \n" \
"*both3d: magenta \n" \
"*none3d: black \n" \
"*spinDelay: 2 \n" \
"*showAxes: false \n" \
"*randomStart: false \n" \
"*debug: false \n"
#include "xlockmore.h" /* in xscreensaver distribution */
#else /* STANDALONE */
#include "xlock.h" /* in xlockmore distribution */
#endif /* STANDALONE */
#ifdef MODE_hyper
static Bool random_start;
static Bool show_axes;
static Bool show_planes;
static int spin_delay;
extern XFontStruct *getFont(Display * display);
static XrmOptionDescRec opts[] =
{
{(char *) "-randomstart", (char *) ".hyper.randomStart", XrmoptionNoArg, (caddr_t) "on"},
{(char *) "+randomstart", (char *) ".hyper.randomStart", XrmoptionNoArg, (caddr_t) "off"},
{(char *) "-showaxes", (char *) ".hyper.showAxes", XrmoptionNoArg, (caddr_t) "on"},
{(char *) "+showaxes", (char *) ".hyper.showAxes", XrmoptionNoArg, (caddr_t) "off"},
{(char *) "-showplanes", (char *) ".hyper.showPlanes", XrmoptionNoArg, (caddr_t) "on"},
{(char *) "+showplanes", (char *) ".hyper.showPlanes", XrmoptionNoArg, (caddr_t) "off"},
{(char *) "-spindelay", (char *) ".hyper.spinDelay", XrmoptionSepArg, 0},
};
static argtype vars[] =
{
{(void *) & random_start, (char *) "randomStart", (char *) "RandomStart", (char *) "True", t_Bool},
{(void *) & show_axes, (char *) "showAxes", (char *) "ShowAxes", (char *) "True", t_Bool},
{(void *) & show_planes, (char *) "showPlanes", (char *) "ShowPlanes", (char *) "False", t_Bool},
{(void *) & spin_delay, (char *) "spinDelay", (char *) "SpinDelay", (char *) "2", t_Int},
};
static OptionStruct desc[] =
{
{(char *) "-/+randomstart", (char *) "turn on/off begining with random rotations"},
{(char *) "-/+showaxes", (char *) "turn on/off showing the axes"},
{(char *) "-/+showplanes", (char *) "turn on/off showing the planes"},
{(char *) "-spindelay num", (char *) "delay in seconds before chaning spin speed"},
};
ModeSpecOpt hyper_opts =
{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
#ifdef USE_MODULES
ModStruct hyper_description =
{"hyper", "init_hyper", "draw_hyper", "release_hyper",
"refresh_hyper", "change_hyper", (char *) NULL, &hyper_opts,
100000, -6, 300, 1, 64, 1.0, "",
"Shows spinning n-dimensional hypercubes", 0, NULL};
#endif
#ifdef DEBUG
#include <assert.h>
#endif
typedef struct {
double x, y;
} dpoint;
typedef double vector; /* Was an array of size (MAX_D + 1) */
typedef double matrix; /* Was a double array of size (MAX_D + 1) ^ 2 */
#define MIN_D 2
#define MAX_D 10 /* Memory use goes up exponentially */
/* Normally DELTA3D is not in degrees, but it works here, so why fight it? */
#define DELDEG (MI_DELTA3D(mi)*M_PI/180.0)
typedef struct {
int from, to;
long color;
} line_segment;
typedef struct {
int a, b, c, d;
long color;
} plane_section;
typedef struct hyper {
GC gc;
Bool redrawing;
Bool painted;
XFontStruct *font;
int show_axes;
int show_planes;
int maxx, maxy;
int delay;
int spinDelay;
Bool normxor;
/*
* Data storage.
*/
int num_d; /* number of dimensions */
int num_mat;
int num_matmat;
/* there are C(num_d,2) planes */
int num_planes; /* #define max_planes 40 */
XPoint *rotation_planes;
/* Formerly max_planes arrays */
double *rotations; /* where we are in each dimension */
double *d_rotations; /* change in rotation */
double *dd_rotations; /* change in change in rotation */
int *cdd_rotations; /* how many turns to apply dd_rotations */
matrix *Trotations;
matrix *Trotationsleft;
matrix *Tall;
matrix *Tallleft;
int num_points;
vector *points;
vector *pointsleft;
int num_lines;
line_segment *lines;
plane_section *planes;
int point_set; /* which bank of points are we using */
XPoint *xpoints[2];
XPoint *xpointsleft[2];
int num_axis_points;
/* Formerly an array of (MAX_D + 1) */
int *axis_points;
/*
* Inter-step state:
*/
int this_set;
Bool stationary;
} hyperstruct;
static hyperstruct *hypers = (hyperstruct *) NULL;
#define allocarray(P,T,N) if((P=(T*)malloc((N)*sizeof(T)))==NULL) \
{free_hyper(display,hp);return False;}
#define callocarray(P,T,N) if((P=(T*)calloc(N,sizeof(T)))==NULL) \
{free_hyper(display,hp);return False;}
/*
* Matrix handling & 3d transformation routines
*/
static void
MatMult(matrix * a, matrix * b, matrix * c, int n)
/* c = a * b for n x n matricies */
{
register int i, j, k;
double temp;
/* Strassen' method... what's that? */
for (i = 0; i < n; i++) /* go through a's rows */
for (j = 0; j < n; j++) { /* go through b's columns */
temp = 0.0;
for (k = 0; k < n; k++)
temp += a[i * n + k] * b[k * n + j];
c[i * n + j] = temp;
}
}
static void
MatVecMult(matrix * a, vector * b, vector * c, int n)
/* c = a * b for a n x n, b&c 1 x n */
{
register int i, k;
double temp;
for (i = 0; i < n; i++) { /* go through a's rows */
temp = 0.0;
for (k = 0; k < n; k++)
temp += a[i * n + k] * b[k];
c[i] = temp;
}
}
static void
MatCopy(matrix * a, matrix * b, int n)
/* b <- a */
{
register int i, j;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
b[i * n + j] = a[i * n + j];
}
static void
MatZero(matrix * a, int n)
/* a = 0 */
{
register int i, j;
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
a[i * n + j] = 0.0;
}
static void
MatIdent(matrix * a, int n)
/* a = I */
{
register int i;
MatZero(a, n);
for (i = 0; i < n; i++)
a[i * n + i] = 1.0;
}
static void
ZeroMultiCounter(int *c, int n)
{
int i;
for (i = 0; i < n; i++)
c[i] = 0;
}
static int
RealIncMultiCounter(int *c, int n, int place)
{
#define BASE 2
if (place >= n)
return 0;
else if (++c[place] >= BASE) {
c[place] = 0;
return RealIncMultiCounter(c, n, place + 1);
} else
return 1;
}
static int
IncMultiCounter(int *c, int n)
{
return RealIncMultiCounter(c, n, 0);
}
static int
figure_num_points(int d)
{
return 1 << d; /* (int)pow(2.0, (double)d); */
}
static int
figure_num_lines(int d)
{
return d * figure_num_points(d - 1);
}
static int
figure_num_planes(int d)
{
return ((d - 1) * d) / 2;
}
static void
free_hyperstuff(hyperstruct * hp)
{
if (hp->axis_points) {
free(hp->axis_points);
hp->axis_points = (int *) NULL;
}
if (hp->points) {
free(hp->points);
hp->points = (vector *) NULL;
}
if (hp->pointsleft) {
free(hp->pointsleft);
hp->pointsleft = (vector *) NULL;
}
if (hp->lines) {
XFree(hp->lines);
hp->lines = (line_segment *) NULL;
}
if (hp->planes) {
XFree(hp->planes);
hp->planes = (plane_section *) NULL;
}
if (hp->rotation_planes) {
XFree(hp->rotation_planes);
hp->rotation_planes = (XPoint *) NULL;
}
if (hp->rotations) {
free(hp->rotations);
hp->rotations = (double *) NULL;
}
if (hp->d_rotations) {
free(hp->d_rotations);
hp->d_rotations = (double *) NULL;
}
if (hp->dd_rotations) {
free(hp->dd_rotations);
hp->dd_rotations = (double *) NULL;
}
if (hp->cdd_rotations) {
free(hp->cdd_rotations);
hp->cdd_rotations = (int *) NULL;
}
if (hp->Trotations) {
free(hp->Trotations);
hp->Trotations = (matrix *) NULL;
}
if (hp->Trotationsleft) {
free(hp->Trotationsleft);
hp->Trotationsleft = (matrix *) NULL;
}
if (hp->Tall) {
free(hp->Tall);
hp->Tall = (matrix *) NULL;
}
if (hp->Tallleft) {
free(hp->Tallleft);
hp->Tallleft = (matrix *) NULL;
}
if (hp->xpoints[0]) {
XFree(hp->xpoints[0]);
hp->xpoints[0] = (XPoint *) NULL;
}
if (hp->xpoints[1]) {
XFree(hp->xpoints[1]);
hp->xpoints[1] = (XPoint *) NULL;
}
if (hp->xpointsleft[0]) {
XFree(hp->xpointsleft[0]);
hp->xpointsleft[0] = (XPoint *) NULL;
}
if (hp->xpointsleft[1]) {
XFree(hp->xpointsleft[1]);
hp->xpointsleft[1] = (XPoint *) NULL;
}
}
static void
free_hyper(Display *display, hyperstruct *hp)
{
if (hp->gc != None) {
XFreeGC(display, hp->gc);
hp->gc = None;
}
if (hp->font != None) {
XFreeFont(display, hp->font);
hp->font = None;
}
free_hyperstuff(hp);
}
static Bool
figure_points(ModeInfo * mi)
{
Display *display = MI_DISPLAY(mi);
hyperstruct *hp = &hypers[MI_SCREEN(mi)];
int i, j, k, n, d, pix = 0;
int *c;
/*
* First, figure out the points.
*/
hp->num_points = figure_num_points(hp->num_d);
#ifdef DEBUG
assert(hp->num_points <= hp->max_points);
#endif
allocarray(hp->points, vector, hp->num_points * hp->num_mat);
allocarray(hp->xpoints[0], XPoint, hp->num_points);
allocarray(hp->xpoints[1], XPoint, hp->num_points);
if (MI_IS_USE3D(mi)) {
allocarray(hp->pointsleft, vector, hp->num_points * hp->num_mat);
allocarray(hp->xpointsleft[0], XPoint, hp->num_points);
allocarray(hp->xpointsleft[1], XPoint, hp->num_points);
}
allocarray(c, int, hp->num_points); /* will be lost, sigh */
ZeroMultiCounter(c, hp->num_d);
n = 0;
do {
for (i = 0; i < hp->num_d; i++) {
hp->points[n * hp->num_mat + i] = c[i];
if (MI_IS_USE3D(mi))
hp->pointsleft[n * hp->num_mat + i] = c[i];
}
n++;
} while (IncMultiCounter(c, hp->num_d));
free(c);
#ifdef DEBUG
assert(hp->num_points == n);
#endif
/*
* Next connect them.
* We could do this more intelligently, but why bother?
*
* Connect points that differ by only one coordinate.
*/
if (MI_NPIXELS(mi) > 2)
pix = NRAND(MI_NPIXELS(mi));
hp->num_lines = figure_num_lines(hp->num_d);
#ifdef DEBUG
assert(hp->num_lines <= hp->max_lines);
#endif
allocarray(hp->lines, line_segment, hp->num_lines);
for (n = i = 0; i < hp->num_points; i++) {
for (j = i + 1; j < hp->num_points; j++) {
for (d = k = 0; k < hp->num_d; k++) {
if (hp->points[i * hp->num_mat + k] != hp->points[j * hp->num_mat + k])
d++;
}
if (d == 1) {
hp->lines[n].from = i;
hp->lines[n].to = j;
/* (void) printf ("from %x to %x ", i, j); */
if (MI_NPIXELS(mi) > 2) {
hp->lines[n].color = MI_PIXEL(mi, pix);
if (++pix >= MI_NPIXELS(mi))
pix = 0;
} else
hp->lines[n].color = MI_WHITE_PIXEL(mi);
n++;
}
}
}
#ifdef DEBUG
assert(hp->num_lines == n);
#endif
/*
* Now determine the planes of rotation.
*/
hp->num_planes = figure_num_planes(hp->num_d);
#ifdef DEBUG
assert(hp->num_planes <= max_planes);
#endif
hp->show_planes = show_planes;
if (hp->show_planes) {
allocarray(hp->planes, plane_section, hp->num_planes);
/* Keeping it simple and just drawing planes that touch the
* axes. Still not that simple, have to figure out which pt c is
* furthest away and draw it first... yuck.
*/
for (n = i = 0; i < hp->num_d; i++) {
for (j = i + 1; j < hp->num_d; j++) {
hp->planes[n].a = 0;
hp->planes[n].b = 1 << i;
hp->planes[n].d = 1 << j;
hp->planes[n].c = hp->planes[n].b + hp->planes[n].d;
/*(void) printf ("a %d, b %d, c %d, d %d\n",
0, 1 << i, (1 << i) + (1 << j), 1 << j);*/
if (MI_NPIXELS(mi) > 2) {
hp->planes[n].color = MI_PIXEL(mi, pix);
if (++pix >= MI_NPIXELS(mi))
pix = 0;
} else
hp->planes[n].color = MI_WHITE_PIXEL(mi);
n++;
}
}
}
allocarray(hp->axis_points, int, hp->num_d + 1);
allocarray(hp->rotations, double, hp->num_planes);
callocarray(hp->d_rotations, double, hp->num_planes);
callocarray(hp->dd_rotations, double, hp->num_planes);
callocarray(hp->cdd_rotations, int, hp->num_planes);
allocarray(hp->Trotations, matrix, hp->num_planes * hp->num_matmat);
allocarray(hp->Tall, matrix, hp->num_matmat);
if (MI_IS_USE3D(mi)) {
allocarray(hp->Trotationsleft, matrix, hp->num_planes * hp->num_matmat);
allocarray(hp->Tallleft, matrix, hp->num_matmat);
}
allocarray(hp->rotation_planes, XPoint, hp->num_planes);
for (n = i = 0; i < hp->num_d; i++)
for (j = i + 1; j < hp->num_d; j++) {
hp->rotation_planes[n].x = i;
hp->rotation_planes[n].y = j;
n++;
}
#ifdef DEBUG
assert(hp->num_planes == n);
#endif
/*
* Potential random initial rotations.
*/
#define FRAC (1024*16)
if (random_start) {
for (i = 0; i < hp->num_planes; i++)
hp->rotations[i] = 2.0 * NRAND(FRAC) * M_PI / FRAC;
}
return True;
}
static void
figure_axis_points(hyperstruct * hp)
{
int i, j, num_set;
hp->show_axes = show_axes;
for (hp->num_axis_points = i = 0; i < hp->num_points; i++) {
for (num_set = j = 0; j < hp->num_d; j++)
if (hp->points[i * hp->num_mat + j] != 0.0)
num_set++;
if (num_set <= 1)
hp->axis_points[hp->num_axis_points++] = i;
}
}
static Bool
init_x_stuff(ModeInfo * mi)
{
hyperstruct *hp = &hypers[MI_SCREEN(mi)];
XGCValues gcv;
hp->maxx = MI_WIDTH(mi);
hp->maxy = MI_HEIGHT(mi);
hp->spinDelay = 10 * spin_delay;
if (hp->spinDelay < 0)
hp->spinDelay = 0;
free_hyperstuff(hp);
hp->num_d = MI_COUNT(mi);
if (hp->num_d < -MAX_D)
hp->num_d = -MAX_D;
if (hp->num_d > MAX_D)
hp->num_d = MAX_D;
else if (hp->num_d < -MIN_D) {
hp->num_d = NRAND(-hp->num_d - MIN_D + 1) + MIN_D;
} else if (hp->num_d < MIN_D)
hp->num_d = MIN_D;
hp->num_mat = hp->num_d + 1;
hp->num_matmat = hp->num_mat * hp->num_mat;
if (hp->font == None) {
if ((hp->font = getFont(MI_DISPLAY(mi))) == None)
return False;
}
if (MI_NPIXELS(mi) <= 2 || MI_IS_USE3D(mi))
hp->normxor = False;
if ((hp->gc == None) && (MI_NPIXELS(mi) <= 2 || !MI_IS_USE3D(mi))) {
long options;
if (hp->normxor) {
options = GCForeground | GCFont | GCFunction;
gcv.foreground = MI_WHITE_PIXEL(mi) ^ MI_BLACK_PIXEL(mi);
gcv.function = GXxor;
} else {
options = GCForeground | GCBackground | GCFont;
gcv.foreground = MI_WHITE_PIXEL(mi);
gcv.background = MI_BLACK_PIXEL(mi);
}
gcv.font = hp->font->fid;
if ((hp->gc = XCreateGC(MI_DISPLAY(mi), MI_WINDOW(mi), options,
&gcv)) == None)
return False;
}
return True;
}
static void
move_line(ModeInfo * mi, int from, int to, int set, long color)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
GC gc = MI_GC(mi);
hyperstruct *hp = &hypers[MI_SCREEN(mi)];
if (MI_NPIXELS(mi) <= 2 || !MI_IS_USE3D(mi))
gc = hp->gc;
if (hp->normxor) {
XSetForeground(display, gc, color);
if (!hp->redrawing)
XDrawLine(display, window, gc,
hp->xpoints[!set][from].x, hp->xpoints[!set][from].y,
hp->xpoints[!set][to].x, hp->xpoints[!set][to].y);
XDrawLine(display, window, gc,
hp->xpoints[set][from].x, hp->xpoints[set][from].y,
hp->xpoints[set][to].x, hp->xpoints[set][to].y);
} else {
if (!hp->redrawing) {
if (MI_IS_INSTALL(mi) && MI_IS_USE3D(mi))
XSetForeground(display, gc, MI_NONE_COLOR(mi));
else
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
XDrawLine(display, window, gc,
hp->xpoints[set][from].x, hp->xpoints[set][from].y,
hp->xpoints[set][to].x, hp->xpoints[set][to].y);
if (MI_IS_USE3D(mi))
XDrawLine(display, window, gc,
hp->xpointsleft[set][from].x, hp->xpointsleft[set][from].y,
hp->xpointsleft[set][to].x, hp->xpointsleft[set][to].y);
}
if (MI_IS_USE3D(mi)) {
if (MI_IS_INSTALL(mi)) {
XSetFunction(display, gc, GXor);
}
XSetForeground(display, gc, MI_LEFT_COLOR(mi));
} else if (MI_NPIXELS(mi) <= 2)
XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
else
XSetForeground(display, gc, color);
XDrawLine(display, window, gc,
hp->xpoints[!set][from].x, hp->xpoints[!set][from].y,
hp->xpoints[!set][to].x, hp->xpoints[!set][to].y);
if (MI_IS_USE3D(mi)) {
XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
XDrawLine(display, window, gc,
hp->xpointsleft[!set][from].x, hp->xpointsleft[!set][from].y,
hp->xpointsleft[!set][to].x, hp->xpointsleft[!set][to].y);
if (MI_IS_INSTALL(mi)) {
XSetFunction(display, gc, GXcopy);
}
}
}
}
static void
move_plane(ModeInfo * mi, int a, int b, int c, int d, int set, long color)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
GC gc = MI_GC(mi);
hyperstruct *hp = &hypers[MI_SCREEN(mi)];
XPoint rect[4];
if (MI_NPIXELS(mi) <= 2 || !MI_IS_USE3D(mi))
gc = hp->gc;
if (hp->normxor) {
XSetForeground(display, gc, color);
if (!hp->redrawing) {
rect[0].x = hp->xpoints[!set][a].x;
rect[0].y = hp->xpoints[!set][a].y;
rect[1].x = hp->xpoints[!set][b].x;
rect[1].y = hp->xpoints[!set][b].y;
rect[2].x = hp->xpoints[!set][c].x;
rect[2].y = hp->xpoints[!set][c].y;
rect[3].x = hp->xpoints[!set][d].x;
rect[3].y = hp->xpoints[!set][d].y;
XFillPolygon(display, window, gc, rect, 4, Convex, CoordModeOrigin);
}
rect[0].x = hp->xpoints[set][a].x;
rect[0].y = hp->xpoints[set][a].y;
rect[1].x = hp->xpoints[set][b].x;
rect[1].y = hp->xpoints[set][b].y;
rect[2].x = hp->xpoints[set][c].x;
rect[2].y = hp->xpoints[set][c].y;
rect[3].x = hp->xpoints[set][d].x;
rect[3].y = hp->xpoints[set][d].y;
XFillPolygon(display, window, gc, rect, 4, Convex, CoordModeOrigin);
} else {
if (!hp->redrawing) {
if (MI_IS_INSTALL(mi) && MI_IS_USE3D(mi))
XSetForeground(display, gc, MI_NONE_COLOR(mi));
else
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
rect[0].x = hp->xpoints[set][a].x;
rect[0].y = hp->xpoints[set][a].y;
rect[1].x = hp->xpoints[set][b].x;
rect[1].y = hp->xpoints[set][b].y;
rect[2].x = hp->xpoints[set][c].x;
rect[2].y = hp->xpoints[set][c].y;
rect[3].x = hp->xpoints[set][d].x;
rect[3].y = hp->xpoints[set][d].y;
XFillPolygon(display, window, gc, rect, 4, Convex, CoordModeOrigin);
if (MI_IS_USE3D(mi)) {
rect[0].x = hp->xpointsleft[set][a].x;
rect[0].y = hp->xpointsleft[set][a].y;
rect[1].x = hp->xpointsleft[set][b].x;
rect[1].y = hp->xpointsleft[set][b].y;
rect[2].x = hp->xpointsleft[set][c].x;
rect[2].y = hp->xpointsleft[set][c].y;
rect[3].x = hp->xpointsleft[set][d].x;
rect[3].y = hp->xpointsleft[set][d].y;
XFillPolygon(display, window, gc, rect, 4, Convex, CoordModeOrigin);
}
}
if (MI_IS_USE3D(mi)) {
if (MI_IS_INSTALL(mi)) {
XSetFunction(display, gc, GXor);
}
XSetForeground(display, gc, MI_LEFT_COLOR(mi));
} else if (MI_NPIXELS(mi) <= 2)
XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
else
XSetForeground(display, gc, color);
rect[0].x = hp->xpoints[!set][a].x;
rect[0].y = hp->xpoints[!set][a].y;
rect[1].x = hp->xpoints[!set][b].x;
rect[1].y = hp->xpoints[!set][b].y;
rect[2].x = hp->xpoints[!set][c].x;
rect[2].y = hp->xpoints[!set][c].y;
rect[3].x = hp->xpoints[!set][d].x;
rect[3].y = hp->xpoints[!set][d].y;
XFillPolygon(display, window, gc, rect, 4, Convex, CoordModeOrigin);
if (MI_IS_USE3D(mi)) {
XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
rect[0].x = hp->xpointsleft[!set][a].x;
rect[0].y = hp->xpointsleft[!set][a].y;
rect[1].x = hp->xpointsleft[!set][b].x;
rect[1].y = hp->xpointsleft[!set][b].y;
rect[2].x = hp->xpointsleft[!set][c].x;
rect[2].y = hp->xpointsleft[!set][c].y;
rect[3].x = hp->xpointsleft[!set][d].x;
rect[3].y = hp->xpointsleft[!set][d].y;
XFillPolygon(display, window, gc, rect, 4, Convex, CoordModeOrigin);
if (MI_IS_INSTALL(mi)) {
XSetFunction(display, gc, GXcopy);
}
}
}
}
static void
move_number(ModeInfo * mi, int axis, char *string, int set, long color)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
GC gc = MI_GC(mi);
hyperstruct *hp = &hypers[MI_SCREEN(mi)];
if (MI_NPIXELS(mi) <= 2 || !MI_IS_USE3D(mi))
gc = hp->gc;
if (hp->normxor) {
XSetForeground(display, gc, color);
if (!hp->redrawing)
(void) XDrawString(display, window, gc,
hp->xpoints[!set][axis].x, hp->xpoints[!set][axis].y,
string, strlen(string));
(void) XDrawString(display, window, gc,
hp->xpoints[set][axis].x, hp->xpoints[set][axis].y,
string, strlen(string));
} else {
if (!hp->redrawing) {
if (MI_IS_INSTALL(mi) && MI_IS_USE3D(mi))
XSetForeground(display, gc, MI_NONE_COLOR(mi));
else
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
(void) XDrawString(display, window, gc,
hp->xpoints[set][axis].x, hp->xpoints[set][axis].y,
string, strlen(string));
if (MI_IS_USE3D(mi))
(void) XDrawString(display, window, gc,
hp->xpointsleft[set][axis].x, hp->xpointsleft[set][axis].y,
string, strlen(string));
}
if (MI_IS_USE3D(mi)) {
if (MI_IS_INSTALL(mi)) {
XSetFunction(display, gc, GXor);
}
XSetForeground(display, gc, MI_LEFT_COLOR(mi));
} else if (MI_NPIXELS(mi) <= 2)
XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
else
XSetForeground(display, gc, color);
(void) XDrawString(display, window, gc,
hp->xpoints[!set][axis].x, hp->xpoints[!set][axis].y,
string, strlen(string));
if (MI_IS_USE3D(mi)) {
XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
(void) XDrawString(display, window, gc,
hp->xpointsleft[!set][axis].x, hp->xpointsleft[!set][axis].y,
string, strlen(string));
if (MI_IS_INSTALL(mi)) {
XSetFunction(display, gc, GXcopy);
}
}
}
}
static void
draw_hyper_step(ModeInfo * mi, int set)
{
hyperstruct *hp = &hypers[MI_SCREEN(mi)];
int i;
char tmps[3];
MI_IS_DRAWN(mi) = True;
if (!hp->stationary || hp->redrawing) {
for (i = 0; i < hp->num_lines; i++) {
move_line(mi, hp->lines[i].from, hp->lines[i].to,
set, hp->lines[i].color);
}
if (hp->show_planes) {
for (i = 0; i < hp->num_planes; i++) {
move_plane(mi, hp->planes[i].a, hp->planes[i].b, hp->planes[i].c,
hp->planes[i].d, set, hp->planes[i].color);
}
}
if (hp->show_axes) {
for (i = 0; i < hp->num_axis_points; i++) {
(void) sprintf(tmps, "%d", i);
move_number(mi, hp->axis_points[i], tmps, set, MI_WHITE_PIXEL(mi));
}
}
}
}
static void
move_hyper(ModeInfo * mi)
{
hyperstruct *hp = &hypers[MI_SCREEN(mi)];
int i;
/* NEEDSWORK: These should be resources */
#define default_cdd_rotation 10
#define default_dd_rotation (M_PI/1024.0)
int axis;
int faster;
hp->stationary = False;
if (hp->spinDelay-- <= 0) {
hp->spinDelay = 10 * spin_delay;
/*
* Change rotation?
*
* 66% chance if stationary, 33% if not.
*/
hp->stationary = True;
for (i = 0; i < hp->num_planes; i++)
if (hp->d_rotations[i] != 0.0 || hp->cdd_rotations[i]) {
hp->stationary = False;
break;
}
if (NRAND(3) < 1 + hp->stationary) {
/* Change! But what? */
axis = NRAND(hp->num_planes);
/*
* And how much? 33% chance faster, 66% slower.
* If stopped, slower doesn't start it moving
* unless we're stationary.
*/
hp->cdd_rotations[axis] = default_cdd_rotation +
NRAND(7) - 3;
faster = (NRAND(3) < 1);
if (faster || hp->dd_rotations[axis] != 0.0 || hp->stationary)
hp->dd_rotations[axis] = default_dd_rotation *
(hp->dd_rotations[axis] >= 0.0 ? 1 : -1) *
(faster ? 1 : -1);
if (MI_IS_DEBUG(mi))
(void) fprintf(stderr,
"axis %d made %s\n", axis, faster ? "faster" : "slower");
}
}
/*
* Rotate.
*/
for (i = 0; i < hp->num_planes; i++) {
if (hp->cdd_rotations[i]) {
hp->cdd_rotations[i]--;
hp->d_rotations[i] += hp->dd_rotations[i];
}
hp->rotations[i] += hp->d_rotations[i];
}
}
static Bool
calc_transformation(ModeInfo * mi)
{
hyperstruct *hp = &hypers[MI_SCREEN(mi)];
double cosa, sina;
double cosb = 0.0, sinb = 0.0;
int p1, p2;
matrix *Ttmp;
matrix *Tpre, *Tuser, *Tuserleft = (matrix *) NULL;
matrix *Tpretranspose, *Tscale, *Tposttranspose;
int i;
dpoint scale, range, offset;
double scale_used;
Ttmp = (matrix *) malloc(hp->num_matmat * sizeof (matrix));
Tpre = (matrix *) malloc(hp->num_matmat * sizeof (matrix));
Tuser = (matrix *) malloc(hp->num_matmat * sizeof (matrix));
Tpretranspose = (matrix *) malloc(hp->num_matmat * sizeof (matrix));
Tscale = (matrix *) malloc(hp->num_matmat * sizeof (matrix));
Tposttranspose = (matrix *) malloc(hp->num_matmat * sizeof (matrix));
if ((Ttmp == NULL) || (Tpre == NULL) || (Tuser == NULL) ||
(Tpretranspose == NULL) || (Tscale == NULL) ||
(Tposttranspose == NULL)) {
if (Ttmp == NULL)
free(Ttmp);
if (Tpre == NULL)
free(Tpre);
if (Tuser == NULL)
free(Tuser);
if (Tpretranspose == NULL)
free(Tpretranspose);
if (Tscale == NULL)
free(Tscale);
if (Tposttranspose == NULL)
free(Tposttranspose);
return False;
}
if (MI_IS_USE3D(mi)) {
if ((Tuserleft = (matrix *) malloc(hp->num_matmat *
sizeof (matrix))) == NULL) {
free(Ttmp);
free(Tpre);
free(Tuser);
free(Tpretranspose);
free(Tscale);
free(Tposttranspose);
return False;
}
}
/*
* Adjust the data.
* Since the data goes from 0->1 on each axis,
* we shift it by -0.5 here to center the figure.
*/
MatIdent(Tpre, hp->num_mat);
for (i = 0; i < hp->num_d; i++) {
Tpre[i * hp->num_mat + hp->num_d] = -0.5;
}
/*
* Figure the rotation.
*/
MatIdent(Tuser, hp->num_mat);
if (MI_IS_USE3D(mi)) {
MatIdent(Tuserleft, hp->num_mat);
}
for (i = 0; i < hp->num_planes; i++) {
p1 = hp->rotation_planes[i].x;
p2 = hp->rotation_planes[i].y;
if (MI_IS_USE3D(mi)) {
sinb = sin(hp->rotations[i] - DELDEG);
cosb = cos(hp->rotations[i] - DELDEG);
sina = sin(hp->rotations[i] + DELDEG);
cosa = cos(hp->rotations[i] + DELDEG);
} else {
sina = sin(hp->rotations[i]);
cosa = cos(hp->rotations[i]);
}
MatIdent(&hp->Trotations[i * hp->num_matmat], hp->num_mat);
hp->Trotations[i * hp->num_matmat + p1 * hp->num_mat + p1] =
hp->Trotations[i * hp->num_matmat + p2 * hp->num_mat + p2] = cosa;
hp->Trotations[i * hp->num_matmat + p1 * hp->num_mat + p2] = sina;
hp->Trotations[i * hp->num_matmat + p2 * hp->num_mat + p1] = -sina;
MatMult(&hp->Trotations[i * hp->num_matmat], Tuser, Ttmp, hp->num_mat);
MatCopy(Ttmp, Tuser, hp->num_mat);
if (MI_IS_USE3D(mi)) {
MatIdent(&hp->Trotationsleft[i * hp->num_matmat], hp->num_mat);
hp->Trotationsleft[i * hp->num_matmat + p1 * hp->num_mat + p1] =
hp->Trotationsleft[i * hp->num_matmat + p2 * hp->num_mat + p2] = cosb;
hp->Trotationsleft[i * hp->num_matmat + p1 * hp->num_mat + p2] = sinb;
hp->Trotationsleft[i * hp->num_matmat + p2 * hp->num_mat + p1] = -sinb;
MatMult(&hp->Trotationsleft[i * hp->num_matmat], Tuserleft, Ttmp, hp->num_mat);
MatCopy(Ttmp, Tuserleft, hp->num_mat);
}
}
/* Now calculate the scaling matrix */
#if 1
/*
* Calculate the best scale of the two axes.
* Multiply by 0.9 to use 90% of the display.
* Divide by the sqrt(2.0) because it's biggest when
* rotated by 45 degrees.
*
* This principle generalizes to sqrt(hp->num_d).
*/
#define border_width (0.05)
range.x = sqrt((double) hp->num_d);
range.y = range.x;
scale.x = (1.0 - 2 * border_width) * hp->maxx / range.x;
scale.y = (1.0 - 2 * border_width) * hp->maxy / range.y;
scale_used = ((scale.x < scale.y) ? scale.x : scale.y);
offset.x = hp->maxx / 2.0;
offset.y = hp->maxy / 2.0;
/*
* Setup & compute the matricies
*/
MatIdent(Tpretranspose, hp->num_mat);
Tpretranspose[0 * hp->num_mat + hp->num_d] = 0;
Tpretranspose[1 * hp->num_mat + hp->num_d] = 0;
MatIdent(Tscale, hp->num_mat);
Tscale[0 * hp->num_mat + 0] = scale_used;
Tscale[1 * hp->num_mat + 1] = -scale_used;
MatIdent(Tposttranspose, hp->num_mat);
Tposttranspose[0 * hp->num_mat + hp->num_d] = offset.x;
Tposttranspose[1 * hp->num_mat + hp->num_d] = offset.y;
MatMult(Tscale, Tpretranspose, Ttmp, hp->num_mat);
MatMult(Tposttranspose, Ttmp, Tscale, hp->num_mat);
#else
MatIdent(Tscale, hp->num_mat);
#endif
free(Tpretranspose);
free(Tposttranspose);
/*
* Put it all together.
*/
MatMult(Tuser, Tpre, Ttmp, hp->num_mat);
MatMult(Tscale, Ttmp, hp->Tall, hp->num_mat);
free(Tuser);
if (MI_IS_USE3D(mi)) {
MatMult(Tuserleft, Tpre, Ttmp, hp->num_mat);
MatMult(Tscale, Ttmp, hp->Tallleft, hp->num_mat);
free(Tuserleft);
}
free(Tpre);
free(Ttmp);
free(Tscale);
return True;
}
static Bool
translate_point(hyperstruct * hp, matrix * Tall, vector * real, XPoint * screen_image)
{
vector *image;
if ((image = (vector *) malloc(hp->num_mat * sizeof (vector))) == NULL)
return False;
MatVecMult(Tall, real, image, hp->num_mat);
screen_image->x = (short) image[0];
screen_image->y = (short) image[1];
free(image);
return True;
}
static Bool
translate_points(ModeInfo * mi, int set)
{
hyperstruct *hp = &hypers[MI_SCREEN(mi)];
int i;
if (!calc_transformation(mi))
return False;
for (i = 0; i < hp->num_points; i++) {
if (!translate_point(hp, hp->Tall, &hp->points[i * hp->num_mat],
&hp->xpoints[set][i]))
return False;
if (MI_IS_USE3D(mi)) {
if (!translate_point(hp, hp->Tallleft,
&hp->pointsleft[i * hp->num_mat],
&hp->xpointsleft[set][i]))
return False;
}
}
return True;
}
void
refresh_hyper(ModeInfo * mi)
{
hyperstruct *hp = &hypers[MI_SCREEN(mi)];
if (!hp->painted) {
MI_CLEARWINDOW(mi);
hp->redrawing = True;
draw_hyper_step(mi, hp->this_set);
hp->redrawing = False;
hp->painted = True;
}
}
void
init_hyper(ModeInfo * mi)
{
Display *display = MI_DISPLAY(mi);
int i;
hyperstruct *hp;
if (hypers == NULL) {
if ((hypers = (hyperstruct *) calloc(MI_NUM_SCREENS(mi),
sizeof (hyperstruct))) == NULL)
return;
}
hp = &hypers[MI_SCREEN(mi)];
if (!init_x_stuff(mi)) {
free_hyper(display, hp);
return;
}
if (!figure_points(mi)) {
free_hyper(display, hp);
return;
}
/*
* Fix the d+1 coord of all points.
*/
for (i = 0; i < hp->num_points; i++) {
hp->points[i * hp->num_mat + hp->num_d] = 1;
if (MI_IS_USE3D(mi))
hp->pointsleft[i * hp->num_mat + hp->num_d] = 1;
}
figure_axis_points(hp);
hp->this_set = 0;
if (!translate_points(mi, !hp->this_set)) {
free_hyper(display, hp);
return;
}
if (!translate_points(mi, hp->this_set)) {
free_hyper(display, hp);
return;
}
refresh_hyper(mi);
hp->painted = True;
hp->stationary = True;
}
void
draw_hyper(ModeInfo * mi)
{
hyperstruct *hp;
if (hypers == NULL)
return;
hp = &hypers[MI_SCREEN(mi)];
if (hp->axis_points == NULL)
return;
hp->painted = False;
draw_hyper_step(mi, hp->this_set);
/* Set up next place */
move_hyper(mi);
if (!translate_points(mi, hp->this_set)) {
free_hyper(MI_DISPLAY(mi), hp);
return;
}
if (!hp->stationary)
hp->this_set = !hp->this_set;
}
void
change_hyper(ModeInfo * mi)
{
hyperstruct *hp = &hypers[MI_SCREEN(mi)];
/* make it change */
hp->spinDelay = 0;
}
void
release_hyper(ModeInfo * mi)
{
if (hypers != NULL) {
int screen;
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
free_hyper(MI_DISPLAY(mi), &hypers[screen]);
free(hypers);
hypers = (hyperstruct *) NULL;
}
}
#endif /* MODE_hyper */