Edit

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

Branch :

  • Show log

    Commit

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

  • app/xlockmore/modes/scooter.c
  • /* -*- Mode: C; tab-width: 4 -*- */
    /* scooter -- a journey through space tunnel and stars */
    
    #if !defined( lint ) && !defined( SABER )
    static const char sccsid[] = "@(#)scooter.c	5.01 2001/03/02 xlockmore";
    
    #endif
    
    /*
     *  scooter.c
     *
     *  Copyright (c) 2001 Sven Thoennissen <posse@gmx.net>
     *
     *  This program is based on the original "scooter", a blanker module from the
     *  Nightshift screensaver which is part of EGS (Enhanced Graphics System) on
     *  the Amiga computer. EGS has been developed by VIONA Development.
     *
     *
     *  (now the obligatory stuff)
     *
     *  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.
     *
     */
    
    #ifdef STANDALONE
    #define MODE_scooter
    #define PROGCLASS "Scooter"
    #define HACK_INIT init_scooter
    #define HACK_DRAW draw_scooter
    #define scooter_opts xlockmore_opts
    #define DEFAULTS "*delay: 20000 \n" \
     "*count: 24 \n" \
     "*cycles: 5 \n" \
     "*size: 100 \n" \
     "*ncolors: 200 \n" \
     "*fullrandom: True \n" \
     "*verbose: False \n"
    #include "xlockmore.h"          /* in xscreensaver distribution */
    #else /* STANDALONE */
    #include "xlock.h"              /* in xlockmore distribution */
    #endif /* STANDALONE */
    
    #ifdef MODE_scooter
    
    ModeSpecOpt scooter_opts =
    {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
    
    #ifdef USE_MODULES
    ModStruct scooter_description =
    {"scooter", "init_scooter", "draw_scooter", "release_scooter",
     "refresh_scooter", "change_scooter", (char *) NULL, &scooter_opts,
     20000, 24, 5, 100, 64, 1.0, "",
     "Shows a journey through space tunnel and stars", 0, NULL};
    /*
     *  count = number of doors
     *  cycles = speed (see MIN/MAX_SPEED below)
     *  size = number of stars
     *
     */
    #endif
    
    typedef struct {
    	int x, y, z;
    } Vec3D;
    
    typedef struct {
    	int x, y, z;
    } Angle3D;
    
    typedef struct {
    	int r, g, b;
    } ColorRGB;
    
    typedef struct {
    	XPoint lefttop, rightbottom;
    } Rect;
    
    typedef struct {
    	Vec3D coords[4]; /* lefttop, righttop, rightbottom, leftbottom */
    	int zelement;
    	unsigned long color;
    	char freecolor;
    	char pad;
    } Door;
    
    typedef struct {
    	int xpos, ypos;
    	int width, height;
    	int zelement;
    	short draw;
    } Star;
    
    /* define this to see a pixel for each zelement */
    /*
    #define _DRAW_ZELEMENTS
    */
    
    typedef struct {
    	Vec3D pos;
    	Angle3D angle;
    } ZElement;
    
    typedef struct {
    	Star *stars;
    	Door *doors;
    	ZElement *zelements;
    	int doorcount, ztotal, speed;
    	int zelements_per_door, zelement_distance, spectator_zelement;
    	int projnorm_z, rotationDuration, rotationStep, starcount;
    	Angle3D currentRotation, rotationDelta;
    
    	/* doors color cycling stuff */
    	ColorRGB begincolor, endcolor;
    	int colorcount, colorsteps;
    
    	/* scale all stars and doors to window dimensions */
    	float aspect_scale;
    
    	Bool halt_scooter;
    } scooterstruct;
    
    static scooterstruct *scooters = (scooterstruct *) NULL;
    
    #define MIN_DOORS 4
    
    #define MIN_SPEED 1
    #define MAX_SPEED 10
    
    #define SPACE_XY_FACTOR 10
    
    #define DOOR_WIDTH  (600*SPACE_XY_FACTOR)
    #define DOOR_HEIGHT (400*SPACE_XY_FACTOR)
    
    /* stars distance from doors center */
    #define STAR_MIN_X  (1000*SPACE_XY_FACTOR)
    #define STAR_MIN_Y  (750*SPACE_XY_FACTOR)
    #define STAR_MAX_X  (10000*SPACE_XY_FACTOR)
    #define STAR_MAX_Y  (7500*SPACE_XY_FACTOR)
    
    /* star size (random) */
    #define STAR_SIZE_MIN (2*SPACE_XY_FACTOR)
    #define STAR_SIZE_MAX (64*SPACE_XY_FACTOR)
    
    /* greater values make scooter run harder curves, smaller values produce calm curves */
    #define DOOR_CURVEDNESS 14
    
    /* 3d->2d projection (greater values create more fish-eye effect) */
    #define PROJECTION_DEGREE 2.4
    
    /*  this is my resolution at which scooter is in its original size, producing a 4:3 aspect ratio.
     *  all variables in this module are adjusted for this screen size; if scooter is run
     *  in windows with different size, it knows how to rescale itself.
     */
    #define ASPECT_SCREENWIDTH 1152
    #define ASPECT_SCREENHEIGHT 864
    
    /* we define our own sin/cos macros to be faaast ;-) (good old Amiga times) */
    #define SINUSTABLE_SIZE 0x8000
    #define SINUSTABLE_MASK 0x7fff
    #define SIN(a) sintable[a & SINUSTABLE_MASK]
    #define COS(a) sintable[(a+(SINUSTABLE_SIZE/4)) & SINUSTABLE_MASK]
    
    /* signum function */
    #define SGN(a) (a < 0 ? -1 : 1)
    
    static float *sintable = (float *) NULL;
    
    static void
    randomcolor(ColorRGB *col)
    {
    	unsigned long n;
    
    /*	col->r = NRAND(65536);
    	col->g = NRAND(65536);
    	col->b = NRAND(65536);
    */
    /*	col->r = LRAND() & 0xffff;
    	col->g = LRAND() & 0xffff;
    	col->b = LRAND() & 0xffff;
    */
    	/* this seems best */
    	n = NRAND(0x1000000);
    	col->r = (n>>16)<<8;
    	col->g = ((n>>8) & 0xff)<<8;
    	col->b = (n & 0xff)<<8;
    }
    
    static void
    initdoorcolors(scooterstruct *sp)
    {
    	/* prepare initial values for nextdoorcolor() */
    
    	randomcolor(&sp->endcolor);
    
    	sp->colorcount = 0;
    	sp->colorsteps = 0;
    }
    
    static void nextdoorcolor(ModeInfo *mi, Door *door)
    {
    	scooterstruct *sp = &scooters[MI_SCREEN(mi)];
    	Display *display = MI_DISPLAY(mi);
    	XColor xcol;
    
    /* uncomment this to color the doors from xlock's palette (created with saturation value) */
    #if 0
    	if (MI_NPIXELS(mi) > 2) {
    		if (++colorcount >= MI_NPIXELS(mi))
    			colorcount = 0;
    		door->color = MI_PIXEL(mi,colorcount);
    	} else
    		door->color = MI_WHITE_PIXEL(mi);
    	return;
    #endif
    	if (door->freecolor) {
    		XFreeColors(display, MI_COLORMAP(mi), &(door->color), 1, 0);
    		door->freecolor = 0;
    	}
    	if (MI_NPIXELS(mi) <= 2) {
    		door->color = MI_WHITE_PIXEL(mi);
    		return;
    	}
    
    	if (sp->colorcount >= sp->colorsteps) {
    
    		/* init next color ramp */
    
    		sp->colorcount = 0;
    		sp->colorsteps = 8 + NRAND(32);
    		sp->begincolor = sp->endcolor;
    		randomcolor(&sp->endcolor);
    	}
    
    	/* compute next color values */
    
    	xcol.red   = sp->begincolor.r + ((sp-> endcolor.r - sp->begincolor.r) *
    		sp->colorcount / sp->colorsteps);
    	xcol.green = sp->begincolor.g + ((sp-> endcolor.g - sp->begincolor.g) *
    		sp->colorcount / sp->colorsteps);
    	xcol.blue  = sp->begincolor.b + ((sp-> endcolor.b - sp->begincolor.b) *
    		sp->colorcount / sp->colorsteps);
    	xcol.pixel = 0;
    	xcol.flags = DoRed | DoGreen | DoBlue;
    
    	sp->colorcount++;
    
    	if (!XAllocColor(display, MI_COLORMAP(mi), &xcol)) {
    		 /* fail safe */
    		door->color = MI_WHITE_PIXEL(mi);
    		door->freecolor = 0;
    	} else {
    		door->color = xcol.pixel;
    		door->freecolor = 1;
    	}
    }
    
    static void
    free_scooter(scooterstruct *sp)
    {
    	if (sp->doors != NULL) {
    		free(sp->doors);
    		sp->doors = (Door *) NULL;
    	}
    	if (sp->stars != NULL) {
    		free(sp->stars);
    		sp->stars = (Star *) NULL;
    	}
    	if (sp->zelements != NULL) {
    		free(sp->zelements);
    		sp->zelements = (ZElement *) NULL;
    	}
    }
    
    void release_scooter(ModeInfo *mi)
    {
            if (scooters != NULL) {
                    int         screen;
    
                    for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
                            free_scooter(&scooters[screen]);
                    free(scooters);
                    scooters = (scooterstruct *) NULL;
            }
    	if (sintable != NULL) {
    		free(sintable);
    		sintable = (float *) NULL;
    	}
    }
    
    void init_scooter(ModeInfo *mi)
    {
    	int i;
    	scooterstruct *sp;
    
    	if (scooters == NULL) {
    		if ((scooters = (scooterstruct *) calloc(MI_NUM_SCREENS(mi),
    				sizeof (scooterstruct))) == NULL)
    			return;
    	}
    	sp = &scooters[MI_SCREEN(mi)];
    
    	sp->doorcount = MAX(MI_COUNT(mi),MIN_DOORS);
    	sp->speed = MI_CYCLES(mi);
    	sp->starcount = MI_SIZE(mi);
    	if (sp->starcount < 1)
    		sp->starcount = 1;
    	if (sp->speed < MIN_SPEED)
    		sp->speed = MIN_SPEED;
    	if (sp->speed > MAX_SPEED)
    		sp->speed = MAX_SPEED;
    	sp->zelements_per_door = 60;
    	sp->zelement_distance = 300;
    	sp->ztotal = sp->doorcount * sp->zelements_per_door;
    	/* sp->z_maxdepth = sp->ztotal * sp->zelement_distance; */
    	if (sp->starcount > sp->ztotal)
    		sp->starcount = sp->ztotal;
    
    	sp->halt_scooter = False;
    
    	initdoorcolors(sp);
    
    	free_scooter(sp);
    	if ((sintable = (float *) malloc(sizeof(float) *
    			SINUSTABLE_SIZE)) == NULL) {
    		release_scooter(mi);
    		return;
    	}
    	for (i = 0; i < SINUSTABLE_SIZE; i++) {
    		sintable[i] = SINF(M_PI*2/SINUSTABLE_SIZE*i);
    	}
    
    	if ((sp->doors = (Door *) malloc(sizeof(Door) *
    			sp->doorcount)) == NULL) {
    		return;
    	}
    
    	if ((sp->zelements = (ZElement *) malloc(sizeof(ZElement) *
    		 sp->ztotal)) == NULL) {
    		free_scooter(sp);
    		return;
    	}
    	for (i = 0; i <  sp->doorcount; i++) {
    		sp->doors[i].zelement =
    			(sp->zelements_per_door * (i + 1)) - 1;
    		sp->doors[i].freecolor = 0;
    		nextdoorcolor(mi, &sp->doors[i]);
    	}
    
    	for (i = 0; i < sp->ztotal; i++) {
    		sp->zelements[i].angle.x = 0;
    		sp->zelements[i].angle.y = 0;
    		sp->zelements[i].angle.z = 0;
    	}
    
    	if ((sp->stars = (Star *) malloc(sizeof(Star) *
    			sp->starcount)) == NULL) {
    		free_scooter(sp);
    		return;
    	}
    	for (i = 0; i < sp->starcount; i++) {
    		sp->stars[i].zelement = sp->ztotal * i / sp->starcount;
    		sp->stars[i].draw = 0;
    	}
    
    	sp->projnorm_z = 50 * 240;
    	sp->spectator_zelement = sp->zelements_per_door;
    
    	sp->currentRotation.x = 0;
    	sp->currentRotation.y = 0;
    	sp->currentRotation.z = 0;
    
    	sp->rotationDelta.x = 0;
    	sp->rotationDelta.y = 0;
    	sp->rotationDelta.z = 0;
    
    	sp->rotationDuration = 1;
    	sp->rotationStep = 0;
    }
    
    static void cleardoors(ModeInfo *mi)
    {
    	MI_CLEARWINDOW(mi);
    }
    
    /* Should be taken care of already... but just in case */
    #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
    #undef inline
    #define inline                  /* */
    #endif
    static inline float
    projection (scooterstruct *sp, int zval)
    {
    	return (sp->projnorm_z / (PROJECTION_DEGREE * zval));
    /* this is another formula. it is not limited to z>0 but it pulls too strong towards the screen center */
    /*      return (sp->projnorm_z * pow(1.22,-(zval/200*PROJ_CURVEDNESS)))*/
    }
    
    /*
    
     y
    
     ^
     |      z
     |    .
     |   /
     |  /
     | /
     |/
    -+------------> x
    /|
    
      rotation angles:
    	  a = alpha (x-rotation), b = beta (y-rotation), c = gamma (z-rotation)
    
      x-axis rotation:
    	(  z  )'  =  ( cos(a) -sin(a) ) (  z  )
    	(  y  )      ( sin(a)  cos(a) ) (  y  )
    
      y-axis rotation:
    	(  z  )'  =  ( cos(b) -sin(b) ) (  z  )
    	(  x  )      ( sin(b)  cos(b) ) (  x  )
    
      z-axis rotation:
    	(  x  )'  =  ( cos(c) -sin(c) ) (  x  )
    	(  y  )      ( sin(c)  cos(c) ) (  y  )
    */
    
    static void
    rotate_3d(Vec3D *src, Vec3D *dest, Angle3D *angle)
    {
    	Vec3D tmp;
    	float	cosa = COS(angle->x),
    		cosb = COS(angle->y),
    		cosc = COS(angle->z),
    		sina = SIN(angle->x),
    		sinb = SIN(angle->y),
    		sinc = SIN(angle->z);
    
    	/* rotate around X, Y and Z axis (see formulae above) */
    
    	/* X axis */
    	tmp.z = src->z;
    	tmp.y = src->y;
    	dest->z = (int) (tmp.z * cosa - tmp.y * sina);
    	dest->y = (int) (tmp.z * sina + tmp.y * cosa);
    
    	/* Y axis */
    	tmp.z = dest->z;
    	tmp.x = src->x;
    	dest->z = (int) (tmp.z * cosb - tmp.x * sinb);
    	dest->x = (int) (tmp.z * sinb + tmp.x * cosb);
    
    	/* Z axis */
    	tmp.x = dest->x;
    	tmp.y = dest->y;
    	dest->x = (int) (tmp.x * cosc - tmp.y * sinc);
    	dest->y = (int) (tmp.x * sinc + tmp.y * cosc);
    }
    
    static void calc_new_element(ModeInfo *mi)
    {
    	scooterstruct *sp = &scooters[MI_SCREEN(mi)];
    	float rot = SIN((SINUSTABLE_SIZE/2)*
    		sp->rotationStep/sp->rotationDuration);
    
    	/* change current rotation 3D angle */
    
    	if (sp->rotationStep++ >= sp->rotationDuration) {
    
    		int fps = 1000000/MI_DELAY(mi); /* frames per second as timebase */
    
     		/*  one rotation interval takes 10-30 seconds at speed 1.
    		 */
     		sp->rotationDuration = 10*fps + NRAND(20*fps);
    
    		/* -DOOR_CURVEDNESS <= delta <= +DOOR_CURVEDNESS */
    		sp->rotationDelta.x =
    			NRAND(DOOR_CURVEDNESS*2+1) - DOOR_CURVEDNESS;
    		sp->rotationDelta.y =
    			NRAND(DOOR_CURVEDNESS*2+1) - DOOR_CURVEDNESS;
    		sp->rotationDelta.z =
    			NRAND(DOOR_CURVEDNESS*2+1) - DOOR_CURVEDNESS;
    
    		sp->rotationStep = 0;
    	}
    
    	sp->currentRotation.x += (int) (rot * sp->rotationDelta.x);
    	sp->currentRotation.y += (int) (rot * sp->rotationDelta.y);
    	sp->currentRotation.z += (int) (rot * sp->rotationDelta.z);
    
    	sp->currentRotation.x &= SINUSTABLE_MASK;
    	sp->currentRotation.y &= SINUSTABLE_MASK;
    	sp->currentRotation.z &= SINUSTABLE_MASK;
    }
    
    static void shift_elements(ModeInfo *mi)
    {
    	scooterstruct *sp = &scooters[MI_SCREEN(mi)];
    	int i, iprev;
    	Vec3D tmpvec;
    	Angle3D tmpangle;
    
    	/* shift angles from zelements */
    
    	for (i = sp->speed; i < sp->ztotal; i++) {
    		sp->zelements[i - sp->speed].angle = sp->zelements[i].angle;
    	}
    	for (i = sp->ztotal - sp->speed; i < sp->ztotal; i++) {
    		calc_new_element(mi);
    		sp->zelements[i].angle = sp->currentRotation;
    	}
    
    	/* calculate new 3D-coords from ALL zelements */
    
    	sp->zelements[sp->spectator_zelement].pos.x = 0;
    	sp->zelements[sp->spectator_zelement].pos.y = 0;
    	sp->zelements[sp->spectator_zelement].pos.z =
    		sp->zelement_distance * sp->spectator_zelement;
    
    	for (i = sp->spectator_zelement - 1; i >= 0; --i) {
    		iprev = i + 1;
    
    		tmpvec.x = 0;
    		tmpvec.y = 0;
    		tmpvec.z = - sp->zelement_distance;
    		tmpangle.x = sp->zelements[i].angle.x -
    			sp->zelements[sp->spectator_zelement].angle.x;
    		tmpangle.y = sp->zelements[i].angle.y -
    			sp->zelements[sp->spectator_zelement].angle.y;
    		tmpangle.z = sp->zelements[i].angle.z -
    			sp->zelements[sp->spectator_zelement].angle.z;
    		rotate_3d(&tmpvec, &(sp->zelements[i].pos), &tmpangle);
    		sp->zelements[i].pos.x += sp->zelements[iprev].pos.x;
    		sp->zelements[i].pos.y += sp->zelements[iprev].pos.y;
    		sp->zelements[i].pos.z += sp->zelements[iprev].pos.z;
    	}
    
    	for (i = sp->spectator_zelement + 1; i < sp->ztotal; i++) {
    		iprev = i - 1;
    
    		tmpvec.x = 0;
    		tmpvec.y = 0;
    		tmpvec.z = sp->zelement_distance;
    		tmpangle.x = sp->zelements[i].angle.x -
    			sp->zelements[sp->spectator_zelement].angle.x;
    		tmpangle.y = sp->zelements[i].angle.y -
    			sp->zelements[sp->spectator_zelement].angle.y;
    		tmpangle.z = sp->zelements[i].angle.z -
    			sp->zelements[sp->spectator_zelement].angle.z;
    		rotate_3d(&tmpvec, &(sp->zelements[i].pos), &tmpangle);
    		sp->zelements[i].pos.x += sp->zelements[iprev].pos.x;
    		sp->zelements[i].pos.y += sp->zelements[iprev].pos.y;
    		sp->zelements[i].pos.z += sp->zelements[iprev].pos.z;
    	}
    
    
    	/* shift doors and wrap around */
    
    	for (i = 0; i < sp->doorcount; i++) {
    		if ((sp->doors[i].zelement -= sp->speed) < 0) {
    			sp->doors[i].zelement += sp->ztotal;
    			nextdoorcolor(mi,&sp->doors[i]);
    		}
    	}
    
    	/* shift stars */
    
    	for (i = 0; i < sp->starcount; i++) {
    		if ((sp->stars[i].zelement -= sp->speed) < 0) {
    			int rnd;
    
    			sp->stars[i].zelement += sp->ztotal;
    			sp->stars[i].draw = 1;
    
    			/* make sure new stars are outside doors */
    
    			rnd = NRAND(2*(STAR_MAX_X - STAR_MIN_X)) -
    				(STAR_MAX_X - STAR_MIN_X);
    			sp->stars[i].xpos = rnd + (STAR_MIN_X * SGN(rnd));
    
    			rnd = NRAND(2*(STAR_MAX_Y - STAR_MIN_Y)) -
    				(STAR_MAX_Y - STAR_MIN_Y);
    			sp->stars[i].ypos = rnd + (STAR_MIN_Y * SGN(rnd));
    
    			rnd = NRAND(STAR_SIZE_MAX - STAR_SIZE_MIN) +
    				STAR_SIZE_MIN;
    			sp->stars[i].width  = rnd;
    			sp->stars[i].height = rnd * 3 / 4;
    		}
    	}
    }
    
    static void door_3d(scooterstruct *sp, Door *door)
    {
    	ZElement *ze = &sp->zelements[door->zelement];
    	Vec3D src;
    	Angle3D tmpangle;
    
    	tmpangle.x = ze->angle.x -
    		sp->zelements[sp->spectator_zelement].angle.x;
    	tmpangle.y = ze->angle.y -
    		sp->zelements[sp->spectator_zelement].angle.y;
    	tmpangle.z = ze->angle.z -
    		sp->zelements[sp->spectator_zelement].angle.z;
    
    	/* calculate 3d coords of all 4 edges */
    
    	src.x = -DOOR_WIDTH/2;
    	src.y = DOOR_HEIGHT/2;
    	src.z = 0;
    	rotate_3d(&src, &(door->coords[0]), &tmpangle);
    	door->coords[0].x += ze->pos.x;
    	door->coords[0].y += ze->pos.y;
    	door->coords[0].z += ze->pos.z;
    
    	src.x = DOOR_WIDTH/2;
    	rotate_3d(&src, &(door->coords[1]), &tmpangle);
    	door->coords[1].x += ze->pos.x;
    	door->coords[1].y += ze->pos.y;
    	door->coords[1].z += ze->pos.z;
    
    	src.y = -DOOR_HEIGHT/2;
    	rotate_3d(&src, &(door->coords[2]), &tmpangle);
    	door->coords[2].x += ze->pos.x;
    	door->coords[2].y += ze->pos.y;
    	door->coords[2].z += ze->pos.z;
    
    	src.x = -DOOR_WIDTH/2;
    	rotate_3d(&src, &(door->coords[3]), &tmpangle);
    	door->coords[3].x += ze->pos.x;
    	door->coords[3].y += ze->pos.y;
    	door->coords[3].z += ze->pos.z;
    }
    
    /*
     *  clip the line p1-p2 at the given rectangle
     *
     */
    static int clipline(XPoint *p1, XPoint *p2, Rect *rect)
    {
    	XPoint new1, new2, tmp;
    	float m;
    
    	new1 = *p1;
    	new2 = *p2;
    
    	/* entire line may not need clipping */
    
    	if (((new1.x >= rect->lefttop.x) && (new1.x <= rect->rightbottom.x))
    	 || ((new1.y >= rect->lefttop.y) && (new1.y <= rect->rightbottom.y))
    	 || ((new2.x >= rect->lefttop.x) && (new2.x <= rect->rightbottom.x))
    	 || ((new2.y >= rect->lefttop.y) && (new2.y <= rect->rightbottom.y)))
    		return 1;
    
    
    	/* first: clip y dimension */
    
    	/* p1 is above p2 */
    	if (new1.y > new2.y) {
    		tmp = new1;
    		new1 = new2;
    		new2 = tmp;
    	}
    
    	/* line could be totally out of view */
    	if ((new2.y < rect->lefttop.y) || (new1.y > rect->rightbottom.y))
    		return 0;
    
    	m = (new2.x == new1.x) ? 0 :
    		((float)(new2.y - new1.y) / (new2.x - new1.x));
    
    	if (new1.y < rect->lefttop.y) {
    		if (m)
    			new1.x += (int) ((rect->lefttop.y - new1.y)/m);
    		new1.y = rect->lefttop.y;
    	}
    	if (new2.y > rect->rightbottom.y) {
    		if (m)
    			new2.x -= (int) ((new2.y - rect->rightbottom.y)/m);
    		new2.y = rect->rightbottom.y;
    	}
    
    
    	/* clip x dimension */
    
    	/* p1 is left to p2 */
    	if (new1.x > new2.x) {
    		tmp = new1;
    		new1 = new2;
    		new2 = tmp;
    	}
    
    	if ((new2.x < rect->lefttop.x) || (new1.x > rect->rightbottom.x))
    		return 0;
    
    	m = (new2.x == new1.x) ? 0 :
    		((float)(new2.y - new1.y) / (new2.x - new1.x));
    
    	if (new1.x < rect->lefttop.x) {
    		new1.y += (int) ((rect->lefttop.y - new1.y)*m);
    		new1.x = rect->lefttop.x;
    	}
    	if (new2.y > rect->rightbottom.y) {
    		new2.y -= (int) ((new2.y - rect->rightbottom.y)*m);
    		new2.x = rect->rightbottom.x;
    	}
    
    
    	/* push values */
    
    	*p1 = new1;
    	*p2 = new2;
    
    	return 1;
    }
    
    static void drawdoors(ModeInfo *mi)
    {
    	scooterstruct *sp = &scooters[MI_SCREEN(mi)];
    	Display *display = MI_DISPLAY(mi);
    	Window window = MI_WINDOW(mi);
    	GC gc = MI_GC(mi);
    	int width = MI_WIDTH(mi), height = MI_HEIGHT(mi),
    		midx = width/2, midy = height/2;
    	int i, j;
    	Rect rect = { {0,0}, {0,0} };
    
            rect.rightbottom.x = width - 1;
            rect.rightbottom.y = height - 1;
    	XSetLineAttributes(display, gc, 2, LineSolid, CapNotLast, JoinRound);
    
    #ifdef _DRAW_ZELEMENTS
            XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
            for (i= sp->spectator_zelement; i<ztotal; i++) {
                    register float proj;
                    XPoint p;
    
                    proj = projection(sp, zelements[i].pos.z) * sp->aspect_scale;
                    p.x = midx + (sp->zelements[i].pos.x * proj / SPACE_XY_FACTOR);
                    p.y = midy - (sp->zelements[i].pos.y * proj / SPACE_XY_FACTOR);
                    XDrawPoint(display, window, gc, p.x, p.y);
            }
    #endif
    
    	for (i = 0; i < sp->doorcount; i++) {
    
    		register float proj;
    		XPoint lines[4], clip1, clip2;
    
    		door_3d(sp, &sp->doors[i]);
    
    		for (j=0; j<4; j++) {
    			if (sp->doors[i].coords[j].z <= 0) break;
    
    			proj = projection(sp, sp->doors[i].coords[j].z) * sp->aspect_scale;
    			lines[j].x = midx + (int) (sp->doors[i].coords[j].x *
    				proj / SPACE_XY_FACTOR);
    			lines[j].y = midy - (int) (sp->doors[i].coords[j].y *
    				proj / SPACE_XY_FACTOR);
    		}
    		if (j<4) continue;
    
    		XSetForeground(display, gc, sp->doors[i].color);
    
    		for (j=0; j<4; j++) {
    			clip1 = lines[j];
    			clip2 = lines[(j+1)%4];
    			if (clipline(&clip1, &clip2, &rect))
    				XDrawLine(display, window, gc, clip1.x, clip1.y, clip2.x, clip2.y);
    		}
    	}
    }
    
    static void drawstars(ModeInfo *mi)
    {
    	scooterstruct *sp = &scooters[MI_SCREEN(mi)];
    	Display *display = MI_DISPLAY(mi);
    	Window window = MI_WINDOW(mi);
    	GC gc = MI_GC(mi);
    	int width = MI_WIDTH(mi), height = MI_HEIGHT(mi),
    		midx = width/2, midy = height/2;
    	int i;
    
    	for (i = 0; i < sp->starcount; i++) {
    
    		float proj;
    		ZElement *ze = &sp->zelements[sp->stars[i].zelement];
    		Vec3D tmpvec, coords;
    		Angle3D tmpangle;
    		XPoint lefttop, rightbottom;
    
    		if (!sp->stars[i].draw) continue;
    
    
    		/* rotate star around its z-element, then add its position */
    
    		tmpangle.x = ze->angle.x -
    			sp->zelements[sp->spectator_zelement].angle.x;
    		tmpangle.y = ze->angle.y -
    			sp->zelements[sp->spectator_zelement].angle.y;
    		tmpangle.z = ze->angle.z -
    			sp->zelements[sp->spectator_zelement].angle.z;
    
    		tmpvec.x = sp->stars[i].xpos;
    		tmpvec.y = sp->stars[i].ypos;
    		tmpvec.z = 0;
    		rotate_3d(&tmpvec, &coords, &tmpangle);
    		coords.x += ze->pos.x;
    		coords.y += ze->pos.y;
    		coords.z += ze->pos.z;
    
    		if (coords.z <= 0) continue;
    
    
    		/* projection and clipping (trivial for a rectangle) */
    
    		proj = projection(sp, coords.z) * sp->aspect_scale;
    
    		lefttop.x = midx + (int) ((coords.x - sp->stars[i].width/2) *
    			proj / SPACE_XY_FACTOR);
    		lefttop.y = midy - (int) ((coords.y + sp->stars[i].height/2) *
    			proj / SPACE_XY_FACTOR);
    		if (lefttop.x < 0)
    			lefttop.x = 0;
    		else if (lefttop.x >= width)
    			continue;
    		if (lefttop.y < 0)
    			lefttop.y = 0;
    		else if (lefttop.y >= height)
    			continue;
    
    		rightbottom.x = midx + (int) ((coords.x + sp->stars[i].width/2) *
    			 proj / SPACE_XY_FACTOR);
    		rightbottom.y = midy - (int) ((coords.y - sp->stars[i].height/2) *
    			 proj / SPACE_XY_FACTOR);
    		if (rightbottom.x < 0)
    			continue;
    		else if (rightbottom.x >= width)
    			rightbottom.x = width - 1;
    		if (rightbottom.y < 0)
    			continue;
    		else if (rightbottom.y >= height)
    			rightbottom.y = height - 1;
    
    
    		/* in white color, small stars look darker than big stars */
    
    		XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
    
    		if ((lefttop.x == rightbottom.x) &&
    				(lefttop.y == rightbottom.y)) {
    			/* star is exactly 1 pixel */
    			XDrawPoint(display, window, gc, lefttop.x, lefttop.y);
    		} else if ((rightbottom.x - lefttop.x) +
    				(rightbottom.y - lefttop.y) == 1) {
    			/* star is 2 pixels wide or high */
    			XDrawPoint(display, window, gc,
    				lefttop.x,     lefttop.y);
    			XDrawPoint(display, window, gc,
    				rightbottom.x, rightbottom.y);
    		} else if ((rightbottom.x - lefttop.x == 1) &&
    				(rightbottom.y - lefttop.y == 1)) {
    
    			/*  star is exactly 2x2 pixels.
    			 *  a 2x2 rectangle should be drawn faster by plotting all 4 pixels
    			 *  than by filling a rectangle (is this really so under X ?)
    			 */
    
    			XDrawPoint(display, window, gc,
    				lefttop.x,     lefttop.y);
    			XDrawPoint(display, window, gc,
    				rightbottom.x, lefttop.y);
    			XDrawPoint(display, window, gc,
    				lefttop.x,     rightbottom.y);
    			XDrawPoint(display, window, gc,
    				rightbottom.x, rightbottom.y);
    		} else {
    			XFillRectangle(display, window, gc,
    				lefttop.x, lefttop.y,
    				rightbottom.x - lefttop.x,
    				rightbottom.y - lefttop.y);
    		}
    	}
    }
    
    void draw_scooter(ModeInfo *mi)
    {
    	scooterstruct *sp;
    
    	if (scooters == NULL)
    		return;
    	sp = &scooters[MI_SCREEN(mi)];
    	if (sp->doors == NULL)
    		return;
    
    	cleardoors(mi);
    
    	shift_elements(mi);
    
    	/* With these scale factors, all doors are sized correctly for any window dimension.
    	 * If aspect ratio is not 4:3, the smaller part of the window is used, e.g.:
    	 *      window = 1000x600
    	 *      => door scale factor is like in a 800x600 window (not 1000x750)
    	 */
    
    	if ((float)MI_WIDTH(mi)/MI_HEIGHT(mi) >=
    			(float)ASPECT_SCREENWIDTH/ASPECT_SCREENHEIGHT) {
    		/* window is wider than or equal 4:3 */
    		sp->aspect_scale = (float)MI_HEIGHT(mi) / ASPECT_SCREENHEIGHT;
    	} else {
    		/* window is higher than 4:3 */
    		sp->aspect_scale = (float)MI_WIDTH(mi) / ASPECT_SCREENWIDTH;
    	}
    
    	drawstars(mi);
    
    	drawdoors(mi);
    }
    
    void refresh_scooter(ModeInfo *mi)
    {
    	MI_CLEARWINDOW(mi);
    }
    
    void change_scooter(ModeInfo *mi)
    {
    	scooterstruct *sp;
    
    	if (scooters == NULL)
    		return;
    	sp = &scooters[MI_SCREEN(mi)];
    
    	sp->halt_scooter = !sp->halt_scooter;
    }
    
    #endif /* MODE_scooter */