render_hkl.c 17.1 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
40
static void show_help(const char *s)
{
	printf("Syntax: %s [options] <file.hkl>\n\n", s);
	printf(
41
"Render intensity lists in various ways.\n"
42
"\n"
43
"      --povray            Render a 3D animation using POV-ray.\n"
44
#ifdef HAVE_CAIRO
45
"      --zone-axis         Render a 2D zone axis pattern.\n"
46
#endif
47
"\n"
48
"  -d, --down=<h>,<k>,<l>  Indices for the axis in the downward direction.\n"
49
"                           Default: 1,0,0.\n"
50
"  -r, --right=<h>,<k>,<l> Indices for the axis in the 'right' (roughly)\n"
51
"                           direction.  Default: 0,1,0.\n"
52
"  -o, --output=<filename> Output filename (not for POV-ray).  Default: za.pdf\n"
53
54
55
"      --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"
56
57
58
59
"\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
60
"                           colour  : Colour scale:\n"
61
62
"                                     black-blue-pink-red-orange-yellow-white\n"
"\n"
63
64
"  -w  --weighting=<wght>  Colour/shade the reciprocal lattice points\n"
"                           according to:\n"
65
66
"                            I      : the intensity of the reflection.\n"
"                            sqrtI  : the square root of the intensity.\n"
Thomas White's avatar
Thomas White committed
67
"                            count  : the number of measurements for the reflection.\n"
68
"                                     (after correcting for 'epsilon')\n"
Thomas White's avatar
Thomas White committed
69
"                            rawcts : the raw number of measurements for the\n"
70
"                                     reflection (no 'epsilon' correction).\n"
71
72
73
74
"\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"
75
);
76
77
78
}


79
#ifdef HAVE_CAIRO
80
81


82
83
84
static void draw_circles(signed int xh, signed int xk, signed int xl,
                         signed int yh, signed int yk, signed int yl,
                         signed int zh, signed int zk, signed int zl,
Thomas White's avatar
Thomas White committed
85
                         RefList *list, const char *sym,
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
                         cairo_t *dctx, int wght, double boost, int colscale,
                         UnitCell *cell, double radius, double theta,
                         double as, double bs, double cx, double cy,
                         double scale,
                         signed int *max_ux, signed int *max_uy,
                         double *max_val, double *max_u, double *max_v,
                         double *max_res)
{
	signed int xi, yi;

	if ( dctx == NULL ) {
		*max_u = 0.0;  *max_v = 0.0;  *max_val = 0.0;
		*max_res = 0.0;  *max_ux = 0;  *max_uy = 0;
	}

Thomas White's avatar
Thomas White committed
101
	/* Iterate over possible reflections in this zone */
102
103
104
105
106
	for ( xi=-INDMAX; xi<INDMAX; xi++ ) {
	for ( yi=-INDMAX; yi<INDMAX; yi++ ) {

		double u, v, val, res;
		signed int h, k, l;
Thomas White's avatar
Thomas White committed
107
		signed int he, ke, le;
Thomas White's avatar
Thomas White committed
108
		Reflection *refl;
Thomas White's avatar
Thomas White committed
109
		int r;
110
111
112
113
114
115

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

		/* Got this reflection? */
Thomas White's avatar
Thomas White committed
116
117
118
		r = find_equiv_in_list(list, h, k, l, sym, &he, &ke, &le);
		if ( !r ) continue;
		refl = find_refl(list, he, ke, le);
119
120
121

		switch ( wght ) {
		case WGHT_I :
Thomas White's avatar
Thomas White committed
122
			val = get_intensity(refl);
123
124
			break;
		case WGHT_SQRTI :
Thomas White's avatar
Thomas White committed
125
			val = get_intensity(refl);
126
127
128
			val = (val>0.0) ? sqrt(val) : 0.0;
			break;
		case WGHT_COUNTS :
Thomas White's avatar
Thomas White committed
129
130
			val = get_redundancy(refl);
			val /= (float)num_equivs(h, k, l, sym);
131
132
			break;
		case WGHT_RAWCOUNTS :
Thomas White's avatar
Thomas White committed
133
			val = get_redundancy(refl);
134
135
136
137
138
139
			break;
		default :
			ERROR("Invalid weighting.\n");
			abort();
		}

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

144
		if ( dctx != NULL ) {
145

146
			float r, g, b;
147

148
149
150
			cairo_arc(dctx, ((double)cx)+u*scale,
			                ((double)cy)+v*scale,
			                radius, 0, 2*M_PI);
151

152
153
154
155
			render_scale(val, *max_val/boost, colscale,
			             &r, &g, &b);
			cairo_set_source_rgb(dctx, r, g, b);
			cairo_fill(dctx);
156

157
		} else {
158

159
160
161
			/* Find max vectors in plane for scaling */
			if ( fabs(u) > fabs(*max_u) ) *max_u = fabs(u);
			if ( fabs(v) > fabs(*max_v) ) *max_v = fabs(v);
162

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

170
			/* Find max indices */
Thomas White's avatar
Thomas White committed
171
172
173
174
			if ( (yi==0) && (fabs(xi) > *max_ux) )
				*max_ux = fabs(xi);
			if ( (xi==0) && (fabs(yi) > *max_uy) )
				*max_uy = fabs(yi);
175
176

			/* Find max resolution */
Thomas White's avatar
Thomas White committed
177
			res = resolution(cell, h, k, l);
178
			if ( res > *max_res ) *max_res = res;
Thomas White's avatar
Thomas White committed
179

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
233
234
235
236
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
237
static void render_za(UnitCell *cell, RefList *list,
238
239
                      double boost, const char *sym, int wght, int colscale,
                      signed int xh, signed int xk, signed int xl,
240
241
                      signed int yh, signed int yk, signed int yl,
                      const char *outfile)
242
{
Thomas White's avatar
Thomas White committed
243
244
	cairo_surface_t *surface;
	cairo_t *dctx;
245
	double max_u, max_v, max_res, max_val;
246
	double scale_u, scale_v, scale;
Thomas White's avatar
Thomas White committed
247
	double sep_u, sep_v, max_r;
248
	double u, v;
249
	signed int max_ux, max_uy;
Thomas White's avatar
Thomas White committed
250
	double as, bs, theta;
251
252
253
	double asx, asy, asz;
	double bsx, bsy, bsz;
	double csx, csy, csz;
Thomas White's avatar
Thomas White committed
254
	float wh, ht;
255
256
257
258
259
260
261
262
	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;

263
	/* Vector product to determine the zone axis. */
264
265
266
267
	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);
268

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

Thomas White's avatar
Thomas White committed
273
	/* Work out reciprocal lattice spacings and angles for this cut */
274
	if ( cell_get_reciprocal(cell, &asx, &asy, &asz,
Thomas White's avatar
Thomas White committed
275
	                          &bsx, &bsy, &bsz,
276
277
278
279
	                          &csx, &csy, &csz) ) {
		ERROR("Couldn't get reciprocal parameters\n");
		return;
	}
280
281
282
283
284
285
286
287
288
289
	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);
	as = modulus(xx, xy, xz) / 1e9;
	bs = modulus(yx, yy, yz) / 1e9;

290
	scale = 1.0;
291
	draw_circles(xh, xk, xl, yh, yk, yl, zh, zk, zl,
Thomas White's avatar
Thomas White committed
292
	             list, sym, NULL, wght, boost, colscale, cell,
Thomas White's avatar
Thomas White committed
293
	             0.0, theta, as, bs, 0.0, 0.0, scale,
294
	             &max_ux, &max_uy, &max_val, &max_u, &max_v, &max_res);
295

Thomas White's avatar
Thomas White committed
296
	max_res /= 1e9;
297
298
	printf("Maximum resolution is 1/d = %5.3f nm^-1, d = %5.3f nm\n",
	       max_res*2.0, 1.0/(max_res*2.0));
Thomas White's avatar
Thomas White committed
299

300
	if ( max_val <= 0.0 ) {
301
		STATUS("Couldn't find max value.\n");
Thomas White's avatar
Thomas White committed
302
		return;
303
304
	}

Thomas White's avatar
Thomas White committed
305
	/* Choose whichever scaling factor gives the smallest value */
306
307
	scale_u = ((double)wh-border) / (2.0*max_u);
	scale_v = ((double)ht-border) / (2.0*max_v);
308
	scale = (scale_u < scale_v) ? scale_u : scale_v;
309

310
311
	sep_u = scale*as;
	sep_v = scale*bs;
312
	/* We are interested in the smaller of the two separations */
313
	max_r = (sep_u < sep_v) ? sep_u : sep_v;
314
	max_r /= 2.0;  /* Max radius is half the separation */
315
	max_r -= 1.0;  /* Add a tiny separation between circles */
316
317
318
	if ( max_r < 1.0 ) {
		ERROR("Circle radius is probably too small (%f).\n", max_r);
	}
Thomas White's avatar
Thomas White committed
319

320
321
	if ( outfile == NULL ) outfile = "za.pdf";
	surface = cairo_pdf_surface_create(outfile, wh, ht);
322
323

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

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

	/* 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;

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

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

360
	/* Draw indexing lines */
361
	cairo_set_line_cap(dctx, CAIRO_LINE_CAP_ROUND);
362
	cairo_set_line_width(dctx, 4.0);
363
	cairo_move_to(dctx, (double)cx, (double)cy);
364
365
	u = (2.0+max_ux)*as*sin(theta);
	v = (2.0+max_ux)*as*cos(theta);
366
	cairo_line_to(dctx, cx+u*scale, cy+v*scale);
367
368
369
	cairo_set_source_rgb(dctx, 0.0, 1.0, 0.0);
	cairo_stroke(dctx);

370
	cairo_set_font_size(dctx, 40.0);
371
	snprintf(tmp, 255, "%i%i%i", abs(xh), abs(xk), abs(xl));
372
373
374
	cairo_text_extents(dctx, tmp, &size);

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

378
	snprintf(tmp, 255, "%i%i%i", abs(yh), abs(yk), abs(yl));
379
380
381
	cairo_text_extents(dctx, tmp, &size);

	cairo_move_to(dctx, (double)cx, (double)cy);
382
	u = 0.0;
383
	v = (2.0+max_uy)*bs;
384
	cairo_line_to(dctx, cx+u*scale, cy+v*scale);
385
386
387
	cairo_set_source_rgb(dctx, 0.0, 1.0, 0.0);
	cairo_stroke(dctx);

388
389
	cairo_move_to(dctx, cx+u*scale - size.width/2.0,
	                    cy+v*scale + size.height + 20.0);
390
	render_overlined_indices(dctx, yh, yk, yl);
391
392
	cairo_fill(dctx);

Thomas White's avatar
Thomas White committed
393
394
395
	cairo_surface_finish(surface);
	cairo_destroy(dctx);
}
396

397

398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
static int render_key(int colscale)
{
	cairo_surface_t *surface;
	cairo_t *dctx;
	float wh, ht;
	float y;

	wh = 128;
	ht = 1024;

	surface = cairo_pdf_surface_create("key.pdf", wh, ht);

	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);

	for ( y=0; y<ht; y++ ) {

		float r, g, b;

		cairo_rectangle(dctx, 0.0, y, wh, y+1.0);

		render_scale(ht-y, ht, colscale, &r, &g, &b);
		cairo_set_source_rgb(dctx, r, g, b);

		cairo_fill(dctx);

	}

	cairo_surface_finish(surface);
	cairo_destroy(dctx);

	return 0;
}
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463


#else  /* HAVE_CAIRO */


static int render_key(int colscale)
{
	ERROR("This version of CrystFEL was compiled without Cairo");
	ERROR(" support, which is required to draw the colour");
	ERROR(" scale.  Sorry!\n");
	return 1;
}


static void render_za(UnitCell *cell, ReflItemList *items,
                      double *ref, unsigned int *counts,
                      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,
                      const char *outfile)
{
	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
464
465
466
467
468
469


int main(int argc, char *argv[])
{
	int c;
	UnitCell *cell;
Thomas White's avatar
Thomas White committed
470
	RefList *list;
Thomas White's avatar
Thomas White committed
471
472
473
	char *infile;
	int config_povray = 0;
	int config_zoneaxis = 0;
Thomas White's avatar
Thomas White committed
474
	int config_sqrt = 0;
475
	int config_colkey = 0;
Thomas White's avatar
Thomas White committed
476
	unsigned int nproc = 1;
477
	char *pdb = NULL;
478
	int r = 0;
479
	double boost = 1.0;
480
	char *sym = NULL;
481
482
	char *weighting = NULL;
	int wght;
483
484
	int colscale;
	char *cscale = NULL;
485
486
487
488
	signed int dh=1, dk=0, dl=0;
	signed int rh=0, rk=1, rl=0;
	char *down = NULL;
	char *right = NULL;
489
	char *outfile = NULL;
Thomas White's avatar
Thomas White committed
490
491
492
493
494
495

	/* Long options */
	const struct option longopts[] = {
		{"help",               0, NULL,               'h'},
		{"povray",             0, &config_povray,      1},
		{"zone-axis",          0, &config_zoneaxis,    1},
496
		{"output",             1, NULL,               'o'},
497
		{"pdb",                1, NULL,               'p'},
498
		{"boost",              1, NULL,               'b'},
499
		{"symmetry",           1, NULL,               'y'},
500
		{"weighting",          1, NULL,               'w'},
501
		{"colscale",           1, NULL,               'c'},
502
503
		{"down",               1, NULL,               'd'},
		{"right",              1, NULL,               'r'},
504
		{"counts",             0, &config_sqrt,        1},
505
		{"colour-key",         0, &config_colkey,      1},
Thomas White's avatar
Thomas White committed
506
507
508
509
		{0, 0, NULL, 0}
	};

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

		switch (c) {
Thomas White's avatar
Thomas White committed
514
		case 'h' :
Thomas White's avatar
Thomas White committed
515
516
517
			show_help(argv[0]);
			return 0;

Thomas White's avatar
Thomas White committed
518
		case 'j' :
Thomas White's avatar
Thomas White committed
519
520
521
			nproc = atoi(optarg);
			break;

522
523
524
525
		case 'p' :
			pdb = strdup(optarg);
			break;

526
527
528
529
		case 'b' :
			boost = atof(optarg);
			break;

530
531
532
533
		case 'y' :
			sym = strdup(optarg);
			break;

534
535
536
537
		case 'w' :
			weighting = strdup(optarg);
			break;

538
539
540
541
		case 'c' :
			cscale = strdup(optarg);
			break;

542
543
544
545
546
547
548
549
		case 'd' :
			down = strdup(optarg);
			break;

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

550
551
552
553
		case 'o' :
			outfile = strdup(optarg);
			break;

Thomas White's avatar
Thomas White committed
554
		case 0 :
Thomas White's avatar
Thomas White committed
555
556
			break;

Thomas White's avatar
Thomas White committed
557
		default :
Thomas White's avatar
Thomas White committed
558
559
560
561
562
			return 1;
		}

	}

563
564
565
566
	if ( pdb == NULL ) {
		pdb = strdup("molecule.pdb");
	}

567
568
569
570
	if ( sym == NULL ) {
		sym = strdup("1");
	}

571
572
573
574
575
576
577
578
579
580
	if ( weighting == NULL ) {
		weighting = strdup("I");
	}

	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;
581
582
	} else if ( strcmp(weighting, "counts") == 0 ) {
		wght = WGHT_COUNTS;
583
584
	} else if ( strcmp(weighting, "rawcts") == 0 ) {
		wght = WGHT_RAWCOUNTS;
585
586
587
588
	} else if ( strcmp(weighting, "rawcount") == 0 ) {
		wght = WGHT_RAWCOUNTS;
	} else if ( strcmp(weighting, "rawcounts") == 0 ) {
		wght = WGHT_RAWCOUNTS;
589
590
591
592
	} else {
		ERROR("Unrecognised weighting '%s'\n", weighting);
		return 1;
	}
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
	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;
	} else {
		ERROR("Unrecognised colour scale '%s'\n", cscale);
		return 1;
	}
	free(cscale);
612

613
614
615
616
	if ( config_colkey ) {
		return render_key(colscale);
	}

617
618
619
	if ( config_zoneaxis ) {
		if ( (( down == NULL ) && ( right != NULL ))
		  || (( down != NULL ) && ( right == NULL )) ) {
Thomas White's avatar
Thomas White committed
620
621
			ERROR("Either specify both 'down' and 'right',"
			      " or neither.\n");
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
			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
642
643
	infile = argv[optind];

644
	cell = load_cell_from_pdb(pdb);
645
646
647
648
	if ( cell == NULL ) {
		ERROR("Couldn't load unit cell from %s\n", pdb);
		return 1;
	}
Thomas White's avatar
Thomas White committed
649
650
651
	list = read_reflections(infile);
	if ( list == NULL ) {
		ERROR("Couldn't read file '%s'\n", infile);
Thomas White's avatar
Thomas White committed
652
653
		return 1;
	}
Thomas White's avatar
Thomas White committed
654
	if ( check_list_symmetry(list, sym) ) {
Thomas White's avatar
Thomas White committed
655
656
657
658
		ERROR("The input reflection list does not appear to"
		      " have symmetry %s\n", sym);
		return 1;
	}
Thomas White's avatar
Thomas White committed
659
660

	if ( config_povray ) {
Thomas White's avatar
Thomas White committed
661
		r = povray_render_animation(cell, list,
662
		                            nproc, sym, wght, boost);
Thomas White's avatar
Thomas White committed
663
	} else if ( config_zoneaxis ) {
Thomas White's avatar
Thomas White committed
664
		render_za(cell, list, boost, sym, wght, colscale,
665
		          rh, rk, rl, dh, dk, dl, outfile);
Thomas White's avatar
Thomas White committed
666
667
668
669
	} else {
		ERROR("Try again with either --povray or --zone-axis.\n");
	}

670
	free(pdb);
671
	free(sym);
Thomas White's avatar
Thomas White committed
672
	reflist_free(list);
673
	if ( outfile != NULL ) free(outfile);
674

675
	return r;
676
}