1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* helix --- string art */
3 
4 #if 0
5 static const char sccsid[] = "@(#)helix.c	5.00 2000/11/01 xlockmore";
6 
7 #endif
8 
9 /*-
10  * Copyright (c) 1992 by Jamie Zawinski
11  *
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * Revision History:
25  * 01-Nov-2000: Allocation checks
26  * 10-May-1997: Compatible with xscreensaver
27  * 06-Apr-1997: new ellipse code from Dan Stromberg <strombrg@nis.acs.uci.edu>
28  * 11-Aug-1995: found some typos, looks more interesting now
29  * 08-Aug-1995: speed up thanks to Heath A. Kehoe <hakehoe AT icaen.uiowa.edu>
30  * 17-Jun-1995: removed sleep statements
31  * 02-Sep-1993: xlock version David Bagley <bagleyd AT verizon.net>
32  * 1992: xscreensaver version Jamie Zawinski <jwz AT jwz.org>
33  */
34 
35 /*-
36  * original copyright
37  * Copyright (c) 1992 by Jamie Zawinski
38  * Permission to use, copy, modify, distribute, and sell this software and
39  * its documentation for any purpose is hereby granted without fee, provided
40  * that the above copyright notice appear in all copies and that both that
41  * copyright notice and this permission notice appear in supporting
42  * documentation.  No representations are made about the suitability of this
43  * software for any purpose.  It is provided "as is" without express or
44  * implied warranty.
45  */
46 
47 #ifdef STANDALONE
48 #define MODE_helix
49 #define DEFAULTS "*delay: 25000 \n" \
50 	"*cycles: 100 \n" \
51 	"*ncolors: 200 \n" \
52 	"*fullrandom: True \n" \
53 
54 # define free_helix 0
55 # define reshape_helix 0
56 # define helix_handle_event 0
57 #define BRIGHT_COLORS
58 #include "xlockmore.h"		/* in xscreensaver distribution */
59 #else /* STANDALONE */
60 #include "xlock.h"		/* in xlockmore distribution */
61 
62 #endif /* STANDALONE */
63 
64 #ifdef MODE_helix
65 
66 #define DEF_ELLIPSE "False"
67 
68 static Bool ellipse;
69 
70 static XrmOptionDescRec opts[] =
71 {
72 	{(char *) "-ellipse", (char *) ".helix.ellipse", XrmoptionNoArg, (caddr_t) "on"},
73 	{(char *) "+ellipse", (char *) ".helix.ellipse", XrmoptionNoArg, (caddr_t) "off"}
74 };
75 static argtype vars[] =
76 {
77 	{(void *) & ellipse, (char *) "ellipse", (char *) "Ellipse", (char *) DEF_ELLIPSE, t_Bool}
78 };
79 static OptionStruct desc[] =
80 {
81 	{(char *) "-/+ellipse", (char *) "turn on/off ellipse format"}
82 };
83 
84 ENTRYPOINT ModeSpecOpt helix_opts =
85 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
86 
87 #ifdef USE_MODULES
88 ModStruct   helix_description =
89 {"helix", "init_helix", "draw_helix", "release_helix",
90  "refresh_helix", "init_helix", (char *) NULL, &helix_opts,
91  25000, 1, 100, 1, 64, 1.0, "",
92  "Shows string art", 0, NULL};
93 
94 #endif
95 
96 #define ANGLES 360
97 
98 static double cos_array[ANGLES], sin_array[ANGLES];
99 
100 typedef struct {
101 	Bool        painted;
102 	int         width, height;
103 	int         xmid, ymid;
104 	int         color;
105 	int         time;
106 	int         radius1, radius2, d_angle, factor1, factor2, factor3,
107 	            factor4;
108 	int         redraw;
109 
110 	Bool        ellipse;
111 	int         d_angle_offset, dir;
112 	int         offset;
113 	int         density;
114 	int         count;
115 } helixstruct;
116 
117 static helixstruct *helixes = (helixstruct *) NULL;
118 
119 static int
gcd(int a,int b)120 gcd(int a, int b)
121 {
122 	while (b > 0) {
123 		int         tmp;
124 
125 		tmp = a % b;
126 		a = b;
127 		b = tmp;
128 	}
129 	return (a < 0 ? -a : a);
130 }
131 
132 static void
helix(ModeInfo * mi,int radius1,int radius2,int d_angle,int factor1,int factor2,int factor3,int factor4)133 helix(ModeInfo * mi, int radius1, int radius2, int d_angle,
134       int factor1, int factor2, int factor3, int factor4)
135 {
136 	Display    *display = MI_DISPLAY(mi);
137 	Window      window = MI_WINDOW(mi);
138 	GC          gc = MI_GC(mi);
139 	helixstruct *hp = &helixes[MI_SCREEN(mi)];
140 	int         x_1, y_1, x_2, y_2, angle, limit;
141 	int         i;
142 
143 	if (MI_NPIXELS(mi) > 2) {
144 		XSetForeground(display, gc, MI_PIXEL(mi, hp->color));
145 		if (++hp->color >= MI_NPIXELS(mi))
146 			hp->color = 0;
147 	} else
148 		XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
149 	x_2 = hp->xmid;
150 	y_2 = hp->ymid + radius1;
151 	angle = 0;
152 	limit = 1 + (ANGLES / gcd(ANGLES, d_angle));
153 
154 	for (i = 0; i < limit; i++) {
155 		int         tmp;
156 
157 #define pmod(x,y) (tmp=((x)%(y)),(tmp>=0?tmp:tmp+y))
158 		x_1 = hp->xmid + (int) (((double) radius1) *
159 				 sin_array[pmod((angle * factor1), ANGLES)]);
160 		y_1 = hp->ymid + (int) (((double) radius2) *
161 				 cos_array[pmod((angle * factor2), ANGLES)]);
162 		if (MI_NPIXELS(mi) > 2) {
163 			XSetForeground(display, gc, MI_PIXEL(mi, hp->color));
164 			if (++hp->color >= MI_NPIXELS(mi))
165 				hp->color = 0;
166 		}
167 		XDrawLine(display, window, gc, x_1, y_1, x_2, y_2);
168 		x_2 = hp->xmid + (int) (((double) radius2) *
169 				 sin_array[pmod((angle * factor3), ANGLES)]);
170 		y_2 = hp->ymid + (int) (((double) radius1) *
171 				 cos_array[pmod((angle * factor4), ANGLES)]);
172 		if (MI_NPIXELS(mi) > 2) {
173 			XSetForeground(display, gc, MI_PIXEL(mi, hp->color));
174 			if (++hp->color >= MI_NPIXELS(mi))
175 				hp->color = 0;
176 		}
177 		XDrawLine(display, window, gc, x_1, y_1, x_2, y_2);
178 		angle += d_angle;
179 	}
180 }
181 
182 static void
trig(ModeInfo * mi)183 trig(ModeInfo * mi)
184 {
185 	Display    *display = MI_DISPLAY(mi);
186 	GC          gc = MI_GC(mi);
187 	helixstruct *hp = &helixes[MI_SCREEN(mi)];
188 	int         x_1, y_1, x_2, y_2;
189 	int         tmp, angle;
190 
191 
192 #define pmod(x,y) (tmp=((x)%(y)),(tmp>=0?tmp:tmp+y))
193 
194 	while (ABS(hp->d_angle) <= ANGLES) {
195 		angle = hp->d_angle + hp->d_angle_offset;
196 		x_1 = (int) (sin_array[pmod(angle * hp->factor1, ANGLES)] * hp->xmid) +
197 			hp->xmid;
198 		y_1 = (int) (cos_array[pmod(angle * hp->factor1, ANGLES)] * hp->ymid) +
199 			hp->ymid;
200 		x_2 = (int) (sin_array[pmod(angle * hp->factor2 + hp->offset, ANGLES)] *
201 			    hp->xmid) + hp->xmid;
202 		y_2 = (int) (cos_array[pmod(angle * hp->factor2 + hp->offset, ANGLES)] *
203 			    hp->ymid) + hp->ymid;
204 		XDrawLine(display, MI_WINDOW(mi), gc, x_1, y_1, x_2, y_2);
205 		if (MI_NPIXELS(mi) > 2) {
206 			XSetForeground(display, gc, MI_PIXEL(mi, hp->color));
207 			if (++hp->color >= MI_NPIXELS(mi))
208 				hp->color = 0;
209 		}
210 		tmp = (int) ANGLES / (2 * hp->density * hp->factor1 * hp->factor2);
211 		if (tmp == 0)	/* Do not want it getting stuck... */
212 			tmp = 1;	/* Would not need if floating point */
213 		hp->d_angle += hp->dir * tmp;
214 	}
215 }
216 
217 static void
random_helix(ModeInfo * mi)218 random_helix(ModeInfo * mi)
219 {
220 	helixstruct *hp = &helixes[MI_SCREEN(mi)];
221 	int         radius;
222 	double      divisor;
223 
224 	radius = MIN(hp->xmid, hp->ymid);
225 
226 	hp->d_angle = 0;
227 	hp->factor1 = 2;
228 	hp->factor2 = 2;
229 	hp->factor3 = 2;
230 	hp->factor4 = 2;
231 
232 	divisor = ((LRAND() / MAXRAND * 3.0 + 1) * (((LRAND() & 1) * 2) - 1));
233 
234 	if ((LRAND() & 1) == 0) {
235 		hp->radius1 = radius;
236 		hp->radius2 = (int) ((double) radius / divisor);
237 	} else {
238 		hp->radius2 = radius;
239 		hp->radius1 = (int) ((double) radius / divisor);
240 	}
241 
242 	while (gcd(ANGLES, hp->d_angle) >= 2)
243 		hp->d_angle = NRAND(ANGLES);
244 
245 #define random_factor()				\
246   (int) (((NRAND(7)) ? ((LRAND() & 1) + 1) : 3)	* (((LRAND() & 1) * 2) - 1))
247 
248 	while (gcd(gcd(gcd(hp->factor1, hp->factor2), hp->factor3), hp->factor4)
249 	       != 1) {
250 		hp->factor1 = random_factor();
251 		hp->factor2 = random_factor();
252 		hp->factor3 = random_factor();
253 		hp->factor4 = random_factor();
254 	}
255 
256 	helix(mi, hp->radius1, hp->radius2, hp->d_angle,
257 	      hp->factor1, hp->factor2, hp->factor3, hp->factor4);
258 }
259 
260 static void
random_trig(ModeInfo * mi)261 random_trig(ModeInfo * mi)
262 {
263 	Display    *display = MI_DISPLAY(mi);
264 	GC          gc = MI_GC(mi);
265 	helixstruct *hp = &helixes[MI_SCREEN(mi)];
266 
267 	hp->d_angle = 0;
268 	hp->factor1 = NRAND(8) + 1;
269 	do
270 		hp->factor2 = NRAND(8) + 1;
271 	while (hp->factor1 == hp->factor2);
272 	hp->dir = (LRAND() & 1) ? 1 : -1;
273 	hp->d_angle_offset = NRAND(ANGLES);
274 	hp->offset = (NRAND(ANGLES / 4 - 1) + 1) / 4;
275 	hp->density = 1 << (NRAND(4) + 4);	/* Higher density, higher ANGLES */
276 	if (MI_NPIXELS(mi) > 2) {
277 		XSetForeground(display, gc, MI_PIXEL(mi, hp->color));
278 		if (++hp->color >= MI_NPIXELS(mi))
279 			hp->color = 0;
280 	} else
281 		XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
282 	trig(mi);
283 }
284 
285 ENTRYPOINT void
init_helix(ModeInfo * mi)286 init_helix(ModeInfo * mi)
287 {
288 	int         i;
289 	static int  first = 1;
290 	helixstruct *hp;
291 
292 	MI_INIT(mi, helixes);
293 	hp = &helixes[MI_SCREEN(mi)];
294 
295 	if (first) {
296 		first = 0;
297 		for (i = 0; i < ANGLES; i++) {
298 			cos_array[i] = cos((((double) i) / (double) (ANGLES / 2)) * M_PI);
299 			sin_array[i] = sin((((double) i) / (double) (ANGLES / 2)) * M_PI);;
300 		}
301 	}
302 	hp->ellipse = ellipse;
303 	if (MI_IS_FULLRANDOM(mi))
304 		hp->ellipse = (Bool) (!NRAND(5));	/* 1:5 chance of running ellipse stuff */
305 
306 	hp->width = MI_WIDTH(mi);
307 	hp->height = MI_HEIGHT(mi);
308 	hp->xmid = hp->width / 2;
309 	hp->ymid = hp->height / 2;
310 	hp->redraw = 0;
311 
312 #ifdef STANDALONE
313 	MI_CLEARWINDOWCOLORMAPFAST(mi, MI_GC(mi), MI_BLACK_PIXEL(mi));
314 #else
315 	MI_CLEARWINDOW(mi);
316 #endif
317 	hp->painted = False;
318 
319 	if (MI_NPIXELS(mi) > 2)
320 		hp->color = NRAND(MI_NPIXELS(mi));
321 	hp->time = 0;
322 
323 	if (hp->ellipse) {
324 		random_trig(mi);
325 	} else
326 		random_helix(mi);
327 }
328 
329 ENTRYPOINT void
draw_helix(ModeInfo * mi)330 draw_helix(ModeInfo * mi)
331 {
332 	helixstruct *hp;
333 
334 	if (helixes == NULL)
335 		return;
336 	hp = &helixes[MI_SCREEN(mi)];
337 
338 	MI_IS_DRAWN(mi) = True;
339 	if (++hp->time > MI_CYCLES(mi))
340 		init_helix(mi);
341 	else
342 		hp->painted = True;
343 	if (hp->redraw) {
344 		if (hp->ellipse) {
345 			trig(mi);
346 		} else {
347 			helix(mi, hp->radius1, hp->radius2, hp->d_angle,
348 			 hp->factor1, hp->factor2, hp->factor3, hp->factor4);
349 		}
350 		hp->redraw = 0;
351 	}
352 }
353 
354 ENTRYPOINT void
release_helix(ModeInfo * mi)355 release_helix(ModeInfo * mi)
356 {
357 	if (helixes != NULL) {
358 		free(helixes);
359 		helixes = (helixstruct *) NULL;
360 	}
361 }
362 
363 #ifndef STANDALONE
364 ENTRYPOINT void
refresh_helix(ModeInfo * mi)365 refresh_helix(ModeInfo * mi)
366 {
367 	helixstruct *hp;
368 
369 	if (helixes == NULL)
370 		return;
371 	hp = &helixes[MI_SCREEN(mi)];
372 
373 	if (hp->painted) {
374 		MI_CLEARWINDOW(mi);
375 		helix(mi, hp->radius1, hp->radius2, hp->d_angle,
376 		      hp->factor1, hp->factor2, hp->factor3, hp->factor4);
377 		hp->painted = False;
378 	}
379 }
380 #endif
381 
382 XSCREENSAVER_MODULE ("Helix", helix)
383 
384 #endif /* MODE_helix */
385