1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* scooter -- a journey through space tunnel and stars */
3 
4 /*
5  *  scooter.c
6  *
7  *  Copyright (c) 2001 Sven Thoennissen <posse@gmx.net>
8  *
9  *  This program is based on the original "scooter", a blanker module from the
10  *  Nightshift screensaver which is part of EGS (Enhanced Graphics System) on
11  *  the Amiga computer. EGS has been developed by VIONA Development.
12  *
13  *
14  *  (now the obligatory stuff)
15  *
16  *  Permission to use, copy, modify, and distribute this software and its
17  *  documentation for any purpose and without fee is hereby granted,
18  *  provided that the above copyright notice appear in all copies and that
19  *  both that copyright notice and this permission notice appear in
20  *  supporting documentation.
21  *
22  *  This file is provided AS IS with no warranties of any kind.  The author
23  *  shall have no liability with respect to the infringement of copyrights,
24  *  trade secrets or any patents by this file or any part thereof.  In no
25  *  event will the author be liable for any lost revenue or profits or
26  *  other special, indirect and consequential damages.
27  *
28  *	Changes:
29  *
30  *	??/??/2001 (Sven Thoennissen <posse@gmx.net>):    Initial release
31  *	05/08/2019 (EoflaOE <eoflaoevicecity@gmail.com>): Ported to XScreenSaver
32  */
33 
34 #ifdef STANDALONE
35 #define MODE_scooter
36 #define PROGCLASS "Scooter"
37 #define HACK_INIT init_scooter
38 #define HACK_DRAW draw_scooter
39 #define scooter_opts xlockmore_opts
40 #define DEFAULTS 	"*delay: 20000 \n" \
41  					"*count: 24 \n" \
42  					"*cycles: 5 \n" \
43  					"*size: 100 \n" \
44  					"*ncolors: 200 \n" \
45  					"*fullrandom: True \n" \
46  					"*verbose: False \n" \
47 					"*fpsSolid: true \n" \
48 				    "*lowrez: True \n"
49 # define reshape_scooter 0
50 # define scooter_handle_event 0
51 #include "xlockmore.h"          /* in xscreensaver distribution */
52 #else /* STANDALONE */
53 #include "xlock.h"              /* in xlockmore distribution */
54 #endif /* STANDALONE */
55 
56 #ifdef MODE_scooter
57 
58 ModeSpecOpt scooter_opts =
59 {0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
60 
61 #ifdef USE_MODULES
62 ModStruct scooter_description =
63 {"scooter", "init_scooter", "draw_scooter", "release_scooter",
64  "refresh_scooter", "change_scooter", (char *) NULL, &scooter_opts,
65  20000, 24, 5, 100, 64, 1.0, "",
66  "Shows a journey through space tunnel and stars", 0, NULL};
67 /*
68  *  count = number of doors
69  *  cycles = speed (see MIN/MAX_SPEED below)
70  *  size = number of stars
71  *
72  */
73 #endif
74 
75 typedef struct {
76 	int x, y, z;
77 } Vec3D;
78 
79 typedef struct {
80 	int x, y, z;
81 } Angle3D;
82 
83 typedef struct {
84 	int r, g, b;
85 } ColorRGB;
86 
87 typedef struct {
88 	XPoint lefttop, rightbottom;
89 } Rect;
90 
91 typedef struct {
92 	Vec3D coords[4]; /* lefttop, righttop, rightbottom, leftbottom */
93 	int zelement;
94 	unsigned long color;
95 	char freecolor;
96 	char pad;
97 } Door;
98 
99 typedef struct {
100 	int xpos, ypos;
101 	int width, height;
102 	int zelement;
103 	short draw;
104 } Star;
105 
106 /* define this to see a pixel for each zelement */
107 /*
108 #define _DRAW_ZELEMENTS
109 */
110 
111 typedef struct {
112 	Vec3D pos;
113 	Angle3D angle;
114 } ZElement;
115 
116 typedef struct {
117 	Star *stars;
118 	Door *doors;
119 	ZElement *zelements;
120 	int doorcount, ztotal, speed;
121 	int zelements_per_door, zelement_distance, spectator_zelement;
122 	int projnorm_z, rotationDuration, rotationStep, starcount;
123 	Angle3D currentRotation, rotationDelta;
124 
125 	/* doors color cycling stuff */
126 	ColorRGB begincolor, endcolor;
127 	int colorcount, colorsteps;
128 
129 	/* scale all stars and doors to window dimensions */
130 	float aspect_scale;
131 
132 	Bool halt_scooter;
133 } scooterstruct;
134 
135 static scooterstruct *scooters = (scooterstruct *) NULL;
136 
137 #define MIN_DOORS 4
138 
139 #define MIN_SPEED 1
140 #define MAX_SPEED 10
141 
142 #define SPACE_XY_FACTOR 10
143 
144 #define DOOR_WIDTH  (600*SPACE_XY_FACTOR)
145 #define DOOR_HEIGHT (400*SPACE_XY_FACTOR)
146 
147 /* stars distance from doors center */
148 #define STAR_MIN_X  (1000*SPACE_XY_FACTOR)
149 #define STAR_MIN_Y  (750*SPACE_XY_FACTOR)
150 #define STAR_MAX_X  (10000*SPACE_XY_FACTOR)
151 #define STAR_MAX_Y  (7500*SPACE_XY_FACTOR)
152 
153 /* star size (random) */
154 #define STAR_SIZE_MIN (2*SPACE_XY_FACTOR)
155 #define STAR_SIZE_MAX (64*SPACE_XY_FACTOR)
156 
157 /* greater values make scooter run harder curves, smaller values produce calm curves */
158 #define DOOR_CURVEDNESS 14
159 
160 /* 3d->2d projection (greater values create more fish-eye effect) */
161 #define PROJECTION_DEGREE 2.4
162 
163 /*  this is my resolution at which scooter is in its original size, producing a 4:3 aspect ratio.
164  *  all variables in this module are adjusted for this screen size; if scooter is run
165  *  in windows with different size, it knows how to rescale itself.
166  */
167 #define ASPECT_SCREENWIDTH 1152
168 #define ASPECT_SCREENHEIGHT 864
169 
170 /* we define our own sin/cos macros to be faaast ;-) (good old Amiga times) */
171 #define SINUSTABLE_SIZE 0x8000
172 #define SINUSTABLE_MASK 0x7fff
173 #define SIN(a) sintable[a & SINUSTABLE_MASK]
174 #define COS(a) sintable[(a+(SINUSTABLE_SIZE/4)) & SINUSTABLE_MASK]
175 
176 /* signum function */
177 #define SGN(a) (a < 0 ? -1 : 1)
178 
179 static float *sintable = (float *) NULL;
180 
181 static void
randomcolor(ColorRGB * col)182 randomcolor(ColorRGB *col)
183 {
184 	unsigned long n;
185 
186 /*	col->r = NRAND(65536);
187 	col->g = NRAND(65536);
188 	col->b = NRAND(65536);
189 */
190 /*	col->r = LRAND() & 0xffff;
191 	col->g = LRAND() & 0xffff;
192 	col->b = LRAND() & 0xffff;
193 */
194 	/* this seems best */
195 	n = NRAND(0x1000000);
196 	col->r = (n>>16)<<8;
197 	col->g = ((n>>8) & 0xff)<<8;
198 	col->b = (n & 0xff)<<8;
199 }
200 
201 static void
initdoorcolors(scooterstruct * sp)202 initdoorcolors(scooterstruct *sp)
203 {
204 	/* prepare initial values for nextdoorcolor() */
205 
206 	randomcolor(&sp->endcolor);
207 
208 	sp->colorcount = 0;
209 	sp->colorsteps = 0;
210 }
211 
nextdoorcolor(ModeInfo * mi,Door * door)212 static void nextdoorcolor(ModeInfo *mi, Door *door)
213 {
214 	scooterstruct *sp = &scooters[MI_SCREEN(mi)];
215 	Display *display = MI_DISPLAY(mi);
216 	XColor xcol;
217 
218 /* comment this to color the doors from xlock's palette (created with saturation value) */
219 #if 1
220 	if (door->freecolor) {
221 		XFreeColors(display, MI_COLORMAP(mi), &(door->color), 1, 0);
222 		door->freecolor = 0;
223 	}
224 	if (MI_NPIXELS(mi) <= 2) {
225 		door->color = MI_WHITE_PIXEL(mi);
226 		return;
227 	}
228 
229 	if (sp->colorcount >= sp->colorsteps) {
230 
231 		/* init next color ramp */
232 
233 		sp->colorcount = 0;
234 		sp->colorsteps = 8 + NRAND(32);
235 		sp->begincolor = sp->endcolor;
236 		randomcolor(&sp->endcolor);
237 	}
238 
239 	/* compute next color values */
240 
241 	xcol.red   = sp->begincolor.r + ((sp-> endcolor.r - sp->begincolor.r) *
242 		sp->colorcount / sp->colorsteps);
243 	xcol.green = sp->begincolor.g + ((sp-> endcolor.g - sp->begincolor.g) *
244 		sp->colorcount / sp->colorsteps);
245 	xcol.blue  = sp->begincolor.b + ((sp-> endcolor.b - sp->begincolor.b) *
246 		sp->colorcount / sp->colorsteps);
247 	xcol.pixel = 0;
248 	xcol.flags = DoRed | DoGreen | DoBlue;
249 
250 	sp->colorcount++;
251 
252 	if (!XAllocColor(display, MI_COLORMAP(mi), &xcol)) {
253 		 /* fail safe */
254 		door->color = MI_WHITE_PIXEL(mi);
255 		door->freecolor = 0;
256 	} else {
257 		door->color = xcol.pixel;
258 		door->freecolor = 1;
259 	}
260 #else
261 	if (MI_NPIXELS(mi) > 2) {
262 		if (++colorcount >= MI_NPIXELS(mi))
263 			colorcount = 0;
264 		door->color = MI_PIXEL(mi,colorcount);
265 	} else
266 		door->color = MI_WHITE_PIXEL(mi);
267 	return;
268 #endif
269 }
270 
271 static void
free_scooter(scooterstruct * sp)272 free_scooter(scooterstruct *sp)
273 {
274 	if (sp->doors != NULL) {
275 		free(sp->doors);
276 		sp->doors = (Door *) NULL;
277 	}
278 	if (sp->stars != NULL) {
279 		free(sp->stars);
280 		sp->stars = (Star *) NULL;
281 	}
282 	if (sp->zelements != NULL) {
283 		free(sp->zelements);
284 		sp->zelements = (ZElement *) NULL;
285 	}
286 }
287 
release_scooter(ModeInfo * mi)288 static void release_scooter(ModeInfo *mi)
289 {
290     if (scooters != NULL) {
291         int screen;
292 
293         for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
294              free_scooter(&scooters[screen]);
295         free(scooters);
296         scooters = (scooterstruct *) NULL;
297     }
298 	if (sintable != NULL) {
299 		free(sintable);
300 		sintable = (float *) NULL;
301 	}
302 }
303 
init_scooter(ModeInfo * mi)304 static void init_scooter(ModeInfo *mi)
305 {
306 	int i;
307 	scooterstruct *sp;
308 
309 	if (scooters == NULL) {
310 		if ((scooters = (scooterstruct *) calloc(MI_NUM_SCREENS(mi),
311 				sizeof (scooterstruct))) == NULL)
312 			return;
313 	}
314 	sp = &scooters[MI_SCREEN(mi)];
315 	sp->doorcount = MAX(MI_COUNT(mi),MIN_DOORS);
316 	sp->speed = MI_CYCLES(mi);
317 	sp->starcount = MI_SIZE(mi);
318 	if (sp->starcount < 1)
319 		sp->starcount = 1;
320 	if (sp->speed < MIN_SPEED)
321 		sp->speed = MIN_SPEED;
322 	if (sp->speed > MAX_SPEED)
323 		sp->speed = MAX_SPEED;
324 	sp->zelements_per_door = 60;
325 	sp->zelement_distance = 300;
326 	sp->ztotal = sp->doorcount * sp->zelements_per_door;
327 	/* sp->z_maxdepth = sp->ztotal * sp->zelement_distance; */
328 	if (sp->starcount > sp->ztotal)
329 		sp->starcount = sp->ztotal;
330 
331 	sp->halt_scooter = False;
332 
333 	initdoorcolors(sp);
334 	free_scooter(sp);
335 	if (sintable == NULL) {
336 		if ((sintable = (float *) malloc(sizeof(float) *
337 			SINUSTABLE_SIZE)) == NULL) {
338 			release_scooter(mi);
339 			return;
340 		}
341 	}
342 	for (i = 0; i < SINUSTABLE_SIZE; i++) {
343 		sintable[i] = SINF(M_PI*2/SINUSTABLE_SIZE*i);
344 	}
345 
346 	if ((sp->doors = (Door *) malloc(sizeof(Door) *
347 			sp->doorcount)) == NULL) {
348 		return;
349 	}
350 
351 	if ((sp->zelements = (ZElement *) malloc(sizeof(ZElement) *
352 		 sp->ztotal)) == NULL) {
353 		free_scooter(sp);
354 		return;
355 	}
356 	for (i = 0; i <  sp->doorcount; i++) {
357 		sp->doors[i].zelement =
358 			(sp->zelements_per_door * (i + 1)) - 1;
359 		sp->doors[i].freecolor = 0;
360 		nextdoorcolor(mi, &sp->doors[i]);
361 	}
362 
363 	for (i = 0; i < sp->ztotal; i++) {
364 		sp->zelements[i].angle.x = 0;
365 		sp->zelements[i].angle.y = 0;
366 		sp->zelements[i].angle.z = 0;
367 	}
368 
369 	if ((sp->stars = (Star *) malloc(sizeof(Star) *
370 			sp->starcount)) == NULL) {
371 		free_scooter(sp);
372 		return;
373 	}
374 	for (i = 0; i < sp->starcount; i++) {
375 		sp->stars[i].zelement = sp->ztotal * i / sp->starcount;
376 		sp->stars[i].draw = 0;
377 	}
378 
379 	sp->projnorm_z = 50 * 240;
380 	sp->spectator_zelement = sp->zelements_per_door;
381 
382 	sp->currentRotation.x = 0;
383 	sp->currentRotation.y = 0;
384 	sp->currentRotation.z = 0;
385 
386 	sp->rotationDelta.x = 0;
387 	sp->rotationDelta.y = 0;
388 	sp->rotationDelta.z = 0;
389 
390 	sp->rotationDuration = 1;
391 	sp->rotationStep = 0;
392 }
393 
cleardoors(ModeInfo * mi)394 static void cleardoors(ModeInfo *mi)
395 {
396 	/* MI_CLEARWINDOW(mi);*/
397 	XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
398 }
399 
400 /* Should be taken care of already... but just in case */
401 #if !defined( __GNUC__ ) && !defined(__cplusplus) && !defined(c_plusplus)
402 #undef inline
403 #define inline                  /* */
404 #endif
405 static inline float
projection(scooterstruct * sp,int zval)406 projection (scooterstruct *sp, int zval)
407 {
408 	return (sp->projnorm_z / (PROJECTION_DEGREE * zval));
409 /* this is another formula. it is not limited to z>0 but it pulls too strong towards the screen center */
410 /*      return (sp->projnorm_z * pow(1.22,-(zval/200*PROJ_CURVEDNESS)))*/
411 }
412 
413 /*
414 
415  y
416 
417  ^
418  |      z
419  |    .
420  |   /
421  |  /
422  | /
423  |/
424 -+------------> x
425 /|
426 
427   rotation angles:
428 	  a = alpha (x-rotation), b = beta (y-rotation), c = gamma (z-rotation)
429 
430   x-axis rotation:
431 	(  z  )'  =  ( cos(a) -sin(a) ) (  z  )
432 	(  y  )      ( sin(a)  cos(a) ) (  y  )
433 
434   y-axis rotation:
435 	(  z  )'  =  ( cos(b) -sin(b) ) (  z  )
436 	(  x  )      ( sin(b)  cos(b) ) (  x  )
437 
438   z-axis rotation:
439 	(  x  )'  =  ( cos(c) -sin(c) ) (  x  )
440 	(  y  )      ( sin(c)  cos(c) ) (  y  )
441 */
442 
443 static void
rotate_3d(Vec3D * src,Vec3D * dest,Angle3D * angle)444 rotate_3d(Vec3D *src, Vec3D *dest, Angle3D *angle)
445 {
446 	Vec3D tmp;
447 	float	cosa = COS(angle->x),
448 		cosb = COS(angle->y),
449 		cosc = COS(angle->z),
450 		sina = SIN(angle->x),
451 		sinb = SIN(angle->y),
452 		sinc = SIN(angle->z);
453 
454 	/* rotate around X, Y and Z axis (see formulae above) */
455 
456 	/* X axis */
457 	tmp.z = src->z;
458 	tmp.y = src->y;
459 	dest->z = (int) (tmp.z * cosa - tmp.y * sina);
460 	dest->y = (int) (tmp.z * sina + tmp.y * cosa);
461 
462 	/* Y axis */
463 	tmp.z = dest->z;
464 	tmp.x = src->x;
465 	dest->z = (int) (tmp.z * cosb - tmp.x * sinb);
466 	dest->x = (int) (tmp.z * sinb + tmp.x * cosb);
467 
468 	/* Z axis */
469 	tmp.x = dest->x;
470 	tmp.y = dest->y;
471 	dest->x = (int) (tmp.x * cosc - tmp.y * sinc);
472 	dest->y = (int) (tmp.x * sinc + tmp.y * cosc);
473 }
474 
calc_new_element(ModeInfo * mi)475 static void calc_new_element(ModeInfo *mi)
476 {
477 	scooterstruct *sp = &scooters[MI_SCREEN(mi)];
478 	float rot = SIN((SINUSTABLE_SIZE/2)*
479 		sp->rotationStep/sp->rotationDuration);
480 
481 	/* change current rotation 3D angle */
482 
483 	if (sp->rotationStep++ >= sp->rotationDuration) {
484 
485 		int fps = 1000000/MI_DELAY(mi); /* frames per second as timebase */
486 
487  		/*  one rotation interval takes 10-30 seconds at speed 1.
488 		 */
489  		sp->rotationDuration = 10*fps + NRAND(20*fps);
490 
491 		/* -DOOR_CURVEDNESS <= delta <= +DOOR_CURVEDNESS */
492 		sp->rotationDelta.x =
493 			NRAND(DOOR_CURVEDNESS*2+1) - DOOR_CURVEDNESS;
494 		sp->rotationDelta.y =
495 			NRAND(DOOR_CURVEDNESS*2+1) - DOOR_CURVEDNESS;
496 		sp->rotationDelta.z =
497 			NRAND(DOOR_CURVEDNESS*2+1) - DOOR_CURVEDNESS;
498 
499 		sp->rotationStep = 0;
500 	}
501 
502 	sp->currentRotation.x += (int) (rot * sp->rotationDelta.x);
503 	sp->currentRotation.y += (int) (rot * sp->rotationDelta.y);
504 	sp->currentRotation.z += (int) (rot * sp->rotationDelta.z);
505 
506 	sp->currentRotation.x &= SINUSTABLE_MASK;
507 	sp->currentRotation.y &= SINUSTABLE_MASK;
508 	sp->currentRotation.z &= SINUSTABLE_MASK;
509 }
510 
shift_elements(ModeInfo * mi)511 static void shift_elements(ModeInfo *mi)
512 {
513 	scooterstruct *sp = &scooters[MI_SCREEN(mi)];
514 	int i, iprev;
515 	Vec3D tmpvec;
516 	Angle3D tmpangle;
517 
518 	/* shift angles from zelements */
519 
520 	for (i = sp->speed; i < sp->ztotal; i++) {
521 		sp->zelements[i - sp->speed].angle = sp->zelements[i].angle;
522 	}
523 	for (i = sp->ztotal - sp->speed; i < sp->ztotal; i++) {
524 		calc_new_element(mi);
525 		sp->zelements[i].angle = sp->currentRotation;
526 	}
527 
528 	/* calculate new 3D-coords from ALL zelements */
529 
530 	sp->zelements[sp->spectator_zelement].pos.x = 0;
531 	sp->zelements[sp->spectator_zelement].pos.y = 0;
532 	sp->zelements[sp->spectator_zelement].pos.z =
533 		sp->zelement_distance * sp->spectator_zelement;
534 
535 	for (i = sp->spectator_zelement - 1; i >= 0; --i) {
536 		iprev = i + 1;
537 
538 		tmpvec.x = 0;
539 		tmpvec.y = 0;
540 		tmpvec.z = - sp->zelement_distance;
541 		tmpangle.x = sp->zelements[i].angle.x -
542 			sp->zelements[sp->spectator_zelement].angle.x;
543 		tmpangle.y = sp->zelements[i].angle.y -
544 			sp->zelements[sp->spectator_zelement].angle.y;
545 		tmpangle.z = sp->zelements[i].angle.z -
546 			sp->zelements[sp->spectator_zelement].angle.z;
547 		rotate_3d(&tmpvec, &(sp->zelements[i].pos), &tmpangle);
548 		sp->zelements[i].pos.x += sp->zelements[iprev].pos.x;
549 		sp->zelements[i].pos.y += sp->zelements[iprev].pos.y;
550 		sp->zelements[i].pos.z += sp->zelements[iprev].pos.z;
551 	}
552 
553 	for (i = sp->spectator_zelement + 1; i < sp->ztotal; i++) {
554 		iprev = i - 1;
555 
556 		tmpvec.x = 0;
557 		tmpvec.y = 0;
558 		tmpvec.z = sp->zelement_distance;
559 		tmpangle.x = sp->zelements[i].angle.x -
560 			sp->zelements[sp->spectator_zelement].angle.x;
561 		tmpangle.y = sp->zelements[i].angle.y -
562 			sp->zelements[sp->spectator_zelement].angle.y;
563 		tmpangle.z = sp->zelements[i].angle.z -
564 			sp->zelements[sp->spectator_zelement].angle.z;
565 		rotate_3d(&tmpvec, &(sp->zelements[i].pos), &tmpangle);
566 		sp->zelements[i].pos.x += sp->zelements[iprev].pos.x;
567 		sp->zelements[i].pos.y += sp->zelements[iprev].pos.y;
568 		sp->zelements[i].pos.z += sp->zelements[iprev].pos.z;
569 	}
570 
571 
572 	/* shift doors and wrap around */
573 
574 	for (i = 0; i < sp->doorcount; i++) {
575 		if ((sp->doors[i].zelement -= sp->speed) < 0) {
576 			sp->doors[i].zelement += sp->ztotal;
577 			nextdoorcolor(mi,&sp->doors[i]);
578 		}
579 	}
580 
581 	/* shift stars */
582 
583 	for (i = 0; i < sp->starcount; i++) {
584 		if ((sp->stars[i].zelement -= sp->speed) < 0) {
585 			int rnd;
586 
587 			sp->stars[i].zelement += sp->ztotal;
588 			sp->stars[i].draw = 1;
589 
590 			/* make sure new stars are outside doors */
591 
592 			rnd = NRAND(2*(STAR_MAX_X - STAR_MIN_X)) -
593 				(STAR_MAX_X - STAR_MIN_X);
594 			sp->stars[i].xpos = rnd + (STAR_MIN_X * SGN(rnd));
595 
596 			rnd = NRAND(2*(STAR_MAX_Y - STAR_MIN_Y)) -
597 				(STAR_MAX_Y - STAR_MIN_Y);
598 			sp->stars[i].ypos = rnd + (STAR_MIN_Y * SGN(rnd));
599 
600 			rnd = NRAND(STAR_SIZE_MAX - STAR_SIZE_MIN) +
601 				STAR_SIZE_MIN;
602 			sp->stars[i].width  = rnd;
603 			sp->stars[i].height = rnd * 3 / 4;
604 		}
605 	}
606 }
607 
door_3d(scooterstruct * sp,Door * door)608 static void door_3d(scooterstruct *sp, Door *door)
609 {
610 	ZElement *ze = &sp->zelements[door->zelement];
611 	Vec3D src;
612 	Angle3D tmpangle;
613 
614 	tmpangle.x = ze->angle.x -
615 		sp->zelements[sp->spectator_zelement].angle.x;
616 	tmpangle.y = ze->angle.y -
617 		sp->zelements[sp->spectator_zelement].angle.y;
618 	tmpangle.z = ze->angle.z -
619 		sp->zelements[sp->spectator_zelement].angle.z;
620 
621 	/* calculate 3d coords of all 4 edges */
622 
623 	src.x = -DOOR_WIDTH/2;
624 	src.y = DOOR_HEIGHT/2;
625 	src.z = 0;
626 	rotate_3d(&src, &(door->coords[0]), &tmpangle);
627 	door->coords[0].x += ze->pos.x;
628 	door->coords[0].y += ze->pos.y;
629 	door->coords[0].z += ze->pos.z;
630 
631 	src.x = DOOR_WIDTH/2;
632 	rotate_3d(&src, &(door->coords[1]), &tmpangle);
633 	door->coords[1].x += ze->pos.x;
634 	door->coords[1].y += ze->pos.y;
635 	door->coords[1].z += ze->pos.z;
636 
637 	src.y = -DOOR_HEIGHT/2;
638 	rotate_3d(&src, &(door->coords[2]), &tmpangle);
639 	door->coords[2].x += ze->pos.x;
640 	door->coords[2].y += ze->pos.y;
641 	door->coords[2].z += ze->pos.z;
642 
643 	src.x = -DOOR_WIDTH/2;
644 	rotate_3d(&src, &(door->coords[3]), &tmpangle);
645 	door->coords[3].x += ze->pos.x;
646 	door->coords[3].y += ze->pos.y;
647 	door->coords[3].z += ze->pos.z;
648 }
649 
650 /*
651  *  clip the line p1-p2 at the given rectangle
652  *
653  */
clipline(XPoint * p1,XPoint * p2,Rect * rect)654 static int clipline(XPoint *p1, XPoint *p2, Rect *rect)
655 {
656 	XPoint new1, new2, tmp;
657 	float m;
658 
659 	new1 = *p1;
660 	new2 = *p2;
661 
662 	/* entire line may not need clipping */
663 
664 	if (((new1.x >= rect->lefttop.x) && (new1.x <= rect->rightbottom.x))
665 	 || ((new1.y >= rect->lefttop.y) && (new1.y <= rect->rightbottom.y))
666 	 || ((new2.x >= rect->lefttop.x) && (new2.x <= rect->rightbottom.x))
667 	 || ((new2.y >= rect->lefttop.y) && (new2.y <= rect->rightbottom.y)))
668 		return 1;
669 
670 
671 	/* first: clip y dimension */
672 
673 	/* p1 is above p2 */
674 	if (new1.y > new2.y) {
675 		tmp = new1;
676 		new1 = new2;
677 		new2 = tmp;
678 	}
679 
680 	/* line could be totally out of view */
681 	if ((new2.y < rect->lefttop.y) || (new1.y > rect->rightbottom.y))
682 		return 0;
683 
684 	m = (new2.x == new1.x) ? 0 :
685 		((float)(new2.y - new1.y) / (new2.x - new1.x));
686 
687 	if (new1.y < rect->lefttop.y) {
688 		if (m)
689 			new1.x += (int) ((rect->lefttop.y - new1.y)/m);
690 		new1.y = rect->lefttop.y;
691 	}
692 	if (new2.y > rect->rightbottom.y) {
693 		if (m)
694 			new2.x -= (int) ((new2.y - rect->rightbottom.y)/m);
695 		new2.y = rect->rightbottom.y;
696 	}
697 
698 
699 	/* clip x dimension */
700 
701 	/* p1 is left to p2 */
702 	if (new1.x > new2.x) {
703 		tmp = new1;
704 		new1 = new2;
705 		new2 = tmp;
706 	}
707 
708 	if ((new2.x < rect->lefttop.x) || (new1.x > rect->rightbottom.x))
709 		return 0;
710 
711 	m = (new2.x == new1.x) ? 0 :
712 		((float)(new2.y - new1.y) / (new2.x - new1.x));
713 
714 	if (new1.x < rect->lefttop.x) {
715 		new1.y += (int) ((rect->lefttop.y - new1.y)*m);
716 		new1.x = rect->lefttop.x;
717 	}
718 	if (new2.y > rect->rightbottom.y) {
719 		new2.y -= (int) ((new2.y - rect->rightbottom.y)*m);
720 		new2.x = rect->rightbottom.x;
721 	}
722 
723 
724 	/* push values */
725 
726 	*p1 = new1;
727 	*p2 = new2;
728 
729 	return 1;
730 }
731 
drawdoors(ModeInfo * mi)732 static void drawdoors(ModeInfo *mi)
733 {
734 	scooterstruct *sp = &scooters[MI_SCREEN(mi)];
735 	Display *display = MI_DISPLAY(mi);
736 	Window window = MI_WINDOW(mi);
737 	GC gc = MI_GC(mi);
738 	int width = MI_WIDTH(mi), height = MI_HEIGHT(mi),
739 		midx = width/2, midy = height/2;
740 	int i, j;
741 	Rect rect = { {0,0}, {0,0} };
742 
743         rect.rightbottom.x = width - 1;
744         rect.rightbottom.y = height - 1;
745 	XSetLineAttributes(display, gc, 2, LineSolid, CapNotLast, JoinRound);
746 
747 #ifdef _DRAW_ZELEMENTS
748         XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
749         for (i= sp->spectator_zelement; i<ztotal; i++) {
750                 register float proj;
751                 XPoint p;
752 
753                 proj = projection(sp, zelements[i].pos.z) * sp->aspect_scale;
754                 p.x = midx + (sp->zelements[i].pos.x * proj / SPACE_XY_FACTOR);
755                 p.y = midy - (sp->zelements[i].pos.y * proj / SPACE_XY_FACTOR);
756                 XDrawPoint(display, window, gc, p.x, p.y);
757         }
758 #endif
759 
760 	for (i = 0; i < sp->doorcount; i++) {
761 
762 		register float proj;
763 		XPoint lines[4], clip1, clip2;
764 
765 		door_3d(sp, &sp->doors[i]);
766 
767 		for (j=0; j<4; j++) {
768 			if (sp->doors[i].coords[j].z <= 0) break;
769 
770 			proj = projection(sp, sp->doors[i].coords[j].z) * sp->aspect_scale;
771 			lines[j].x = midx + (int) (sp->doors[i].coords[j].x *
772 				proj / SPACE_XY_FACTOR);
773 			lines[j].y = midy - (int) (sp->doors[i].coords[j].y *
774 				proj / SPACE_XY_FACTOR);
775 		}
776 		if (j<4) continue;
777 
778 		XSetForeground(display, gc, sp->doors[i].color);
779 
780 		for (j=0; j<4; j++) {
781 			clip1 = lines[j];
782 			clip2 = lines[(j+1)%4];
783 			if (clipline(&clip1, &clip2, &rect))
784 				XDrawLine(display, window, gc, clip1.x, clip1.y, clip2.x, clip2.y);
785 		}
786 	}
787 }
788 
drawstars(ModeInfo * mi)789 ENTRYPOINT void drawstars(ModeInfo *mi)
790 {
791 	scooterstruct *sp = &scooters[MI_SCREEN(mi)];
792 	Display *display = MI_DISPLAY(mi);
793 	Window window = MI_WINDOW(mi);
794 	GC gc = MI_GC(mi);
795 	int width = MI_WIDTH(mi), height = MI_HEIGHT(mi),
796 		midx = width/2, midy = height/2;
797 	int i;
798 
799 	for (i = 0; i < sp->starcount; i++) {
800 
801 		float proj;
802 		ZElement *ze = &sp->zelements[sp->stars[i].zelement];
803 		Vec3D tmpvec, coords;
804 		Angle3D tmpangle;
805 		XPoint lefttop, rightbottom;
806 
807 		if (!sp->stars[i].draw) continue;
808 
809 
810 		/* rotate star around its z-element, then add its position */
811 
812 		tmpangle.x = ze->angle.x -
813 			sp->zelements[sp->spectator_zelement].angle.x;
814 		tmpangle.y = ze->angle.y -
815 			sp->zelements[sp->spectator_zelement].angle.y;
816 		tmpangle.z = ze->angle.z -
817 			sp->zelements[sp->spectator_zelement].angle.z;
818 
819 		tmpvec.x = sp->stars[i].xpos;
820 		tmpvec.y = sp->stars[i].ypos;
821 		tmpvec.z = 0;
822 		rotate_3d(&tmpvec, &coords, &tmpangle);
823 		coords.x += ze->pos.x;
824 		coords.y += ze->pos.y;
825 		coords.z += ze->pos.z;
826 
827 		if (coords.z <= 0) continue;
828 
829 
830 		/* projection and clipping (trivial for a rectangle) */
831 
832 		proj = projection(sp, coords.z) * sp->aspect_scale;
833 
834 		lefttop.x = midx + (int) ((coords.x - sp->stars[i].width/2) *
835 			proj / SPACE_XY_FACTOR);
836 		lefttop.y = midy - (int) ((coords.y + sp->stars[i].height/2) *
837 			proj / SPACE_XY_FACTOR);
838 		if (lefttop.x < 0)
839 			lefttop.x = 0;
840 		else if (lefttop.x >= width)
841 			continue;
842 		if (lefttop.y < 0)
843 			lefttop.y = 0;
844 		else if (lefttop.y >= height)
845 			continue;
846 
847 		rightbottom.x = midx + (int) ((coords.x + sp->stars[i].width/2) *
848 			 proj / SPACE_XY_FACTOR);
849 		rightbottom.y = midy - (int) ((coords.y - sp->stars[i].height/2) *
850 			 proj / SPACE_XY_FACTOR);
851 		if (rightbottom.x < 0)
852 			continue;
853 		else if (rightbottom.x >= width)
854 			rightbottom.x = width - 1;
855 		if (rightbottom.y < 0)
856 			continue;
857 		else if (rightbottom.y >= height)
858 			rightbottom.y = height - 1;
859 
860 
861 		/* in white color, small stars look darker than big stars */
862 
863 		XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
864 
865 		if ((lefttop.x == rightbottom.x) &&
866 				(lefttop.y == rightbottom.y)) {
867 			/* star is exactly 1 pixel */
868 			XDrawPoint(display, window, gc, lefttop.x, lefttop.y);
869 		} else if ((rightbottom.x - lefttop.x) +
870 				(rightbottom.y - lefttop.y) == 1) {
871 			/* star is 2 pixels wide or high */
872 			XDrawPoint(display, window, gc,
873 				lefttop.x,     lefttop.y);
874 			XDrawPoint(display, window, gc,
875 				rightbottom.x, rightbottom.y);
876 		} else if ((rightbottom.x - lefttop.x == 1) &&
877 				(rightbottom.y - lefttop.y == 1)) {
878 
879 			/*  star is exactly 2x2 pixels.
880 			 *  a 2x2 rectangle should be drawn faster by plotting all 4 pixels
881 			 *  than by filling a rectangle (is this really so under X ?)
882 			 */
883 
884 			XDrawPoint(display, window, gc,
885 				lefttop.x,     lefttop.y);
886 			XDrawPoint(display, window, gc,
887 				rightbottom.x, lefttop.y);
888 			XDrawPoint(display, window, gc,
889 				lefttop.x,     rightbottom.y);
890 			XDrawPoint(display, window, gc,
891 				rightbottom.x, rightbottom.y);
892 		} else {
893 			XFillRectangle(display, window, gc,
894 				lefttop.x, lefttop.y,
895 				rightbottom.x - lefttop.x,
896 				rightbottom.y - lefttop.y);
897 		}
898 	}
899 }
900 
draw_scooter(ModeInfo * mi)901 ENTRYPOINT void draw_scooter(ModeInfo *mi)
902 {
903 	scooterstruct *sp;
904 
905 	if (scooters == NULL)
906 		return;
907 	sp = &scooters[MI_SCREEN(mi)];
908 	if (sp->doors == NULL)
909 		return;
910 
911 	cleardoors(mi);
912 
913 	shift_elements(mi);
914 
915 	/* With these scale factors, all doors are sized correctly for any window dimension.
916 	 * If aspect ratio is not 4:3, the smaller part of the window is used, e.g.:
917 	 *      window = 1000x600
918 	 *      => door scale factor is like in a 800x600 window (not 1000x750)
919 	 */
920 
921 	if ((float)MI_WIDTH(mi)/MI_HEIGHT(mi) >=
922 			(float)ASPECT_SCREENWIDTH/ASPECT_SCREENHEIGHT) {
923 		/* window is wider than or equal 4:3 */
924 		sp->aspect_scale = (float)MI_HEIGHT(mi) / ASPECT_SCREENHEIGHT;
925 	} else {
926 		/* window is higher than 4:3 */
927 		sp->aspect_scale = (float)MI_WIDTH(mi) / ASPECT_SCREENWIDTH;
928 	}
929 
930 	drawstars(mi);
931 
932 	drawdoors(mi);
933 }
934 
935 #if 0
936 ENTRYPOINT void refresh_scooter(ModeInfo *mi)
937 {
938 	/* MI_CLEARWINDOW(mi); */
939 	XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
940 }
941 
942 ENTRYPOINT void change_scooter(ModeInfo *mi)
943 {
944 	scooterstruct *sp;
945 
946 	if (scooters == NULL)
947 		return;
948 	sp = &scooters[MI_SCREEN(mi)];
949 
950 	sp->halt_scooter = !sp->halt_scooter;
951 }
952 #endif
953 
954 #define free_scooter 0
955 XSCREENSAVER_MODULE ("Scooter", scooter)
956 
957 #endif /* MODE_scooter */
958