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