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