aboutsummaryrefslogtreecommitdiff
path: root/src/dpsynth.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dpsynth.c')
-rw-r--r--src/dpsynth.c291
1 files changed, 291 insertions, 0 deletions
diff --git a/src/dpsynth.c b/src/dpsynth.c
new file mode 100644
index 0000000..33e9c3e
--- /dev/null
+++ b/src/dpsynth.c
@@ -0,0 +1,291 @@
+/*
+ * dpsynth.c
+ *
+ * Draw synthetic diffraction patterns
+ *
+ * (c) 2006-2009 Thomas White <taw27@cam.ac.uk>
+ * (c) 2007-2008 Alex Eggeman <ase25@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <png.h>
+#include <math.h>
+#include <stdlib.h>
+
+#if HAVE_CAIRO
+#include <cairo.h>
+#endif
+
+#include <inttypes.h>
+
+#include "reflist.h"
+#include "data.h"
+#include "displaywindow.h"
+#include "colwheel.h"
+#include "gsf.h"
+#include "normalise.h"
+
+typedef struct {
+ GtkWidget *image_widget;
+ GdkPixbuf *pixbuf;
+ GtkWidget *window;
+ unsigned int width;
+ unsigned int height;
+ unsigned int colour;
+
+} DPSynthWindow;
+
+#if HAVE_CAIRO
+static void dpsynth_swizzle_data(unsigned char *data,
+ size_t width, size_t height)
+{
+ size_t i, x, y;
+ uint32_t *dataw;
+
+ for ( i=0; i<4*width*height; i+=4 ) {
+ unsigned char a, r, g, b;
+ r = data[i]; g = data[i+1]; b = data[i+2]; a = data[i+3];
+ data[i] = b; data[i+1] = g; data[i+2] = r; data[i+3] = a;
+ }
+
+ /* Now make it be the right way up */
+ dataw = (uint32_t *)data;
+ for ( y=0; y<height/2; y++ ) {
+ for ( x=0; x<width; x++ ) {
+ uint32_t word;
+ word = dataw[x+width*y];
+ dataw[x+width*y] = dataw[x+width*(height-1-y)];
+ dataw[x+width*(height-1-y)] = word;
+ }
+ }
+
+}
+
+static void dpsynth_free_data(guchar *image_data, cairo_t *dctx)
+{
+ cairo_surface_finish(cairo_get_target(dctx));
+ cairo_destroy(dctx);
+}
+#endif
+
+static GdkPixbuf *dpsynth_render_pixbuf(DPSynthWindow *dpsynth,
+ ReflectionList *reflections)
+{
+#ifdef HAVE_CAIRO
+ cairo_surface_t *surface;
+ cairo_t *dctx;
+ GdkPixbuf *pixbuf;
+ unsigned char *data;
+ size_t i;
+ double max_u, max_v, max_res, max_intensity, scale;
+ double sep_u, sep_v, max_r;
+ double as, bs, theta;
+
+ surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ dpsynth->width, dpsynth->height);
+
+ if ( cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS ) {
+ fprintf(stderr, "Couldn't create Cairo surface\n");
+ cairo_surface_destroy(surface);
+ return NULL;
+ }
+
+ dctx = cairo_create(surface);
+
+ /* Black background */
+ cairo_rectangle(dctx, 0.0, 0.0, dpsynth->width, dpsynth->height);
+ cairo_set_source_rgb(dctx, 0.0, 0.0, 0.0);
+
+ cairo_fill(dctx);
+
+ max_u = 0.0; max_v = 0.0; max_intensity = 0.0;
+ max_res = 0.0;
+ /* NB This isn't really "the angle between a* and b*" */
+ theta = data_gamma()-M_PI_2;
+ as = 1/(data_a() * cos(theta));
+ bs = 1/(data_b() * cos(theta));
+ for ( i=0; i<reflections->n_reflections; i++ ) {
+
+ double u, v, intensity, ph, res;
+
+ /* Convert to intensity */
+ intensity = pow(reflections->refs[i].amplitude, 2.0);
+ ph = reflections->refs[i].phase_known;
+
+ res = resolution(reflections->refs[i].h,
+ reflections->refs[i].k,
+ reflections->refs[i].l,
+ data_a(), data_b(), data_c(), data_gamma());
+ if ( res > max_res ) max_res = res;
+
+ if ( intensity != 0 ) {
+ u = (double)reflections->refs[i].h * as * cos(theta);
+ v = (double)reflections->refs[i].h * as * sin(theta)
+ + reflections->refs[i].k * bs;
+ if ( fabs(u) > fabs(max_u) ) max_u = fabs(u);
+ if ( fabs(v) > fabs(max_v) ) max_v = fabs(v);
+ if ( fabs(intensity) > fabs(max_intensity) )
+ max_intensity = fabs(intensity);
+ }
+
+ }
+
+ max_u *= 2.5;
+ max_v *= 2.5;
+ printf("DP: Maximum resolution is %f nm^-1\n", max_res);
+
+ if ( max_intensity > 0 ) {
+
+ scale = ((double)dpsynth->width-50.0) / (2*max_u);
+ if ( ((double)dpsynth->height-50.0) / (2*max_v) < scale )
+ scale = ((double)dpsynth->height-50.0) / (2*max_v);
+
+ sep_u = as * scale * cos(theta);
+ sep_v = bs * scale;
+ max_r = ((sep_u < sep_v)?sep_u:sep_v) / 2;
+
+ for ( i=0; i<reflections->n_reflections; i++ ) {
+
+ double u, v, intensity, ph, val;
+
+ intensity = pow(reflections->refs[i].amplitude, 2.0);
+ ph = reflections->refs[i].phase_known;
+ val = 2.0*intensity/max_intensity;
+
+ if ( intensity != 0 ) {
+
+ u = (double)reflections->refs[i].h * as
+ * cos(theta);
+ v = (double)reflections->refs[i].h * as
+ * sin(theta) + reflections->refs[i].k * bs;
+
+ cairo_arc(dctx, ((double)dpsynth->width/2)
+ +u*scale*2,
+ ((double)dpsynth->height/2)
+ +v*scale*2, max_r, 0, 2*M_PI);
+
+
+ if ( dpsynth->colour == 0 ) {
+ cairo_set_source_rgb(dctx, val, val, val); } else {
+ cairo_set_source_rgb(dctx,
+ colwheel_red(val, ph),
+ colwheel_green(val, ph),
+ colwheel_blue(val, ph)
+ );
+ }
+ cairo_fill(dctx);
+
+ }
+
+ }
+
+ } else {
+ max_r = 4.0;
+ }
+
+ /* Centre marker */
+ cairo_arc(dctx, (double)dpsynth->width/2,
+ (double)dpsynth->height/2, max_r, 0, 2*M_PI);
+ cairo_set_source_rgb(dctx, 1.0, 0.0, 0.0);
+ cairo_fill(dctx);
+
+ cairo_surface_flush(surface);
+
+ data = cairo_image_surface_get_data(surface);
+ dpsynth_swizzle_data(data, cairo_image_surface_get_width(surface),
+ cairo_image_surface_get_height(surface));
+ pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, TRUE, 8,
+ cairo_image_surface_get_width(surface),
+ cairo_image_surface_get_height(surface),
+ cairo_image_surface_get_stride(surface),
+ (GdkPixbufDestroyNotify)dpsynth_free_data, dctx);
+
+ return pixbuf;
+#else
+ /* Cannot do this without Cairo */
+ return NULL;
+#endif
+}
+
+static void dpsynth_close(GtkWidget *widget, DPSynthWindow *dpsynth)
+{
+ free(dpsynth);
+}
+
+void dpsynth_update(DPSynthWindow *dpsynth, ReflectionList *reflections)
+{
+ if ( dpsynth->pixbuf ) gdk_pixbuf_unref(dpsynth->pixbuf);
+
+ dpsynth->pixbuf = dpsynth_render_pixbuf(dpsynth, reflections);
+ if ( !dpsynth->pixbuf ) return;
+
+ if ( dpsynth->image_widget ) {
+ g_object_set(G_OBJECT(dpsynth->image_widget), "pixbuf",
+ dpsynth->pixbuf, NULL);
+ } else {
+ dpsynth->image_widget = gtk_image_new_from_pixbuf(
+ dpsynth->pixbuf);
+ gtk_container_add(GTK_CONTAINER(dpsynth->window),
+ GTK_WIDGET(dpsynth->image_widget));
+ gtk_widget_show(dpsynth->image_widget);
+ }
+}
+
+DPSynthWindow *dpsynth_open(ReflectionList *reflections, const char *title,
+ int colour)
+{
+ DPSynthWindow *dpsynth;
+
+ dpsynth = malloc(sizeof(DPSynthWindow));
+ dpsynth->width = 512;
+ dpsynth->height = 512;
+ dpsynth->pixbuf = NULL;
+ dpsynth->image_widget = NULL;
+ dpsynth->colour = colour;
+
+ dpsynth->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(dpsynth->window), title);
+ g_signal_connect(GTK_OBJECT(dpsynth->window), "destroy",
+ G_CALLBACK(dpsynth_close), dpsynth);
+ gtk_widget_show_all(dpsynth->window);
+
+ dpsynth_update(dpsynth, reflections);
+
+ return dpsynth;
+}
+
+/* "Glue" - to be removed eventually... */
+DPSynthWindow *dpsynth_main_dpsynth = NULL;
+static void dpsynth_main_close(GtkWidget *widget, DPSynthWindow *dpsynth) {
+ dpsynth_main_dpsynth = NULL;
+}
+void dpsynth_main_update(ReflectionList *reflections) {
+ if ( !dpsynth_main_dpsynth ) return;
+ dpsynth_update(dpsynth_main_dpsynth, reflections);
+}
+void dpsynth_main_open(ReflectionList *reflections) {
+ if ( dpsynth_main_dpsynth ) return;
+ dpsynth_main_dpsynth = dpsynth_open(reflections, "Diffraction Pattern", 0);
+ g_signal_connect(GTK_OBJECT(dpsynth_main_dpsynth->window), "destroy", G_CALLBACK(dpsynth_main_close), dpsynth_main_dpsynth);
+}
+
+DPSynthWindow *dpsynth_sim_dpsynth = NULL;
+static void dpsynth_simdp_close(GtkWidget *widget, DPSynthWindow *dpsynth) {
+ dpsynth_sim_dpsynth = NULL;
+}
+void dpsynth_simdp_update(ReflectionList *reflections) {
+ if ( !dpsynth_sim_dpsynth ) return;
+ dpsynth_update(dpsynth_sim_dpsynth, reflections);
+}
+void dpsynth_simdp_open(ReflectionList *reflections) {
+ if ( dpsynth_sim_dpsynth ) return;
+ dpsynth_sim_dpsynth = dpsynth_open(reflections, "Simulated Diffraction Pattern", 0);
+ g_signal_connect(GTK_OBJECT(dpsynth_sim_dpsynth->window), "destroy", G_CALLBACK(dpsynth_simdp_close), dpsynth_sim_dpsynth);
+}