render_hkl.c 19.6 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
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
88
                         RefList *list, const SymOpList *sym,
89
90
91
92
93
94
95
96
                         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)
{
Thomas White's avatar
Thomas White committed
97
98
	Reflection *refl;
	RefListIterator *iter;
Thomas White's avatar
Thomas White committed
99
	SymOpMask *m;
100
101
102
103
104
105

	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
106
107
	m = new_symopmask(sym);

Thomas White's avatar
Thomas White committed
108
109
110
111
	/* Iterate over all reflections */
	for ( refl = first_refl(list, &iter);
	      refl != NULL;
	      refl = next_refl(refl, iter) ) {
112
113

		double u, v, val, res;
Thomas White's avatar
Thomas White committed
114
115
		signed int ha, ka, la;
		int xi, yi;
Thomas White's avatar
Thomas White committed
116
		int i, n;
Thomas White's avatar
Thomas White committed
117
118
119

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

Thomas White's avatar
Thomas White committed
120
121
		special_position(sym, m, ha, ka, la);
		n = num_equivs(sym, m);
Thomas White's avatar
Thomas White committed
122
123

		for ( i=0; i<n; i++ ) {
Thomas White's avatar
Thomas White committed
124
125
126

			signed int h, k, l;

Thomas White's avatar
Thomas White committed
127
			get_equiv(sym, m, i, ha, ka, la, &h, &k, &l);
Thomas White's avatar
Thomas White committed
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144

			/* 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
145
				val /= (double)n;
Thomas White's avatar
Thomas White committed
146
147
148
149
150
151
152
153
				break;
			case WGHT_RAWCOUNTS :
				val = get_redundancy(refl);
				break;
			default :
				ERROR("Invalid weighting.\n");
				abort();
			}
154

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

Thomas White's avatar
Thomas White committed
159
			if ( dctx != NULL ) {
160

Thomas White's avatar
Thomas White committed
161
				double r, g, b;
162

Thomas White's avatar
Thomas White committed
163
164
165
				cairo_arc(dctx, ((double)cx)+u*scale,
					        ((double)cy)+v*scale,
					        radius, 0, 2*M_PI);
166

Thomas White's avatar
Thomas White committed
167
168
169
170
				render_scale(val, *max_val/boost, colscale,
					     &r, &g, &b);
				cairo_set_source_rgb(dctx, r, g, b);
				cairo_fill(dctx);
171

Thomas White's avatar
Thomas White committed
172
			} else {
173

Thomas White's avatar
Thomas White committed
174
175
176
				/* 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);
177

Thomas White's avatar
Thomas White committed
178
179
180
181
182
183
				/* Find max value for colour scale */
				if ( !isnan(val) && !isinf(val)
				  && (fabs(val) > fabs(*max_val)) )
				{
					*max_val = fabs(val);
				}
184

Thomas White's avatar
Thomas White committed
185
186
187
188
189
				/* Find max indices */
				if ( (yi==0) && (fabs(xi) > *max_ux) )
					*max_ux = fabs(xi);
				if ( (xi==0) && (fabs(yi) > *max_uy) )
					*max_uy = fabs(yi);
190

Thomas White's avatar
Thomas White committed
191
192
193
194
195
				/* Find max resolution */
				res = resolution(cell, h, k, l);
				if ( res > *max_res ) *max_res = res;

			}
Thomas White's avatar
Thomas White committed
196

197
198
199
		}

	}
Thomas White's avatar
Thomas White committed
200
201

	free_symopmask(m);
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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
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
255
static void render_za(UnitCell *cell, RefList *list,
Thomas White's avatar
Thomas White committed
256
257
                      double boost, const SymOpList *sym, int wght,
                      int colscale,
258
                      signed int xh, signed int xk, signed int xl,
259
                      signed int yh, signed int yk, signed int yl,
260
                      const char *outfile, double scale_top)
261
{
Thomas White's avatar
Thomas White committed
262
263
	cairo_surface_t *surface;
	cairo_t *dctx;
264
	double max_u, max_v, max_res, max_val;
265
	double scale_u, scale_v, scale;
Thomas White's avatar
Thomas White committed
266
	double sep_u, sep_v, max_r;
267
	double u, v;
268
	signed int max_ux, max_uy;
Thomas White's avatar
Thomas White committed
269
	double as, bs, theta;
270
271
272
	double asx, asy, asz;
	double bsx, bsy, bsz;
	double csx, csy, csz;
Thomas White's avatar
Thomas White committed
273
	float wh, ht;
274
275
276
277
278
279
280
	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;
281
	int png;
282

283
	/* Vector product to determine the zone axis. */
284
285
286
287
	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);
288

289
	/* Size of output and centre definition */
Thomas White's avatar
Thomas White committed
290
291
	wh = 1024;
	ht = 1024;
292

Thomas White's avatar
Thomas White committed
293
	/* Work out reciprocal lattice spacings and angles for this cut */
294
	if ( cell_get_reciprocal(cell, &asx, &asy, &asz,
Thomas White's avatar
Thomas White committed
295
	                          &bsx, &bsy, &bsz,
296
297
298
299
	                          &csx, &csy, &csz) ) {
		ERROR("Couldn't get reciprocal parameters\n");
		return;
	}
300
301
302
303
304
305
306
307
308
309
	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;

310
	scale = 1.0;
311
	draw_circles(xh, xk, xl, yh, yk, yl, zh, zk, zl,
Thomas White's avatar
Thomas White committed
312
	             list, sym, NULL, wght, boost, colscale, cell,
Thomas White's avatar
Thomas White committed
313
	             0.0, theta, as, bs, 0.0, 0.0, scale,
314
	             &max_ux, &max_uy, &max_val, &max_u, &max_v, &max_res);
315

Thomas White's avatar
Thomas White committed
316
	max_res /= 1e9;
317
318
	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
319

320
	if ( max_val <= 0.0 ) {
321
		STATUS("Couldn't find max value.\n");
Thomas White's avatar
Thomas White committed
322
		return;
323
324
	}

325
326
327
328
329
	/* Use manual scale top if specified */
	if ( scale_top > 0.0 ) {
		max_val = scale_top;
	}

Thomas White's avatar
Thomas White committed
330
	/* Choose whichever scaling factor gives the smallest value */
331
332
	scale_u = ((double)wh-border) / (2.0*max_u);
	scale_v = ((double)ht-border) / (2.0*max_v);
333
	scale = (scale_u < scale_v) ? scale_u : scale_v;
334

335
336
	sep_u = scale*as;
	sep_v = scale*bs;
337
	/* We are interested in the smaller of the two separations */
338
	max_r = (sep_u < sep_v) ? sep_u : sep_v;
339
	max_r /= 2.0;  /* Max radius is half the separation */
340
	max_r -= 1.0;  /* Add a tiny separation between circles */
341
342
343
	if ( max_r < 1.0 ) {
		ERROR("Circle radius is probably too small (%f).\n", max_r);
	}
Thomas White's avatar
Thomas White committed
344

345
	if ( outfile == NULL ) outfile = "za.pdf";
346
347
348
349
350
351
352
353
354

	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);
	}
355
356

	if ( cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS ) {
Thomas White's avatar
Thomas White committed
357
		ERROR("Couldn't create Cairo surface\n");
358
359
360
361
362
		cairo_surface_destroy(surface);
		return;
	}

	dctx = cairo_create(surface);
Thomas White's avatar
Thomas White committed
363
364
365
366
367
	if ( cairo_status(dctx) != CAIRO_STATUS_SUCCESS ) {
		ERROR("Couldn't create Cairo context\n");
		cairo_surface_destroy(surface);
		return;
	}
368
369
370
371
372
373
374
375
376
377
378
379
380
381

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

382
	draw_circles(xh, xk, xl, yh, yk, yl, zh, zk, zl,
Thomas White's avatar
Thomas White committed
383
	             list, sym, dctx, wght, boost, colscale, cell,
384
385
	             max_r, theta, as, bs, cx, cy, scale,
	             NULL, NULL, &max_val, NULL, NULL, NULL);
386

Thomas White's avatar
Thomas White committed
387
	/* Centre marker */
388
389
	cairo_arc(dctx, (double)cx,
			(double)cy, max_r, 0, 2*M_PI);
Thomas White's avatar
Thomas White committed
390
391
392
	cairo_set_source_rgb(dctx, 1.0, 0.0, 0.0);
	cairo_fill(dctx);

393
	/* Draw indexing lines */
394
	cairo_set_line_cap(dctx, CAIRO_LINE_CAP_ROUND);
395
	cairo_set_line_width(dctx, 4.0);
396
	cairo_move_to(dctx, (double)cx, (double)cy);
397
398
	u = (2.0+max_ux)*as*sin(theta);
	v = (2.0+max_ux)*as*cos(theta);
399
	cairo_line_to(dctx, cx+u*scale, cy+v*scale);
400
401
402
	cairo_set_source_rgb(dctx, 0.0, 1.0, 0.0);
	cairo_stroke(dctx);

403
	cairo_set_font_size(dctx, 40.0);
404
	snprintf(tmp, 255, "%i%i%i", abs(xh), abs(xk), abs(xl));
405
406
407
	cairo_text_extents(dctx, tmp, &size);

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

411
	snprintf(tmp, 255, "%i%i%i", abs(yh), abs(yk), abs(yl));
412
413
414
	cairo_text_extents(dctx, tmp, &size);

	cairo_move_to(dctx, (double)cx, (double)cy);
415
	u = 0.0;
416
	v = (2.0+max_uy)*bs;
417
	cairo_line_to(dctx, cx+u*scale, cy+v*scale);
418
419
420
	cairo_set_source_rgb(dctx, 0.0, 1.0, 0.0);
	cairo_stroke(dctx);

421
422
	cairo_move_to(dctx, cx+u*scale - size.width/2.0,
	                    cy+v*scale + size.height + 20.0);
423
	render_overlined_indices(dctx, yh, yk, yl);
424
425
	cairo_fill(dctx);

426
427
428
429
430
431
432
	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
433
434
435
	cairo_surface_finish(surface);
	cairo_destroy(dctx);
}
436

437

438
static int render_key(int colscale, double scale_top)
439
440
441
{
	cairo_surface_t *surface;
	cairo_t *dctx;
442
443
	double top, wh, ht, y;
	double slice;
444

445
446
447
448
449
450
451
452
453
	wh = 128.0;
	ht = 1024.0;
	slice = 1.0;

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

455
	surface = cairo_pdf_surface_create(KEY_FILENAME, wh, ht);
456
457
458
459
460
461
462
463
464

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

465
	for ( y=0.0; y<ht; y+=slice ) {
466

467
468
469
		double r, g, b;
		double val;
		double v = y;
470

471
472
473
474
475
476
477
478
479
480
481
		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;
		}
482

483
		render_scale(val, top, colscale, &r, &g, &b);
484
485
		cairo_set_source_rgb(dctx, r, g, b);

486
		cairo_stroke_preserve(dctx);
487
488
489
490
		cairo_fill(dctx);

	}

491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
	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);

	}


520
521
522
	cairo_surface_finish(surface);
	cairo_destroy(dctx);

523
524
	STATUS("Colour key written to "KEY_FILENAME"\n");

525
526
	return 0;
}
527
528
529
530
531


#else  /* HAVE_CAIRO */


532
static int render_key(int colscale, double scale_top)
533
534
535
536
537
538
539
540
{
	ERROR("This version of CrystFEL was compiled without Cairo");
	ERROR(" support, which is required to draw the colour");
	ERROR(" scale.  Sorry!\n");
	return 1;
}


541
static void render_za(UnitCell *cell, RefList *list,
542
543
544
                      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,
545
                      const char *outfile, double scale_top)
546
547
548
549
550
551
552
553
{
	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
554
555
556
557
558
559


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

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

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

		switch (c) {
Thomas White's avatar
Thomas White committed
608
		case 'h' :
Thomas White's avatar
Thomas White committed
609
610
611
			show_help(argv[0]);
			return 0;

Thomas White's avatar
Thomas White committed
612
		case 'j' :
Thomas White's avatar
Thomas White committed
613
614
615
			nproc = atoi(optarg);
			break;

616
617
618
619
		case 'p' :
			pdb = strdup(optarg);
			break;

620
621
622
623
		case 'b' :
			boost = atof(optarg);
			break;

624
		case 'y' :
Thomas White's avatar
Thomas White committed
625
			sym_str = strdup(optarg);
626
627
			break;

628
629
630
631
		case 'w' :
			weighting = strdup(optarg);
			break;

632
633
634
635
		case 'c' :
			cscale = strdup(optarg);
			break;

636
637
638
639
640
641
642
643
		case 'd' :
			down = strdup(optarg);
			break;

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

644
645
646
647
		case 'o' :
			outfile = strdup(optarg);
			break;

648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
		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
663
		case 0 :
Thomas White's avatar
Thomas White committed
664
665
			break;

Thomas White's avatar
Thomas White committed
666
		default :
Thomas White's avatar
Thomas White committed
667
668
669
670
671
			return 1;
		}

	}

672
673
674
	if ( (pdb == NULL) && !config_colkey ) {
		ERROR("You must specify the PDB containing the unit cell.\n");
		return 1;
675
676
	}

Thomas White's avatar
Thomas White committed
677
678
	if ( sym_str == NULL ) {
		sym_str = strdup("1");
679
	}
Thomas White's avatar
Thomas White committed
680
681
	sym = get_pointgroup(sym_str);
	free(sym_str);
682

683
684
685
686
687
688
689
690
691
692
	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;
693
694
	} else if ( strcmp(weighting, "counts") == 0 ) {
		wght = WGHT_COUNTS;
695
696
	} else if ( strcmp(weighting, "rawcts") == 0 ) {
		wght = WGHT_RAWCOUNTS;
697
698
699
700
	} else if ( strcmp(weighting, "rawcount") == 0 ) {
		wght = WGHT_RAWCOUNTS;
	} else if ( strcmp(weighting, "rawcounts") == 0 ) {
		wght = WGHT_RAWCOUNTS;
701
702
703
704
	} else {
		ERROR("Unrecognised weighting '%s'\n", weighting);
		return 1;
	}
705
706
707
708
709
710
711
712
713
714
715
716
717
718
	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;
719
720
	} else if ( strcmp(cscale, "ratio") == 0 ) {
		colscale = SCALE_RATIO;
721
722
723
724
725
	} else {
		ERROR("Unrecognised colour scale '%s'\n", cscale);
		return 1;
	}
	free(cscale);
726

727
	if ( config_colkey ) {
728
		return render_key(colscale, scale_top);
729
730
	}

731
732
733
	if ( config_zoneaxis ) {
		if ( (( down == NULL ) && ( right != NULL ))
		  || (( down != NULL ) && ( right == NULL )) ) {
Thomas White's avatar
Thomas White committed
734
735
			ERROR("Either specify both 'down' and 'right',"
			      " or neither.\n");
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
			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
756
757
	infile = argv[optind];

758
	cell = load_cell_from_pdb(pdb);
759
760
761
762
	if ( cell == NULL ) {
		ERROR("Couldn't load unit cell from %s\n", pdb);
		return 1;
	}
Thomas White's avatar
Thomas White committed
763
764
765
	list = read_reflections(infile);
	if ( list == NULL ) {
		ERROR("Couldn't read file '%s'\n", infile);
Thomas White's avatar
Thomas White committed
766
767
		return 1;
	}
Thomas White's avatar
Thomas White committed
768
	if ( check_list_symmetry(list, sym) ) {
Thomas White's avatar
Thomas White committed
769
		ERROR("The input reflection list does not appear to"
Thomas White's avatar
Thomas White committed
770
		      " have symmetry %s\n", symmetry_name(sym));
Thomas White's avatar
Thomas White committed
771
772
		return 1;
	}
Thomas White's avatar
Thomas White committed
773
774

	if ( config_povray ) {
Thomas White's avatar
Thomas White committed
775
		r = povray_render_animation(cell, list,
776
		                            nproc, sym, wght, boost, scale_top);
Thomas White's avatar
Thomas White committed
777
	} else if ( config_zoneaxis ) {
Thomas White's avatar
Thomas White committed
778
		render_za(cell, list, boost, sym, wght, colscale,
779
		          rh, rk, rl, dh, dk, dl, outfile, scale_top);
Thomas White's avatar
Thomas White committed
780
781
782
783
	} else {
		ERROR("Try again with either --povray or --zone-axis.\n");
	}

784
	free(pdb);
Thomas White's avatar
Thomas White committed
785
	free_symoplist(sym);
Thomas White's avatar
Thomas White committed
786
	reflist_free(list);
787
	if ( outfile != NULL ) free(outfile);
788

789
	return r;
790
}