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