render_hkl.c 18.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
 * render_hkl.c
 *
 * Draw pretty renderings of reflection lists
 *
 * (c) 2006-2010 Thomas White <taw@physics.org>
 *
 * Part of CrystFEL - crystallography with a FEL
 *
 */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
23
#ifdef HAVE_CAIRO
Thomas White's avatar
Thomas White committed
24
25
#include <cairo.h>
#include <cairo-pdf.h>
26
#endif
27
28

#include "utils.h"
29
#include "povray.h"
30
#include "symmetry.h"
31
#include "render.h"
32
#include "render_hkl.h"
Thomas White's avatar
Thomas White committed
33
34
#include "reflist.h"
#include "reflist-utils.h"
35
36


37
38
39
#define KEY_FILENAME "key.pdf"


40
41
42
43
static void show_help(const char *s)
{
	printf("Syntax: %s [options] <file.hkl>\n\n", s);
	printf(
44
"Render intensity lists in various ways.\n"
45
"\n"
46
"      --povray            Render a 3D animation using POV-ray.\n"
47
#ifdef HAVE_CAIRO
48
"      --zone-axis         Render a 2D zone axis pattern.\n"
49
#endif
50
"\n"
51
"  -d, --down=<h>,<k>,<l>  Indices for the axis in the downward direction.\n"
52
"                           Default: 1,0,0.\n"
53
"  -r, --right=<h>,<k>,<l> Indices for the axis in the 'right' (roughly)\n"
54
"                           direction.  Default: 0,1,0.\n"
55
"  -o, --output=<filename> Output filename (not for POV-ray).  Default: za.pdf\n"
56
57
58
"      --boost=<val>       Squash colour scale by <val>.\n"
"  -p, --pdb=<file>        PDB file from which to get the unit cell.\n"
"  -y, --symmetry=<sym>    Expand reflections according to point group <sym>.\n"
59
60
61
62
"\n"
"  -c, --colscale=<scale>  Use the given colour scale.  Choose from:\n"
"                           mono    : Greyscale, black is zero.\n"
"                           invmono : Greyscale, white is zero.\n"
Thomas White's avatar
Thomas White committed
63
"                           colour  : Colour scale:\n"
64
65
"                                     black-blue-pink-red-orange-yellow-white\n"
"\n"
66
67
"  -w  --weighting=<wght>  Colour/shade the reciprocal lattice points\n"
"                           according to:\n"
68
69
"                            I      : the intensity of the reflection.\n"
"                            sqrtI  : the square root of the intensity.\n"
Thomas White's avatar
Thomas White committed
70
"                            count  : the number of measurements for the reflection.\n"
71
"                                     (after correcting for 'epsilon')\n"
Thomas White's avatar
Thomas White committed
72
"                            rawcts : the raw number of measurements for the\n"
73
"                                     reflection (no 'epsilon' correction).\n"
74
75
76
77
"\n"
"      --colour-key        Draw (only) the key for the current colour scale.\n"
"  -j <n>                  Run <n> instances of POV-ray in parallel.\n"
"  -h, --help              Display this help message.\n"
78
);
79
80
81
}


82
#ifdef HAVE_CAIRO
83
84


Thomas White's avatar
Thomas White committed
85
86
static void draw_circles(double xh, double xk, double xl,
                         double yh, double yk, double yl,
87
                         signed int zh, signed int zk, signed int zl,
Thomas White's avatar
Thomas White committed
88
                         RefList *list, const SymOpList *sym,
89
90
91
                         cairo_t *dctx, int wght, double boost, int colscale,
                         UnitCell *cell, double radius, double theta,
                         double as, double bs, double cx, double cy,
Thomas White's avatar
Thomas White committed
92
                         double scale, double *max_val)
93
{
Thomas White's avatar
Thomas White committed
94
95
	Reflection *refl;
	RefListIterator *iter;
Thomas White's avatar
Thomas White committed
96
	SymOpMask *m;
97

Thomas White's avatar
Thomas White committed
98
99
	m = new_symopmask(sym);

Thomas White's avatar
Thomas White committed
100
101
102
103
	/* Iterate over all reflections */
	for ( refl = first_refl(list, &iter);
	      refl != NULL;
	      refl = next_refl(refl, iter) ) {
104

Thomas White's avatar
Thomas White committed
105
		double u, v, val;
Thomas White's avatar
Thomas White committed
106
		signed int ha, ka, la;
Thomas White's avatar
Thomas White committed
107
		int i, n;
Thomas White's avatar
Thomas White committed
108
109
110

		get_indices(refl, &ha, &ka, &la);

Thomas White's avatar
Thomas White committed
111
112
		special_position(sym, m, ha, ka, la);
		n = num_equivs(sym, m);
Thomas White's avatar
Thomas White committed
113
114

		for ( i=0; i<n; i++ ) {
Thomas White's avatar
Thomas White committed
115
116

			signed int h, k, l;
Thomas White's avatar
Thomas White committed
117
			double xi, yi;
Thomas White's avatar
Thomas White committed
118

Thomas White's avatar
Thomas White committed
119
			get_equiv(sym, m, i, ha, ka, la, &h, &k, &l);
Thomas White's avatar
Thomas White committed
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136

			/* Is the reflection in the zone? */
			if ( h*zh + k*zk + l*zl != 0 ) continue;

			xi = h*xh + k*xk + l*xl;
			yi = h*yh + k*yk + l*yl;

			switch ( wght) {
			case WGHT_I :
				val = get_intensity(refl);
				break;
			case WGHT_SQRTI :
				val = get_intensity(refl);
				val = (val>0.0) ? sqrt(val) : 0.0;
				break;
			case WGHT_COUNTS :
				val = get_redundancy(refl);
Thomas White's avatar
Thomas White committed
137
				val /= (double)n;
Thomas White's avatar
Thomas White committed
138
139
140
141
142
143
144
145
				break;
			case WGHT_RAWCOUNTS :
				val = get_redundancy(refl);
				break;
			default :
				ERROR("Invalid weighting.\n");
				abort();
			}
146

Thomas White's avatar
Thomas White committed
147
148
149
			/* Absolute location in image based on 2D basis */
			u = (double)xi*as*sin(theta);
			v = (double)xi*as*cos(theta) + (double)yi*bs;
150

Thomas White's avatar
Thomas White committed
151
			if ( dctx != NULL ) {
152

Thomas White's avatar
Thomas White committed
153
				double r, g, b;
154

Thomas White's avatar
Thomas White committed
155
156
157
				cairo_arc(dctx, ((double)cx)+u*scale,
					        ((double)cy)+v*scale,
					        radius, 0, 2*M_PI);
158

Thomas White's avatar
Thomas White committed
159
160
161
162
				render_scale(val, *max_val/boost, colscale,
					     &r, &g, &b);
				cairo_set_source_rgb(dctx, r, g, b);
				cairo_fill(dctx);
163

Thomas White's avatar
Thomas White committed
164
			} else {
165

Thomas White's avatar
Thomas White committed
166
167
168
169
170
171
				/* Find max value for colour scale */
				if ( !isnan(val) && !isinf(val)
				  && (fabs(val) > fabs(*max_val)) )
				{
					*max_val = fabs(val);
				}
172

Thomas White's avatar
Thomas White committed
173
			}
Thomas White's avatar
Thomas White committed
174

175
176
177
		}

	}
Thomas White's avatar
Thomas White committed
178
179

	free_symopmask(m);
180
181
182
}


183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
static void render_overlined_indices(cairo_t *dctx,
                                     signed int h, signed int k, signed int l)
{
	char tmp[256];
	cairo_text_extents_t size;
	double x, y;
	const double sh = 39.0;

	cairo_get_current_point(dctx, &x, &y);
	cairo_set_line_width(dctx, 4.0);

	/* Draw 'h' */
	snprintf(tmp, 255, "%i", abs(h));
	cairo_text_extents(dctx, tmp, &size);
	cairo_show_text(dctx, tmp);
	cairo_fill(dctx);
	if ( h < 0 ) {
		cairo_move_to(dctx, x+size.x_bearing, y-sh);
		cairo_rel_line_to(dctx, size.width, 0.0);
		cairo_stroke(dctx);
	}
	x += size.x_advance;

	/* Draw 'k' */
	cairo_move_to(dctx, x, y);
	snprintf(tmp, 255, "%i", abs(k));
	cairo_text_extents(dctx, tmp, &size);
	cairo_show_text(dctx, tmp);
	cairo_fill(dctx);
	if ( k < 0 ) {
		cairo_move_to(dctx, x+size.x_bearing, y-sh);
		cairo_rel_line_to(dctx, size.width, 0.0);
		cairo_stroke(dctx);
	}
	x += size.x_advance;

	/* Draw 'l' */
	cairo_move_to(dctx, x, y);
	snprintf(tmp, 255, "%i", abs(l));
	cairo_text_extents(dctx, tmp, &size);
	cairo_show_text(dctx, tmp);
	cairo_fill(dctx);
	if ( l < 0 ) {
		cairo_move_to(dctx, x+size.x_bearing, y-sh);
		cairo_rel_line_to(dctx, size.width, 0.0);
		cairo_stroke(dctx);
	}
}


Thomas White's avatar
Thomas White committed
233
static void render_za(UnitCell *cell, RefList *list,
Thomas White's avatar
Thomas White committed
234
235
                      double boost, const SymOpList *sym, int wght,
                      int colscale,
236
                      signed int xh, signed int xk, signed int xl,
237
                      signed int yh, signed int yk, signed int yl,
238
                      const char *outfile, double scale_top)
239
{
Thomas White's avatar
Thomas White committed
240
241
	cairo_surface_t *surface;
	cairo_t *dctx;
Thomas White's avatar
Thomas White committed
242
243
	double max_val;
	double scale1, scale2, scale;
Thomas White's avatar
Thomas White committed
244
	double sep_u, sep_v, max_r;
245
	double u, v;
Thomas White's avatar
Thomas White committed
246
	double as, bs, theta;
247
248
249
	double asx, asy, asz;
	double bsx, bsy, bsz;
	double csx, csy, csz;
Thomas White's avatar
Thomas White committed
250
	float wh, ht;
251
252
253
254
255
256
257
	signed int zh, zk, zl;
	double xx, xy, xz;
	double yx, yy, yz;
	char tmp[256];
	cairo_text_extents_t size;
	double cx, cy;
	const double border = 200.0;
258
	int png;
Thomas White's avatar
Thomas White committed
259
	double rmin, rmax;
260

261
	/* Vector product to determine the zone axis. */
262
263
264
265
	zh = xk*yl - xl*yk;
	zk = - xh*yl + xl*yh;
	zl = xh*yk - xk*yh;
	STATUS("Zone axis is %i %i %i\n", zh, zk, zl);
266

267
	/* Size of output and centre definition */
Thomas White's avatar
Thomas White committed
268
269
	wh = 1024;
	ht = 1024;
270

Thomas White's avatar
Thomas White committed
271
	/* Work out reciprocal lattice spacings and angles for this cut */
272
	if ( cell_get_reciprocal(cell, &asx, &asy, &asz,
Thomas White's avatar
Thomas White committed
273
	                          &bsx, &bsy, &bsz,
274
275
276
277
	                          &csx, &csy, &csz) ) {
		ERROR("Couldn't get reciprocal parameters\n");
		return;
	}
278
279
280
281
282
283
284
	xx = xh*asx + xk*bsx + xl*csx;
	xy = xh*asy + xk*bsy + xl*csy;
	xz = xh*asz + xk*bsz + xl*csz;
	yx = yh*asx + yk*bsx + yl*csx;
	yy = yh*asy + yk*bsy + yl*csy;
	yz = yh*asz + yk*bsz + yl*csz;
	theta = angle_between(xx, xy, xz, yx, yy, yz);
Thomas White's avatar
Thomas White committed
285
286
	as = modulus(xx, xy, xz);
	bs = modulus(yx, yy, yz);
287

Thomas White's avatar
Thomas White committed
288
289
290
291
	resolution_limits(list, cell, &rmin, &rmax);
	printf("Resolution limits: 1/d = %.2f - %.2f nm^-1"
	       " (d = %.2f - %.2f A)\n",
	       rmin/1e9, rmax/1e9, (1.0/rmin)/1e-10, (1.0/rmax)/1e-10);
292

Thomas White's avatar
Thomas White committed
293

Thomas White's avatar
Thomas White committed
294
	max_val = max_intensity(list);
295
	if ( max_val <= 0.0 ) {
296
		STATUS("Couldn't find max value.\n");
Thomas White's avatar
Thomas White committed
297
		return;
298
299
	}

300
301
302
303
304
	/* Use manual scale top if specified */
	if ( scale_top > 0.0 ) {
		max_val = scale_top;
	}

Thomas White's avatar
Thomas White committed
305
306
307
	scale1 = ((double)wh-border) / (2.0*rmax);
	scale2 = ((double)ht-border) / (2.0*rmax);
	scale = (scale1 < scale2) ? scale1 : scale2;
308

Thomas White's avatar
Thomas White committed
309
	/* Work out the spot radius */
310
311
	sep_u = scale*as;
	sep_v = scale*bs;
312
	max_r = (sep_u < sep_v) ? sep_u : sep_v;
313
	max_r /= 2.0;  /* Max radius is half the separation */
314
	max_r -= 1.0;  /* Add a tiny separation between circles */
315

Thomas White's avatar
Thomas White committed
316
	/* Create surface */
317
318
319
320
321
322
323
324
	if ( strcmp(outfile+strlen(outfile)-4, ".png") == 0 ) {
		png = 1;
		surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
		                                     wh, ht);
	} else {
		png = 0;
		surface = cairo_pdf_surface_create(outfile, wh, ht);
	}
325
326

	if ( cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS ) {
Thomas White's avatar
Thomas White committed
327
		ERROR("Couldn't create Cairo surface\n");
328
329
330
331
332
		cairo_surface_destroy(surface);
		return;
	}

	dctx = cairo_create(surface);
Thomas White's avatar
Thomas White committed
333
334
335
336
337
	if ( cairo_status(dctx) != CAIRO_STATUS_SUCCESS ) {
		ERROR("Couldn't create Cairo context\n");
		cairo_surface_destroy(surface);
		return;
	}
338
339
340
341
342
343
344
345
346
347
348
349
350
351

	/* Black background */
	cairo_rectangle(dctx, 0.0, 0.0, wh, ht);
	cairo_set_source_rgb(dctx, 0.0, 0.0, 0.0);
	cairo_fill(dctx);

	/* Test size of text that goes to the right(ish) */
	cairo_set_font_size(dctx, 40.0);
	snprintf(tmp, 255, "%i%i%i", abs(xh), abs(xk), abs(xl));
	cairo_text_extents(dctx, tmp, &size);

	cx = 532.0 - size.width;
	cy = 512.0 - 20.0;

352
	draw_circles(xh, xk, xl, yh, yk, yl, zh, zk, zl,
Thomas White's avatar
Thomas White committed
353
	             list, sym, dctx, wght, boost, colscale, cell,
354
	             max_r, theta, as, bs, cx, cy, scale,
Thomas White's avatar
Thomas White committed
355
	             &max_val);
356

Thomas White's avatar
Thomas White committed
357
	/* Centre marker */
358
359
	cairo_arc(dctx, (double)cx,
			(double)cy, max_r, 0, 2*M_PI);
Thomas White's avatar
Thomas White committed
360
361
362
	cairo_set_source_rgb(dctx, 1.0, 0.0, 0.0);
	cairo_fill(dctx);

363
	/* Draw indexing lines */
364
	cairo_set_line_cap(dctx, CAIRO_LINE_CAP_ROUND);
365
	cairo_set_line_width(dctx, 4.0);
366
	cairo_move_to(dctx, (double)cx, (double)cy);
Thomas White's avatar
Thomas White committed
367
368
	u = rmax*sin(theta);
	v = rmax*cos(theta);
369
	cairo_line_to(dctx, cx+u*scale, cy+v*scale);
370
371
372
	cairo_set_source_rgb(dctx, 0.0, 1.0, 0.0);
	cairo_stroke(dctx);

373
	cairo_set_font_size(dctx, 40.0);
374
	snprintf(tmp, 255, "%i%i%i", abs(xh), abs(xk), abs(xl));
375
376
377
	cairo_text_extents(dctx, tmp, &size);

	cairo_move_to(dctx, cx+u*scale + 20.0, cy+v*scale + size.height/2.0);
378
	render_overlined_indices(dctx, xh, xk, xl);
379
380
	cairo_fill(dctx);

381
	snprintf(tmp, 255, "%i%i%i", abs(yh), abs(yk), abs(yl));
382
383
384
	cairo_text_extents(dctx, tmp, &size);

	cairo_move_to(dctx, (double)cx, (double)cy);
Thomas White's avatar
Thomas White committed
385
	cairo_line_to(dctx, cx, cy+rmax*scale);
386
387
388
	cairo_set_source_rgb(dctx, 0.0, 1.0, 0.0);
	cairo_stroke(dctx);

Thomas White's avatar
Thomas White committed
389
390
	cairo_move_to(dctx, cx - size.width/2.0,
	                    cy+rmax*scale + size.height + 20.0);
391
	render_overlined_indices(dctx, yh, yk, yl);
392
393
	cairo_fill(dctx);

394
395
396
397
398
399
400
	if ( png ) {
		int r = cairo_surface_write_to_png(surface, outfile);
		if ( r != CAIRO_STATUS_SUCCESS ) {
			ERROR("Failed to write PNG to '%s'\n", outfile);
		}
	}

Thomas White's avatar
Thomas White committed
401
402
403
	cairo_surface_finish(surface);
	cairo_destroy(dctx);
}
404

405

406
static int render_key(int colscale, double scale_top)
407
408
409
{
	cairo_surface_t *surface;
	cairo_t *dctx;
410
411
	double top, wh, ht, y;
	double slice;
412

413
414
415
416
417
418
419
420
421
	wh = 128.0;
	ht = 1024.0;
	slice = 1.0;

	if ( scale_top > 0.0 ) {
		top = scale_top;
	} else {
		top = 1.0;
	}
422

423
	surface = cairo_pdf_surface_create(KEY_FILENAME, wh, ht);
424
425
426
427
428
429
430
431
432

	if ( cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS ) {
		fprintf(stderr, "Couldn't create Cairo surface\n");
		cairo_surface_destroy(surface);
		return 1;
	}

	dctx = cairo_create(surface);

433
	for ( y=0.0; y<ht; y+=slice ) {
434

435
436
437
		double r, g, b;
		double val;
		double v = y;
438

439
440
441
442
443
444
445
446
447
448
449
		cairo_rectangle(dctx, 0.0, ht-y, wh/2.0, slice);

		if ( colscale == SCALE_RATIO ) {
			if ( v < ht/2.0 ) {
				val = v/(ht/2.0);
			} else {
				val = (((v-ht/2.0)/(ht/2.0))*(top-1.0))+1.0;
			}
		} else {
			val = v/ht;
		}
450

451
		render_scale(val, top, colscale, &r, &g, &b);
452
453
		cairo_set_source_rgb(dctx, r, g, b);

454
		cairo_stroke_preserve(dctx);
455
456
457
458
		cairo_fill(dctx);

	}

459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
	if ( colscale == SCALE_RATIO ) {

		cairo_text_extents_t size;
		char tmp[32];

		cairo_rectangle(dctx, 0.0, ht/2.0-2.0, wh/2.0, 4.0);
		cairo_set_source_rgb(dctx, 0.0, 0.0, 0.0);
		cairo_stroke_preserve(dctx);
		cairo_fill(dctx);

		cairo_set_font_size(dctx, 20.0);
		cairo_text_extents(dctx, "1.0", &size);
		cairo_move_to(dctx, wh/2.0+5.0, ht/2.0+size.height/2.0);
		cairo_show_text(dctx, "1.0");

		cairo_set_font_size(dctx, 20.0);
		cairo_text_extents(dctx, "0.0", &size);
		cairo_move_to(dctx, wh/2.0+5.0, ht-5.0);
		cairo_show_text(dctx, "0.0");

		cairo_set_font_size(dctx, 20.0);
		snprintf(tmp, 31, "%.1f", top);
		cairo_text_extents(dctx, tmp, &size);
		cairo_move_to(dctx, wh/2.0+5.0, size.height+5.0);
		cairo_show_text(dctx, tmp);

	}


488
489
490
	cairo_surface_finish(surface);
	cairo_destroy(dctx);

491
492
	STATUS("Colour key written to "KEY_FILENAME"\n");

493
494
	return 0;
}
495
496
497
498
499


#else  /* HAVE_CAIRO */


500
static int render_key(int colscale, double scale_top)
501
502
503
504
505
506
507
508
{
	ERROR("This version of CrystFEL was compiled without Cairo");
	ERROR(" support, which is required to draw the colour");
	ERROR(" scale.  Sorry!\n");
	return 1;
}


509
static void render_za(UnitCell *cell, RefList *list,
510
511
512
                      double boost, const char *sym, int wght, int colscale,
                      signed int xh, signed int xk, signed int xl,
                      signed int yh, signed int yk, signed int yl,
513
                      const char *outfile, double scale_top)
514
515
516
517
518
519
520
521
{
	ERROR("This version of CrystFEL was compiled without Cairo");
	ERROR(" support, which is required to plot a zone axis");
	ERROR(" pattern.  Sorry!\n");
}


#endif /* HAVE_CAIRO */
Thomas White's avatar
Thomas White committed
522
523
524
525
526
527


int main(int argc, char *argv[])
{
	int c;
	UnitCell *cell;
Thomas White's avatar
Thomas White committed
528
	RefList *list;
Thomas White's avatar
Thomas White committed
529
530
531
	char *infile;
	int config_povray = 0;
	int config_zoneaxis = 0;
Thomas White's avatar
Thomas White committed
532
	int config_sqrt = 0;
533
	int config_colkey = 0;
Thomas White's avatar
Thomas White committed
534
	unsigned int nproc = 1;
535
	char *pdb = NULL;
536
	int r = 0;
537
	double boost = 1.0;
Thomas White's avatar
Thomas White committed
538
539
	char *sym_str = NULL;
	SymOpList *sym;
540
541
	char *weighting = NULL;
	int wght;
542
543
	int colscale;
	char *cscale = NULL;
544
545
546
547
	signed int dh=1, dk=0, dl=0;
	signed int rh=0, rk=1, rl=0;
	char *down = NULL;
	char *right = NULL;
548
	char *outfile = NULL;
549
550
	double scale_top = -1.0;
	char *endptr;
Thomas White's avatar
Thomas White committed
551
552
553
554
555
556

	/* Long options */
	const struct option longopts[] = {
		{"help",               0, NULL,               'h'},
		{"povray",             0, &config_povray,      1},
		{"zone-axis",          0, &config_zoneaxis,    1},
557
		{"output",             1, NULL,               'o'},
558
		{"pdb",                1, NULL,               'p'},
559
		{"boost",              1, NULL,               'b'},
560
		{"symmetry",           1, NULL,               'y'},
561
		{"weighting",          1, NULL,               'w'},
562
		{"colscale",           1, NULL,               'c'},
563
564
		{"down",               1, NULL,               'd'},
		{"right",              1, NULL,               'r'},
565
		{"counts",             0, &config_sqrt,        1},
566
		{"colour-key",         0, &config_colkey,      1},
567
		{"scale-top",          1, NULL,                2},
Thomas White's avatar
Thomas White committed
568
569
570
571
		{0, 0, NULL, 0}
	};

	/* Short options */
572
	while ((c = getopt_long(argc, argv, "hj:p:w:c:y:d:r:o:",
573
	                        longopts, NULL)) != -1) {
Thomas White's avatar
Thomas White committed
574
575

		switch (c) {
Thomas White's avatar
Thomas White committed
576
		case 'h' :
Thomas White's avatar
Thomas White committed
577
578
579
			show_help(argv[0]);
			return 0;

Thomas White's avatar
Thomas White committed
580
		case 'j' :
Thomas White's avatar
Thomas White committed
581
582
583
			nproc = atoi(optarg);
			break;

584
585
586
587
		case 'p' :
			pdb = strdup(optarg);
			break;

588
589
590
591
		case 'b' :
			boost = atof(optarg);
			break;

592
		case 'y' :
Thomas White's avatar
Thomas White committed
593
			sym_str = strdup(optarg);
594
595
			break;

596
597
598
599
		case 'w' :
			weighting = strdup(optarg);
			break;

600
601
602
603
		case 'c' :
			cscale = strdup(optarg);
			break;

604
605
606
607
608
609
610
611
		case 'd' :
			down = strdup(optarg);
			break;

		case 'r' :
			right = strdup(optarg);
			break;

612
613
614
615
		case 'o' :
			outfile = strdup(optarg);
			break;

616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
		case 2 :
			errno = 0;
			scale_top = strtod(optarg, &endptr);
			if ( !( (optarg[0] != '\0') && (endptr[0] == '\0') )
			   || (errno != 0) )
			{
				ERROR("Invalid scale top('%s')\n", optarg);
				return 1;
			}
			if ( scale_top < 0.0 ) {
				ERROR("Scale top must be positive.\n");
				return 1;
			}
			break;

Thomas White's avatar
Thomas White committed
631
		case 0 :
Thomas White's avatar
Thomas White committed
632
633
			break;

Thomas White's avatar
Thomas White committed
634
		default :
Thomas White's avatar
Thomas White committed
635
636
637
638
639
			return 1;
		}

	}

640
641
642
	if ( (pdb == NULL) && !config_colkey ) {
		ERROR("You must specify the PDB containing the unit cell.\n");
		return 1;
643
644
	}

Thomas White's avatar
Thomas White committed
645
646
	if ( sym_str == NULL ) {
		sym_str = strdup("1");
647
	}
Thomas White's avatar
Thomas White committed
648
649
	sym = get_pointgroup(sym_str);
	free(sym_str);
650

651
652
653
654
	if ( weighting == NULL ) {
		weighting = strdup("I");
	}

Thomas White's avatar
Thomas White committed
655
656
657
	if ( outfile == NULL ) outfile = "za.pdf";


658
659
660
661
662
663
	if ( strcmp(weighting, "I") == 0 ) {
		wght = WGHT_I;
	} else if ( strcmp(weighting, "sqrtI") == 0 ) {
		wght = WGHT_SQRTI;
	} else if ( strcmp(weighting, "count") == 0 ) {
		wght = WGHT_COUNTS;
664
665
	} else if ( strcmp(weighting, "counts") == 0 ) {
		wght = WGHT_COUNTS;
666
667
	} else if ( strcmp(weighting, "rawcts") == 0 ) {
		wght = WGHT_RAWCOUNTS;
668
669
670
671
	} else if ( strcmp(weighting, "rawcount") == 0 ) {
		wght = WGHT_RAWCOUNTS;
	} else if ( strcmp(weighting, "rawcounts") == 0 ) {
		wght = WGHT_RAWCOUNTS;
672
673
674
675
	} else {
		ERROR("Unrecognised weighting '%s'\n", weighting);
		return 1;
	}
676
677
678
679
680
681
682
683
684
685
686
687
688
689
	free(weighting);

	if ( cscale == NULL ) {
		cscale = strdup("mono");
	}

	if ( strcmp(cscale, "mono") == 0 ) {
		colscale = SCALE_MONO;
	} else if ( strcmp(cscale, "invmono") == 0 ) {
		colscale = SCALE_INVMONO;
	} else if ( strcmp(cscale, "colour") == 0 ) {
		colscale = SCALE_COLOUR;
	} else if ( strcmp(cscale, "color") == 0 ) {
		colscale = SCALE_COLOUR;
690
691
	} else if ( strcmp(cscale, "ratio") == 0 ) {
		colscale = SCALE_RATIO;
692
693
694
695
696
	} else {
		ERROR("Unrecognised colour scale '%s'\n", cscale);
		return 1;
	}
	free(cscale);
697

698
	if ( config_colkey ) {
699
		return render_key(colscale, scale_top);
700
701
	}

702
703
704
	if ( config_zoneaxis ) {
		if ( (( down == NULL ) && ( right != NULL ))
		  || (( down != NULL ) && ( right == NULL )) ) {
Thomas White's avatar
Thomas White committed
705
706
			ERROR("Either specify both 'down' and 'right',"
			      " or neither.\n");
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
			return 1;
		}
		if ( down != NULL ) {
			int r;
			r = sscanf(down, "%i,%i,%i", &dh, &dk, &dl);
			if ( r != 3 ) {
				ERROR("Invalid format for 'down'\n");
				return 1;
			}
		}
		if ( right != NULL ) {
			int r;
			r = sscanf(right, "%i,%i,%i", &rh, &rk, &rl);
			if ( r != 3 ) {
				ERROR("Invalid format for 'right'\n");
				return 1;
			}
		}
	}

Thomas White's avatar
Thomas White committed
727
728
	infile = argv[optind];

729
	cell = load_cell_from_pdb(pdb);
730
731
732
733
	if ( cell == NULL ) {
		ERROR("Couldn't load unit cell from %s\n", pdb);
		return 1;
	}
Thomas White's avatar
Thomas White committed
734
735
736
	list = read_reflections(infile);
	if ( list == NULL ) {
		ERROR("Couldn't read file '%s'\n", infile);
Thomas White's avatar
Thomas White committed
737
738
		return 1;
	}
Thomas White's avatar
Thomas White committed
739
	if ( check_list_symmetry(list, sym) ) {
Thomas White's avatar
Thomas White committed
740
		ERROR("The input reflection list does not appear to"
Thomas White's avatar
Thomas White committed
741
		      " have symmetry %s\n", symmetry_name(sym));
Thomas White's avatar
Thomas White committed
742
743
		return 1;
	}
Thomas White's avatar
Thomas White committed
744
745

	if ( config_povray ) {
Thomas White's avatar
Thomas White committed
746
		r = povray_render_animation(cell, list,
747
		                            nproc, sym, wght, boost, scale_top);
Thomas White's avatar
Thomas White committed
748
	} else if ( config_zoneaxis ) {
Thomas White's avatar
Thomas White committed
749
		render_za(cell, list, boost, sym, wght, colscale,
750
		          rh, rk, rl, dh, dk, dl, outfile, scale_top);
Thomas White's avatar
Thomas White committed
751
752
753
754
	} else {
		ERROR("Try again with either --povray or --zone-axis.\n");
	}

755
	free(pdb);
Thomas White's avatar
Thomas White committed
756
	free_symoplist(sym);
Thomas White's avatar
Thomas White committed
757
	reflist_free(list);
758
	if ( outfile != NULL ) free(outfile);
759

760
	return r;
761
}