1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* kaleid --- Brewster's Kaleidoscope */
3 
4 #if 0
5 static const char sccsid[] = "@(#)kaleid.c	5.00 2000/11/01 xlockmore";
6 
7 #endif
8 
9 /*-
10  *
11  *  kaleid.c - Brewster's Kaleidoscope  (Sir David Brewster invented the
12  *     kaleidoscope in 1816 and patented it in 1817.)
13  *
14  *  Copyright (c) 1998 by Robert Adam, II  <raii@comm.net>
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  * Revision History:
29  * 01-Nov-2000: Allocation checks
30  * 1998: Written
31  *
32  *
33  *  -batchcount n     number of pens [default 4]
34  *
35  *  -cycle n          percentage of black in the pattern (0%-95%)
36  *
37  *  -size n          symmetry mode [default -9]
38  *                         <0     = random from 0 to -number
39  *                         >0     = mirrored                  (-alternate)
40  *                                  rotated                   (+alternate)
41  *
42  *
43  *  -/+disconnected   turn on/off disconnected pen movement
44  *
45  *  -/+serial         turn on/off sequential allocation of colors
46  *
47  *  -/+alternate      turn on/off alternate display mode
48  *
49  *  -/+spiral         turn on/off spiral mode
50  *
51  *  -/+spots          turn on/off spots mode
52  *
53  *  -/+quad           turn on/off quad mirrored/rotated mode similar
54  *                       to size 4, works with alternate
55  *
56  *  -/+oct            turn on/off oct mirrored/rotated moded similar
57  *                       to size 8, works with alternate
58  *
59  *  -/+linear         select Cartesian/Polar coordinate mode. Cartesian
60  *                       uses straight lines similar to -oct and -quad
61  *                       mode instead of the curved lines of Polar mode.
62  *                       [default off]
63  *
64  */
65 
66 #ifdef STANDALONE
67 #define MODE_kaleid
68 #define DEFAULTS "*delay: 80000 \n" \
69 	"*count: 4 \n" \
70 	"*cycles: 40 \n" \
71 	"*size: -9 \n" \
72 	"*ncolors: 200 \n" \
73 	"*fullrandom: False \n" \
74 
75 # define free_kaleid 0
76 # define reshape_kaleid 0
77 # define kaleid_handle_event 0
78 #define UNIFORM_COLORS
79 #define BRIGHT_COLORS
80 #include "xlockmore.h"		/* in xscreensaver distribution */
81 #else /* STANDALONE */
82 #include "xlock.h"		/* in xlockmore distribution */
83 #endif /* STANDALONE */
84 
85 #ifdef MODE_kaleid
86 
87 #include <math.h>
88 
89 #define DEF_DISCONNECTED  "True"
90 #define DEF_SERIAL        "False"
91 #define DEF_ALTERNATE     "False"
92 #define DEF_QUAD          "False"
93 #define DEF_OCT           "False"
94 #define DEF_LINEAR        "False"
95 #define DEF_SPIRAL        "False"
96 #define DEF_SPOTS         "False"
97 
98 static Bool Disconnected;
99 static Bool Serial;
100 static Bool Alternate;
101 static Bool Quad;
102 static Bool Oct;
103 static Bool Linear;
104 static Bool Spiral;
105 static Bool Spots;
106 
107 static XrmOptionDescRec opts[] =
108 {
109    {(char *) "-disconnected", (char *) ".kaleid.disconnected", XrmoptionNoArg, (caddr_t) "on"},
110   {(char *) "+disconnected", (char *) ".kaleid.disconnected", XrmoptionNoArg, (caddr_t) "off"},
111 	{(char *) "-serial", (char *) ".kaleid.serial", XrmoptionNoArg, (caddr_t) "on"},
112 	{(char *) "+serial", (char *) ".kaleid.serial", XrmoptionNoArg, (caddr_t) "off"},
113 	{(char *) "-alternate", (char *) ".kaleid.alternate", XrmoptionNoArg, (caddr_t) "on"},
114 	{(char *) "+alternate", (char *) ".kaleid.alternate", XrmoptionNoArg, (caddr_t) "off"},
115 	{(char *) "-spiral", (char *) ".kaleid.spiral", XrmoptionNoArg, (caddr_t) "on"},
116 	{(char *) "+spiral", (char *) ".kaleid.spiral", XrmoptionNoArg, (caddr_t) "off"},
117 	{(char *) "-spots", (char *) ".kaleid.spots", XrmoptionNoArg, (caddr_t) "on"},
118 	{(char *) "+spots", (char *) ".kaleid.spots", XrmoptionNoArg, (caddr_t) "off"},
119 	{(char *) "-quad", (char *) ".kaleid.quad", XrmoptionNoArg, (caddr_t) "on"},
120 	{(char *) "+quad", (char *) ".kaleid.quad", XrmoptionNoArg, (caddr_t) "off"},
121 	{(char *) "-oct", (char *) ".kaleid.oct", XrmoptionNoArg, (caddr_t) "on"},
122 	{(char *) "+oct", (char *) ".kaleid.oct", XrmoptionNoArg, (caddr_t) "off"},
123 	{(char *) "-linear", (char *) ".kaleid.linear", XrmoptionNoArg, (caddr_t) "on"},
124 	{(char *) "+linear", (char *) ".kaleid.linear", XrmoptionNoArg, (caddr_t) "off"}
125 };
126 
127 static argtype vars[] =
128 {
129 	{(void *) & Disconnected, (char *) "disconnected", (char *) "Disconnected", (char *) DEF_DISCONNECTED, t_Bool},
130 	{(void *) & Serial, (char *) "serial", (char *) "Serial", (char *) DEF_SERIAL, t_Bool},
131 	{(void *) & Alternate, (char *) "alternate", (char *) "Alternate", (char *) DEF_ALTERNATE, t_Bool},
132 	{(void *) & Spiral, (char *) "spiral", (char *) "Spiral", (char *) DEF_SPIRAL, t_Bool},
133 	{(void *) & Spots, (char *) "spots", (char *) "Spots", (char *) DEF_SPOTS, t_Bool},
134 	{(void *) & Quad, (char *) "quad", (char *) "Quad", (char *) DEF_QUAD, t_Bool},
135 	{(void *) & Oct, (char *) "oct", (char *) "Oct", (char *) DEF_OCT, t_Bool},
136 	{(void *) & Linear, (char *) "linear", (char *) "Linear", (char *) DEF_LINEAR, t_Bool}
137 };
138 
139 static OptionStruct desc[] =
140 {
141 	{(char *) "-/+disconnected", (char *) "turn on/off disconnected pen movement"},
142 	{(char *) "-/+serial", (char *) "turn on/off sequential color selection"},
143 	{(char *) "-/+alternate", (char *) "turn on/off alternate display mode"},
144 	{(char *) "-/+spiral", (char *) "turn on/off angular bounding mode"},
145 	{(char *) "-/+spots", (char *) "turn on/off circle mode"},
146 	{(char *) "-/+quad", (char *) "turn on/off quad mirrored display mode"},
147 	{(char *) "-/+oct", (char *) "turn on/off oct mirrored display mode"},
148 	{(char *) "-/+linear", (char *) "select Cartesian/Polar coordinate display mode"}
149 };
150 
151 ENTRYPOINT ModeSpecOpt kaleid_opts =
152 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
153 
154 #ifdef USE_MODULES
155 ModStruct   kaleid_description =
156 {
157 	"kaleid", "init_kaleid", "draw_kaleid", "release_kaleid",
158 	"refresh_kaleid", "init_kaleid", (char *) NULL, &kaleid_opts,
159 	80000, 4, 40, -9, 64, 0.6, "",
160 	"Shows Brewster's Kaleidoscope", 0, NULL
161 };
162 
163 #endif
164 
165 #define INTRAND(min,max) (NRAND(((max+1)-(min)))+(min))
166 
167 #define MINPENS 1
168 #define MINSIZE 1
169 #define QUAD (-4)
170 #define OCT (-8)
171 
172 #define SpotsMult           3
173 
174 #define MinVelocity         6
175 #define MaxVelocity         4
176 
177 #define MinRadialVelocity   6
178 #define MaxRadialVelocity   30
179 
180 #define MinAngularVelocity  5
181 #define MaxAngularVelocity  3
182 
183 #define WidthPercent  25
184 #define ChangeChance  2
185 
186 #define ToRadians 0.017453293
187 #define ToDegrees 57.29577951
188 #define PolarToCartX(r, t) ((r) * cos((t) * ToRadians))
189 #define PolarToCartY(r, t) ((r) * sin((t) * ToRadians))
190 #define CartToRadius(x, y) (sqrt(((x)*(x)) + ((y)*(y))))
191 #define CartToAngle(x,y)   ((((x) == 0.0) && ((y) == 0.0)) ? 0.0 : (atan2((y),(x)) * ToDegrees))
192 
193 typedef struct {
194 	double      cx;
195 	double      cy;
196 	double      ox;
197 	double      oy;
198 	double      xv;
199 	double      yv;
200 	int         curlwidth;
201 	int         pix;
202 	int         White;
203 	Bool        RadiusOut;
204 	Bool        AngleOut;
205 	Bool        DeferredChange;
206 } penstruct;
207 
208 typedef struct {
209 	penstruct  *pen;
210 
211 	int         PercentBlack;
212 	int         PenCount;
213 	int         maxlwidth;
214 	double      width, widthby2;
215 	double      height, heightby2;
216 	double      radius;
217 	double      slice;
218 	int         bouncetype;
219 	int         modetype;
220 	Bool        alternate, disconnected, serial, linear, spiral, spots;
221 } kaleidstruct;
222 
223 static kaleidstruct *kaleids = (kaleidstruct *) NULL;
224 
225 /*-
226  *
227  */
228 static void
QuadMirrored(kaleidstruct * kp,int pn,XSegment segs[4])229 QuadMirrored(
230 		    kaleidstruct * kp,
231 		    int pn,
232 		    XSegment segs[4]
233 )
234 {
235 	kp->pen[pn].cx = kp->pen[pn].cx + kp->pen[pn].xv;
236 	if (kp->pen[pn].cx < 0.0) {
237 		kp->pen[pn].cx = -kp->pen[pn].cx;
238 		kp->pen[pn].xv = -kp->pen[pn].xv;
239 	} else if (kp->pen[pn].cx >= kp->widthby2) {
240 		kp->pen[pn].cx = (kp->widthby2 - 1.0)
241 			- (kp->pen[pn].cx - kp->widthby2);
242 		kp->pen[pn].xv = -kp->pen[pn].xv;
243 	}
244 	kp->pen[pn].cy = kp->pen[pn].cy + kp->pen[pn].yv;
245 	if (kp->pen[pn].cy < 0.0) {
246 		kp->pen[pn].cy = -kp->pen[pn].cy;
247 		kp->pen[pn].yv = -kp->pen[pn].yv;
248 	} else if (kp->pen[pn].cy >= kp->heightby2) {
249 		kp->pen[pn].cy = (kp->heightby2 - 1.0)
250 			- (kp->pen[pn].cy - kp->heightby2);
251 		kp->pen[pn].yv = -kp->pen[pn].yv;
252 	}
253 	segs[0].x1 = (int) kp->pen[pn].ox;
254 	segs[0].y1 = (int) kp->pen[pn].oy;
255 	segs[0].x2 = (int) kp->pen[pn].cx;
256 	segs[0].y2 = (int) kp->pen[pn].cy;
257 
258 	segs[1].x1 = (int) (kp->width - kp->pen[pn].ox);
259 	segs[1].y1 = (int) kp->pen[pn].oy;
260 	segs[1].x2 = (int) (kp->width - kp->pen[pn].cx);
261 	segs[1].y2 = (int) kp->pen[pn].cy;
262 
263 	segs[2].x1 = (int) (kp->width - kp->pen[pn].ox);
264 	segs[2].y1 = (int) (kp->height - kp->pen[pn].oy);
265 	segs[2].x2 = (int) (kp->width - kp->pen[pn].cx);
266 	segs[2].y2 = (int) (kp->height - kp->pen[pn].cy);
267 
268 	segs[3].x1 = (int) kp->pen[pn].ox;
269 	segs[3].y1 = (int) (kp->height - kp->pen[pn].oy);
270 	segs[3].x2 = (int) kp->pen[pn].cx;
271 	segs[3].y2 = (int) (kp->height - kp->pen[pn].cy);
272 
273 }
274 
275 /*-
276  *
277  */
278 static void
QuadRotated(kaleidstruct * kp,int pn,XSegment segs[4])279 QuadRotated(
280 		   kaleidstruct * kp,
281 		   int pn,
282 		   XSegment segs[4]
283 )
284 {
285 	double      oxscaled2y;
286 	double      oyscaled2x;
287 	double      cxscaled2y;
288 	double      cyscaled2x;
289 
290 	kp->pen[pn].cx = kp->pen[pn].cx + kp->pen[pn].xv;
291 	if (kp->pen[pn].cx < 0.0) {
292 		kp->pen[pn].cx = -kp->pen[pn].cx;
293 		kp->pen[pn].xv = -kp->pen[pn].xv;
294 	} else if (kp->pen[pn].cx >= kp->widthby2) {
295 		kp->pen[pn].cx = (kp->widthby2 - 1.0)
296 			- (kp->pen[pn].cx - kp->widthby2);
297 		kp->pen[pn].xv = -kp->pen[pn].xv;
298 	}
299 	kp->pen[pn].cy = kp->pen[pn].cy + kp->pen[pn].yv;
300 	if (kp->pen[pn].cy < 0.0) {
301 		kp->pen[pn].cy = -kp->pen[pn].cy;
302 		kp->pen[pn].yv = -kp->pen[pn].yv;
303 	} else if (kp->pen[pn].cy >= kp->heightby2) {
304 		kp->pen[pn].cy = (kp->heightby2 - 1.0)
305 			- (kp->pen[pn].cy - kp->heightby2);
306 		kp->pen[pn].yv = -kp->pen[pn].yv;
307 	}
308 	segs[0].x1 = (int) kp->pen[pn].ox;
309 	segs[0].y1 = (int) kp->pen[pn].oy;
310 	segs[0].x2 = (int) kp->pen[pn].cx;
311 	segs[0].y2 = (int) kp->pen[pn].cy;
312 
313 	oxscaled2y = ((kp->pen[pn].ox * kp->heightby2) / kp->widthby2);
314 	oyscaled2x = ((kp->pen[pn].oy * kp->widthby2) / kp->heightby2);
315 	cxscaled2y = ((kp->pen[pn].cx * kp->heightby2) / kp->widthby2);
316 	cyscaled2x = ((kp->pen[pn].cy * kp->widthby2) / kp->heightby2);
317 
318 	segs[1].x1 = (int) (kp->width - oyscaled2x);
319 	segs[1].y1 = (int) oxscaled2y;
320 	segs[1].x2 = (int) (kp->width - cyscaled2x);
321 	segs[1].y2 = (int) cxscaled2y;
322 
323 	segs[2].x1 = (int) (kp->width - kp->pen[pn].ox);
324 	segs[2].y1 = (int) (kp->height - kp->pen[pn].oy);
325 	segs[2].x2 = (int) (kp->width - kp->pen[pn].cx);
326 	segs[2].y2 = (int) (kp->height - kp->pen[pn].cy);
327 
328 	segs[3].x1 = (int) oyscaled2x;
329 	segs[3].y1 = (int) (kp->height - oxscaled2y);
330 	segs[3].x2 = (int) cyscaled2x;
331 	segs[3].y2 = (int) (kp->height - cxscaled2y);
332 
333 }
334 
335 /*-
336  *
337  */
338 static void
GeneralPolarMoveAndBounce(kaleidstruct * kp,int pn)339 GeneralPolarMoveAndBounce(
340 				 kaleidstruct * kp,
341 				 int pn
342 )
343 {
344 	kp->pen[pn].cx = kp->pen[pn].cx + kp->pen[pn].xv;
345 	if (kp->pen[pn].cx < 0.0) {
346 	    kp->pen[pn].cx = -kp->pen[pn].cx;
347 		kp->pen[pn].xv = -kp->pen[pn].xv;
348 	} else if (kp->pen[pn].cx >= kp->radius) {
349 	    kp->pen[pn].cx = (kp->radius - 1.0)
350 		    - (kp->pen[pn].cx - kp->radius);
351 		kp->pen[pn].xv = -kp->pen[pn].xv;
352 	}
353 	switch (kp->bouncetype) {
354 		case 0:
355 			{
356 				kp->pen[pn].cy = kp->pen[pn].cy + kp->pen[pn].yv;
357 				break;
358 			}
359 		case 1:
360 			{
361 				kp->pen[pn].cy = kp->pen[pn].cy
362 					+ ((kp->pen[pn].yv * kp->pen[pn].cx) / kp->radius);
363 				break;
364 			}
365 		case 2:
366 			{
367 				kp->pen[pn].cy = kp->pen[pn].cy
368 					+ ((kp->pen[pn].yv * (kp->radius - kp->pen[pn].cx)) / kp->radius);
369 				break;
370 			}
371 		default:
372 			{
373 				kp->pen[pn].cy = kp->slice / 2.0;
374 				kp->pen[pn].cx = kp->radius / 2.0;
375 				break;
376 			}
377 	}
378 
379 	if (kp->pen[pn].cy < 0) {
380 	    if (kp->spiral) {
381 		    kp->pen[pn].RadiusOut = False;
382 		    kp->pen[pn].cy = kp->pen[pn].cy + 360.0;
383 		} else {
384 		    kp->pen[pn].RadiusOut = True;
385 			if (kp->pen[pn].oy >= 0) {
386 			    kp->pen[pn].yv = -kp->pen[pn].yv;
387 			}
388 		}
389 	} else if (kp->spiral) {
390 	    if (kp->pen[pn].cy > 360.0) {
391 		    kp->pen[pn].RadiusOut = False;
392 			kp->pen[pn].cy = kp->pen[pn].cy - 360.0;
393 		}
394 	} else if (kp->pen[pn].cy >= kp->slice) {
395 	    kp->pen[pn].RadiusOut = True;
396 		if (kp->pen[pn].oy < kp->slice) {
397 		    kp->pen[pn].yv = -kp->pen[pn].yv;
398 		}
399 	} else {
400 		kp->pen[pn].RadiusOut = False;
401 	}
402 
403 }
404 
405 
406 /*-
407  *
408  */
409 static void
GeneralRotated(kaleidstruct * kp,int pn,XSegment * segs)410 GeneralRotated(
411 		      kaleidstruct * kp,
412 		      int pn,
413 		      XSegment * segs
414 )
415 {
416 	double      Angle;
417 	int         segnum;
418 
419 
420 	GeneralPolarMoveAndBounce(kp, pn);
421 
422 	Angle = 0.0;
423 	for (segnum = 0; segnum < kp->modetype; segnum += 1) {
424 	    if (! kp->spots) {
425 		    segs[segnum].x1 = (int) (PolarToCartX(kp->pen[pn].ox,
426 								  kp->pen[pn].oy + Angle
427 					     ) + kp->widthby2
428 		        );
429 
430 			segs[segnum].y1 = (int) (PolarToCartY(kp->pen[pn].ox,
431 								  kp->pen[pn].oy + Angle
432 						 ) + kp->heightby2
433 				);
434 		}
435 		segs[segnum].x2 = (int) (PolarToCartX(kp->pen[pn].cx,
436 						      kp->pen[pn].cy + Angle
437 					 ) + kp->widthby2
438 			);
439 		segs[segnum].y2 = (int) (PolarToCartY(kp->pen[pn].cx,
440 						      kp->pen[pn].cy + Angle
441 					 ) + kp->heightby2
442 			);
443 
444 		if (kp->spots) {
445 		    segs[segnum].x1 = segs[segnum].x2;
446 		    segs[segnum].y1 = segs[segnum].y2;
447 		}
448 
449 		Angle += kp->slice;
450 	}
451 }
452 
453 /*-
454  *
455  */
456 static void
GeneralMirrored(kaleidstruct * kp,int pn,XSegment * segs)457 GeneralMirrored(
458 		       kaleidstruct * kp,
459 		       int pn,
460 		       XSegment * segs
461 )
462 {
463 	double      Angle;
464 	int         segnum;
465 
466 	GeneralPolarMoveAndBounce(kp, pn);
467 
468 	Angle = 0.0;
469 	for (segnum = 0; segnum < kp->modetype; segnum += 2) {
470 	    if (! kp->spots) {
471 		    segs[segnum].x1 = (int) (PolarToCartX(kp->pen[pn].ox,
472 								  kp->pen[pn].oy + Angle
473 						 ) + kp->widthby2
474 				);
475 
476 			segs[segnum].y1 = (int) (PolarToCartY(kp->pen[pn].ox,
477 								  kp->pen[pn].oy + Angle
478 						 ) + kp->heightby2
479 				);
480         }
481 
482 		segs[segnum].x2 = (int) (PolarToCartX(kp->pen[pn].cx,
483 						      kp->pen[pn].cy + Angle
484 					 ) + kp->widthby2
485 			);
486 
487 		segs[segnum].y2 = (int) (PolarToCartY(kp->pen[pn].cx,
488 						      kp->pen[pn].cy + Angle
489 					 ) + kp->heightby2
490 			);
491 
492 		if (kp->spots) {
493 		    segs[segnum].x1 = segs[segnum].x2;
494 		    segs[segnum].y1 = segs[segnum].y2;
495 		}
496 
497 		Angle += (2.0 * kp->slice);
498 	}
499 
500 	Angle = 360.0 - kp->slice;
501 	for (segnum = 1; segnum < kp->modetype; segnum += 2) {
502 	    if (! kp->spots) {
503 		    segs[segnum].x1 =
504 			    (int) (PolarToCartX(kp->pen[pn].ox,
505 						 (kp->slice - kp->pen[pn].oy) + Angle
506 					   ) + kp->widthby2
507 			    );
508 
509 			segs[segnum].y1 =
510 			    (int) (PolarToCartY(kp->pen[pn].ox,
511 					     (kp->slice - kp->pen[pn].oy) + Angle
512 					   ) + kp->heightby2
513 				);
514 		}
515 
516 		segs[segnum].x2 =
517 			(int) (PolarToCartX(kp->pen[pn].cx,
518 					 (kp->slice - kp->pen[pn].cy) + Angle
519 			       ) + kp->widthby2
520 			);
521 
522 		segs[segnum].y2 =
523 			(int) (PolarToCartY(kp->pen[pn].cx,
524 					 (kp->slice - kp->pen[pn].cy) + Angle
525 			       ) + kp->heightby2
526 			);
527 
528 		if (kp->spots) {
529 		    segs[segnum].x1 = segs[segnum].x2;
530 		    segs[segnum].y1 = segs[segnum].y2;
531 		}
532 		Angle -= (2.0 * kp->slice);
533 	}
534 }
535 
536 /*-
537  *
538  */
539 static void
OctMirrored(kaleidstruct * kp,int pn,XSegment * segs)540 OctMirrored(
541 		   kaleidstruct * kp,
542 		   int pn,
543 		   XSegment * segs
544 )
545 {
546 	double      xdiag;
547 	double      ydiag;
548 	double      oxscaled2y, cxscaled2y;
549 	double      oyscaled2x, cyscaled2x;
550 
551 	/*
552 	 *  I know that the "bounce" is not really accurate, but I like the way
553 	 * it looks.
554 	 */
555 
556 	xdiag = (kp->widthby2 * kp->pen[pn].oy) / kp->heightby2;
557 
558 	kp->pen[pn].cx = kp->pen[pn].cx + kp->pen[pn].xv;
559 	if (kp->pen[pn].cx < xdiag) {
560 		kp->pen[pn].cx = xdiag + (xdiag - kp->pen[pn].cx);
561 		kp->pen[pn].xv = -kp->pen[pn].xv;
562 	} else if (kp->pen[pn].cx >= kp->widthby2) {
563 		kp->pen[pn].cx = (kp->widthby2 - 1.0)
564 			- (kp->pen[pn].cx - kp->widthby2);
565 		kp->pen[pn].xv = -kp->pen[pn].xv;
566 	}
567 	ydiag = (kp->heightby2 * kp->pen[pn].cx) / kp->widthby2;
568 
569 	kp->pen[pn].cy = kp->pen[pn].cy + kp->pen[pn].yv;
570 	if (kp->pen[pn].cy < 0.0) {
571 		kp->pen[pn].cy = -kp->pen[pn].cy;
572 		kp->pen[pn].yv = -kp->pen[pn].yv;
573 	} else if (kp->pen[pn].cy > ydiag) {
574 		kp->pen[pn].cy = ydiag - (kp->pen[pn].cy - ydiag);
575 		kp->pen[pn].yv = -kp->pen[pn].yv;
576 	}
577 	segs[0].x1 = (int) kp->pen[pn].ox;
578 	segs[0].y1 = (int) kp->pen[pn].oy;
579 	segs[0].x2 = (int) kp->pen[pn].cx;
580 	segs[0].y2 = (int) kp->pen[pn].cy;
581 
582 	segs[1].x1 = (int) (kp->width - kp->pen[pn].ox);
583 	segs[1].y1 = (int) kp->pen[pn].oy;
584 	segs[1].x2 = (int) (kp->width - kp->pen[pn].cx);
585 	segs[1].y2 = (int) kp->pen[pn].cy;
586 
587 	segs[4].x1 = segs[1].x1;
588 	segs[4].y1 = (int) kp->height - segs[0].y1;
589 	segs[4].x2 = segs[1].x2;
590 	segs[4].y2 = (int) kp->height - segs[0].y2;
591 
592 	segs[5].x1 = segs[0].x1;
593 	segs[5].y1 = (int) kp->height - segs[0].y1;
594 	segs[5].x2 = segs[0].x2;
595 	segs[5].y2 = (int) kp->height - segs[0].y2;
596 
597 
598 	oxscaled2y = ((kp->pen[pn].ox * kp->heightby2) / kp->widthby2);
599 	oyscaled2x = ((kp->pen[pn].oy * kp->widthby2) / kp->heightby2);
600 	cxscaled2y = ((kp->pen[pn].cx * kp->heightby2) / kp->widthby2);
601 	cyscaled2x = ((kp->pen[pn].cy * kp->widthby2) / kp->heightby2);
602 
603 
604 	segs[7].x1 = (int) oyscaled2x;
605 	segs[7].y1 = (int) oxscaled2y;
606 	segs[7].x2 = (int) cyscaled2x;
607 	segs[7].y2 = (int) cxscaled2y;
608 
609 
610 	segs[2].x1 = (int) kp->width - segs[7].x1;
611 	segs[2].y1 = segs[7].y1;
612 	segs[2].x2 = (int) kp->width - segs[7].x2;
613 	segs[2].y2 = segs[7].y2;
614 
615 	segs[6].x1 = segs[7].x1;
616 	segs[6].y1 = (int) kp->height - segs[7].y1;
617 	segs[6].x2 = segs[7].x2;
618 	segs[6].y2 = (int) kp->height - segs[7].y2;
619 
620 	segs[3].x1 = (int) kp->width - segs[7].x1;
621 	segs[3].y1 = segs[6].y1;
622 	segs[3].x2 = (int) kp->width - segs[7].x2;
623 	segs[3].y2 = segs[6].y2;
624 
625 }
626 
627 
628 
629 /*-
630  *
631  */
632 #if 0
633 static void
634 OldOctRotated(
635 		     kaleidstruct * kp,
636 		     int pn,
637 		     XSegment * segs
638 )
639 {
640 	double      angle, radius;
641 	double      oangle, oradius;
642 	double      rv, av;
643 	double      perp, para;
644 	int         i;
645 
646 	/*
647 	 *  I know that the "bounce" is not really accurate, but I like the way
648 	 * it looks.
649 	 */
650 
651 	kp->pen[pn].cx = kp->pen[pn].cx + kp->pen[pn].xv;
652 	kp->pen[pn].cy = kp->pen[pn].cy + kp->pen[pn].yv;
653 	angle = CartToAngle(kp->pen[pn].cx, kp->pen[pn].cy);
654 	radius = CartToRadius(kp->pen[pn].cx, kp->pen[pn].cy);
655 
656 	oangle = CartToAngle(kp->pen[pn].ox, kp->pen[pn].oy);
657 	oradius = CartToRadius(kp->pen[pn].ox, kp->pen[pn].oy);
658 
659 	if (radius < 0.0) {
660 		if (kp->pen[pn].xv < 0.0) {
661 			kp->pen[pn].xv = -kp->pen[pn].xv;
662 		}
663 	} else if (radius > kp->radius) {
664 		if (kp->pen[pn].xv > 0.0) {
665 			kp->pen[pn].xv = -kp->pen[pn].xv;
666 		}
667 	}
668 	if (angle < 0.0) {
669 		if (oangle > 0.0) {
670 			kp->pen[pn].yv = -kp->pen[pn].yv;
671 		}
672 	} else if (angle > 45.0) {
673 		if (oangle < 45.0) {
674 			rv = CartToRadius(kp->pen[pn].xv, kp->pen[pn].yv);
675 			av = CartToAngle(kp->pen[pn].xv, kp->pen[pn].yv);
676 
677 			para = PolarToCartX(rv, av - 45.0);
678 			perp = PolarToCartY(rv, av - 45.0);
679 
680 			rv = CartToRadius(para, -perp);
681 			av = CartToAngle(para, -perp);
682 
683 			kp->pen[pn].xv = PolarToCartX(rv, av + 45.0);
684 			kp->pen[pn].yv = PolarToCartY(rv, av + 45.0);
685 		}
686 	}
687 	for (i = 0; i < 8; i += 1) {
688 		segs[i].x1 = (int) (kp->widthby2 + PolarToCartX(oradius, oangle));
689 		segs[i].y1 = (int) (kp->heightby2 - PolarToCartY(oradius, oangle));
690 		segs[i].x2 = (int) (kp->widthby2 + PolarToCartX(radius, angle));
691 		segs[i].y2 = (int) (kp->heightby2 - PolarToCartY(radius, angle));
692 
693 		oangle += 45.0;
694 		angle += 45.0;
695 	}
696 }
697 
698 #endif
699 
700 /*-
701  *
702  */
703 static void
OctRotated(kaleidstruct * kp,int pn,XSegment * segs)704 OctRotated(
705 		  kaleidstruct * kp,
706 		  int pn,
707 		  XSegment * segs
708 )
709 {
710 	double      xdiag;
711 	double      ydiag;
712 	double      radius, angle;
713 	double      oradius, oangle;
714 	double      ox, oy, cx, cy;
715 	double      offset;
716 	int         i;
717 
718 	/*
719 	 *  I know that the "bounce" is not really accurate, but I like the way
720 	 * it looks.
721 	 */
722 
723 	xdiag = (kp->widthby2 * kp->pen[pn].oy) / kp->heightby2;
724 
725 	kp->pen[pn].cx = kp->pen[pn].cx + kp->pen[pn].xv;
726 	if (kp->pen[pn].cx < xdiag) {
727 		kp->pen[pn].cx = xdiag + (xdiag - kp->pen[pn].cx);
728 		kp->pen[pn].xv = -kp->pen[pn].xv;
729 	} else if (kp->pen[pn].cx >= kp->widthby2) {
730 		kp->pen[pn].cx = (kp->widthby2 - 1.0)
731 			- (kp->pen[pn].cx - kp->widthby2);
732 		kp->pen[pn].xv = -kp->pen[pn].xv;
733 	}
734 	ydiag = (kp->heightby2 * kp->pen[pn].cx) / kp->widthby2;
735 
736 	kp->pen[pn].cy = kp->pen[pn].cy + kp->pen[pn].yv;
737 	if (kp->pen[pn].cy < 0.0) {
738 		kp->pen[pn].cy = -kp->pen[pn].cy;
739 		kp->pen[pn].yv = -kp->pen[pn].yv;
740 	} else if (kp->pen[pn].cy > ydiag) {
741 		kp->pen[pn].cy = ydiag - (kp->pen[pn].cy - ydiag);
742 		kp->pen[pn].yv = -kp->pen[pn].yv;
743 	}
744 	offset = CartToRadius(kp->heightby2, kp->widthby2);
745 	ox = (kp->pen[pn].ox * offset) / kp->widthby2;
746 	oy = (kp->pen[pn].oy * offset) / kp->heightby2;
747 	cx = (kp->pen[pn].cx * offset) / kp->widthby2;
748 	cy = (kp->pen[pn].cy * offset) / kp->heightby2;
749 
750 	angle = CartToAngle(cx - offset,
751 			    offset - cy
752 		) - 90.0;
753 	radius = CartToRadius(cx - offset,
754 			      offset - cy
755 		);
756 
757 	oangle = CartToAngle(ox - offset,
758 			     offset - oy
759 		) - 90.0;
760 	oradius = CartToRadius(ox - offset,
761 			       offset - oy
762 		);
763 
764 
765 	for (i = 0; i < 8; i += 1) {
766 		segs[i].x1 = (int) (kp->widthby2 + PolarToCartX(oradius, oangle));
767 		segs[i].y1 = (int) (kp->heightby2 - PolarToCartY(oradius, oangle));
768 		segs[i].x2 = (int) (kp->widthby2 + PolarToCartX(radius, angle));
769 		segs[i].y2 = (int) (kp->heightby2 - PolarToCartY(radius, angle));
770 
771 		oangle += 45.0;
772 		angle += 45.0;
773 	}
774 
775 }
776 
777 /*-
778  *
779  */
780 static void
GeneralLinearMoveAndBounce(kaleidstruct * kp,int pn,double * angle,double * radius,double * oangle,double * oradius)781 GeneralLinearMoveAndBounce(
782 				  kaleidstruct * kp,
783 				  int pn,
784 				  double *angle,
785 				  double *radius,
786 				  double *oangle,
787 				  double *oradius
788 )
789 {
790 	double      rv, av;
791 	double      perp, para;
792 
793 	kp->pen[pn].cx = kp->pen[pn].cx + kp->pen[pn].xv;
794 	kp->pen[pn].cy = kp->pen[pn].cy + kp->pen[pn].yv;
795 
796 	*angle = CartToAngle(kp->pen[pn].cx, kp->pen[pn].cy);
797 	*radius = CartToRadius(kp->pen[pn].cx, kp->pen[pn].cy);
798 
799 	*oangle = CartToAngle(kp->pen[pn].ox, kp->pen[pn].oy);
800 	*oradius = CartToRadius(kp->pen[pn].ox, kp->pen[pn].oy);
801 
802 	if (*radius < 0.0) {
803 		kp->pen[pn].RadiusOut = True;
804 		if (*oradius > 0.0) {
805 			rv = CartToRadius(kp->pen[pn].xv, kp->pen[pn].yv);
806 			av = CartToAngle(kp->pen[pn].xv, kp->pen[pn].yv);
807 
808 			para = PolarToCartX(rv, av - (*angle + 90.0));
809 			perp = PolarToCartY(rv, av - (*angle + 90.0));
810 
811 			rv = CartToRadius(para, -perp);
812 			av = CartToAngle(para, -perp);
813 
814 			kp->pen[pn].xv = PolarToCartX(rv, av + (*angle + 90.0));
815 			kp->pen[pn].yv = PolarToCartY(rv, av + (*angle + 90.0));
816 		}
817 	} else if (*radius > kp->radius) {
818 		kp->pen[pn].RadiusOut = True;
819 		if (*oradius < kp->radius) {
820 			rv = CartToRadius(kp->pen[pn].xv, kp->pen[pn].yv);
821 			av = CartToAngle(kp->pen[pn].xv, kp->pen[pn].yv);
822 
823 			para = PolarToCartX(rv, av - (*angle + 90.0));
824 			perp = PolarToCartY(rv, av - (*angle + 90.0));
825 
826 			rv = CartToRadius(para, -perp);
827 			av = CartToAngle(para, -perp);
828 
829 			kp->pen[pn].xv = PolarToCartX(rv, av + (*angle + 90.0));
830 			kp->pen[pn].yv = PolarToCartY(rv, av + (*angle + 90.0));
831 		}
832 	} else {
833 		kp->pen[pn].RadiusOut = False;
834 	}
835 
836 
837 	if (*angle < 0.0) {
838 	    if (kp->spiral) {
839 		    kp->pen[pn].AngleOut = False;
840 		    *angle = *angle + 360.0;
841 		} else {
842 		    kp->pen[pn].AngleOut = True;
843 			  if (*oangle > 0.0) {
844 				  rv = CartToRadius(kp->pen[pn].xv, kp->pen[pn].yv);
845 				  av = CartToAngle(kp->pen[pn].xv, kp->pen[pn].yv);
846 
847 				  para = PolarToCartX(rv, av);
848 				  perp = PolarToCartY(rv, av);
849 
850 				  rv = CartToRadius(para, -perp);
851 				  av = CartToAngle(para, -perp);
852 
853 				  kp->pen[pn].xv = PolarToCartX(rv, av);
854 				  kp->pen[pn].yv = PolarToCartY(rv, av);
855 			  }
856 		}
857 	} else if (kp->spiral) {
858 	    if (*angle > 360.0) {
859 	         kp->pen[pn].AngleOut = False;
860 			 *angle = *angle - 360.0;
861 	    }
862 	} else if (*angle > kp->slice) {
863 		kp->pen[pn].AngleOut = True;
864 		if (*oangle < kp->slice) {
865 			rv = CartToRadius(kp->pen[pn].xv, kp->pen[pn].yv);
866 			av = CartToAngle(kp->pen[pn].xv, kp->pen[pn].yv);
867 
868 			para = PolarToCartX(rv, av - kp->slice);
869 			perp = PolarToCartY(rv, av - kp->slice);
870 
871 			rv = CartToRadius(para, -perp);
872 			av = CartToAngle(para, -perp);
873 
874 			kp->pen[pn].xv = PolarToCartX(rv, av + kp->slice);
875 			kp->pen[pn].yv = PolarToCartY(rv, av + kp->slice);
876 		}
877 	} else {
878 		kp->pen[pn].AngleOut = False;
879 	}
880 }
881 
882 /*-
883  *
884  */
885 static void
GeneralLinearRotated(kaleidstruct * kp,int pn,XSegment * segs)886 GeneralLinearRotated(
887 			    kaleidstruct * kp,
888 			    int pn,
889 			    XSegment * segs
890 )
891 {
892 	double      angle, radius;
893 	double      oangle, oradius;
894 	int         i;
895 
896 	GeneralLinearMoveAndBounce(kp, pn, &angle, &radius, &oangle, &oradius);
897 
898 	for (i = 0; i < kp->modetype; i += 1) {
899 	    if (! kp->spots) {
900 		    segs[i].x1 = (int) (kp->widthby2 + PolarToCartX(oradius, oangle));
901 			segs[i].y1 = (int) (kp->heightby2 - PolarToCartY(oradius, oangle));
902 		}
903 
904 		segs[i].x2 = (int) (kp->widthby2 + PolarToCartX(radius, angle));
905 		segs[i].y2 = (int) (kp->heightby2 - PolarToCartY(radius, angle));
906 
907 		if (kp->spots) {
908 		    segs[i].x1 = segs[i].x2;
909 			segs[i].y1 = segs[i].y2;
910 		}
911 
912 		oangle += kp->slice;
913 		angle += kp->slice;
914 	}
915 }
916 
917 
918 
919 /*-
920  *
921  */
922 static void
GeneralLinearMirrored(kaleidstruct * kp,int pn,XSegment * segs)923 GeneralLinearMirrored(
924 			     kaleidstruct * kp,
925 			     int pn,
926 			     XSegment * segs
927 )
928 {
929 	double      hangle, angle, radius;
930 	double      hoangle, oangle, oradius;
931 	int         i;
932 
933 
934 	GeneralLinearMoveAndBounce(kp, pn, &angle, &radius, &oangle, &oradius);
935 
936 	hoangle = oangle;
937 	hangle = angle;
938 
939 	for (i = 0; i < kp->modetype; i += 2) {
940 	    if (! kp->spots) {
941 		    segs[i].x1 = (int) (kp->widthby2 + PolarToCartX(oradius, oangle));
942 			segs[i].y1 = (int) (kp->heightby2 - PolarToCartY(oradius, oangle));
943 		}
944 
945 		segs[i].x2 = (int) (kp->widthby2 + PolarToCartX(radius, angle));
946 		segs[i].y2 = (int) (kp->heightby2 - PolarToCartY(radius, angle));
947 
948 		if (kp->spots) {
949 		    segs[i].x1 = segs[i].x2;
950 			segs[i].y1 = segs[i].y2;
951 		}
952 
953 		oangle += 2.0 * kp->slice;
954 		angle += 2.0 * kp->slice;
955 	}
956 
957 	oangle = kp->slice * 2.0;
958 	angle = kp->slice * 2.0;
959 	for (i = 1; i < kp->modetype; i += 2) {
960 	    if (! kp->spots) {
961 		    segs[i].x1 = (int) (kp->widthby2 + PolarToCartX(oradius, oangle - hoangle));
962 			segs[i].y1 = (int) (kp->heightby2 - PolarToCartY(oradius, oangle - hoangle));
963 		}
964 
965 		segs[i].x2 = (int) (kp->widthby2 + PolarToCartX(radius, angle - hangle));
966 		segs[i].y2 = (int) (kp->heightby2 - PolarToCartY(radius, angle - hangle));
967 
968 		if (kp->spots) {
969 		    segs[i].x1 = segs[i].x2;
970 			segs[i].y1 = segs[i].y2;
971 		}
972 
973 		oangle += 2.0 * kp->slice;
974 		angle += 2.0 * kp->slice;
975 	}
976 }
977 
978 /*-
979  *
980  */
981 static void
random_velocity(kaleidstruct * kp,int i)982 random_velocity(kaleidstruct * kp, int i)
983 {
984 	int tMxRV, tMnRV;
985 	int tMxAV, tMnAV;
986 	int tMxV,  tMnV;
987 
988 	if (kp->spots) {
989 	    tMxRV = MaxRadialVelocity * SpotsMult;
990 	    tMnRV = MinRadialVelocity * SpotsMult;
991 	    tMxAV = MaxAngularVelocity * SpotsMult;
992 	    tMnAV = MinAngularVelocity * SpotsMult;
993         tMxV  = MaxVelocity * SpotsMult;
994         tMnV  = MinVelocity * SpotsMult;
995 	} else {
996 	    tMxRV = MaxRadialVelocity;
997 	    tMnRV = MinRadialVelocity;
998 	    tMxAV = MaxAngularVelocity;
999 	    tMnAV = MinAngularVelocity;
1000         tMxV  = MaxVelocity;
1001         tMnV  = MinVelocity;
1002 	}
1003 
1004 
1005 	if (kp->modetype > 0) {
1006 		kp->pen[i].xv = INTRAND(-tMxRV, tMxRV);
1007 		if (kp->pen[i].xv > 0.0) {
1008 			kp->pen[i].xv += tMnRV;
1009 		} else if (kp->pen[i].xv < 0.0) {
1010 			kp->pen[i].xv -= tMnRV;
1011 		}
1012 		kp->pen[i].yv = INTRAND(-tMxAV, tMxAV);
1013 		if (kp->pen[i].yv > 0.0) {
1014 			kp->pen[i].yv += tMnAV;
1015 		} else if (kp->pen[i].yv < 0.0) {
1016 			kp->pen[i].yv -= tMnAV;
1017 		}
1018 	} else {
1019 		kp->pen[i].xv = INTRAND(-tMxV, tMxV);
1020 		if (kp->pen[i].xv > 0.0) {
1021 			kp->pen[i].xv += tMnV;
1022 		} else if (kp->pen[i].xv < 0.0) {
1023 			kp->pen[i].xv -= tMnV;
1024 		}
1025 		kp->pen[i].yv = INTRAND(-tMxV, tMxV);
1026 		if (kp->pen[i].yv > 0.0) {
1027 			kp->pen[i].yv += tMnV;
1028 		} else if (kp->pen[i].yv < 0.0) {
1029 			kp->pen[i].yv -= tMnV;
1030 		}
1031 	}
1032 }
1033 
1034 
1035 static void
random_position(kaleidstruct * kp,int i)1036 random_position(kaleidstruct * kp, int i)
1037 {
1038 
1039 	if (kp->modetype >= 0) {
1040 		if (kp->linear) {
1041 			double      radius, angle;
1042 
1043 			radius = (double) INTRAND(0, (int) (kp->radius - 1.0));
1044 			angle = (double) INTRAND(0, (int) (kp->slice - 1.0));
1045 
1046 			kp->pen[i].cx = PolarToCartX(radius, angle);
1047 			kp->pen[i].cy = PolarToCartY(radius, angle);
1048 		} else {
1049 			kp->pen[i].cx = (double) INTRAND(0, (int) (kp->radius - 1.0));
1050 			kp->pen[i].cy = (double) INTRAND(0, (int) (kp->slice - 1.0));
1051 		}
1052 	} else if (kp->modetype == OCT) {
1053 		double      radius, angle;
1054 
1055 		radius = (double) INTRAND(0, (int) (kp->radius - 1.0));
1056 		angle = (double) INTRAND(0, 44);
1057 
1058 		kp->pen[i].cx = PolarToCartX(radius, angle);
1059 		kp->pen[i].cy = PolarToCartY(radius, angle);
1060 	} else {
1061 		kp->pen[i].cx = (double) INTRAND(0, (int) (kp->widthby2 - 1.0));
1062 		kp->pen[i].cy = (double) INTRAND(0,
1063 		     (int) ((kp->heightby2 * kp->pen[i].cx) / kp->widthby2));
1064 	}
1065 }
1066 
1067 static void
free_kaleid_screen(kaleidstruct * kp)1068 free_kaleid_screen(kaleidstruct *kp)
1069 {
1070 	if (kp == NULL) {
1071 		return;
1072 	}
1073 	if (kp->pen != NULL)
1074 		free(kp->pen);
1075 	kp = NULL;
1076 }
1077 
1078 /*-
1079  *
1080  */
1081 ENTRYPOINT void
init_kaleid(ModeInfo * mi)1082 init_kaleid(ModeInfo * mi)
1083 {
1084 	int         i;
1085 	kaleidstruct *kp;
1086 
1087 	MI_INIT(mi, kaleids);
1088 	kp = &kaleids[MI_SCREEN(mi)];
1089 
1090 	kp->PenCount = MI_COUNT(mi);
1091 
1092 	if (MI_IS_FULLRANDOM(mi)) {
1093 		kp->alternate = (Bool) (LRAND() & 1);
1094 		kp->disconnected = (Bool) (LRAND() & 1);
1095 		kp->serial = (Bool) (LRAND() & 1);
1096 		kp->linear = (Bool) (LRAND() & 1);
1097 		kp->spiral = (Bool) (LRAND() & 1);
1098 		kp->spots = (Bool) (LRAND() & 1);
1099 	} else {
1100 		kp->alternate = Alternate;
1101 		kp->disconnected = Disconnected;
1102 		kp->serial = Serial;
1103 		kp->linear = Linear;
1104 		kp->spiral = Spiral;
1105 		kp->spots = Spots;
1106 	}
1107 
1108 	if (kp->PenCount < -MINPENS) {
1109 		/* if kp->PenCount is random ... the size can change */
1110 		if (kp->pen != NULL) {
1111 			free(kp->pen);
1112 			kp->pen = (penstruct *) NULL;
1113 		}
1114 		kp->PenCount = NRAND(-kp->PenCount - MINPENS + 1) + MINPENS;
1115 	} else if (kp->PenCount < MINPENS)
1116 		kp->PenCount = MINPENS;
1117 
1118 	if (kp->pen == NULL) {
1119 		if ((kp->pen = (penstruct *) malloc(kp->PenCount *
1120 				sizeof (penstruct))) == NULL)
1121 			return;
1122 	}
1123 
1124 	if ((MI_SIZE(mi)) > MINSIZE) {
1125 		kp->modetype = (!kp->alternate + 1) * MI_SIZE(mi);
1126 	} else if ((MI_SIZE(mi)) < -MINSIZE) {
1127 		kp->modetype = (!kp->alternate + 1) * (NRAND(-MI_SIZE(mi) + 1) + MINSIZE);
1128 	} else {
1129 		kp->modetype = (!kp->alternate + 1) * MINSIZE;
1130 	}
1131 	if (MI_IS_FULLRANDOM(mi)) {
1132 		int         tmp;
1133 
1134 		tmp = NRAND(ABS(MI_SIZE(mi)) + 2);
1135 		if (tmp == 0)
1136 			kp->modetype = OCT;
1137 		else if (tmp == 1)
1138 			kp->modetype = QUAD;
1139 	} else {
1140 		if (Oct)
1141 			kp->modetype = OCT;
1142 		else if (Quad)
1143 			kp->modetype = QUAD;
1144 	}
1145 
1146 	kp->PercentBlack = (int) MAX(0, MIN(MI_CYCLES(mi), 95));
1147 
1148 
1149 	/* set various size parameters */
1150 
1151 	kp->width = (double) MI_WIDTH(mi);
1152 	kp->height = (double) MI_HEIGHT(mi);
1153 
1154 	if (kp->width < 2.0)
1155 		kp->width = 2.0;
1156 	if (kp->height < 2.0)
1157 		kp->height = 2.0;
1158 
1159 	kp->radius = sqrt(((kp->width * kp->width) +
1160 			   (kp->height * kp->height)
1161 			  ) / 4.0
1162 		);
1163 
1164 
1165 	if (kp->modetype >= 0) {
1166 		kp->bouncetype = INTRAND(0, 2);
1167 
1168 		kp->slice = 360.0 / (double) kp->modetype;
1169 
1170 		kp->widthby2 = kp->width / 2.0;
1171 		kp->heightby2 = kp->height / 2.0;
1172 	} else {
1173 		kp->widthby2 = kp->width / 2.0;
1174 		kp->heightby2 = kp->height / 2.0;
1175 	}
1176 
1177 	/* set the maximum pen width */
1178 	if (kp->modetype >= 0) {
1179 		if ((kp->slice == 360.0) || (kp->slice == 180.0)) {
1180 			kp->maxlwidth = (int) ((((double) MIN(kp->widthby2, kp->heightby2)) *
1181 					     (double) WidthPercent) / 100.0);
1182 		} else {
1183 			kp->maxlwidth = (int) (((sin(kp->slice * ToRadians) *
1184 					  MIN(kp->widthby2, kp->heightby2)) *
1185 					     (double) WidthPercent) / 100.0);
1186 		}
1187 	} else {
1188 		kp->maxlwidth = (int) ((MIN(kp->widthby2,
1189 					    kp->heightby2
1190 					) * (double) WidthPercent
1191 				       ) / 100.0
1192 			);
1193 	}
1194 
1195 	if (kp->spots) {
1196 	    kp->maxlwidth = 2 * kp->maxlwidth;
1197 	}
1198 
1199 	if (kp->maxlwidth <= 0) {
1200 		kp->maxlwidth = 1;
1201 	}
1202 
1203 	for (i = 0; i < kp->PenCount; i += 1) {
1204 		if (MI_NPIXELS(mi) > 2) {
1205 			kp->pen[i].pix = NRAND(MI_NPIXELS(mi));
1206 			kp->pen[i].White = 1;
1207 		} else {
1208 			kp->pen[i].White = 1;
1209 		}
1210 
1211 		kp->pen[i].curlwidth = INTRAND(1, kp->maxlwidth);
1212 
1213 		kp->pen[i].RadiusOut = False;
1214 		kp->pen[i].AngleOut = False;
1215 
1216 		random_position(kp, i);
1217 
1218 		kp->pen[i].ox = kp->pen[i].cx;
1219 		kp->pen[i].oy = kp->pen[i].cy;
1220 		kp->pen[i].DeferredChange = False;
1221 
1222 		random_velocity(kp, i);
1223 	}
1224 
1225 	MI_CLEARWINDOW(mi);
1226 
1227 }
1228 
1229 /*-
1230  *
1231  */
1232 static void
set_pen_attributes(ModeInfo * mi,kaleidstruct * kp,int i)1233 set_pen_attributes(ModeInfo * mi, kaleidstruct * kp, int i)
1234 {
1235 	Display    *display = MI_DISPLAY(mi);
1236 	GC          gc = MI_GC(mi);
1237 
1238 	if (kp->pen[i].White) {
1239 		if (MI_NPIXELS(mi) > 2)
1240 			XSetForeground(display, gc, MI_PIXEL(mi, kp->pen[i].pix));
1241 		else
1242 			XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
1243 	} else {
1244 		XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
1245 	}
1246 	XSetLineAttributes(display, gc,
1247 		       kp->pen[i].curlwidth, LineSolid, CapRound, JoinRound);
1248 }
1249 
1250 /*-
1251  *
1252  */
1253 static void
change_pen(ModeInfo * mi,kaleidstruct * kp,int i)1254 change_pen(ModeInfo * mi, kaleidstruct * kp, int i)
1255 {
1256 	if (INTRAND(0, 100) < kp->PercentBlack) {
1257 		kp->pen[i].White = 0;
1258 	} else {
1259 		kp->pen[i].White = 1;
1260 		if (kp->serial) {
1261 			if (MI_NPIXELS(mi) > 2) {
1262 				if (++kp->pen[i].pix >= MI_NPIXELS(mi))
1263 					kp->pen[i].pix = 0;
1264 			}
1265 		} else {
1266 			if (MI_NPIXELS(mi) > 2) {
1267 				kp->pen[i].pix = NRAND(MI_NPIXELS(mi));
1268 			}
1269 		}
1270 	}
1271 
1272 	random_velocity(kp, i);
1273 
1274 	kp->pen[i].curlwidth = INTRAND(1, kp->maxlwidth);
1275 
1276 	if (kp->modetype >= 0) {
1277 		kp->bouncetype = INTRAND(0, 2);
1278 	}
1279 	if (kp->disconnected) {
1280 		random_position(kp, i);
1281 		kp->pen[i].ox = kp->pen[i].cx;
1282 		kp->pen[i].oy = kp->pen[i].cy;
1283 	}
1284 }
1285 
1286 /*-
1287  *
1288  */
1289 ENTRYPOINT void
draw_kaleid(ModeInfo * mi)1290 draw_kaleid(ModeInfo * mi)
1291 {
1292 	Display    *display = MI_DISPLAY(mi);
1293 	GC          gc = MI_GC(mi);
1294 	XSegment   *segs;
1295 	int         NumberOfSegments;
1296 	int         i;
1297 	kaleidstruct *kp;
1298 
1299 	if (kaleids == NULL)
1300 			return;
1301 	kp = &kaleids[MI_SCREEN(mi)];
1302 	if (kp->pen == NULL)
1303 			return;
1304 
1305 	MI_IS_DRAWN(mi) = True;
1306 	if (kp->modetype == QUAD) {
1307 		NumberOfSegments = 4;
1308 	} else if (kp->modetype == OCT) {
1309 		NumberOfSegments = 8;
1310 	} else {		/* if (kp->modetype > 0) */
1311 		NumberOfSegments = kp->modetype;
1312 	}
1313 	if ((segs = (XSegment *) malloc(NumberOfSegments *
1314 			sizeof (XSegment))) == NULL) {
1315 		free(kp->pen);
1316 		kp->pen = (penstruct *) NULL;
1317 		return;
1318 	}
1319 
1320 	for (i = 0; i < kp->PenCount; i++) {
1321 		set_pen_attributes(mi, kp, i);
1322 
1323 		if (kp->modetype == QUAD) {
1324 			if (kp->alternate) {
1325 				QuadRotated(kp, i, segs);
1326 			} else {
1327 				QuadMirrored(kp, i, segs);
1328 			}
1329 		} else if (kp->modetype == OCT) {
1330 			if (kp->alternate) {
1331 				OctRotated(kp, i, segs);
1332 			} else {
1333 				OctMirrored(kp, i, segs);
1334 			}
1335 		} else {
1336 			if (kp->alternate) {
1337 
1338 				if (kp->linear) {
1339 					GeneralLinearRotated(kp, i, segs);
1340 				} else {
1341 					GeneralRotated(kp, i, segs);
1342 				}
1343 			} else {
1344 				if (kp->linear) {
1345 					GeneralLinearMirrored(kp, i, segs);
1346 				} else {
1347 					GeneralMirrored(kp, i, segs);
1348 				}
1349 			}
1350 		}
1351 		XDrawSegments(
1352 				     display,
1353 				     MI_WINDOW(mi),
1354 				     gc,
1355 				     segs,
1356 				     NumberOfSegments
1357 			);
1358 
1359 		kp->pen[i].ox = kp->pen[i].cx;
1360 		kp->pen[i].oy = kp->pen[i].cy;
1361 
1362 
1363 		if ((INTRAND(0, 100) < ChangeChance) || kp->pen[i].DeferredChange) {
1364 			if (!kp->pen[i].AngleOut && !kp->pen[i].RadiusOut) {
1365 				kp->pen[i].DeferredChange = False;
1366 				change_pen(mi, kp, i);
1367 			} else {
1368 				kp->pen[i].DeferredChange = True;
1369 			}
1370 		}
1371 	}
1372 
1373 
1374 	XSetLineAttributes(display, gc, 1, LineSolid, CapRound, JoinRound);
1375 	free(segs);
1376 }
1377 
1378 /*-
1379  *
1380  */
1381 ENTRYPOINT void
release_kaleid(ModeInfo * mi)1382 release_kaleid(ModeInfo * mi)
1383 {
1384 	if (kaleids != NULL) {
1385 		int         screen;
1386 
1387 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
1388 			free_kaleid_screen(&kaleids[screen]);
1389 		}
1390 		free(kaleids);
1391 		kaleids = (kaleidstruct *) NULL;
1392 	}
1393 }
1394 
1395 #ifndef STANDALONE
1396 /*-
1397  *
1398  */
1399 ENTRYPOINT void
refresh_kaleid(ModeInfo * mi)1400 refresh_kaleid(ModeInfo * mi)
1401 {
1402 	MI_CLEARWINDOW(mi);
1403 }
1404 #endif
1405 
1406 XSCREENSAVER_MODULE ("Kaleid", kaleid)
1407 
1408 #endif /* MODE_kaleid */
1409