render_hkl.c 19.3 KB
Newer Older
1
2
3
4
5
/*
 * render_hkl.c
 *
 * Draw pretty renderings of reflection lists
 *
Thomas White's avatar
Thomas White committed
6
 * Copyright © 2012 Thomas White <taw@physics.org>
7
 *
Thomas White's avatar
Thomas White committed
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 * This file is part of CrystFEL.
 *
 * CrystFEL is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * CrystFEL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with CrystFEL.  If not, see <http://www.gnu.org/licenses/>.
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 *
 */


#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>
36
#ifdef HAVE_CAIRO
Thomas White's avatar
Thomas White committed
37
38
#include <cairo.h>
#include <cairo-pdf.h>
39
#endif
40
41

#include "utils.h"
42
#include "symmetry.h"
43
#include "render.h"
44
#include "render_hkl.h"
Thomas White's avatar
Thomas White committed
45
46
#include "reflist.h"
#include "reflist-utils.h"
47
48


49
50
51
#define KEY_FILENAME "key.pdf"


52
53
54
55
static void show_help(const char *s)
{
	printf("Syntax: %s [options] <file.hkl>\n\n", s);
	printf(
56
"Render intensity lists in 2D slices.\n"
57
"\n"
58
"  -d, --down=<h>,<k>,<l>  Indices for the axis in the downward direction.\n"
59
"                           Default: 1,0,0.\n"
60
"  -r, --right=<h>,<k>,<l> Indices for the axis in the 'right' (roughly)\n"
61
"                           direction.  Default: 0,1,0.\n"
62
"  -o, --output=<filename> Output filename (not for POV-ray).  Default: za.pdf\n"
63
64
65
"      --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"
66
67
68
69
"\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
70
"                           colour  : Colour scale:\n"
71
72
"                                     black-blue-pink-red-orange-yellow-white\n"
"\n"
73
74
"  -w  --weighting=<wght>  Colour/shade the reciprocal lattice points\n"
"                           according to:\n"
75
76
"                            I      : the intensity of the reflection.\n"
"                            sqrtI  : the square root of the intensity.\n"
Thomas White's avatar
Thomas White committed
77
"                            count  : the number of measurements for the reflection.\n"
78
"                                     (after correcting for 'epsilon')\n"
Thomas White's avatar
Thomas White committed
79
"                            rawcts : the raw number of measurements for the\n"
80
"                                     reflection (no 'epsilon' correction).\n"
81
82
"\n"
"      --colour-key        Draw (only) the key for the current colour scale.\n"
Thomas White's avatar
Thomas White committed
83
"\n"
84
"  -h, --help              Display this help message.\n"
85
);
86
87
88
}


89
#ifdef HAVE_CAIRO
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
134
135
136
137
138
139
140
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
141
142
static void draw_circles(double xh, double xk, double xl,
                         double yh, double yk, double yl,
143
                         signed int zh, signed int zk, signed int zl,
Thomas White's avatar
Thomas White committed
144
                         RefList *list, const SymOpList *sym,
145
146
147
                         cairo_t *dctx, int wght, double boost, int colscale,
                         UnitCell *cell, double radius, double theta,
                         double as, double bs, double cx, double cy,
148
                         double scale, double max_val)
149
{
Thomas White's avatar
Thomas White committed
150
151
	Reflection *refl;
	RefListIterator *iter;
Thomas White's avatar
Thomas White committed
152
	SymOpMask *m;
153
	double nx, ny;
154

Thomas White's avatar
Thomas White committed
155
156
	m = new_symopmask(sym);

157
158
159
	nx = xh*xh + xk*xk + xl*xl;
	ny = yh*yh + yk*yk + yl*yl;

Thomas White's avatar
Thomas White committed
160
161
162
	/* Iterate over all reflections */
	for ( refl = first_refl(list, &iter);
	      refl != NULL;
163
164
	      refl = next_refl(refl, iter) )
	{
Thomas White's avatar
Thomas White committed
165
		double u, v, val;
Thomas White's avatar
Thomas White committed
166
		signed int ha, ka, la;
Thomas White's avatar
Thomas White committed
167
		int i, n;
168
		double r, g, b;
Thomas White's avatar
Thomas White committed
169
170
171

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

Thomas White's avatar
Thomas White committed
172
173
		special_position(sym, m, ha, ka, la);
		n = num_equivs(sym, m);
Thomas White's avatar
Thomas White committed
174
175

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

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

Thomas White's avatar
Thomas White committed
180
			get_equiv(sym, m, i, ha, ka, la, &h, &k, &l);
Thomas White's avatar
Thomas White committed
181
182
183
184

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

185
186
			xi = (h*xh + k*xk + l*xl) / nx;
			yi = (h*yh + k*yk + l*yl) / ny;
Thomas White's avatar
Thomas White committed
187
188
189
190
191
192
193
194
195
196
197

			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
198
				val /= (double)n;
Thomas White's avatar
Thomas White committed
199
200
201
202
203
204
205
206
				break;
			case WGHT_RAWCOUNTS :
				val = get_redundancy(refl);
				break;
			default :
				ERROR("Invalid weighting.\n");
				abort();
			}
207

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

212
213
214
			cairo_arc(dctx, ((double)cx)+u*scale,
				        ((double)cy)+v*scale,
				        radius, 0.0, 2.0*M_PI);
215

216
217
218
219
			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
220

221
222
223
		}

	}
Thomas White's avatar
Thomas White committed
224
225

	free_symopmask(m);
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
272
273
274
275
276
277
278
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
279
static void render_za(UnitCell *cell, RefList *list,
Thomas White's avatar
Thomas White committed
280
281
                      double boost, const SymOpList *sym, int wght,
                      int colscale,
282
                      signed int xh, signed int xk, signed int xl,
283
                      signed int yh, signed int yk, signed int yl,
284
                      const char *outfile, double scale_top)
285
{
Thomas White's avatar
Thomas White committed
286
287
	cairo_surface_t *surface;
	cairo_t *dctx;
Thomas White's avatar
Thomas White committed
288
289
	double max_val;
	double scale1, scale2, scale;
Thomas White's avatar
Thomas White committed
290
	double sep_u, sep_v, max_r;
291
	double u, v;
Thomas White's avatar
Thomas White committed
292
	double as, bs, theta;
293
294
295
	double asx, asy, asz;
	double bsx, bsy, bsz;
	double csx, csy, csz;
Thomas White's avatar
Thomas White committed
296
	float wh, ht;
297
298
299
300
301
302
303
	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;
304
	int png;
Thomas White's avatar
Thomas White committed
305
	double rmin, rmax;
306

307
	/* Vector product to determine the zone axis. */
308
309
310
311
	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);
312

313
	/* Size of output and centre definition */
Thomas White's avatar
Thomas White committed
314
315
	wh = 1024;
	ht = 1024;
316

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

Thomas White's avatar
Thomas White committed
334
335
336
337
	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);
338

339
	max_val = max_value(list, wght, sym);
340
	if ( max_val <= 0.0 ) {
341
		STATUS("Couldn't find max value.\n");
Thomas White's avatar
Thomas White committed
342
		return;
343
344
	}

345
346
347
348
349
	/* Use manual scale top if specified */
	if ( scale_top > 0.0 ) {
		max_val = scale_top;
	}

Thomas White's avatar
Thomas White committed
350
351
352
	scale1 = ((double)wh-border) / (2.0*rmax);
	scale2 = ((double)ht-border) / (2.0*rmax);
	scale = (scale1 < scale2) ? scale1 : scale2;
353

Thomas White's avatar
Thomas White committed
354
	/* Work out the spot radius */
355
356
	sep_u = scale*as;
	sep_v = scale*bs;
357
	max_r = (sep_u < sep_v) ? sep_u : sep_v;
358
	max_r /= 2.0;  /* Max radius is half the separation */
359
	max_r -= (max_r/10.0);  /* Add a tiny separation between circles */
360

Thomas White's avatar
Thomas White committed
361
	/* Create surface */
362
363
364
365
366
367
368
369
	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);
	}
370
371

	if ( cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS ) {
Thomas White's avatar
Thomas White committed
372
		ERROR("Couldn't create Cairo surface\n");
373
374
375
376
377
		cairo_surface_destroy(surface);
		return;
	}

	dctx = cairo_create(surface);
Thomas White's avatar
Thomas White committed
378
379
380
381
382
	if ( cairo_status(dctx) != CAIRO_STATUS_SUCCESS ) {
		ERROR("Couldn't create Cairo context\n");
		cairo_surface_destroy(surface);
		return;
	}
383
384
385
386
387
388
389
390
391
392
393
394
395
396

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

397
	draw_circles(xh, xk, xl, yh, yk, yl, zh, zk, zl,
Thomas White's avatar
Thomas White committed
398
	             list, sym, dctx, wght, boost, colscale, cell,
399
	             max_r, theta, as, bs, cx, cy, scale,
400
	             max_val);
401

Thomas White's avatar
Thomas White committed
402
	/* Centre marker */
403
404
	cairo_arc(dctx, (double)cx,
			(double)cy, max_r, 0, 2*M_PI);
Thomas White's avatar
Thomas White committed
405
406
407
	cairo_set_source_rgb(dctx, 1.0, 0.0, 0.0);
	cairo_fill(dctx);

408
	/* Draw indexing lines */
409
	cairo_set_line_cap(dctx, CAIRO_LINE_CAP_ROUND);
410
	cairo_set_line_width(dctx, 4.0);
411
	cairo_move_to(dctx, (double)cx, (double)cy);
Thomas White's avatar
Thomas White committed
412
413
	u = rmax*sin(theta);
	v = rmax*cos(theta);
414
	cairo_line_to(dctx, cx+u*scale, cy+v*scale);
415
416
417
	cairo_set_source_rgb(dctx, 0.0, 1.0, 0.0);
	cairo_stroke(dctx);

418
	cairo_set_font_size(dctx, 40.0);
419
	snprintf(tmp, 255, "%i%i%i", abs(xh), abs(xk), abs(xl));
420
421
422
	cairo_text_extents(dctx, tmp, &size);

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

426
	snprintf(tmp, 255, "%i%i%i", abs(yh), abs(yk), abs(yl));
427
428
429
	cairo_text_extents(dctx, tmp, &size);

	cairo_move_to(dctx, (double)cx, (double)cy);
Thomas White's avatar
Thomas White committed
430
	cairo_line_to(dctx, cx, cy+rmax*scale);
431
432
433
	cairo_set_source_rgb(dctx, 0.0, 1.0, 0.0);
	cairo_stroke(dctx);

Thomas White's avatar
Thomas White committed
434
435
	cairo_move_to(dctx, cx - size.width/2.0,
	                    cy+rmax*scale + size.height + 20.0);
436
	render_overlined_indices(dctx, yh, yk, yl);
437
438
	cairo_fill(dctx);

439
440
441
442
443
444
445
	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
446
447
448
	cairo_surface_finish(surface);
	cairo_destroy(dctx);
}
449

450

451
static int render_key(int colscale, double scale_top)
452
453
454
{
	cairo_surface_t *surface;
	cairo_t *dctx;
455
456
	double top, wh, ht, y;
	double slice;
457

458
459
460
461
462
463
464
465
466
	wh = 128.0;
	ht = 1024.0;
	slice = 1.0;

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

468
	surface = cairo_pdf_surface_create(KEY_FILENAME, wh, ht);
469
470
471
472
473
474
475
476
477

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

478
	for ( y=0.0; y<ht; y+=slice ) {
479

480
481
482
		double r, g, b;
		double val;
		double v = y;
483

484
485
486
487
488
489
490
491
492
493
494
		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;
		}
495

496
		render_scale(val, top, colscale, &r, &g, &b);
497
498
		cairo_set_source_rgb(dctx, r, g, b);

499
		cairo_stroke_preserve(dctx);
500
501
502
503
		cairo_fill(dctx);

	}

504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
	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);

	}


533
534
535
	cairo_surface_finish(surface);
	cairo_destroy(dctx);

536
537
	STATUS("Colour key written to "KEY_FILENAME"\n");

538
539
	return 0;
}
540
541
542
543
544


#else  /* HAVE_CAIRO */


545
static int render_key(int colscale, 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 draw the colour");
	ERROR(" scale.  Sorry!\n");
	return 1;
}


554
static void render_za(UnitCell *cell, RefList *list,
555
556
557
                      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,
558
                      const char *outfile, double scale_top)
559
560
561
562
563
564
565
566
{
	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
567
568
569
570
571
572


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

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

	/* Short options */
Thomas White's avatar
Thomas White committed
614
	while ((c = getopt_long(argc, argv, "hp:w:c:y:d:r:o:",
615
	                        longopts, NULL)) != -1) {
Thomas White's avatar
Thomas White committed
616
617

		switch (c) {
Thomas White's avatar
Thomas White committed
618
		case 'h' :
Thomas White's avatar
Thomas White committed
619
620
621
			show_help(argv[0]);
			return 0;

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
	if ( config_zawhinge ) {
679
680
681
682
		ERROR("Friendly warning: The --zone-axis option isn't needed"
		      " any longer (I ignored it for you).\n");
	}

683
684
685
	if ( (pdb == NULL) && !config_colkey ) {
		ERROR("You must specify the PDB containing the unit cell.\n");
		return 1;
686
687
	}

Thomas White's avatar
Thomas White committed
688
689
	if ( sym_str == NULL ) {
		sym_str = strdup("1");
690
	}
Thomas White's avatar
Thomas White committed
691
692
	sym = get_pointgroup(sym_str);
	free(sym_str);
693

694
695
696
697
	if ( weighting == NULL ) {
		weighting = strdup("I");
	}

698
	if ( outfile == NULL ) outfile = strdup("za.pdf");
Thomas White's avatar
Thomas White committed
699

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

740
	if ( config_colkey ) {
741
		return render_key(colscale, scale_top);
742
743
	}

744
745
746
747
748
749
750
751
752
753
754
	if ( (( down == NULL ) && ( right != NULL ))
	  || (( down != NULL ) && ( right == NULL )) ) {
		ERROR("Either specify both 'down' and 'right',"
		      " or neither.\n");
		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");
755
756
			return 1;
		}
757
758
759
760
761
762
763
	}
	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;
764
765
766
		}
	}

Thomas White's avatar
Thomas White committed
767
768
	infile = argv[optind];

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

785
786
	render_za(cell, list, boost, sym, wght, colscale,
	          rh, rk, rl, dh, dk, dl, outfile, scale_top);
Thomas White's avatar
Thomas White committed
787

788
	free(pdb);
Thomas White's avatar
Thomas White committed
789
	free_symoplist(sym);
Thomas White's avatar
Thomas White committed
790
	reflist_free(list);
791
	if ( outfile != NULL ) free(outfile);
792

793
	return r;
794
}