render_hkl.c 19.4 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"
Thomas White's avatar
Thomas White committed
62
"  -o, --output=<filename> Output filename.  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
84
"                           The key will be written to 'key.pdf' in the\n"
"                           current directory.\n"
Thomas White's avatar
Thomas White committed
85
"\n"
86
"  -h, --help              Display this help message.\n"
87
);
88
89
90
}


91
#ifdef HAVE_CAIRO
92
93


94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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);

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

Thomas White's avatar
Thomas White committed
157
158
	m = new_symopmask(sym);

159
160
161
	nx = xh*xh + xk*xk + xl*xl;
	ny = yh*yh + yk*yk + yl*yl;

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

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

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

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

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

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

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

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

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

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

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

218
219
220
221
			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
222

223
224
225
		}

	}
Thomas White's avatar
Thomas White committed
226
227

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

452

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

460
461
462
463
464
465
466
467
468
	wh = 128.0;
	ht = 1024.0;
	slice = 1.0;

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

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

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

480
	for ( y=0.0; y<ht; y+=slice ) {
481

482
483
484
		double r, g, b;
		double val;
		double v = y;
485

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

498
		render_scale(val, top, colscale, &r, &g, &b);
499
500
		cairo_set_source_rgb(dctx, r, g, b);

501
		cairo_stroke_preserve(dctx);
502
503
504
505
		cairo_fill(dctx);

	}

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

	}


535
536
537
	cairo_surface_finish(surface);
	cairo_destroy(dctx);

538
539
	STATUS("Colour key written to "KEY_FILENAME"\n");

540
541
	return 0;
}
542
543
544
545
546


#else  /* HAVE_CAIRO */


547
static int render_key(int colscale, double scale_top)
548
549
550
551
552
553
554
555
{
	ERROR("This version of CrystFEL was compiled without Cairo");
	ERROR(" support, which is required to draw the colour");
	ERROR(" scale.  Sorry!\n");
	return 1;
}


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


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

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

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

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

624
625
626
627
		case 'p' :
			pdb = strdup(optarg);
			break;

628
629
630
631
		case 'b' :
			boost = atof(optarg);
			break;

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

636
637
638
639
		case 'w' :
			weighting = strdup(optarg);
			break;

640
641
642
643
		case 'c' :
			cscale = strdup(optarg);
			break;

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

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

652
653
654
655
		case 'o' :
			outfile = strdup(optarg);
			break;

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

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

	}

680
	if ( config_zawhinge ) {
681
682
683
684
		ERROR("Friendly warning: The --zone-axis option isn't needed"
		      " any longer (I ignored it for you).\n");
	}

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

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

696
697
698
699
	if ( weighting == NULL ) {
		weighting = strdup("I");
	}

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

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

742
	if ( config_colkey ) {
743
		return render_key(colscale, scale_top);
744
745
	}

746
747
748
749
750
751
752
753
754
755
756
	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");
757
758
			return 1;
		}
759
760
761
762
763
764
765
	}
	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;
766
767
768
		}
	}

Thomas White's avatar
Thomas White committed
769
770
	infile = argv[optind];

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

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

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

795
	return r;
796
}