/*
Copyright (C) 2003 by Sean David Fleming
sean@ivec.org
This program 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 2
of the License, or (at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
The GNU GPL can also be found at http://www.gnu.org
*/
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "gdis.h"
#include "coords.h"
#include "model.h"
#include "edit.h"
#include "file.h"
#include "render.h"
#include "matrix.h"
#include "quaternion.h"
#include "measure.h"
#include "numeric.h"
#include "opengl.h"
#include "shortcuts.h"
#include "interface.h"
#include "dialog.h"
#define DEBUG 0
#define DEBUG2 0
/* global pak structures */
extern struct sysenv_pak sysenv;
extern struct elem_pak elements[];
GtkWidget *cf_spin;
struct frame_pak
{
gdouble latmat[9];
GSList *core_list;
GSList *shell_list;
};
/******************************/
/* free all pre-loaded frames */
/******************************/
void anim_free_all_frames(struct model_pak *model)
{
GSList *list;
struct frame_pak *frame;
g_assert(model != NULL);
for (list = model->frame_data_list ; list ; list=g_slist_next(list))
{
frame = list->data;
free_slist(frame->core_list);
free_slist(frame->shell_list);
g_free(frame);
}
g_slist_free(model->frame_data_list);
model->frame_data_list = NULL;
}
/**********************************/
/* attempt to pre-load all frames */
/**********************************/
gint anim_read_all_frames(struct model_pak *model)
{
gint i;
struct frame_pak *frame;
FILE *fp;
/* checks */
g_assert(model != NULL);
g_assert(model->animation == TRUE);
/* TODO - test is we have enough memory by using g_try_malloc() */
/* read in all frames and store relevant data */
fp = fopen(model->filename, "r");
if (!fp)
{
printf("Could not open source file.");
return(1);
}
for (i=0 ; i<model->num_frames ; i++)
{
/* NB: frames start at 1 */
read_raw_frame(fp, i+1, model);
matrix_lattice_init(model);
frame = g_malloc(sizeof(struct frame_pak *));
memcpy(frame->latmat, model->latmat, 9*sizeof(gdouble));
frame->core_list = dup_core_list(model->cores);
frame->shell_list = dup_shell_list(model->shels);
model->frame_data_list = g_slist_prepend(model->frame_data_list, frame);
}
model->frame_data_list = g_slist_reverse(model->frame_data_list);
return(1);
}
/*************************************************/
/* store the current position of the file stream */
/*************************************************/
#define DEBUG_ADD_FRAME_OFFSET 0
gint add_frame_offset(FILE *fp, struct model_pak *data)
{
fpos_t *offset;
g_assert(fp != NULL);
g_assert(data != NULL);
offset = g_malloc(sizeof(fpos_t));
/* prepend & reverse? */
if (!fgetpos(fp, offset))
data->frame_list = g_list_append(data->frame_list, offset);
else
{
g_free(offset);
return(1);
}
#if DEBUG_ADD_FRAME_OFFSET
printf("stored: %p\n", offset);
#endif
return(0);
}
/***********************************/
/* retrieve the nth transformation */
/***********************************/
gint read_transform(gint n, struct model_pak *model)
{
gpointer camera;
g_assert(model != NULL);
g_assert(model->camera != NULL);
camera = g_slist_nth_data(model->transform_list, n);
g_assert(camera != NULL);
model->camera = camera;
return(0);
}
/**************************/
/* retrieve the nth frame */
/**************************/
#define DEBUG_READ_FRAME 0
gint read_frame(FILE *fp, gint n, struct model_pak *model)
{
gint status;
gdouble rmax, v1[3], v2[3];
gpointer camera=NULL;
GString *err_text;
g_assert(model != NULL);
rmax = model->rmax;
/* conventional or transformation style animation */
if (model->transform_list)
{
/* NEW - process transformation list as an animation */
status = read_transform(n, model);
}
else
{
g_assert(fp != NULL);
ARR3SET(v1, model->centroid);
ARR3SET(v2, model->offset);
status = read_raw_frame(fp, n, model);
if (status)
{
err_text = g_string_new("");
g_string_printf(err_text, "Error reading frame: %d\n", n);
gui_text_show(ERROR, err_text->str);
g_string_free(err_text, TRUE);
return(1);
}
/* setup (have to save camera) */
camera = camera_dup(model->camera);
model_prep(model);
model->camera = camera;
g_free(model->camera_default);
model->camera_default = camera;
ARR3SET(model->centroid, v1);
ARR3SET(model->offset, v2);
}
/* apply desired constraint */
if (model->periodic && !model->anim_fix)
{
switch (model->anim_confine)
{
case PBC_CONFINE_ATOMS:
coords_confine_cores(model->cores, model);
case PBC_CONFINE_MOLS:
coords_compute(model);
connect_bonds(model);
connect_molecules(model);
break;
}
}
if (model->anim_noscale)
model->rmax = rmax;
return(0);
}
/***************************************/
/* current frame modification callback */
/***************************************/
#define DEBUG_SELECT_FRAME 0
void select_frame(GtkWidget *w, struct model_pak *model)
{
FILE *fp=NULL;
g_assert(w != NULL);
g_assert(model != NULL);
#if DEBUG_SELECT_FRAME
printf("request frame: %d, model: %p\n", model->cur_frame, model);
#endif
/* FIXME - better way to cope with transform animations */
if (!model->transform_list)
fp = fopen(model->filename, "r");
read_frame(fp, model->cur_frame, model);
meas_graft_model(model);
gui_active_refresh();
redraw_canvas(SINGLE);
if (!model->transform_list)
fclose(fp);
}
/*********************************************************/
/* read and draw the next frame in the current animation */
/*********************************************************/
#define DEBUG_DISPLAY_NEXT_FRAME 0
gint anim_next_frame(struct model_pak *model)
{
gulong time;
gchar *text, *name;
g_assert(model != NULL);
/* test if should we return to the start */
/*
model->cur_frame++;
*/
model->cur_frame += model->anim_step;
if (model->cur_frame == model->num_frames)
if (model->anim_loop)
model->cur_frame = 1;
/* continue until we run out of frames (or a stop is flagged) */
if (model->cur_frame < model->num_frames && model->animating)
{
#if DEBUG_DISPLAY_NEXT_FRAME
printf("displaying [%d]\n", model->cur_frame);
#endif
time = mytimer();
/* if a dialog exists - update via the current frame spinner */
if (dialog_exists(ANIM, model))
gui_relation_update(model);
else
{
/* otherwise, update manually */
read_frame(model->afp, model->cur_frame, model);
meas_graft_model(model);
gui_active_refresh();
redraw_canvas(SINGLE);
}
/* animation adjusted redraw time */
time = mytimer() - time;
model->redraw_cumulative += time;
/* NEW - render to file */
if (sysenv.render.animate)
{
text = g_strdup_printf("%s_%06d.pov", sysenv.render.animate_file, model->cur_frame);
name = g_build_filename(sysenv.cwd, text, NULL);
write_povray(name, model);
/* NB: added this as jago keeps locking up on multi-frame renders */
if (!sysenv.render.no_povray_exec)
povray_exec(name);
g_free(text);
g_free(name);
}
return(TRUE);
}
/* FIXME - find a better way to do this... */
if (!model->transform_list)
fclose(model->afp);
/* done animation */
model->animating = FALSE;
model->cur_frame--;
/* create movie? */
if (sysenv.render.animate && !sysenv.render.no_povray_exec)
{
text = NULL;
switch (sysenv.render.animate_type)
{
case ANIM_GIF:
text = g_strdup_printf("%s -delay %d %s_*.tga %s.gif",
sysenv.convert_path,
(gint) sysenv.render.delay,
sysenv.render.animate_file, sysenv.render.animate_file);
break;
case ANIM_MPEG:
text = g_strdup_printf("%s -quality %d -delay %d %s_*.tga %s.mpg",
sysenv.convert_path, (gint) sysenv.render.mpeg_quality,
(gint) sysenv.render.delay,
sysenv.render.animate_file, sysenv.render.animate_file);
break;
}
if (text)
{
system(text);
g_free(text);
gui_text_show(DEFAULT, "Completed movie creation.\n");
}
}
/* cleanup */
if (sysenv.render.no_keep_tempfiles)
{
text = g_strdup_printf("rm -rf %s_*.pov", sysenv.render.animate_file);
system(text);
g_free(text);
text = g_strdup_printf("rm -rf %s_*.tga", sysenv.render.animate_file);
system(text);
g_free(text);
}
/* done - return FALSE to terminate the timer */
return(FALSE);
}
/**********************************/
/* set current frame to beginning */
/**********************************/
void anim_rewind(GtkWidget *w, gpointer data)
{
struct model_pak *model;
model = dialog_model(data);
g_assert(model != NULL);
model->cur_frame = 1;
gui_relation_update(model);
}
/****************************/
/* set current frame to end */
/****************************/
void anim_fastforward(GtkWidget *w, gpointer data)
{
struct model_pak *model;
model = dialog_model(data);
g_assert(model != NULL);
model->cur_frame = model->num_frames-1;
gui_relation_update(model);
}
/**********************/
/* one frame backward */
/**********************/
void anim_step_backward(GtkWidget *w, gpointer data)
{
struct model_pak *model;
model = dialog_model(data);
g_assert(model != NULL);
if (model->cur_frame > 1)
{
model->cur_frame--;
gui_relation_update(model);
}
}
/*********************/
/* one frame forward */
/*********************/
void anim_step_forward(GtkWidget *w, gpointer data)
{
struct model_pak *model;
model = dialog_model(data);
g_assert(model != NULL);
if (model->cur_frame < model->num_frames-1)
{
model->cur_frame++;
gui_relation_update(model);
}
}
/****************************/
/* start an animation timer */
/****************************/
void anim_start(GtkWidget *w, gpointer data)
{
gint freq;
struct model_pak *model;
/* don't allow more than one */
model = dialog_model(data);
g_assert(model != NULL);
if (model->animating)
return;
/* timeout frequency */
/* NB: we can't refresh any faster than units of 25 (ie the canvas redraw frequency) */
freq = 25 * model->anim_speed;
if (!model->transform_list)
{
model->afp = fopen(model->filename, "r");
if (!model->afp)
{
gui_text_show(ERROR, "Failed to open animation stream.\n");
return;
}
}
g_timeout_add(freq, (GSourceFunc) &anim_next_frame, model);
model->animating = TRUE;
}
/****************************/
/* stop the animation timer */
/****************************/
void anim_stop(GtkWidget *w, gpointer data)
{
struct model_pak *model;
model = dialog_model(data);
g_assert(model != NULL);
model->animating = FALSE;
}
/************************************/
/* toggle the render to file option */
/************************************/
void anim_render(GtkWidget *w, GtkWidget *a_frame)
{
if (sysenv.render.animate)
gtk_widget_set_sensitive(GTK_WIDGET(a_frame), TRUE);
else
gtk_widget_set_sensitive(GTK_WIDGET(a_frame), FALSE);
}
/***********************************/
/* pbc confinement option handlers */
/***********************************/
void atom_pbc(struct model_pak *model)
{
model->anim_confine = PBC_CONFINE_ATOMS;
}
void mol_pbc(struct model_pak *model)
{
model->anim_confine = PBC_CONFINE_MOLS;
}
void no_pbc(struct model_pak *model)
{
model->anim_confine = PBC_CONFINE_NONE;
}
/********************/
/* cleanup callback */
/********************/
void animate_cleanup(struct model_pak *model)
{
g_assert(model != NULL);
model->animating = FALSE;
}
/***********************/
/* configure animation */
/***********************/
void gui_animate_dialog(void)
{
gchar *tmp, *title;
gpointer dialog;
GtkWidget *window, *frame, *vbox, *hbox, *hbox2, *anim_box;
GtkWidget *notebook, *page, *button, *label, *entry, *scale;
GSList *group=NULL;
struct model_pak *data;
/* checks */
data = sysenv.active_model;
if (!data)
return;
if (!data->animation)
return;
/* CURRENT */
/*
anim_read_all_frames(data);
*/
/* prevent recording whilst in animation */
gui_mode_switch(FREE);
/* dialog setup */
title = g_strdup_printf("Animation: %s", data->basename);
dialog = dialog_request(ANIM, title, NULL, animate_cleanup, data);
g_free(title);
if (!dialog)
return;
window = dialog_window(dialog);
/* notebook frame */
frame = gtk_frame_new(NULL);
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), frame, FALSE, FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(frame), PANEL_SPACING);
/* create notebook */
notebook = gtk_notebook_new();
gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP);
gtk_container_add(GTK_CONTAINER(frame), notebook);
gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), TRUE);
/* page 1 */
page = gtk_vbox_new(FALSE, PANEL_SPACING);
label = gtk_label_new("Control");
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
/* general animation stuff */
frame = gtk_frame_new(NULL);
gtk_box_pack_start(GTK_BOX(page),frame,FALSE,FALSE,0);
/* create a vbox in the frame */
vbox = gtk_vbox_new(TRUE, PANEL_SPACING);
gtk_container_add(GTK_CONTAINER(frame), vbox);
/* num frames */
hbox = gtk_hbox_new(FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(hbox), PANEL_SPACING);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
label = gtk_label_new("Number of frames:");
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
tmp = g_strdup_printf("%9d", data->num_frames);
label = gtk_label_new(tmp);
gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
/* desired delay */
gui_direct_spin("Animation delay", &data->anim_speed,
1.0, 40.0, 1.0, NULL, NULL, vbox);
gui_direct_spin("Animation step size ", &data->anim_step,
1.0, data->num_frames, 1.0, NULL, NULL, vbox);
gui_direct_check("Don't recalculate connectivity", &data->anim_fix, NULL, NULL, vbox);
gui_direct_check("Don't recalculate scale", &data->anim_noscale, NULL, NULL, vbox);
gui_direct_check("Loop", &data->anim_loop, NULL, NULL, vbox);
/* page 2 */
page = gtk_vbox_new(FALSE, PANEL_SPACING);
label = gtk_label_new("Processing");
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
/* cycle options */
frame = gtk_frame_new(NULL);
gtk_box_pack_start(GTK_BOX(page),frame,FALSE,FALSE,0);
vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(frame), vbox);
gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING);
/* actions at start of each cycle */
new_radio_group(0, vbox, FF);
button = add_radio_button("Confine atoms to PBC", (gpointer) atom_pbc, data);
if (data->anim_confine == PBC_CONFINE_ATOMS)
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
button = add_radio_button("Confine mols to PBC", (gpointer) mol_pbc, data);
if (data->anim_confine == PBC_CONFINE_MOLS)
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
button = add_radio_button("Cell confinement off", (gpointer) no_pbc, data);
if (data->anim_confine == PBC_CONFINE_NONE)
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
/* page 3 */
page = gtk_vbox_new(FALSE, PANEL_SPACING);
label = gtk_label_new("Rendering");
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
/* cycle options */
frame = gtk_frame_new(NULL);
gtk_box_pack_start(GTK_BOX(page), frame, FALSE, FALSE, 0);
vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(frame), vbox);
gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING);
anim_box = gtk_vbox_new(TRUE, 0);
gui_direct_check("Create movie", &sysenv.render.animate,
anim_render, anim_box, vbox);
gtk_box_pack_end(GTK_BOX(vbox), anim_box, TRUE, TRUE, 0);
/* sensitive box */
vbox = gtk_vbox_new(FALSE,0);
gtk_box_pack_start(GTK_BOX(anim_box), vbox, TRUE, TRUE, 0);
gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING);
/* start off with a button */
button = gtk_radio_button_new_with_label (NULL, "Animated GIF");
g_signal_connect(GTK_OBJECT(button), "clicked",
GTK_SIGNAL_FUNC(event_render_modify), (gpointer) button);
gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, TRUE, 0);
g_object_set_data(G_OBJECT(button), "id", (gpointer) ANIM_GIF);
/* make a radio group */
group = gtk_radio_button_group(GTK_RADIO_BUTTON(button));
/* do the rest of the buttons */
button = gtk_radio_button_new_with_label(group, "MPEG");
g_signal_connect(GTK_OBJECT(button), "clicked",
GTK_SIGNAL_FUNC(event_render_modify), (gpointer) button);
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, TRUE, 0);
g_object_set_data(G_OBJECT(button), "id", (gpointer) ANIM_MPEG);
/* movie creation parameters */
gui_direct_spin("MPEG quality",
&sysenv.render.mpeg_quality, 1, 100, 1,
NULL, NULL, vbox);
gui_direct_spin("Delay (ms)",
&sysenv.render.delay, 0, 100, 5,
NULL, NULL, vbox);
/* file entry */
hbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,TRUE,0);
label = gtk_label_new("Filename ");
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
entry = gtk_entry_new();
gtk_box_pack_end(GTK_BOX (hbox), entry, FALSE, TRUE, 0);
gtk_entry_set_text(GTK_ENTRY(entry), sysenv.render.animate_file);
/* update hook */
g_signal_connect(GTK_OBJECT(entry), "changed",
GTK_SIGNAL_FUNC(event_render_modify), (gpointer) entry);
g_object_set_data(G_OBJECT(entry), "id", (gpointer) ANIM_NAME);
/* misc options */
/*
gui_direct_check("Create povray input files then stop",
&sysenv.render.no_povray_exec,
NULL, NULL, vbox);
gui_direct_check("Delete intermediate input/image files",
&sysenv.render.no_keep_tempfiles,
NULL, NULL, vbox);
*/
/* set initial state */
if (!sysenv.render.animate)
gtk_widget_set_sensitive(anim_box, FALSE);
/* NEW - slider for current frame */
frame = gtk_frame_new(NULL);
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox), frame, FALSE, FALSE, 0);
vbox = gtk_vbox_new(FALSE, PANEL_SPACING);
gtk_container_add(GTK_CONTAINER(frame), vbox);
scale = gui_direct_hscale(0, data->num_frames-1, 1, &data->cur_frame, select_frame, data, vbox);
gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_DISCONTINUOUS);
/* control buttons */
hbox2 = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox2, FALSE, FALSE, PANEL_SPACING);
hbox = gtk_hbox_new(TRUE, PANEL_SPACING);
gtk_box_pack_start(GTK_BOX(hbox2), hbox, TRUE, FALSE, 0);
gui_icon_button("GDIS_REWIND", NULL,
anim_rewind, dialog,
hbox);
gui_icon_button("GDIS_STEP_BACKWARD", NULL,
anim_step_backward, dialog,
hbox);
gui_icon_button("GDIS_PLAY", NULL,
anim_start, dialog,
hbox);
gui_icon_button("GDIS_PAUSE", NULL,
anim_stop, dialog,
hbox);
gui_icon_button("GDIS_STEP_FORWARD", NULL,
anim_step_forward, dialog,
hbox);
gui_icon_button("GDIS_FASTFORWARD", NULL,
anim_fastforward, dialog,
hbox);
gui_stock_button(GTK_STOCK_CLOSE, dialog_destroy, dialog,
GTK_DIALOG(window)->action_area);
/* display the dialog */
gtk_widget_show_all(window);
gui_relation_update(data);
}
syntax highlighted by Code2HTML, v. 0.9.1