http://ftp.x.org/contrib/applications/xscreensaver-2.23.tar.gz
[xscreensaver] / hacks / crystal.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* crystal --- polygons moving according to plane group rules */
3
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)crystal.c     4.07 97/11/24 xlockmore";
6
7 #endif
8
9 /*-
10  * Copyright (c) 1997 by Jouk Jansen <joukj@crys.chem.uva.nl>
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  * The author should like to be notified if changes have been made to the
25  * routine.  Response will only be guaranteed when a VMS version of the 
26  * program is available.
27  *
28  * A moving polygon-mode. The polygons obey 2D-planegroup symmetry.
29  *
30  * Revision History:
31  * 24-Feb-98: added option centre which turns on/off forcing the centre of
32  *              the screen to be used
33  *            added option maxsize which forces the dimensions to be chasen
34  *              in such ua way that the largest possible part of the screen is
35  *              used 
36  *            When only one unit cell is drawn, it is chosen at random
37  * 18-Feb-98: added support for negative numbers with -nx and -ny meaning
38  *            "random" choice with geiven maximum
39  *            added +/-grid option. If -cell is specified this option
40  *            determines if one or all unit cells are drawn.
41  *            -batchcount is now a parameter for all the objects on the screen
42  *            instead of the number of "unique" objects
43  *            The maximum size of the objects now scales with the part
44  *            of the screen used.
45  *            fixed "size" problem. Now very small non-vissable objects
46  *            are not allowed
47  * 13-Feb-98: randomized the unit cell size
48  *            runtime options -/+cell (turn on/off unit cell drawing)
49  *             -nx num (number of translational symmetries in x-direction
50  *             -ny num (idem y-direction but ignored for square and
51  *               hexagonal space groups
52  *               i.e. try xlock -mode crystal -nx 3 -ny 2
53  *            Fullrandom overrules the -/+cell option.
54  * 05-Feb-98: Revision + bug repairs
55  *            shows unit cell
56  *            use part of the screen for unit cell
57  *            in hexagonal and square groups a&b axis forced to be equal
58  *            cell angle for oblique groups randomly chosen between 60 and 120
59  *   bugs solved: planegroups with cell angles <> 90.0 now work properly
60  * 19-Sep-97: Added remaining hexagonal groups
61  * 12-Jun-97: Created
62  */
63
64 #ifdef STANDALONE
65 # define PROGCLASS              "Crystal"
66 # define HACK_INIT              init_crystal
67 # define HACK_DRAW              draw_crystal
68 # define crystal_opts   xlockmore_opts
69 # define DEFAULTS               "*delay:                60000   \n" \
70                                                  "*count:                -500   \n" \
71                                                  "*cycles:                200   \n" \
72                                                  "*size:                  -15   \n" \
73                                                  "*ncolors:               200   \n" \
74                                                  "*fullrandom:   True   \n" \
75                                                  "*verbose:             False   \n"
76 # include "xlockmore.h"         /* in xscreensaver distribution */
77 #else /* STANDALONE */
78 # include "xlock.h"                     /* in xlockmore distribution */
79 #endif /* STANDALONE */
80
81 #define DEF_CELL "True"         /* Draw unit cell */
82 #define DEF_GRID "False"        /* Draw unit all cell if DEF_CELL is True */
83 #define DEF_NX "-3"             /* number of unit cells in x-direction */
84 #define DEF_NX1 1               /* number of unit cells in x-direction */
85 #define DEF_NY "-3"             /* number of unit cells in y-direction */
86 #define DEF_NY1 1               /* number of unit cells in y-direction */
87 #define DEF_CENTRE "False"
88 #define DEF_MAXSIZE "False"
89
90 #define min(a,b) ((a) <= (b) ? (a) : (b))
91
92 static int  nx, ny;
93
94 static Bool unit_cell, grid_cell, centre, maxsize;
95
96 static XrmOptionDescRec opts[] =
97 {
98         {"-nx", "crystal.nx", XrmoptionSepArg, (caddr_t) NULL},
99         {"-ny", "crystal.ny", XrmoptionSepArg, (caddr_t) NULL},
100         {"-centre", ".crystal.centre", XrmoptionNoArg, (caddr_t) "on"},
101         {"+centre", ".crystal.centre", XrmoptionNoArg, (caddr_t) "off"},
102         {"-maxsize", ".crystal.maxsize", XrmoptionNoArg, (caddr_t) "on"},
103         {"+maxsize", ".crystal.maxsize", XrmoptionNoArg, (caddr_t) "off"},
104         {"-cell", ".crystal.cell", XrmoptionNoArg, (caddr_t) "on"},
105         {"+cell", ".crystal.cell", XrmoptionNoArg, (caddr_t) "off"},
106         {"-grid", ".crystal.grid", XrmoptionNoArg, (caddr_t) "on"},
107         {"+grid", ".crystal.grid", XrmoptionNoArg, (caddr_t) "off"}
108 };
109
110 static argtype vars[] =
111 {
112         {(caddr_t *) & nx, "nx", "nx", DEF_NX, t_Int},
113         {(caddr_t *) & ny, "ny", "ny", DEF_NY, t_Int},
114         {(caddr_t *) & centre, "centre", "Centre", DEF_CENTRE, t_Bool},
115         {(caddr_t *) & maxsize, "maxsize", "Maxsize", DEF_MAXSIZE, t_Bool},
116         {(caddr_t *) & unit_cell, "cell", "Cell", DEF_CELL, t_Bool},
117         {(caddr_t *) & grid_cell, "grid", "Grid", DEF_GRID, t_Bool}
118 };
119 static OptionStruct desc[] =
120 {
121         {"-nx num", "Number of unit cells in x-direction"},
122         {"-ny num", "Number of unit cells in y-direction"},
123         {"-/+centre", "turn on/off cenetering on screen"},
124         {"-/+maxsize", "turn on/off use of maximum part of screen"},
125         {"-/+cell", "turn on/off drawing of unit cell"},
126     {"-/+grid", "turn on/off drawing of grid of unit cells (if -cell is on)"}
127 };
128
129 ModeSpecOpt crystal_opts =
130 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
131
132 #ifdef USE_MODULES
133 ModStruct   crystal_description =
134 {"crystal", "init_crystal", "draw_crystal", "release_crystal",
135  "refresh_crystal", "init_crystal", NULL, &crystal_opts,
136  60000, -40, 200, -15, 64, 1.0, "",
137  "Shows polygons in 2D plane groups", 0, NULL};
138
139 #endif
140
141 #define DEF_NUM_ATOM 10
142
143 #define DEF_SIZ_ATOM 10
144
145 #define PI_RAD (M_PI / 180.0)
146
147 static Bool centro[17] =
148 {
149         False,
150         True,
151         False,
152         False,
153         False,
154         True,
155         True,
156         True,
157         True,
158         True,
159         True,
160         True,
161         False,
162         False,
163         False,
164         True,
165         True
166 };
167
168 static Bool primitive[17] =
169 {
170         True,
171         True,
172         True,
173         True,
174         False,
175         True,
176         True,
177         True,
178         False,
179         True,
180         True,
181         True,
182         True,
183         True,
184         True,
185         True,
186         True
187 };
188
189 static short numops[34] =
190 {
191         1, 0,
192         1, 0,
193         9, 7,
194         2, 0,
195         9, 7,
196         9, 7,
197         4, 2,
198         5, 3,
199         9, 7,
200         8, 6,
201         10, 6,
202         8, 4,
203         16, 13,
204         19, 13,
205         16, 10,
206         19, 13,
207         19, 13
208 };
209
210 static short operation[114] =
211 {
212         1, 0, 0, 1, 0, 0,
213         -1, 0, 0, 1, 0, 1,
214         -1, 0, 0, 1, 1, 0,
215         1, 0, 0, 1, 0, 0,
216         -1, 0, 0, 1, 1, 1,
217         1, 0, 0, 1, 1, 1,
218         0, -1, 1, 0, 0, 0,
219         1, 0, 0, 1, 0, 0,
220         -1, 0, 0, 1, 0, 0,
221         0, 1, 1, 0, 0, 0,
222         -1, 0, -1, 1, 0, 0,
223         1, -1, 0, -1, 0, 0,
224         0, 1, 1, 0, 0, 0,
225         0, -1, 1, -1, 0, 0,
226         -1, 1, -1, 0, 0, 0,
227         1, 0, 0, 1, 0, 0,
228         0, -1, -1, 0, 0, 0,
229         -1, 1, 0, 1, 0, 0,
230         1, 0, 1, -1, 0, 0
231 };
232
233 typedef struct {
234         unsigned long colour;
235         int         x0, y0, velocity[2];
236         float       angle, velocity_a;
237         int         num_point, at_type, size_at;
238         XPoint      xy[5];
239 } crystalatom;
240
241 typedef struct {
242         Bool        painted;
243         int         win_width, win_height, num_atom;
244         int         planegroup, a, b, offset_w, offset_h, nx, ny;
245         float       gamma;
246         crystalatom *atom;
247         GC          gc;
248         Bool        unit_cell, grid_cell;
249 } crystalstruct;
250
251 static crystalstruct *crystals = NULL;
252
253 static void
254 trans_coor(XPoint * xyp, XPoint * new_xyp, int num_points,
255            float gamma)
256 {
257         int         i;
258
259         for (i = 0; i <= num_points; i++) {
260                 new_xyp[i].x = xyp[i].x +
261                         (int) (xyp[i].y * sin((gamma - 90.0) * PI_RAD));
262                 new_xyp[i].y = (int) (xyp[i].y / cos((gamma - 90.0) * PI_RAD));
263         }
264 }
265
266 static void
267 trans_coor_back(XPoint * xyp, XPoint * new_xyp,
268                 int num_points, float gamma, int offset_w, int offset_h)
269 {
270         int         i;
271
272         for (i = 0; i <= num_points; i++) {
273                 new_xyp[i].y = (int) (xyp[i].y * cos((gamma - 90) * PI_RAD)) +
274                         offset_h;
275                 new_xyp[i].x = xyp[i].x - (int) (xyp[i].y * sin((gamma - 90.0)
276                                                        * PI_RAD)) + offset_w;
277         }
278 }
279
280 static void
281 crystal_setupatom(crystalatom * atom0, float gamma)
282 {
283         XPoint      xy[5];
284         int         x0, y0;
285
286         y0 = (int) (atom0->y0 * cos((gamma - 90) * PI_RAD));
287         x0 = atom0->x0 - (int) (atom0->y0 * sin((gamma - 90.0) * PI_RAD));
288         switch (atom0->at_type) {
289                 case 0: /* rectangles */
290                         xy[0].x = x0 + (int) (2 * atom0->size_at *
291                                               cos(atom0->angle)) +
292                                 (int) (atom0->size_at * sin(atom0->angle));
293                         xy[0].y = y0 + (int) (atom0->size_at *
294                                               cos(atom0->angle)) -
295                                 (int) (2 * atom0->size_at * sin(atom0->angle));
296                         xy[1].x = x0 + (int) (2 * atom0->size_at *
297                                               cos(atom0->angle)) -
298                                 (int) (atom0->size_at * sin(atom0->angle));
299                         xy[1].y = y0 - (int) (atom0->size_at *
300                                               cos(atom0->angle)) -
301                                 (int) (2 * atom0->size_at * sin(atom0->angle));
302                         xy[2].x = x0 - (int) (2 * atom0->size_at *
303                                               cos(atom0->angle)) -
304                                 (int) (atom0->size_at * sin(atom0->angle));
305                         xy[2].y = y0 - (int) (atom0->size_at *
306                                               cos(atom0->angle)) +
307                                 (int) (2 * atom0->size_at * sin(atom0->angle));
308                         xy[3].x = x0 - (int) (2 * atom0->size_at *
309                                               cos(atom0->angle)) +
310                                 (int) (atom0->size_at * sin(atom0->angle));
311                         xy[3].y = y0 + (int) (atom0->size_at *
312                                               cos(atom0->angle)) +
313                                 (int) (2 * atom0->size_at *
314                                        sin(atom0->angle));
315                         xy[4].x = xy[0].x;
316                         xy[4].y = xy[0].y;
317                         trans_coor(xy, atom0->xy, 4, gamma);
318                         return;
319                 case 1: /* squares */
320                         xy[0].x = x0 + (int) (1.5 * atom0->size_at *
321                                               cos(atom0->angle)) +
322                                 (int) (1.5 * atom0->size_at *
323                                        sin(atom0->angle));
324                         xy[0].y = y0 + (int) (1.5 * atom0->size_at *
325                                               cos(atom0->angle)) -
326                                 (int) (1.5 * atom0->size_at *
327                                        sin(atom0->angle));
328                         xy[1].x = x0 + (int) (1.5 * atom0->size_at *
329                                               cos(atom0->angle)) -
330                                 (int) (1.5 * atom0->size_at *
331                                        sin(atom0->angle));
332                         xy[1].y = y0 - (int) (1.5 * atom0->size_at *
333                                               cos(atom0->angle)) -
334                                 (int) (1.5 * atom0->size_at *
335                                        sin(atom0->angle));
336                         xy[2].x = x0 - (int) (1.5 * atom0->size_at *
337                                               cos(atom0->angle)) -
338                                 (int) (1.5 * atom0->size_at *
339                                        sin(atom0->angle));
340                         xy[2].y = y0 - (int) (1.5 * atom0->size_at *
341                                               cos(atom0->angle)) +
342                                 (int) (1.5 * atom0->size_at *
343                                        sin(atom0->angle));
344                         xy[3].x = x0 - (int) (1.5 * atom0->size_at *
345                                               cos(atom0->angle)) +
346                                 (int) (1.5 * atom0->size_at *
347                                        sin(atom0->angle));
348                         xy[3].y = y0 + (int) (1.5 * atom0->size_at *
349                                               cos(atom0->angle)) +
350                                 (int) (1.5 * atom0->size_at *
351                                        sin(atom0->angle));
352                         xy[4].x = xy[0].x;
353                         xy[4].y = xy[0].y;
354                         trans_coor(xy, atom0->xy, 4, gamma);
355                         return;
356                 case 2: /* triangles */
357                         xy[0].x = x0 + (int) (1.5 * atom0->size_at *
358                                               sin(atom0->angle));
359                         xy[0].y = y0 + (int) (1.5 * atom0->size_at *
360                                               cos(atom0->angle));
361                         xy[1].x = x0 + (int) (1.5 * atom0->size_at *
362                                               cos(atom0->angle)) -
363                                 (int) (1.5 * atom0->size_at *
364                                        sin(atom0->angle));
365                         xy[1].y = y0 - (int) (1.5 * atom0->size_at *
366                                               cos(atom0->angle)) -
367                                 (int) (1.5 * atom0->size_at *
368                                        sin(atom0->angle));
369                         xy[2].x = x0 - (int) (1.5 * atom0->size_at *
370                                               cos(atom0->angle)) -
371                                 (int) (1.5 * atom0->size_at *
372                                        sin(atom0->angle));
373                         xy[2].y = y0 - (int) (1.5 * atom0->size_at *
374                                               cos(atom0->angle)) +
375                                 (int) (1.5 * atom0->size_at *
376                                        sin(atom0->angle));
377                         xy[3].x = xy[0].x;
378                         xy[3].y = xy[0].y;
379                         trans_coor(xy, atom0->xy, 3, gamma);
380                         return;
381         }
382 }
383
384 static void
385 crystal_drawatom(ModeInfo * mi, crystalatom * atom0)
386 {
387         crystalstruct *cryst;
388         Display    *display = MI_DISPLAY(mi);
389         Window      window = MI_WINDOW(mi);
390         int         j, k, l, m;
391
392         cryst = &crystals[MI_SCREEN(mi)];
393         for (j = numops[2 * cryst->planegroup + 1];
394              j < numops[2 * cryst->planegroup]; j++) {
395                 XPoint      xy[5], new_xy[5];
396                 XPoint      xy_1[5];
397                 int         xtrans, ytrans;
398
399                 xtrans = operation[j * 6] * atom0->x0 + operation[j * 6 + 1] *
400                         atom0->y0 + (int) (operation[j * 6 + 4] * cryst->a /
401                                            2.0);
402                 ytrans = operation[j * 6 + 2] * atom0->x0 + operation[j * 6 +
403                                3] * atom0->y0 + (int) (operation[j * 6 + 5] *
404                                                        cryst->b / 2.0);
405                 if (xtrans < 0) {
406                         if (xtrans < -cryst->a)
407                                 xtrans = 2 * cryst->a;
408                         else
409                                 xtrans = cryst->a;
410                 } else if (xtrans >= cryst->a)
411                         xtrans = -cryst->a;
412                 else
413                         xtrans = 0;
414                 if (ytrans < 0)
415                         ytrans = cryst->b;
416                 else if (ytrans >= cryst->b)
417                         ytrans = -cryst->b;
418                 else
419                         ytrans = 0;
420                 for (k = 0; k < atom0->num_point; k++) {
421                         xy[k].x = operation[j * 6] * atom0->xy[k].x +
422                                 operation[j * 6 + 1] *
423                                 atom0->xy[k].y + (int) (operation[j * 6 + 4] *
424                                                         cryst->a / 2.0) +
425                                 xtrans;
426                         xy[k].y = operation[j * 6 + 2] * atom0->xy[k].x +
427                                 operation[j * 6 + 3] *
428                                 atom0->xy[k].y + (int) (operation[j * 6 + 5] *
429                                                         cryst->b / 2.0) +
430                                 ytrans;
431                 }
432                 xy[atom0->num_point].x = xy[0].x;
433                 xy[atom0->num_point].y = xy[0].y;
434                 for (l = 0; l < cryst->nx; l++) {
435                         for (m = 0; m < cryst->ny; m++) {
436
437                                 for (k = 0; k <= atom0->num_point; k++) {
438                                         xy_1[k].x = xy[k].x + l * cryst->a;
439                                         xy_1[k].y = xy[k].y + m * cryst->b;
440                                 }
441                                 trans_coor_back(xy_1, new_xy, atom0->num_point,
442                                                 cryst->gamma, cryst->offset_w, cryst->offset_h);
443                                 XFillPolygon(display, window, cryst->gc, new_xy,
444                                   atom0->num_point, Convex, CoordModeOrigin);
445                         }
446                 }
447                 if (centro[cryst->planegroup] == True) {
448                         for (k = 0; k <= atom0->num_point; k++) {
449                                 xy[k].x = cryst->a - xy[k].x;
450                                 xy[k].y = cryst->b - xy[k].y;
451                         }
452                         for (l = 0; l < cryst->nx; l++) {
453                                 for (m = 0; m < cryst->ny; m++) {
454
455                                         for (k = 0; k <= atom0->num_point; k++) {
456                                                 xy_1[k].x = xy[k].x + l * cryst->a;
457                                                 xy_1[k].y = xy[k].y + m * cryst->b;
458                                         }
459                                         trans_coor_back(xy_1, new_xy, atom0->num_point,
460                                                         cryst->gamma, cryst->offset_w, cryst->offset_h);
461                                         XFillPolygon(display, window, cryst->gc,
462                                                      new_xy,
463                                                      atom0->num_point, Convex,
464                                                      CoordModeOrigin);
465                                 }
466                         }
467                 }
468                 if (primitive[cryst->planegroup] == False) {
469                         if (xy[atom0->num_point].x >= (int) (cryst->a / 2.0))
470                                 xtrans = (int) (-cryst->a / 2.0);
471                         else
472                                 xtrans = (int) (cryst->a / 2.0);
473                         if (xy[atom0->num_point].y >= (int) (cryst->b / 2.0))
474                                 ytrans = (int) (-cryst->b / 2.0);
475                         else
476                                 ytrans = (int) (cryst->b / 2.0);
477                         for (k = 0; k <= atom0->num_point; k++) {
478                                 xy[k].x = xy[k].x + xtrans;
479                                 xy[k].y = xy[k].y + ytrans;
480                         }
481                         for (l = 0; l < cryst->nx; l++) {
482                                 for (m = 0; m < cryst->ny; m++) {
483
484                                         for (k = 0; k <= atom0->num_point; k++) {
485                                                 xy_1[k].x = xy[k].x + l * cryst->a;
486                                                 xy_1[k].y = xy[k].y + m * cryst->b;
487                                         }
488                                         trans_coor_back(xy_1, new_xy, atom0->num_point,
489                                                         cryst->gamma, cryst->offset_w, cryst->offset_h);
490                                         XFillPolygon(display, window, cryst->gc,
491                                                      new_xy,
492                                                      atom0->num_point, Convex,
493                                                      CoordModeOrigin);
494                                 }
495                         }
496                         if (centro[cryst->planegroup] == True) {
497                                 XPoint      xy1[5];
498
499                                 for (k = 0; k <= atom0->num_point; k++) {
500                                         xy1[k].x = cryst->a - xy[k].x;
501                                         xy1[k].y = cryst->b - xy[k].y;
502                                 }
503                                 for (l = 0; l < cryst->nx; l++) {
504                                         for (m = 0; m < cryst->ny; m++) {
505
506                                                 for (k = 0; k <= atom0->num_point; k++) {
507                                                         xy_1[k].x = xy1[k].x + l * cryst->a;
508                                                         xy_1[k].y = xy1[k].y + m * cryst->b;
509                                                 }
510                                                 trans_coor_back(xy_1, new_xy, atom0->num_point,
511                                                                 cryst->gamma, cryst->offset_w, cryst->offset_h);
512                                                 XFillPolygon(display, window,
513                                                              cryst->gc,
514                                                     new_xy, atom0->num_point,
515                                                     Convex, CoordModeOrigin);
516                                         }
517                                 }
518                         }
519                 }
520         }
521 }
522
523 void
524 draw_crystal(ModeInfo * mi)
525 {
526         Display    *display = MI_DISPLAY(mi);
527         crystalstruct *cryst = &crystals[MI_SCREEN(mi)];
528         int         i;
529
530         cryst->painted = True;
531         XSetFunction(display, cryst->gc, GXxor);
532         for (i = 0; i < cryst->num_atom; i++) {
533                 crystalatom *atom0;
534
535                 atom0 = &cryst->atom[i];
536                 XSetForeground(display, cryst->gc, atom0->colour);
537                 crystal_drawatom(mi, atom0);
538                 atom0->velocity[0] += NRAND(3) - 1;
539                 atom0->velocity[0] = MAX(-20, MIN(20, atom0->velocity[0]));
540                 atom0->velocity[1] += NRAND(3) - 1;
541                 atom0->velocity[1] = MAX(-20, MIN(20, atom0->velocity[1]));
542                 atom0->x0 += atom0->velocity[0];
543                 /*if (cryst->gamma == 90.0) { */
544                 if (atom0->x0 < 0)
545                         atom0->x0 += cryst->a;
546                 else if (atom0->x0 >= cryst->a)
547                         atom0->x0 -= cryst->a;
548                 atom0->y0 += atom0->velocity[1];
549                 if (atom0->y0 < 0)
550                         atom0->y0 += cryst->b;
551                 else if (atom0->y0 >= cryst->b)
552                         atom0->y0 -= cryst->b;
553                 /*} */
554                 atom0->velocity_a += ((float) NRAND(1001) - 500.0) / 2000.0;
555                 atom0->angle += atom0->velocity_a;
556                 crystal_setupatom(atom0, cryst->gamma);
557                 crystal_drawatom(mi, atom0);
558         }
559         XSetFunction(display, cryst->gc, GXcopy);
560 }
561
562 void
563 refresh_crystal(ModeInfo * mi)
564 {
565         Display    *display = MI_DISPLAY(mi);
566         Window      window = MI_WINDOW(mi);
567         crystalstruct *cryst = &crystals[MI_SCREEN(mi)];
568         int         i;
569
570         if (!cryst->painted)
571                 return;
572         MI_CLEARWINDOW(mi);
573         XSetFunction(display, cryst->gc, GXxor);
574
575         if (cryst->unit_cell) {
576                 if (MI_NPIXELS(mi) > 2)
577                         XSetForeground(display, cryst->gc, MI_PIXEL(mi, NRAND(MI_NPIXELS(mi))));
578                 else
579                         XSetForeground(display, cryst->gc, MI_BLACK_PIXEL(mi));
580                 if (cryst->grid_cell) {
581                         int         inx, iny;
582
583                         XDrawLine(display, window, cryst->gc, cryst->offset_w,
584                                   cryst->offset_h, cryst->offset_w + cryst->nx * cryst->a,
585                                   cryst->offset_h);
586                         XDrawLine(display, window, cryst->gc, cryst->offset_w,
587                                   cryst->offset_h, (int) (cryst->offset_w - cryst->ny * cryst->b *
588                                           sin((cryst->gamma - 90) * PI_RAD)),
589                                   (int) (cryst->ny * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h);
590                         inx = cryst->nx;
591                         for (iny = 1; iny <= cryst->ny; iny++) {
592                                 XDrawLine(display, window, cryst->gc,
593                                           (int) (cryst->offset_w +
594                                      inx * cryst->a - (int) (iny * cryst->b *
595                                          sin((cryst->gamma - 90) * PI_RAD))),
596                                           (int) (iny * cryst->b * cos((cryst->gamma - 90) *
597                                                   PI_RAD)) + cryst->offset_h,
598                                     (int) (cryst->offset_w - iny * cryst->b *
599                                            sin((cryst->gamma - 90) * PI_RAD)),
600                                           (int) (iny * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) +
601                                           cryst->offset_h);
602                         }
603                         iny = cryst->ny;
604                         for (inx = 1; inx <= cryst->nx; inx++) {
605                                 XDrawLine(display, window, cryst->gc,
606                                           (int) (cryst->offset_w +
607                                      inx * cryst->a - (int) (iny * cryst->b *
608                                          sin((cryst->gamma - 90) * PI_RAD))),
609                                           (int) (iny * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h,
610                                           cryst->offset_w + inx * cryst->a, cryst->offset_h);
611                         }
612                 } else {
613                         int         inx, iny;
614
615                         inx = NRAND(cryst->nx);
616                         iny = NRAND(cryst->ny);
617                         XDrawLine(display, window, cryst->gc,
618                                   cryst->offset_w + inx * cryst->a - (int) (iny * cryst->b * sin((cryst->gamma - 90) * PI_RAD)),
619                                   (int) (iny * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h,
620                                   cryst->offset_w + (inx + 1) * cryst->a - (int) (iny * cryst->b * sin((cryst->gamma - 90) * PI_RAD)),
621                                   (int) (iny * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h);
622                         XDrawLine(display, window, cryst->gc,
623                                   cryst->offset_w + inx * cryst->a - (int) (iny * cryst->b * sin((cryst->gamma - 90) * PI_RAD)),
624                                   (int) (iny * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h,
625                                   cryst->offset_w + inx * cryst->a - (int) ((iny + 1) * cryst->b * sin((cryst->gamma - 90) * PI_RAD)),
626                                   (int) ((iny + 1) * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h);
627                         XDrawLine(display, window, cryst->gc,
628                                   cryst->offset_w + (inx + 1) * cryst->a - (int) (iny * cryst->b * sin((cryst->gamma - 90) * PI_RAD)),
629                                   (int) (iny * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h,
630                                   cryst->offset_w + (inx + 1) * cryst->a - (int) ((iny + 1) * cryst->b * sin((cryst->gamma - 90) * PI_RAD)),
631                                   (int) ((iny + 1) * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h);
632                         XDrawLine(display, window, cryst->gc,
633                                   cryst->offset_w + inx * cryst->a - (int) ((iny + 1) * cryst->b * sin((cryst->gamma - 90) * PI_RAD)),
634                                   (int) ((iny + 1) * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h,
635                                   cryst->offset_w + (inx + 1) * cryst->a - (int) ((iny + 1) * cryst->b * sin((cryst->gamma - 90) * PI_RAD)),
636                                   (int) ((iny + 1) * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h);
637                 }
638         }
639         for (i = 0; i < cryst->num_atom; i++) {
640                 crystalatom *atom0;
641
642                 atom0 = &cryst->atom[i];
643                 XSetForeground(display, cryst->gc, atom0->colour);
644                 crystal_drawatom(mi, atom0);
645         }
646         XSetFunction(display, cryst->gc, GXcopy);
647 }
648
649 void
650 release_crystal(ModeInfo * mi)
651 {
652         Display    *display = MI_DISPLAY(mi);
653
654         if (crystals != NULL) {
655                 int         screen;
656
657                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
658                         crystalstruct *cryst = &crystals[screen];
659
660                         if (cryst->gc != NULL)
661                                 XFreeGC(display, cryst->gc);
662                         if (cryst->atom != NULL)
663                                 (void) free((void *) cryst->atom);
664                 }
665                 (void) free((void *) crystals);
666                 crystals = NULL;
667         }
668 }
669
670 void
671 init_crystal(ModeInfo * mi)
672 {
673         Display    *display = MI_DISPLAY(mi);
674         Window      window = MI_WINDOW(mi);
675         crystalstruct *cryst;
676         int         i, max_atoms, size_atom, neqv;
677         int         cell_min;
678
679 #define MIN_CELL 200
680
681 /* initialize */
682         if (crystals == NULL) {
683                 if ((crystals = (crystalstruct *) calloc(MI_NUM_SCREENS(mi),
684                                             sizeof (crystalstruct))) == NULL)
685                         return;
686         }
687         cryst = &crystals[MI_SCREEN(mi)];
688
689         if (!cryst->gc) {
690                 if ((cryst->gc = XCreateGC(display, MI_WINDOW(mi),
691                              (unsigned long) 0, (XGCValues *) NULL)) == None)
692                         return;
693         }
694 /* Clear Display */
695         MI_CLEARWINDOW(mi);
696         cryst->painted = False;
697         XSetFunction(display, cryst->gc, GXxor);
698
699 /*Set up crystal data */
700         if (MI_IS_FULLRANDOM(mi)) {
701                 if (LRAND() & 1)
702                         cryst->unit_cell = True;
703                 else
704                         cryst->unit_cell = False;
705         } else
706                 cryst->unit_cell = unit_cell;
707         if (cryst->unit_cell) {
708                 if (MI_IS_FULLRANDOM(mi)) {
709                         if (LRAND() & 1)
710                                 cryst->grid_cell = True;
711                         else
712                                 cryst->grid_cell = False;
713                 } else
714                         cryst->grid_cell = grid_cell;
715         }
716         cryst->win_width = MI_WIDTH(mi);
717         cryst->win_height = MI_HEIGHT(mi);
718         cell_min = min(cryst->win_width / 2 + 1, MIN_CELL);
719         cell_min = min(cell_min, cryst->win_height / 2 + 1);
720         cryst->planegroup = NRAND(17);
721         if (MI_IS_VERBOSE(mi))
722                 (void) fprintf(stdout, "Selected plane group no %d\n",
723                                cryst->planegroup + 1);
724         if (cryst->planegroup > 11)
725                 cryst->gamma = 120.0;
726         else if (cryst->planegroup < 2)
727                 cryst->gamma = 60.0 + NRAND(60);
728         else
729                 cryst->gamma = 90.0;
730         neqv = numops[2 * cryst->planegroup] - numops[2 * cryst->planegroup + 1];
731         if (centro[cryst->planegroup] == True)
732                 neqv = 2 * neqv;
733         if (primitive[cryst->planegroup] == False)
734                 neqv = 2 * neqv;
735
736
737         if (nx > 0)
738                 cryst->nx = nx;
739         else if (nx < 0)
740                 cryst->nx = NRAND(-nx) + 1;
741         else
742                 cryst->nx = DEF_NX1;
743         if (cryst->planegroup > 8)
744                 cryst->ny = cryst->nx;
745         else if (ny > 0)
746                 cryst->ny = ny;
747         else if (ny < 0)
748                 cryst->ny = NRAND(-ny) + 1;
749         else
750                 cryst->ny = DEF_NY1;
751         neqv = neqv * cryst->nx * cryst->ny;
752
753         cryst->num_atom = MI_COUNT(mi);
754         max_atoms = MI_COUNT(mi);
755         if (cryst->num_atom == 0) {
756                 cryst->num_atom = DEF_NUM_ATOM;
757                 max_atoms = DEF_NUM_ATOM;
758         } else if (cryst->num_atom < 0) {
759                 max_atoms = -cryst->num_atom;
760                 cryst->num_atom = NRAND(-cryst->num_atom) + 1;
761         }
762         if (neqv > 1)
763                 cryst->num_atom = cryst->num_atom / neqv + 1;
764
765         if (cryst->atom == NULL)
766                 cryst->atom = (crystalatom *) calloc(max_atoms, sizeof (
767                                                                crystalatom));
768
769         if (maxsize) {
770                 if (cryst->planegroup < 13) {
771                         cryst->gamma = 90.0;
772                         cryst->offset_w = 0;
773                         cryst->offset_h = 0;
774                         if (cryst->planegroup < 10) {
775                                 cryst->b = cryst->win_height;
776                                 cryst->a = cryst->win_width;
777                         } else {
778                                 cryst->b = min(cryst->win_height, cryst->win_width);
779                                 cryst->a = cryst->b;
780                         }
781                 } else {
782                         cryst->gamma = 120.0;
783                         cryst->a = (int) (cryst->win_width * 2.0 / 3.0);
784                         cryst->b = cryst->a;
785                         cryst->offset_h = (int) (cryst->b * 0.25 *
786                                           cos((cryst->gamma - 90) * PI_RAD));
787                         cryst->offset_w = (int) (cryst->b * 0.5);
788                 }
789         } else {
790                 cryst->offset_w = -1;
791                 while (cryst->offset_w < 4 || (int) (cryst->offset_w - cryst->b *
792                                     sin((cryst->gamma - 90) * PI_RAD)) < 4) {
793                         cryst->b = NRAND((int) (cryst->win_height / (cos((cryst->gamma - 90) *
794                                             PI_RAD))) - cell_min) + cell_min;
795                         if (cryst->planegroup > 8)
796                                 cryst->a = cryst->b;
797                         else
798                                 cryst->a = NRAND(cryst->win_width - cell_min) + cell_min;
799                         cryst->offset_w = (int) ((cryst->win_width - (cryst->a - cryst->b *
800                                                     sin((cryst->gamma - 90) *
801                                                         PI_RAD))) / 2.0);
802                 }
803                 cryst->offset_h = (int) ((cryst->win_height - cryst->b * cos((
804                                         cryst->gamma - 90) * PI_RAD)) / 2.0);
805                 if (!centre) {
806                         if (cryst->offset_h > 0)
807                                 cryst->offset_h = NRAND(2 * cryst->offset_h);
808                         cryst->offset_w = (int) (cryst->win_width - cryst->a -
809                                                  cryst->b *
810                                     fabs(sin((cryst->gamma - 90) * PI_RAD)));
811                         if (cryst->gamma > 90.0) {
812                                 if (cryst->offset_w > 0)
813                                         cryst->offset_w = NRAND(cryst->offset_w) +
814                                                 cryst->b * sin((cryst->gamma - 90) * PI_RAD);
815                                 else
816                                         cryst->offset_w = (int) (cryst->b * sin((cryst->gamma - 90) *
817                                                                     PI_RAD));
818                         } else if (cryst->offset_w > 0)
819                                 cryst->offset_w = NRAND(cryst->offset_w);
820                         else
821                                 cryst->offset_w = 0;
822                 }
823         }
824
825         size_atom = min((int) ((float) (cryst->a) / 40.) + 1,
826                         (int) ((float) (cryst->b) / 40.) + 1);
827         if (MI_SIZE(mi) < size_atom) {
828                 if (MI_SIZE(mi) < -size_atom)
829                         size_atom = -size_atom;
830                 else
831                         size_atom = MI_SIZE(mi);
832         }
833         cryst->a = cryst->a / cryst->nx;
834         cryst->b = cryst->b / cryst->ny;
835         if (cryst->unit_cell) {
836                 if (MI_NPIXELS(mi) > 2)
837                         XSetForeground(display, cryst->gc, MI_PIXEL(mi, NRAND(MI_NPIXELS(mi))));
838                 else
839                         XSetForeground(display, cryst->gc, MI_BLACK_PIXEL(mi));
840                 if (cryst->grid_cell) {
841                         int         inx, iny;
842
843                         XDrawLine(display, window, cryst->gc, cryst->offset_w,
844                                   cryst->offset_h, cryst->offset_w + cryst->nx * cryst->a,
845                                   cryst->offset_h);
846                         XDrawLine(display, window, cryst->gc, cryst->offset_w,
847                                   cryst->offset_h, (int) (cryst->offset_w - cryst->ny * cryst->b *
848                                           sin((cryst->gamma - 90) * PI_RAD)),
849                                   (int) (cryst->ny * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h);
850                         inx = cryst->nx;
851                         for (iny = 1; iny <= cryst->ny; iny++) {
852                                 XDrawLine(display, window, cryst->gc,
853                                           (int) (cryst->offset_w +
854                                      inx * cryst->a - (int) (iny * cryst->b *
855                                          sin((cryst->gamma - 90) * PI_RAD))),
856                                           (int) (iny * cryst->b * cos((cryst->gamma - 90) *
857                                                   PI_RAD)) + cryst->offset_h,
858                                     (int) (cryst->offset_w - iny * cryst->b *
859                                            sin((cryst->gamma - 90) * PI_RAD)),
860                                           (int) (iny * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) +
861                                           cryst->offset_h);
862                         }
863                         iny = cryst->ny;
864                         for (inx = 1; inx <= cryst->nx; inx++) {
865                                 XDrawLine(display, window, cryst->gc,
866                                           (int) (cryst->offset_w +
867                                      inx * cryst->a - (int) (iny * cryst->b *
868                                          sin((cryst->gamma - 90) * PI_RAD))),
869                                           (int) (iny * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h,
870                                           cryst->offset_w + inx * cryst->a, cryst->offset_h);
871                         }
872                 } else {
873                         int         inx, iny;
874
875                         inx = NRAND(cryst->nx);
876                         iny = NRAND(cryst->ny);
877                         XDrawLine(display, window, cryst->gc,
878                                   cryst->offset_w + inx * cryst->a - (int) (iny * cryst->b * sin((cryst->gamma - 90) * PI_RAD)),
879                                   (int) (iny * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h,
880                                   cryst->offset_w + (inx + 1) * cryst->a - (int) (iny * cryst->b * sin((cryst->gamma - 90) * PI_RAD)),
881                                   (int) (iny * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h);
882                         XDrawLine(display, window, cryst->gc,
883                                   cryst->offset_w + inx * cryst->a - (int) (iny * cryst->b * sin((cryst->gamma - 90) * PI_RAD)),
884                                   (int) (iny * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h,
885                                   cryst->offset_w + inx * cryst->a - (int) ((iny + 1) * cryst->b * sin((cryst->gamma - 90) * PI_RAD)),
886                                   (int) ((iny + 1) * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h);
887                         XDrawLine(display, window, cryst->gc,
888                                   cryst->offset_w + (inx + 1) * cryst->a - (int) (iny * cryst->b * sin((cryst->gamma - 90) * PI_RAD)),
889                                   (int) (iny * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h,
890                                   cryst->offset_w + (inx + 1) * cryst->a - (int) ((iny + 1) * cryst->b * sin((cryst->gamma - 90) * PI_RAD)),
891                                   (int) ((iny + 1) * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h);
892                         XDrawLine(display, window, cryst->gc,
893                                   cryst->offset_w + inx * cryst->a - (int) ((iny + 1) * cryst->b * sin((cryst->gamma - 90) * PI_RAD)),
894                                   (int) ((iny + 1) * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h,
895                                   cryst->offset_w + (inx + 1) * cryst->a - (int) ((iny + 1) * cryst->b * sin((cryst->gamma - 90) * PI_RAD)),
896                                   (int) ((iny + 1) * cryst->b * cos((cryst->gamma - 90) * PI_RAD)) + cryst->offset_h);
897                 }
898         }
899         for (i = 0; i < cryst->num_atom; i++) {
900                 crystalatom *atom0;
901
902                 atom0 = &cryst->atom[i];
903                 if (MI_NPIXELS(mi) > 2)
904                         atom0->colour = MI_PIXEL(mi, NRAND(MI_NPIXELS(mi)));
905                 else
906                         atom0->colour = 1;      /*Xor'red so WHITE may not be appropriate */
907                 XSetForeground(display, cryst->gc, atom0->colour);
908                 atom0->x0 = NRAND(cryst->a);
909                 atom0->y0 = NRAND(cryst->b);
910                 atom0->velocity[0] = NRAND(7) - 3;
911                 atom0->velocity[1] = NRAND(7) - 3;
912                 atom0->velocity_a = (NRAND(7) - 3) * PI_RAD;
913                 atom0->angle = NRAND(90) * PI_RAD;
914                 atom0->at_type = NRAND(3);
915                 if (size_atom == 0)
916                         atom0->size_at = DEF_SIZ_ATOM;
917                 else if (size_atom > 0)
918                         atom0->size_at = size_atom;
919                 else
920                         atom0->size_at = NRAND(-size_atom) + 1;
921                 atom0->size_at++;
922                 if (atom0->at_type == 2)
923                         atom0->num_point = 3;
924                 else
925                         atom0->num_point = 4;
926                 crystal_setupatom(atom0, cryst->gamma);
927                 crystal_drawatom(mi, atom0);
928         }
929         XSetFunction(display, cryst->gc, GXcopy);
930 }