Edit

IABSD.fr/xenocara/app/xlockmore/modes/hyper.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2006-11-26 11:07:42
    Hash : 110b2a92
    Message : Importing xlockmore 5.22

  • app/xlockmore/modes/hyper.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 */