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;
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
		signed int ha, ka, la;
		int xi, yi;
Thomas White's avatar
Thomas White committed
113
114
		int i, n;
		SymOpList *sp;
Thomas White's avatar
Thomas White committed
115
116
117

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

Thomas White's avatar
Thomas White committed
118
119
120
121
		sp = special_position(sym, ha, ka, la);
		n = num_equivs(sp);

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

			signed int h, k, l;

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

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

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

Thomas White's avatar
Thomas White committed
157
			if ( dctx != NULL ) {
158

Thomas White's avatar
Thomas White committed
159
				double r, g, b;
160

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

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

Thomas White's avatar
Thomas White committed
170
			} else {
171

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

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

Thomas White's avatar
Thomas White committed
183
184
185
186
187
				/* 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);
188

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

			}
Thomas White's avatar
Thomas White committed
194

195
196
		}

Thomas White's avatar
Thomas White committed
197
198
		free_symoplist(sp);

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

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

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

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

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

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

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

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

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

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

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

	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);
	}
353
354

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

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

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

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

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

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

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

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

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

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

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

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

435

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

443
444
445
446
447
448
449
450
451
	wh = 128.0;
	ht = 1024.0;
	slice = 1.0;

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

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

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

463
	for ( y=0.0; y<ht; y+=slice ) {
464

465
466
467
		double r, g, b;
		double val;
		double v = y;
468

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

481
		render_scale(val, top, colscale, &r, &g, &b);
482
483
		cairo_set_source_rgb(dctx, r, g, b);

484
		cairo_stroke_preserve(dctx);
485
486
487
488
		cairo_fill(dctx);

	}

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

	}


518
519
520
	cairo_surface_finish(surface);
	cairo_destroy(dctx);

521
522
	STATUS("Colour key written to "KEY_FILENAME"\n");

523
524
	return 0;
}
525
526
527
528
529


#else  /* HAVE_CAIRO */


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


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


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

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

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

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

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

614
615
616
617
		case 'p' :
			pdb = strdup(optarg);
			break;

618
619
620
621
		case 'b' :
			boost = atof(optarg);
			break;

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

626
627
628
629
		case 'w' :
			weighting = strdup(optarg);
			break;

630
631
632
633
		case 'c' :
			cscale = strdup(optarg);
			break;

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

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

642
643
644
645
		case 'o' :
			outfile = strdup(optarg);
			break;

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

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

	}

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

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

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

725
	if ( config_colkey ) {
726
		return render_key(colscale, scale_top);
727
728
	}

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

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

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

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

787
	return r;
788
}