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