render_hkl.c 19.4 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 char *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;
99
100
101
102
103
104

	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
105
106
107
108
	/* Iterate over all reflections */
	for ( refl = first_refl(list, &iter);
	      refl != NULL;
	      refl = next_refl(refl, iter) ) {
109
110

		double u, v, val, res;
Thomas White's avatar
Thomas White committed
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
		signed int ha, ka, la;
		int xi, yi;
		int i;

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

		for ( i=0; i<num_equivs(ha, ka, la, sym); i++ ) {

			signed int h, k, l;

			get_equiv(ha, ka, la, &h, &k, &l, sym, i);

			/* 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);
				val /= (float)num_equivs(h, k, l, sym);
				break;
			case WGHT_RAWCOUNTS :
				val = get_redundancy(refl);
				break;
			default :
				ERROR("Invalid weighting.\n");
				abort();
			}
148

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

Thomas White's avatar
Thomas White committed
153
			if ( dctx != NULL ) {
154

Thomas White's avatar
Thomas White committed
155
				double r, g, b;
156

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

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

Thomas White's avatar
Thomas White committed
166
			} else {
167

Thomas White's avatar
Thomas White committed
168
169
170
				/* 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);
171

Thomas White's avatar
Thomas White committed
172
173
174
175
176
177
				/* Find max value for colour scale */
				if ( !isnan(val) && !isinf(val)
				  && (fabs(val) > fabs(*max_val)) )
				{
					*max_val = fabs(val);
				}
178

Thomas White's avatar
Thomas White committed
179
180
181
182
183
				/* 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);
184

Thomas White's avatar
Thomas White committed
185
186
187
188
189
				/* Find max resolution */
				res = resolution(cell, h, k, l);
				if ( res > *max_res ) *max_res = res;

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

274
	/* Vector product to determine the zone axis. */
275
276
277
278
	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);
279

280
	/* Size of output and centre definition */
Thomas White's avatar
Thomas White committed
281
282
	wh = 1024;
	ht = 1024;
283

Thomas White's avatar
Thomas White committed
284
	/* Work out reciprocal lattice spacings and angles for this cut */
285
	if ( cell_get_reciprocal(cell, &asx, &asy, &asz,
Thomas White's avatar
Thomas White committed
286
	                          &bsx, &bsy, &bsz,
287
288
289
290
	                          &csx, &csy, &csz) ) {
		ERROR("Couldn't get reciprocal parameters\n");
		return;
	}
291
292
293
294
295
296
297
298
299
300
	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;

301
	scale = 1.0;
302
	draw_circles(xh, xk, xl, yh, yk, yl, zh, zk, zl,
Thomas White's avatar
Thomas White committed
303
	             list, sym, NULL, wght, boost, colscale, cell,
Thomas White's avatar
Thomas White committed
304
	             0.0, theta, as, bs, 0.0, 0.0, scale,
305
	             &max_ux, &max_uy, &max_val, &max_u, &max_v, &max_res);
306

Thomas White's avatar
Thomas White committed
307
	max_res /= 1e9;
308
309
	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
310

311
	if ( max_val <= 0.0 ) {
312
		STATUS("Couldn't find max value.\n");
Thomas White's avatar
Thomas White committed
313
		return;
314
315
	}

316
317
318
319
320
	/* Use manual scale top if specified */
	if ( scale_top > 0.0 ) {
		max_val = scale_top;
	}

Thomas White's avatar
Thomas White committed
321
	/* Choose whichever scaling factor gives the smallest value */
322
323
	scale_u = ((double)wh-border) / (2.0*max_u);
	scale_v = ((double)ht-border) / (2.0*max_v);
324
	scale = (scale_u < scale_v) ? scale_u : scale_v;
325

326
327
	sep_u = scale*as;
	sep_v = scale*bs;
328
	/* We are interested in the smaller of the two separations */
329
	max_r = (sep_u < sep_v) ? sep_u : sep_v;
330
	max_r /= 2.0;  /* Max radius is half the separation */
331
	max_r -= 1.0;  /* Add a tiny separation between circles */
332
333
334
	if ( max_r < 1.0 ) {
		ERROR("Circle radius is probably too small (%f).\n", max_r);
	}
Thomas White's avatar
Thomas White committed
335

336
	if ( outfile == NULL ) outfile = "za.pdf";
337
338
339
340
341
342
343
344
345

	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);
	}
346
347

	if ( cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS ) {
Thomas White's avatar
Thomas White committed
348
		ERROR("Couldn't create Cairo surface\n");
349
350
351
352
353
		cairo_surface_destroy(surface);
		return;
	}

	dctx = cairo_create(surface);
Thomas White's avatar
Thomas White committed
354
355
356
357
358
	if ( cairo_status(dctx) != CAIRO_STATUS_SUCCESS ) {
		ERROR("Couldn't create Cairo context\n");
		cairo_surface_destroy(surface);
		return;
	}
359
360
361
362
363
364
365
366
367
368
369
370
371
372

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

373
	draw_circles(xh, xk, xl, yh, yk, yl, zh, zk, zl,
Thomas White's avatar
Thomas White committed
374
	             list, sym, dctx, wght, boost, colscale, cell,
375
376
	             max_r, theta, as, bs, cx, cy, scale,
	             NULL, NULL, &max_val, NULL, NULL, NULL);
377

Thomas White's avatar
Thomas White committed
378
	/* Centre marker */
379
380
	cairo_arc(dctx, (double)cx,
			(double)cy, max_r, 0, 2*M_PI);
Thomas White's avatar
Thomas White committed
381
382
383
	cairo_set_source_rgb(dctx, 1.0, 0.0, 0.0);
	cairo_fill(dctx);

384
	/* Draw indexing lines */
385
	cairo_set_line_cap(dctx, CAIRO_LINE_CAP_ROUND);
386
	cairo_set_line_width(dctx, 4.0);
387
	cairo_move_to(dctx, (double)cx, (double)cy);
388
389
	u = (2.0+max_ux)*as*sin(theta);
	v = (2.0+max_ux)*as*cos(theta);
390
	cairo_line_to(dctx, cx+u*scale, cy+v*scale);
391
392
393
	cairo_set_source_rgb(dctx, 0.0, 1.0, 0.0);
	cairo_stroke(dctx);

394
	cairo_set_font_size(dctx, 40.0);
395
	snprintf(tmp, 255, "%i%i%i", abs(xh), abs(xk), abs(xl));
396
397
398
	cairo_text_extents(dctx, tmp, &size);

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

402
	snprintf(tmp, 255, "%i%i%i", abs(yh), abs(yk), abs(yl));
403
404
405
	cairo_text_extents(dctx, tmp, &size);

	cairo_move_to(dctx, (double)cx, (double)cy);
406
	u = 0.0;
407
	v = (2.0+max_uy)*bs;
408
	cairo_line_to(dctx, cx+u*scale, cy+v*scale);
409
410
411
	cairo_set_source_rgb(dctx, 0.0, 1.0, 0.0);
	cairo_stroke(dctx);

412
413
	cairo_move_to(dctx, cx+u*scale - size.width/2.0,
	                    cy+v*scale + size.height + 20.0);
414
	render_overlined_indices(dctx, yh, yk, yl);
415
416
	cairo_fill(dctx);

417
418
419
420
421
422
423
	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
424
425
426
	cairo_surface_finish(surface);
	cairo_destroy(dctx);
}
427

428

429
static int render_key(int colscale, double scale_top)
430
431
432
{
	cairo_surface_t *surface;
	cairo_t *dctx;
433
434
	double top, wh, ht, y;
	double slice;
435

436
437
438
439
440
441
442
443
444
	wh = 128.0;
	ht = 1024.0;
	slice = 1.0;

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

446
	surface = cairo_pdf_surface_create(KEY_FILENAME, wh, ht);
447
448
449
450
451
452
453
454
455

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

456
	for ( y=0.0; y<ht; y+=slice ) {
457

458
459
460
		double r, g, b;
		double val;
		double v = y;
461

462
463
464
465
466
467
468
469
470
471
472
		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;
		}
473

474
		render_scale(val, top, colscale, &r, &g, &b);
475
476
		cairo_set_source_rgb(dctx, r, g, b);

477
		cairo_stroke_preserve(dctx);
478
479
480
481
		cairo_fill(dctx);

	}

482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
	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);

	}


511
512
513
	cairo_surface_finish(surface);
	cairo_destroy(dctx);

514
515
	STATUS("Colour key written to "KEY_FILENAME"\n");

516
517
	return 0;
}
518
519
520
521
522


#else  /* HAVE_CAIRO */


523
static int render_key(int colscale, double scale_top)
524
525
526
527
528
529
530
531
{
	ERROR("This version of CrystFEL was compiled without Cairo");
	ERROR(" support, which is required to draw the colour");
	ERROR(" scale.  Sorry!\n");
	return 1;
}


532
static void render_za(UnitCell *cell, RefList *list,
533
534
535
                      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,
536
                      const char *outfile, double scale_top)
537
538
539
540
541
542
543
544
{
	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
545
546
547
548
549
550


int main(int argc, char *argv[])
{
	int c;
	UnitCell *cell;
Thomas White's avatar
Thomas White committed
551
	RefList *list;
Thomas White's avatar
Thomas White committed
552
553
554
	char *infile;
	int config_povray = 0;
	int config_zoneaxis = 0;
Thomas White's avatar
Thomas White committed
555
	int config_sqrt = 0;
556
	int config_colkey = 0;
Thomas White's avatar
Thomas White committed
557
	unsigned int nproc = 1;
558
	char *pdb = NULL;
559
	int r = 0;
560
	double boost = 1.0;
561
	char *sym = NULL;
562
563
	char *weighting = NULL;
	int wght;
564
565
	int colscale;
	char *cscale = NULL;
566
567
568
569
	signed int dh=1, dk=0, dl=0;
	signed int rh=0, rk=1, rl=0;
	char *down = NULL;
	char *right = NULL;
570
	char *outfile = NULL;
571
572
	double scale_top = -1.0;
	char *endptr;
Thomas White's avatar
Thomas White committed
573
574
575
576
577
578

	/* Long options */
	const struct option longopts[] = {
		{"help",               0, NULL,               'h'},
		{"povray",             0, &config_povray,      1},
		{"zone-axis",          0, &config_zoneaxis,    1},
579
		{"output",             1, NULL,               'o'},
580
		{"pdb",                1, NULL,               'p'},
581
		{"boost",              1, NULL,               'b'},
582
		{"symmetry",           1, NULL,               'y'},
583
		{"weighting",          1, NULL,               'w'},
584
		{"colscale",           1, NULL,               'c'},
585
586
		{"down",               1, NULL,               'd'},
		{"right",              1, NULL,               'r'},
587
		{"counts",             0, &config_sqrt,        1},
588
		{"colour-key",         0, &config_colkey,      1},
589
		{"scale-top",          1, NULL,                2},
Thomas White's avatar
Thomas White committed
590
591
592
593
		{0, 0, NULL, 0}
	};

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

		switch (c) {
Thomas White's avatar
Thomas White committed
598
		case 'h' :
Thomas White's avatar
Thomas White committed
599
600
601
			show_help(argv[0]);
			return 0;

Thomas White's avatar
Thomas White committed
602
		case 'j' :
Thomas White's avatar
Thomas White committed
603
604
605
			nproc = atoi(optarg);
			break;

606
607
608
609
		case 'p' :
			pdb = strdup(optarg);
			break;

610
611
612
613
		case 'b' :
			boost = atof(optarg);
			break;

614
615
616
617
		case 'y' :
			sym = strdup(optarg);
			break;

618
619
620
621
		case 'w' :
			weighting = strdup(optarg);
			break;

622
623
624
625
		case 'c' :
			cscale = strdup(optarg);
			break;

626
627
628
629
630
631
632
633
		case 'd' :
			down = strdup(optarg);
			break;

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

634
635
636
637
		case 'o' :
			outfile = strdup(optarg);
			break;

638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
		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
653
		case 0 :
Thomas White's avatar
Thomas White committed
654
655
			break;

Thomas White's avatar
Thomas White committed
656
		default :
Thomas White's avatar
Thomas White committed
657
658
659
660
661
			return 1;
		}

	}

662
663
664
	if ( (pdb == NULL) && !config_colkey ) {
		ERROR("You must specify the PDB containing the unit cell.\n");
		return 1;
665
666
	}

667
668
669
670
	if ( sym == NULL ) {
		sym = strdup("1");
	}

671
672
673
674
675
676
677
678
679
680
	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;
681
682
	} else if ( strcmp(weighting, "counts") == 0 ) {
		wght = WGHT_COUNTS;
683
684
	} else if ( strcmp(weighting, "rawcts") == 0 ) {
		wght = WGHT_RAWCOUNTS;
685
686
687
688
	} else if ( strcmp(weighting, "rawcount") == 0 ) {
		wght = WGHT_RAWCOUNTS;
	} else if ( strcmp(weighting, "rawcounts") == 0 ) {
		wght = WGHT_RAWCOUNTS;
689
690
691
692
	} else {
		ERROR("Unrecognised weighting '%s'\n", weighting);
		return 1;
	}
693
694
695
696
697
698
699
700
701
702
703
704
705
706
	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;
707
708
	} else if ( strcmp(cscale, "ratio") == 0 ) {
		colscale = SCALE_RATIO;
709
710
711
712
713
	} else {
		ERROR("Unrecognised colour scale '%s'\n", cscale);
		return 1;
	}
	free(cscale);
714

715
	if ( config_colkey ) {
716
		return render_key(colscale, scale_top);
717
718
	}

719
720
721
	if ( config_zoneaxis ) {
		if ( (( down == NULL ) && ( right != NULL ))
		  || (( down != NULL ) && ( right == NULL )) ) {
Thomas White's avatar
Thomas White committed
722
723
			ERROR("Either specify both 'down' and 'right',"
			      " or neither.\n");
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
			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
744
745
	infile = argv[optind];

746
	cell = load_cell_from_pdb(pdb);
747
748
749
750
	if ( cell == NULL ) {
		ERROR("Couldn't load unit cell from %s\n", pdb);
		return 1;
	}
Thomas White's avatar
Thomas White committed
751
752
753
	list = read_reflections(infile);
	if ( list == NULL ) {
		ERROR("Couldn't read file '%s'\n", infile);
Thomas White's avatar
Thomas White committed
754
755
		return 1;
	}
Thomas White's avatar
Thomas White committed
756
	if ( check_list_symmetry(list, sym) ) {
Thomas White's avatar
Thomas White committed
757
758
759
760
		ERROR("The input reflection list does not appear to"
		      " have symmetry %s\n", sym);
		return 1;
	}
Thomas White's avatar
Thomas White committed
761
762

	if ( config_povray ) {
Thomas White's avatar
Thomas White committed
763
		r = povray_render_animation(cell, list,
764
		                            nproc, sym, wght, boost, scale_top);
Thomas White's avatar
Thomas White committed
765
	} else if ( config_zoneaxis ) {
Thomas White's avatar
Thomas White committed
766
		render_za(cell, list, boost, sym, wght, colscale,
767
		          rh, rk, rl, dh, dk, dl, outfile, scale_top);
Thomas White's avatar
Thomas White committed
768
769
770
771
	} else {
		ERROR("Try again with either --povray or --zone-axis.\n");
	}

772
	free(pdb);
773
	free(sym);
Thomas White's avatar
Thomas White committed
774
	reflist_free(list);
775
	if ( outfile != NULL ) free(outfile);
776

777
	return r;
778
}