render_hkl.c 19.2 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


85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
static double max_value(RefList *list, int wght, const SymOpList *sym)
{
	Reflection *refl;
	RefListIterator *iter;
	double max = -INFINITY;
	SymOpMask *m;

	m = new_symopmask(sym);

	for ( refl = first_refl(list, &iter);
	      refl != NULL;
	      refl = next_refl(refl, iter) )
	{
		double val;
		int n;
		signed int h, k, l;

		get_indices(refl, &h, &k, &l);

		special_position(sym, m, h, k, l);
		n = num_equivs(sym, m);

		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);
			val /= (double)n;
			break;
		case WGHT_RAWCOUNTS :
			val = get_redundancy(refl);
			break;
		default :
			ERROR("Invalid weighting.\n");
			abort();
		}

		if ( val > max ) max = val;
	}

	return max;
}


Thomas White's avatar
Thomas White committed
134
135
static void draw_circles(double xh, double xk, double xl,
                         double yh, double yk, double yl,
136
                         signed int zh, signed int zk, signed int zl,
Thomas White's avatar
Thomas White committed
137
                         RefList *list, const SymOpList *sym,
138
139
140
                         cairo_t *dctx, int wght, double boost, int colscale,
                         UnitCell *cell, double radius, double theta,
                         double as, double bs, double cx, double cy,
141
                         double scale, double max_val)
142
{
Thomas White's avatar
Thomas White committed
143
144
	Reflection *refl;
	RefListIterator *iter;
Thomas White's avatar
Thomas White committed
145
	SymOpMask *m;
146
	double nx, ny;
147

Thomas White's avatar
Thomas White committed
148
149
	m = new_symopmask(sym);

150
151
152
	nx = xh*xh + xk*xk + xl*xl;
	ny = yh*yh + yk*yk + yl*yl;

Thomas White's avatar
Thomas White committed
153
154
155
	/* Iterate over all reflections */
	for ( refl = first_refl(list, &iter);
	      refl != NULL;
156
157
	      refl = next_refl(refl, iter) )
	{
Thomas White's avatar
Thomas White committed
158
		double u, v, val;
Thomas White's avatar
Thomas White committed
159
		signed int ha, ka, la;
Thomas White's avatar
Thomas White committed
160
		int i, n;
161
		double r, g, b;
Thomas White's avatar
Thomas White committed
162
163
164

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

Thomas White's avatar
Thomas White committed
165
166
		special_position(sym, m, ha, ka, la);
		n = num_equivs(sym, m);
Thomas White's avatar
Thomas White committed
167
168

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

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

Thomas White's avatar
Thomas White committed
173
			get_equiv(sym, m, i, ha, ka, la, &h, &k, &l);
Thomas White's avatar
Thomas White committed
174
175
176
177

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

178
179
			xi = (h*xh + k*xk + l*xl) / nx;
			yi = (h*yh + k*yk + l*yl) / ny;
Thomas White's avatar
Thomas White committed
180
181
182
183
184
185
186
187
188
189
190

			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
191
				val /= (double)n;
Thomas White's avatar
Thomas White committed
192
193
194
195
196
197
198
199
				break;
			case WGHT_RAWCOUNTS :
				val = get_redundancy(refl);
				break;
			default :
				ERROR("Invalid weighting.\n");
				abort();
			}
200

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

205
206
207
			cairo_arc(dctx, ((double)cx)+u*scale,
				        ((double)cy)+v*scale,
				        radius, 0.0, 2.0*M_PI);
208

209
210
211
212
			render_scale(val, max_val/boost, colscale,
				     &r, &g, &b);
			cairo_set_source_rgb(dctx, r, g, b);
			cairo_fill(dctx);
Thomas White's avatar
Thomas White committed
213

214
215
216
		}

	}
Thomas White's avatar
Thomas White committed
217
218

	free_symopmask(m);
219
220
221
}


222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
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
272
static void render_za(UnitCell *cell, RefList *list,
Thomas White's avatar
Thomas White committed
273
274
                      double boost, const SymOpList *sym, int wght,
                      int colscale,
275
                      signed int xh, signed int xk, signed int xl,
276
                      signed int yh, signed int yk, signed int yl,
277
                      const char *outfile, double scale_top)
278
{
Thomas White's avatar
Thomas White committed
279
280
	cairo_surface_t *surface;
	cairo_t *dctx;
Thomas White's avatar
Thomas White committed
281
282
	double max_val;
	double scale1, scale2, scale;
Thomas White's avatar
Thomas White committed
283
	double sep_u, sep_v, max_r;
284
	double u, v;
Thomas White's avatar
Thomas White committed
285
	double as, bs, theta;
286
287
288
	double asx, asy, asz;
	double bsx, bsy, bsz;
	double csx, csy, csz;
Thomas White's avatar
Thomas White committed
289
	float wh, ht;
290
291
292
293
294
295
296
	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;
297
	int png;
Thomas White's avatar
Thomas White committed
298
	double rmin, rmax;
299

300
	/* Vector product to determine the zone axis. */
301
302
303
304
	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);
305

306
	/* Size of output and centre definition */
Thomas White's avatar
Thomas White committed
307
308
	wh = 1024;
	ht = 1024;
309

Thomas White's avatar
Thomas White committed
310
	/* Work out reciprocal lattice spacings and angles for this cut */
311
	if ( cell_get_reciprocal(cell, &asx, &asy, &asz,
Thomas White's avatar
Thomas White committed
312
	                          &bsx, &bsy, &bsz,
313
314
315
316
	                          &csx, &csy, &csz) ) {
		ERROR("Couldn't get reciprocal parameters\n");
		return;
	}
317
318
319
320
321
322
323
	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
324
325
	as = modulus(xx, xy, xz);
	bs = modulus(yx, yy, yz);
326

Thomas White's avatar
Thomas White committed
327
328
329
330
	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);
331

332
	max_val = max_value(list, wght, sym);
333
	if ( max_val <= 0.0 ) {
334
		STATUS("Couldn't find max value.\n");
Thomas White's avatar
Thomas White committed
335
		return;
336
337
	}

338
339
340
341
342
	/* Use manual scale top if specified */
	if ( scale_top > 0.0 ) {
		max_val = scale_top;
	}

Thomas White's avatar
Thomas White committed
343
344
345
	scale1 = ((double)wh-border) / (2.0*rmax);
	scale2 = ((double)ht-border) / (2.0*rmax);
	scale = (scale1 < scale2) ? scale1 : scale2;
346

Thomas White's avatar
Thomas White committed
347
	/* Work out the spot radius */
348
349
	sep_u = scale*as;
	sep_v = scale*bs;
350
	max_r = (sep_u < sep_v) ? sep_u : sep_v;
351
	max_r /= 2.0;  /* Max radius is half the separation */
352
	max_r -= (max_r/10.0);  /* Add a tiny separation between circles */
353

Thomas White's avatar
Thomas White committed
354
	/* Create surface */
355
356
357
358
359
360
361
362
	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);
	}
363
364

	if ( cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS ) {
Thomas White's avatar
Thomas White committed
365
		ERROR("Couldn't create Cairo surface\n");
366
367
368
369
370
		cairo_surface_destroy(surface);
		return;
	}

	dctx = cairo_create(surface);
Thomas White's avatar
Thomas White committed
371
372
373
374
375
	if ( cairo_status(dctx) != CAIRO_STATUS_SUCCESS ) {
		ERROR("Couldn't create Cairo context\n");
		cairo_surface_destroy(surface);
		return;
	}
376
377
378
379
380
381
382
383
384
385
386
387
388
389

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

390
	draw_circles(xh, xk, xl, yh, yk, yl, zh, zk, zl,
Thomas White's avatar
Thomas White committed
391
	             list, sym, dctx, wght, boost, colscale, cell,
392
	             max_r, theta, as, bs, cx, cy, scale,
393
	             max_val);
394

Thomas White's avatar
Thomas White committed
395
	/* Centre marker */
396
397
	cairo_arc(dctx, (double)cx,
			(double)cy, max_r, 0, 2*M_PI);
Thomas White's avatar
Thomas White committed
398
399
400
	cairo_set_source_rgb(dctx, 1.0, 0.0, 0.0);
	cairo_fill(dctx);

401
	/* Draw indexing lines */
402
	cairo_set_line_cap(dctx, CAIRO_LINE_CAP_ROUND);
403
	cairo_set_line_width(dctx, 4.0);
404
	cairo_move_to(dctx, (double)cx, (double)cy);
Thomas White's avatar
Thomas White committed
405
406
	u = rmax*sin(theta);
	v = rmax*cos(theta);
407
	cairo_line_to(dctx, cx+u*scale, cy+v*scale);
408
409
410
	cairo_set_source_rgb(dctx, 0.0, 1.0, 0.0);
	cairo_stroke(dctx);

411
	cairo_set_font_size(dctx, 40.0);
412
	snprintf(tmp, 255, "%i%i%i", abs(xh), abs(xk), abs(xl));
413
414
415
	cairo_text_extents(dctx, tmp, &size);

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

419
	snprintf(tmp, 255, "%i%i%i", abs(yh), abs(yk), abs(yl));
420
421
422
	cairo_text_extents(dctx, tmp, &size);

	cairo_move_to(dctx, (double)cx, (double)cy);
Thomas White's avatar
Thomas White committed
423
	cairo_line_to(dctx, cx, cy+rmax*scale);
424
425
426
	cairo_set_source_rgb(dctx, 0.0, 1.0, 0.0);
	cairo_stroke(dctx);

Thomas White's avatar
Thomas White committed
427
428
	cairo_move_to(dctx, cx - size.width/2.0,
	                    cy+rmax*scale + size.height + 20.0);
429
	render_overlined_indices(dctx, yh, yk, yl);
430
431
	cairo_fill(dctx);

432
433
434
435
436
437
438
	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
439
440
441
	cairo_surface_finish(surface);
	cairo_destroy(dctx);
}
442

443

444
static int render_key(int colscale, double scale_top)
445
446
447
{
	cairo_surface_t *surface;
	cairo_t *dctx;
448
449
	double top, wh, ht, y;
	double slice;
450

451
452
453
454
455
456
457
458
459
	wh = 128.0;
	ht = 1024.0;
	slice = 1.0;

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

461
	surface = cairo_pdf_surface_create(KEY_FILENAME, wh, ht);
462
463
464
465
466
467
468
469
470

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

471
	for ( y=0.0; y<ht; y+=slice ) {
472

473
474
475
		double r, g, b;
		double val;
		double v = y;
476

477
478
479
480
481
482
483
484
485
486
487
		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;
		}
488

489
		render_scale(val, top, colscale, &r, &g, &b);
490
491
		cairo_set_source_rgb(dctx, r, g, b);

492
		cairo_stroke_preserve(dctx);
493
494
495
496
		cairo_fill(dctx);

	}

497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
	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);

	}


526
527
528
	cairo_surface_finish(surface);
	cairo_destroy(dctx);

529
530
	STATUS("Colour key written to "KEY_FILENAME"\n");

531
532
	return 0;
}
533
534
535
536
537


#else  /* HAVE_CAIRO */


538
static int render_key(int colscale, double scale_top)
539
540
541
542
543
544
545
546
{
	ERROR("This version of CrystFEL was compiled without Cairo");
	ERROR(" support, which is required to draw the colour");
	ERROR(" scale.  Sorry!\n");
	return 1;
}


547
static void render_za(UnitCell *cell, RefList *list,
548
549
550
                      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,
551
                      const char *outfile, double scale_top)
552
553
554
555
556
557
558
559
{
	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
560
561
562
563
564
565


int main(int argc, char *argv[])
{
	int c;
	UnitCell *cell;
Thomas White's avatar
Thomas White committed
566
	RefList *list;
Thomas White's avatar
Thomas White committed
567
568
569
	char *infile;
	int config_povray = 0;
	int config_zoneaxis = 0;
Thomas White's avatar
Thomas White committed
570
	int config_sqrt = 0;
571
	int config_colkey = 0;
Thomas White's avatar
Thomas White committed
572
	unsigned int nproc = 1;
573
	char *pdb = NULL;
574
	int r = 0;
575
	double boost = 1.0;
Thomas White's avatar
Thomas White committed
576
577
	char *sym_str = NULL;
	SymOpList *sym;
578
579
	char *weighting = NULL;
	int wght;
580
581
	int colscale;
	char *cscale = NULL;
582
583
584
585
	signed int dh=1, dk=0, dl=0;
	signed int rh=0, rk=1, rl=0;
	char *down = NULL;
	char *right = NULL;
586
	char *outfile = NULL;
587
588
	double scale_top = -1.0;
	char *endptr;
Thomas White's avatar
Thomas White committed
589
590
591
592
593
594

	/* Long options */
	const struct option longopts[] = {
		{"help",               0, NULL,               'h'},
		{"povray",             0, &config_povray,      1},
		{"zone-axis",          0, &config_zoneaxis,    1},
595
		{"output",             1, NULL,               'o'},
596
		{"pdb",                1, NULL,               'p'},
597
		{"boost",              1, NULL,               'b'},
598
		{"symmetry",           1, NULL,               'y'},
599
		{"weighting",          1, NULL,               'w'},
600
		{"colscale",           1, NULL,               'c'},
601
602
		{"down",               1, NULL,               'd'},
		{"right",              1, NULL,               'r'},
603
		{"counts",             0, &config_sqrt,        1},
604
		{"colour-key",         0, &config_colkey,      1},
605
		{"scale-top",          1, NULL,                2},
Thomas White's avatar
Thomas White committed
606
607
608
609
		{0, 0, NULL, 0}
	};

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

		switch (c) {
Thomas White's avatar
Thomas White committed
614
		case 'h' :
Thomas White's avatar
Thomas White committed
615
616
617
			show_help(argv[0]);
			return 0;

Thomas White's avatar
Thomas White committed
618
		case 'j' :
Thomas White's avatar
Thomas White committed
619
620
621
			nproc = atoi(optarg);
			break;

622
623
624
625
		case 'p' :
			pdb = strdup(optarg);
			break;

626
627
628
629
		case 'b' :
			boost = atof(optarg);
			break;

630
		case 'y' :
Thomas White's avatar
Thomas White committed
631
			sym_str = strdup(optarg);
632
633
			break;

634
635
636
637
		case 'w' :
			weighting = strdup(optarg);
			break;

638
639
640
641
		case 'c' :
			cscale = strdup(optarg);
			break;

642
643
644
645
646
647
648
649
		case 'd' :
			down = strdup(optarg);
			break;

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

650
651
652
653
		case 'o' :
			outfile = strdup(optarg);
			break;

654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
		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
669
		case 0 :
Thomas White's avatar
Thomas White committed
670
671
			break;

Thomas White's avatar
Thomas White committed
672
		default :
Thomas White's avatar
Thomas White committed
673
674
675
676
677
			return 1;
		}

	}

678
679
680
	if ( (pdb == NULL) && !config_colkey ) {
		ERROR("You must specify the PDB containing the unit cell.\n");
		return 1;
681
682
	}

Thomas White's avatar
Thomas White committed
683
684
	if ( sym_str == NULL ) {
		sym_str = strdup("1");
685
	}
Thomas White's avatar
Thomas White committed
686
687
	sym = get_pointgroup(sym_str);
	free(sym_str);
688

689
690
691
692
	if ( weighting == NULL ) {
		weighting = strdup("I");
	}

693
	if ( outfile == NULL ) outfile = strdup("za.pdf");
Thomas White's avatar
Thomas White committed
694
695


696
697
698
699
700
701
	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;
702
703
	} else if ( strcmp(weighting, "counts") == 0 ) {
		wght = WGHT_COUNTS;
704
705
	} else if ( strcmp(weighting, "rawcts") == 0 ) {
		wght = WGHT_RAWCOUNTS;
706
707
708
709
	} else if ( strcmp(weighting, "rawcount") == 0 ) {
		wght = WGHT_RAWCOUNTS;
	} else if ( strcmp(weighting, "rawcounts") == 0 ) {
		wght = WGHT_RAWCOUNTS;
710
711
712
713
	} else {
		ERROR("Unrecognised weighting '%s'\n", weighting);
		return 1;
	}
714
715
716
717
718
719
720
721
722
723
724
725
726
727
	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;
728
729
	} else if ( strcmp(cscale, "ratio") == 0 ) {
		colscale = SCALE_RATIO;
730
731
732
733
734
	} else {
		ERROR("Unrecognised colour scale '%s'\n", cscale);
		return 1;
	}
	free(cscale);
735

736
	if ( config_colkey ) {
737
		return render_key(colscale, scale_top);
738
739
	}

740
741
742
	if ( config_zoneaxis ) {
		if ( (( down == NULL ) && ( right != NULL ))
		  || (( down != NULL ) && ( right == NULL )) ) {
Thomas White's avatar
Thomas White committed
743
744
			ERROR("Either specify both 'down' and 'right',"
			      " or neither.\n");
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
			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
765
766
	infile = argv[optind];

767
	cell = load_cell_from_pdb(pdb);
768
769
770
771
	if ( cell == NULL ) {
		ERROR("Couldn't load unit cell from %s\n", pdb);
		return 1;
	}
Thomas White's avatar
Thomas White committed
772
773
774
	list = read_reflections(infile);
	if ( list == NULL ) {
		ERROR("Couldn't read file '%s'\n", infile);
Thomas White's avatar
Thomas White committed
775
776
		return 1;
	}
Thomas White's avatar
Thomas White committed
777
	if ( check_list_symmetry(list, sym) ) {
Thomas White's avatar
Thomas White committed
778
		ERROR("The input reflection list does not appear to"
Thomas White's avatar
Thomas White committed
779
		      " have symmetry %s\n", symmetry_name(sym));
Thomas White's avatar
Thomas White committed
780
781
		return 1;
	}
Thomas White's avatar
Thomas White committed
782
783

	if ( config_povray ) {
Thomas White's avatar
Thomas White committed
784
		r = povray_render_animation(cell, list,
785
		                            nproc, sym, wght, boost, scale_top);
Thomas White's avatar
Thomas White committed
786
	} else if ( config_zoneaxis ) {
Thomas White's avatar
Thomas White committed
787
		render_za(cell, list, boost, sym, wght, colscale,
788
		          rh, rk, rl, dh, dk, dl, outfile, scale_top);
Thomas White's avatar
Thomas White committed
789
790
791
792
	} else {
		ERROR("Try again with either --povray or --zone-axis.\n");
	}

793
	free(pdb);
Thomas White's avatar
Thomas White committed
794
	free_symoplist(sym);
Thomas White's avatar
Thomas White committed
795
	reflist_free(list);
796
	if ( outfile != NULL ) free(outfile);
797

798
	return r;
799
}