aboutsummaryrefslogtreecommitdiff
path: root/src/model-display.c
diff options
context:
space:
mode:
authorThomas White <taw@physics.org>2020-08-01 15:13:49 +0200
committerThomas White <taw@physics.org>2020-08-01 15:19:26 +0200
commit189da15810deabd739d7c11c6e95fea55739fe60 (patch)
treee86e43fcbbf8278b792a74daf684cde3bd6e2bbf /src/model-display.c
Initial import from archive
Diffstat (limited to 'src/model-display.c')
-rw-r--r--src/model-display.c411
1 files changed, 411 insertions, 0 deletions
diff --git a/src/model-display.c b/src/model-display.c
new file mode 100644
index 0000000..4eeb83a
--- /dev/null
+++ b/src/model-display.c
@@ -0,0 +1,411 @@
+/*
+ * model-display.c
+ *
+ * Display atomic models
+ *
+ * (c) 2006-2007 Thomas White <taw27@cam.ac.uk>
+ *
+ * synth2d - Two-Dimensional Crystallographic Fourier Synthesis
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <math.h>
+#include <inttypes.h>
+
+#if HAVE_CAIRO
+#include <cairo.h>
+#include <cairo-pdf.h>
+#endif
+
+#include "model.h"
+#include "elements.h"
+#include "symmetry.h"
+
+#if HAVE_CAIRO
+static double model_oblique_x(double x, double y, int height, double gamma, int ny) {
+
+ double offs;
+
+ if ( gamma <= M_PI_2 ) {
+ offs = 0;
+ } else {
+ offs = (height*ny)*(-cos(gamma));
+ }
+
+ return 20.0 + offs + x + (y*cos(gamma));
+
+}
+
+static double model_oblique_y(double x, double y, double gamma) {
+
+ return 20.0 + y * sin(gamma);
+
+}
+
+static void model_display_draw_atoms(cairo_t *dctx, AtomicModel *model, int width, int height, double gamma, int nx, int ny, double h, int names, int heights) {
+
+ cairo_surface_t *surface;
+ size_t i;
+ cairo_font_extents_t ext;
+
+ surface = cairo_get_target(dctx);
+
+ for ( i=0; i<model->n_atoms; i++ ) {
+
+ AtomicModel *equivalents;
+ size_t j;
+ double x, y;
+
+ if ( !model->atoms[i].active ) continue;
+
+ equivalents = symmetry_generate_equivalent_atoms(model, i, MODEL_TARGET_DISPLAY);
+
+ for ( j=0; j<equivalents->n_atoms; j++ ) {
+
+ double R;
+ int xp, yp;
+
+ x = width*(double)equivalents->atoms[j].x;
+ y = height*(double)equivalents->atoms[j].y;
+ R = sqrt(elements[equivalents->atoms[j].ref].z);
+
+ for ( xp=0; xp<nx; xp++ ) {
+ for ( yp=0; yp<ny; yp++ ) {
+ cairo_new_path(dctx);
+
+ if ( model->atoms[i].occ < 1.0 ) {
+ cairo_set_source_rgb(dctx, 0.4, 0.4, 0.4);
+ } else if ( model->atoms[i].occ > 1.0 ) {
+ cairo_set_source_rgb(dctx, 0.4, 0, 0);
+ } else {
+ cairo_set_source_rgb(dctx, 0, 0, 0);
+ }
+
+ cairo_arc(dctx, model_oblique_x(x+(width*xp), y+(height*yp), height, gamma, ny),
+ h-model_oblique_y(x+(width*xp), y+(height*yp), gamma),
+ R, 0, 2*M_PI);
+ cairo_fill(dctx);
+ }
+ }
+
+ }
+
+ model_free(equivalents);
+
+ cairo_set_source_rgb(dctx, 0, 0, 1);
+ cairo_select_font_face(dctx, "Sans", CAIRO_FONT_SLANT_ITALIC, CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_font_extents(dctx, &ext);
+
+ /* Only the unique atoms are labelled */
+ x = 5 + width*(double)model->atoms[i].x + sqrt(elements[model->atoms[i].ref].z);
+ y = height*(double)model->atoms[i].y;
+ cairo_move_to(dctx, model_oblique_x(x,y,height,gamma, ny), h-model_oblique_y(x,y,gamma));
+ if ( names ) {
+ cairo_show_text(dctx, elements[model->atoms[i].ref].element_name);
+ y -= ext.height;
+ }
+ x = 5 + width*(double)model->atoms[i].x + sqrt(elements[model->atoms[i].ref].z);
+ if ( heights ) {
+
+ char tmp[32];
+
+ cairo_move_to(dctx, model_oblique_x(x,y,height,gamma, ny), h-model_oblique_y(x,y,gamma));
+ cairo_show_text(dctx, "z=");
+ snprintf(tmp, 31, "%.3f", model->atoms[i].z);
+ cairo_show_text(dctx, tmp);
+ y -= ext.height;
+
+ }
+ if ( model->atoms[i].occ != 1.0 ) {
+
+ char tmp[32];
+
+ cairo_move_to(dctx, model_oblique_x(x,y,height,gamma, ny), h-model_oblique_y(x,y,gamma));
+ cairo_show_text(dctx, "occ: ");
+ snprintf(tmp, 31, "%.2f", model->atoms[i].occ);
+ cairo_show_text(dctx, tmp);
+ y -= ext.height;
+
+ }
+
+ }
+
+}
+
+static void model_display_draw_diad(cairo_t *dctx, double x, double y) {
+
+ cairo_matrix_t matrix;
+
+ cairo_get_matrix(dctx, &matrix);
+ cairo_new_path(dctx);
+ cairo_set_source_rgb(dctx, 1, 0, 0);
+ cairo_translate(dctx, x, y);
+ cairo_scale(dctx, 1.0, 2.0);
+ cairo_arc(dctx, 0.0, 0.0, 3, 0, 2*M_PI);
+ cairo_fill(dctx);
+ cairo_set_matrix(dctx, &matrix);
+
+}
+
+static void model_display_draw_glide(cairo_t *dctx, double x1, double y1, double x2, double y2, int height, double gamma, int ny, double h) {
+
+ double dash;
+
+ cairo_new_path(dctx);
+ cairo_set_source_rgb(dctx, 1, 0, 0);
+ cairo_set_line_width(dctx, 1.0);
+ dash = 3.0; cairo_set_dash(dctx, &dash, 1, 0);
+ cairo_move_to(dctx, model_oblique_x(x1, y1, height, gamma, ny), h-model_oblique_y(x1, y1, gamma));
+ cairo_line_to(dctx, model_oblique_x(x2, y2, height, gamma, ny), h-model_oblique_y(x2, y2, gamma));
+ cairo_stroke(dctx);
+ dash = 0.0; cairo_set_dash(dctx, &dash, 0, 0);
+
+}
+
+static void model_display_draw_mirror(cairo_t *dctx, double x1, double y1, double x2, double y2, int height, double gamma, int ny, double h) {
+
+ cairo_new_path(dctx);
+ cairo_set_source_rgb(dctx, 1, 0, 0);
+ cairo_set_line_width(dctx, 1.0);
+ cairo_move_to(dctx, model_oblique_x(x1, y1, height, gamma, ny), h-model_oblique_y(x1, y1, gamma));
+ cairo_line_to(dctx, model_oblique_x(x2, y2, height, gamma, ny), h-model_oblique_y(x2, y2, gamma));
+ cairo_stroke(dctx);
+
+}
+
+static void model_display_draw_symmetry(cairo_t *dctx, Symmetry sym, int width, int height, double gamma, int nx, int ny, double h) {
+
+ cairo_surface_t *surface;
+
+ surface = cairo_get_target(dctx);
+
+ if ( sym & SYMMETRY_CENTRE ) {
+ int x, y;
+ for ( x=0; x<nx; x++ ) {
+ for ( y=0; y<ny; y++ ) {
+ model_display_draw_diad(dctx, model_oblique_x(0.0, 0.0, height, gamma, ny),
+ h-model_oblique_y(0.0, 0.0, gamma));
+ model_display_draw_diad(dctx, model_oblique_x((x*width)+width/2, 0.0, height, gamma, ny),
+ h-model_oblique_y((x*width)+width/2, 0.0, gamma));
+ model_display_draw_diad(dctx, model_oblique_x((x*width)+width, 0.0, height, gamma, ny),
+ h-model_oblique_y((x*width)+width, 0.0, gamma));
+ model_display_draw_diad(dctx, model_oblique_x(0.0, (y*height)+height/2, height, gamma, ny),
+ h-model_oblique_y(0.0, (y*height)+height/2, gamma));
+ model_display_draw_diad(dctx, model_oblique_x((x*width)+width/2, (y*height)+height/2, height, gamma, ny),
+ h-model_oblique_y((x*width)+width/2, (y*height)+height/2, gamma));
+ model_display_draw_diad(dctx, model_oblique_x((x*width)+width, (y*height)+height/2, height, gamma, ny),
+ h-model_oblique_y((x*width)+width, (y*height)+height/2, gamma));
+ model_display_draw_diad(dctx, model_oblique_x(0.0, (y+1)*height, height, gamma, ny),
+ h-model_oblique_y(0.0, (y+1)*height, gamma));
+ model_display_draw_diad(dctx, model_oblique_x((x*width)+width/2, (y*height)+height, height, gamma, ny),
+ h-model_oblique_y((x*width)+width/2, (y*height)+height, gamma));
+ model_display_draw_diad(dctx, model_oblique_x((x*width)+width, (y*height)+height, height, gamma, ny),
+ h-model_oblique_y((x*width)+width, (y*height)+height, gamma));
+ }
+ }
+ }
+
+ if ( sym & SYMMETRY_GLIDE_VERTICAL_QUARTER ) {
+ int x;
+ for ( x=0; x<nx; x++ ) {
+ model_display_draw_glide(dctx, (x*width)+width/4, 0.0, (x*width)+width/4, height*ny, height, gamma, ny, h);
+ model_display_draw_glide(dctx, (x*width)+3*width/4, 0.0, (x*width)+3*width/4, height*ny, height, gamma, ny, h);
+ }
+ }
+
+ if ( sym & SYMMETRY_GLIDE_HORIZONTAL_QUARTER ) {
+ int y;
+ for ( y=0; y<ny; y++ ) {
+ model_display_draw_glide(dctx, 0.0, (height*y)+height/4, nx*width, (height*y)+height/4, height, gamma, ny, h);
+ model_display_draw_glide(dctx, 0.0, (height*y)+3*height/4, nx*width, (height*y)+3*height/4, height, gamma, ny, h);
+ }
+ }
+
+ if ( sym & SYMMETRY_GLIDE_HORIZONTAL ) {
+ int y;
+ model_display_draw_glide(dctx, 0.0, 0.0, nx*width, 0.0, height, gamma, ny, h);
+ for ( y=0; y<ny; y++ ) {
+ model_display_draw_glide(dctx, 0.0, (height*y)+height/2, nx*width, (height*y)+height/2, height, gamma, ny, h);
+ model_display_draw_glide(dctx, 0.0, (height*y)+height, nx*width, (height*y)+height, height, gamma, ny, h);
+ }
+ }
+
+ if ( sym & SYMMETRY_GLIDE_VERTICAL ) {
+ int x;
+ model_display_draw_glide(dctx, 0.0, 0.0, 0.0, height*ny, height, gamma, ny, h);
+ for ( x=0; x<nx; x++ ) {
+ model_display_draw_glide(dctx, (x*width)+width/2, 0.0, (x*width)+width/2, ny*height, height, gamma, ny, h);
+ model_display_draw_glide(dctx, (x*width)+width, 0.0, (x*width)+width, ny*height, height, gamma, ny, h);
+ }
+ }
+
+ if ( sym & SYMMETRY_MIRROR_HORIZONTAL ) {
+ int y;
+ model_display_draw_mirror(dctx, 0.0, 0.0, nx*width, 0.0, height, gamma, ny, h);
+ for ( y=0; y<ny; y++ ) {
+ model_display_draw_mirror(dctx, 0.0, (height*y)+height/2, nx*width, (height*y)+height/2, height, gamma, ny, h);
+ model_display_draw_mirror(dctx, 0.0, (height*y)+height, nx*width, (height*y)+height, height, gamma, ny, h);
+ }
+ }
+
+ if ( sym & SYMMETRY_MIRROR_VERTICAL ) {
+ int x;
+ model_display_draw_mirror(dctx, 0.0, 0.0, 0.0, height*ny, height, gamma, ny, h);
+ for ( x=0; x<nx; x++ ) {
+ model_display_draw_mirror(dctx, (x*width)+width/2, 0.0, (x*width)+width/2, ny*height, height, gamma, ny, h);
+ model_display_draw_mirror(dctx, (x*width)+width, 0.0, (x*width)+width, ny*height, height, gamma, ny, h);
+ }
+ }
+
+ if ( sym & SYMMETRY_MIRROR_VERTICAL_QUARTER ) {
+ int x;
+ for ( x=0; x<nx; x++ ) {
+ model_display_draw_mirror(dctx, (width*x)+width/4, 0.0, (width*x)+width/4, ny*height, height, gamma, ny, h);
+ model_display_draw_mirror(dctx, (width*x)+3*width/4, 0.0, (width*x)+3*width/4, ny*height, height, gamma, ny, h);
+ }
+ }
+
+ if ( sym & SYMMETRY_MIRROR_HORIZONTAL_QUARTER ) {
+ int y;
+ for ( y=0; y<ny; y++ ) {
+ model_display_draw_mirror(dctx, 0.0, (height*y)+height/4, nx*width, (height*y)+height/4, height, gamma, ny, h);
+ model_display_draw_mirror(dctx, 0, (height*y)+3*height/4, nx*width, (height*y)+3*height/4, height, gamma, ny, h);
+ }
+ }
+
+}
+
+static void model_display_swizzle_data(unsigned char *data, size_t n) {
+
+ size_t i;
+
+ for ( i=0; i<4*n; 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;
+ }
+
+}
+
+static double model_display_surface_width(int width_o, int height_o, double gamma) {
+ if ( gamma <= M_PI_2 ) {
+ return 40+width_o+height_o*cos(gamma);
+ } else {
+ return 40+width_o-height_o*cos(gamma);
+ }
+}
+
+static double model_display_surface_height(int width_o, int height_o, double gamma) {
+ return 40+height_o*sin(gamma);
+}
+
+static cairo_t *model_display_do_cairo(cairo_t *dctx, AtomicModel *model, int width, int height, double gamma, int nx, int ny, double h, int names, int heights) {
+
+ int x, y;
+
+ cairo_rectangle(dctx, 0.0, 0.0, model_display_surface_width(nx*width, ny*height, gamma),
+ model_display_surface_height(nx*width, ny*height, gamma));
+ cairo_set_source_rgb(dctx, 1.0, 1.0, 1.0);
+ cairo_fill(dctx);
+
+ cairo_set_line_width(dctx, 0.5);
+
+ for ( x=0; x<nx; x++ ) {
+ for ( y=0; y<ny; y++ ) {
+ cairo_move_to(dctx, model_oblique_x(width*x, height*y, height, gamma, ny),
+ h-model_oblique_y(width*x, height*y, gamma));
+ cairo_line_to(dctx, model_oblique_x(width*(x+1), height*y, height, gamma, ny),
+ h-model_oblique_y(width*(x+1), height*y, gamma));
+ cairo_line_to(dctx, model_oblique_x(width*(x+1), height*(y+1), height, gamma, ny),
+ h-model_oblique_y(width*(x+1), height*(y+1), gamma));
+ cairo_line_to(dctx, model_oblique_x(width*x, height*(y+1), height, gamma, ny),
+ h-model_oblique_y(width*x, height*(y+1), gamma));
+ cairo_line_to(dctx, model_oblique_x(width*x, height*y, height, gamma, ny),
+ h-model_oblique_y(width*x, height*y, gamma));
+ }
+ }
+ cairo_set_source_rgb(dctx, 0.8, 0.8, 0.8);
+ cairo_stroke(dctx);
+
+ model_display_draw_atoms(dctx, model, width, height, gamma, nx, ny, h, names, heights);
+ model_display_draw_symmetry(dctx, model->sym, width, height, gamma, nx, ny, h);
+
+ return dctx;
+
+}
+
+static void model_display_free_data(guchar *image_data, cairo_t *dctx) {
+ cairo_surface_finish(cairo_get_target(dctx));
+ cairo_destroy(dctx);
+}
+#endif
+
+GdkPixbuf *model_display_render_pixbuf(AtomicModel *model, int width, int height, double gamma, int names, int heights, int nx, int ny) {
+
+#if HAVE_CAIRO
+ cairo_surface_t *surface;
+ cairo_t *dctx;
+ GdkPixbuf *pixbuf;
+ unsigned char *data;
+
+ surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, model_display_surface_width(nx*width, ny*height, gamma),
+ model_display_surface_height(nx*width, ny*height, gamma));
+
+ 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);
+ model_display_do_cairo(dctx, model, width, height, gamma, nx, ny, cairo_image_surface_get_height(surface), names, heights);
+
+ cairo_surface_flush(surface);
+
+ data = cairo_image_surface_get_data(surface);
+ model_display_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)model_display_free_data, dctx);
+
+ return pixbuf;
+#else
+ return NULL;
+#endif
+
+}
+
+int model_display_render_pdf(AtomicModel *model, int width, int height, double gamma, const char *filename, int names, int heights, int nx, int ny) {
+
+#if HAVE_CAIRO
+ cairo_surface_t *surface;
+ cairo_t *dctx;
+
+ surface = cairo_pdf_surface_create(filename, model_display_surface_width(nx*width, ny*height, gamma),
+ model_display_surface_height(nx*width, ny*height, gamma));
+
+ 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);
+
+ model_display_do_cairo(dctx, model, width, height, gamma, nx, ny, model_display_surface_height(nx*width, ny*height, gamma), names, heights);
+
+ cairo_surface_finish(surface);
+ cairo_destroy(dctx);
+#endif
+
+ return 0;
+
+}
+