/***************************************************************
 *
 * MODULE:       togl_flythrough.c 0.993
 *
 * AUTHOR(S):    ACS - Massimo Cuomo - m.cuomo at acsys.it
 *
 * PURPOSE:		 "Immersive" navigation by means of mouse buttons and movement
 * 				 In conjunction with flythrough.tcl
 *
 * COPYRIGHT:    (C) 2005 by the ACS / GRASS Development Team
 *
 *               This program is free software under the
 *               GNU General Public License (>=v2).
 *               Read the file COPYING that comes with GRASS
 *               for details.
 *
 * changes from 0.99
 * 09/02/05 0.991 solved drawing at each frame in do_navigation() when TOGL_FLY_ORBIT
 *					and buttonAny now works fine
 * 23/02/05 0.992 now "orbit" flythrough works also when kanimator plays
 *				  only one line in file nviz_init.c must be added
 * 					(a call to togl_flythrough_init_tcl)
 *				  Function Ndraw_all_together_cmd overrides tcl command Ndraw_all
 *				  New Tcl commands Nset/get_viewdir are defined here
 * 02/03/05 0.993 added flythrough_postdraw_cb mechanism to call registered functions
 *					after drawing (here and in quick_draw.c)
 **************************************************************/
#if 0
********************************************************************************
*** flythrough "README" ********************************************************
********************************************************************************

New (new) and modified (mod) files for flythrough implementation:

+++ visualization/nviz/scripts

(new) visualization/nviz/scripts/flythrough.tcl
	tcl part for togl_flythrough.c

(mod) visualization/nviz/scripts/panel_main.tcl
	"source" for flythrough.tcl and set UI for flythrough

(mod) visualization/nviz/scripts/panel_kanimator.tcl
	replaced get_focus with get_viewdir to work with flythrough


+++ visualization/nviz/src

(new) visualization/nviz/src/togl_flythrough.c
	all flythrough navigation
	Nset/Nget_viewdir commands
	override of command Ndraw_all (function draw_all
	in file draw.c) by function draw_all_together for drawing
		surf/vector/site in "fine" resolution
		all together with no flickering (no CLR
		between any of them)
	added semaphore "is_drawing" to avoid "clogging"
		between ordinary drawing and flythrough that
		try to draw continuously

(mod) visualization/nviz/src/quick_draw.c
	calls "flythrough_draw_cb()" after drawing, before returning
		in order to execute callbacks at the right moment
	functions that call "GS_alldraw_wire" do not execute
		"flythrough_draw_cb()"

(mod) visualization/nviz/src/nviz_init.c
	creates tcl commands and variables by calling
		togl_flythrough_init_tcl(interp, &data);
    		that also adds timer function by calling
				Togl_TimerFunc(togl_flythrough_timer_cb);
					that first time calls
						togl_flythrough_init(togl);

THIS IS THE Ninit FUNCTION WITH flythrough ADDITION
int Ninit(Tcl_Interp * interp, Tk_Window w)
{
    static Nv_data data;
    init_commands(interp, &data);
    Ninitdata(interp, &data);

/*** ACS_MODIFY flythrough  ONE LINE ******************************************/
	togl_flythrough_init_tcl(interp, &data);

    /* compile in the home directory */
    Tcl_SetVar(interp, "src_boot", getenv("GISBASE"), TCL_GLOBAL_ONLY);
    return(TCL_OK);
}

THIS IS THE END PART OF THE NUNCTION Nquick_draw_cmd WITH flythrough_postdraw_cb() ADDITION
{
	...
    GS_done_draw();
    G_free (surf_list);

/*** ACS_MODIFY flythrough  ONE LINE ******************************************/
	flythrough_postdraw_cb();

	return (TCL_OK);
}

WARNING: 	remember to add "togl_flythrough.o"
			into the visualization/nviz/src/Makefile

+++ visualization/nviz/bitmaps

(new) visualization/nviz/bitmaps/flythrough/.gif
	a_lr.gif  b_c.gif   b_lc.gif  b_lr.gif  vuota.gif
	a_ud.gif  b_cr.gif  b_l.gif   b_r.gif
		icons for flythrough help


+++ lib/ogsf

(mod) lib/ogsf/gk.c
	changed get/set_focus with get/set_viewdir for kanimator
		to work with flythrough
	corrected bug when adding point with disabled channels

(mod) lib/ogsf/GK2.c
	changed get/set_focus with get/set_viewdir for kanimator
		to work with flythrough

********************************************************************************
********************************************************************************
********************************************************************************
#endif

/* from togl_cb.c */
#include <stdlib.h>
#include <string.h>
#include "togl.h"
#include "interface.h"

#include <sys/time.h>
#ifdef __MINGW32__
#include <sys/timeb.h>
#endif
#include <math.h>
#include <grass/gis.h>

#define TOGL_FLY_OTHER	-1
#define TOGL_FLY_BASIC	0
#define TOGL_FLY_ORBIT	1
#define TOGL_FLY_SIMPLE	2
#define TOGL_FLY_MAX	2

#define QUASI_ZERO	0.0001

#define TOGL_MOVE	0
#define TOGL_TURN	1
#define TOGL_SCALE_DIM	2

extern void display_cb();

void event_proc(ClientData clientData, XEvent *eventPtr);
void togl_flythrough_timer_cb(struct Togl *togl);
void mouse_valuator (struct Togl *togl);
void do_navigation (struct Togl *togl);
double this_time(void);

static float pi, half_pi, double_pi, quasi_half_pi;

struct mouseData
{
	int	x;
	int	y;
	int button[3];
	int buttonAny;
};

struct flyData
{
	int	mode;
	float	valuator[3];
	float	scale[TOGL_SCALE_DIM];
	float	accelRate;
	float	maxSpeed;
	float	curSpeed;
	double	prevTime;
	float	dx;
	float	dy;
	float	mx;
	float	my;
	int	lateral;
	int	twist;
	float	center[3];
};

struct cbData
{
	int	coarse_draw;
	struct 	mouseData mouse;
	struct 	flyData	fly;
	Nv_data *nv_data;
	Tcl_Interp *interp;
};

/* This contains almost everything*/
static struct cbData cb_data;

/*
 PostDraw Callbacks called here and in quick_draw.c
*/
static int postdraw_count=0;
static void (*postdraw_func[256])(void*);
static void* postdraw_data[256];

void flythrough_postdraw_cb()
{
	int i;
	for(i=0; i<postdraw_count; i++)
		(*postdraw_func[i])(postdraw_data[i]);
}

void flythrough_postdraw_cb_set(void (*func)(void*), void* data)
{
	if (postdraw_count > 255) {
		printf("flythrough_postdraw_cb_set: TOO MANY CALLBACKS!\n");
		return;
	}
	postdraw_func[postdraw_count]=func;
	postdraw_data[postdraw_count]=data;
	postdraw_count++;
}

void flythrough_postdraw_cb_remove(void (*func)(void*))
{
	int i;
	for(i=0; i<postdraw_count && postdraw_func[i] != func; i++);
	for(i++; i<postdraw_count; i++) {
		postdraw_func[i-1] = postdraw_func[i];
		postdraw_data[i-1] = postdraw_data[i];
	}
	--postdraw_count;
}


/*
 New tcl commands for changing navigations mode and coarse/fine draw styles
*/
int Nset_fly_mode_cmd(Nv_data * data, Tcl_Interp * interp, int argc, char **argv)
{
int mode;
    if (argc != 2) 
	    return(TCL_ERROR);

    mode = atoi(argv[1]);

	if (mode != TOGL_FLY_SIMPLE) GS_set_twist(0);

	if (mode < 0 || mode > TOGL_FLY_MAX) {
		cb_data.fly.mode = TOGL_FLY_OTHER;
		Tcl_Eval(interp, "fly_deselect");
	} else {
		cb_data.fly.mode = mode;
		Tcl_Eval(interp, "fly_select");
	}

	return TCL_OK;
}

int Nset_fly_scale_cmd(Nv_data * data, Tcl_Interp * interp, int argc, char **argv)
{
	if (argc != 3) return (TCL_ERROR);

	cb_data.fly.scale[TOGL_MOVE]=atof(argv[1]);
	cb_data.fly.scale[TOGL_TURN]=atof(argv[2]);
	return TCL_OK;
}

int Nget_fly_scale_cmd(Nv_data * data, Tcl_Interp * interp, int argc, char **argv)
{
    char *list[2];
    char scale0[32], scale1[32];

    if (argc != 1) return (TCL_ERROR);

	sprintf (scale0, "%f" , cb_data.fly.scale[TOGL_MOVE]);
	sprintf (scale1, "%f" , cb_data.fly.scale[TOGL_TURN]);

    list[0] = scale0; list[1] = scale1;

    interp->result = Tcl_Merge(2, list);
    interp->freeProc = TCL_DYNAMIC;
    return (TCL_OK);
}

/*
 Init function: called first time by togl_flythrough_timer_cb
 (needs togl to be alredy created)
*/
void togl_flythrough_init(struct Togl *togl)
{
	float n, s, w, e;

/* Useful constants */
half_pi	= 2.0f * atan(1.0);
pi = 2.0f * half_pi;
double_pi = 2.0f * pi;
quasi_half_pi = half_pi - 0.001f;

	cb_data.coarse_draw = TRUE;

	/* Navigation parameters, can be read from a resource file */
	cb_data.fly.mode=TOGL_FLY_OTHER;
	cb_data.fly.accelRate = 1.0f;
	cb_data.fly.maxSpeed = 5.0f;

	cb_data.fly.curSpeed = 0.0f;
	cb_data.fly.prevTime = this_time();

	cb_data.fly.dx = 0.01f;		/* dead zone */
	cb_data.fly.dy = 0.01f;		/* dead zone */

	cb_data.fly.scale[TOGL_MOVE]=1.0f;
	cb_data.fly.scale[TOGL_TURN]=1.0f;

	GS_get_region(&n, &s, &w, &e);
	cb_data.fly.center[0] = (e + w)/2.0;
	cb_data.fly.center[1] = (n + s)/2.0;
	cb_data.fly.center[2] = 0.0;
	gsd_real2model(cb_data.fly.center);

	/* Data to be passed among callbacks */
	Togl_SetClientData( togl, (ClientData)(&cb_data) );

	/* Private event handler function */
	Tk_CreateEventHandler(Togl_TkWin( togl ),
			ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
            event_proc,
            (ClientData)(&cb_data));
}


/* Creates tcl commands and variables
 Called by Ninit() in file "nviz_init.c"
*/
void togl_flythrough_init_tcl(Tcl_Interp * interp, Nv_data * data)
{
	/* Added Togl_Timer_Function: be sure others don't replace it with another one*/
    Togl_TimerFunc(togl_flythrough_timer_cb);

	cb_data.nv_data = data;
	cb_data.interp = interp;

	Tcl_CreateCommand(interp, "Nset_fly_scale", (Tcl_CmdProc*)Nset_fly_scale_cmd, data, NULL);
	Tcl_CreateCommand(interp, "Nget_fly_scale", (Tcl_CmdProc*)Nget_fly_scale_cmd, data, NULL);

	Tcl_CreateCommand(interp, "Nset_fly_mode", (Tcl_CmdProc*)Nset_fly_mode_cmd, data, NULL);

	Tcl_LinkVar(interp, "coarse_draw", (char*)&(cb_data.coarse_draw), TCL_LINK_BOOLEAN);

	Tcl_CreateCommand(interp, "Nget_viewdir", (Tcl_CmdProc*)Nget_viewdir_cmd, data, NULL);
	Tcl_CreateCommand(interp, "Nset_viewdir", (Tcl_CmdProc*)Nset_viewdir_cmd, data, NULL);

	/* Override Ndraw_all_cmd */
	/* declared in init_commands 
    Tcl_CreateCommand(interp, "Ndraw_all", Ndraw_all_together_cmd, data, NULL);
    */
/*	Tcl_CreateCommand(interp, "Ndraw_all_together", (Tcl_CmdProc*)Ndraw_all_together_cmd, data, NULL);
*/
}


/* Callback registered by NVIZ_AppInit() in file "nvizAppInit"
*/
void togl_flythrough_timer_cb(struct Togl *togl)
{
	/* it's here in order to avoid to modify other files to call togl_flythrough_init() */
	static int first_time=1;
	struct cbData *cb = (struct cbData *)Togl_GetClientData( togl );
	
	
	if (first_time) {
		first_time=0;
		togl_flythrough_init(togl);
		return;
	}

	if (cb->fly.mode != TOGL_FLY_OTHER) {
		mouse_valuator(togl);
		do_navigation (togl);
	}
}



/* INTERNAL FUNCTIONS */

double this_time(void)
{
#ifdef __MINGW32__
	struct timeb tb;

	ftime(&tb);
	return( (float)tb.time + ((float)tb.millitm/1000.0) );
#else
	struct timeval tv;

	gettimeofday(&tv, NULL);
	return( (float)tv.tv_sec + ((float)tv.tv_usec/1000000.0) );
#endif
}

void event_proc(ClientData clientData, XEvent *eventPtr)
{
   struct cbData *cb = (struct cbData *)clientData;

   switch (eventPtr->type) {
      case MotionNotify:
		cb->mouse.x=eventPtr->xbutton.x;
		cb->mouse.y=eventPtr->xbutton.y;
        break;

      case ButtonPress:
	  	if (eventPtr->xbutton.button < 4) {
			cb->mouse.button[eventPtr->xbutton.button-1]=1;
		}
		cb->mouse.x=eventPtr->xbutton.x;
		cb->mouse.y=eventPtr->xbutton.y;
        break;

	  case ButtonRelease:
	  	if (eventPtr->xbutton.button < 4) {
			cb->mouse.button[eventPtr->xbutton.button-1]=0;
		}
		cb->mouse.x=eventPtr->xbutton.x;
		cb->mouse.y=eventPtr->xbutton.y;
        break;

	  default:
        break;
	}
	cb->mouse.buttonAny=cb->mouse.button[0] || cb->mouse.button[1] || cb->mouse.button[2];

}

void calc_mxmy(struct Togl *togl)
{
    float mx, my;
    struct cbData *cb = (struct cbData *) Togl_GetClientData( togl );

	mx = 2.0f * ((float)cb->mouse.x / (float)Togl_Width(togl)) - 1.0f;
	my = 2.0f * ((float)cb->mouse.y / (float)Togl_Height(togl)) - 1.0f;

	if(mx < -cb->fly.dx) mx+=cb->fly.dx; else if(mx > cb->fly.dx) mx-=cb->fly.dx; else mx=0.0f;
	if(my < -cb->fly.dy) my+=cb->fly.dy; else if(my > cb->fly.dy) my-=cb->fly.dy; else my=0.0f;

	cb->fly.mx = mx / (1.0f - cb->fly.dx);
	cb->fly.my = my / (1.0f - cb->fly.dy);

	/* Quadratic seems smoother */
	cb->fly.mx = cb->fly.mx * fabsf(cb->fly.mx);
	cb->fly.my = cb->fly.my * fabsf(cb->fly.my);
}


void mouse_valuator (struct Togl *togl)
{
	double thisTime;
	double deltaTime;
	struct cbData *cb = (struct cbData *) Togl_GetClientData( togl );

	cb->fly.valuator[0] = 0.0; cb->fly.valuator[1] = 0.0; cb->fly.valuator[2] = 0.0;
	cb->fly.lateral=0;
	cb->fly.twist=0;

	thisTime = this_time();
	deltaTime = thisTime - cb->fly.prevTime;
    	cb->fly.prevTime = thisTime;

    switch (cb->fly.mode) {

    case TOGL_FLY_BASIC:
    case TOGL_FLY_ORBIT:

		if (cb->mouse.button[1]) {
			if (cb->mouse.button[0]) { /* accelerate up to maxSpeed */
				cb->fly.curSpeed += cb->fly.accelRate * deltaTime;
				if (cb->fly.curSpeed > cb->fly.maxSpeed) cb->fly.curSpeed = cb->fly.maxSpeed;
			}
			else if (cb->mouse.button[2]) {  /* decelerate down to -maxSpeed */
				cb->fly.curSpeed -= cb->fly.accelRate * deltaTime;
				if (cb->fly.curSpeed < -cb->fly.maxSpeed) cb->fly.curSpeed = -cb->fly.maxSpeed;
			}
			calc_mxmy(togl);

			cb->fly.valuator[0] = cb->fly.curSpeed; /* speed (forward) */
			cb->fly.valuator[1] = cb->fly.mx * deltaTime; /* heading */
			cb->fly.valuator[2] = cb->fly.my * deltaTime; /* picth */
			return;
		}

		else if (cb->mouse.button[0] && cb->mouse.button[2]) {
			cb->fly.lateral=1;
			cb->fly.curSpeed=0.0;

			calc_mxmy(togl);

			cb->fly.valuator[0] = cb->fly.mx * 100.0 * deltaTime; /* lateral */
			cb->fly.valuator[2] = - cb->fly.my * 100.0 * deltaTime; /* vertical */
			return;
		}

		else {
			cb->fly.curSpeed=0.0;
			return;
		}
		break;

    case TOGL_FLY_SIMPLE:
		if (cb->mouse.button[0] || cb->mouse.button[2])	calc_mxmy(togl);
		else return;

		if (cb->mouse.button[0] && !cb->mouse.button[2]) {
			/* only left button */
			cb->fly.valuator[0] = -cb->fly.my * 100.0 * deltaTime; /* forward */
			cb->fly.valuator[1] = cb->fly.mx * deltaTime; /* heading */
			return;
		}

		if (!cb->mouse.button[0] && cb->mouse.button[2]) {
			/* only right button */
			cb->fly.lateral=1;
			cb->fly.valuator[0] = cb->fly.mx * 100.0 * deltaTime; /* lateral */
			cb->fly.valuator[2] = - cb->fly.my * 100.0 * deltaTime; /* vertical */
			return;
		}

		if (cb->mouse.button[0] && cb->mouse.button[2]) {
			/* rigth and left buttons */
			cb->fly.twist=1;
			cb->fly.valuator[1] = cb->fly.mx * deltaTime; /* roll */
			cb->fly.valuator[2] = cb->fly.my * deltaTime; /* pitch */
			return;
		}
		break;
	}
}



void do_navigation (struct Togl *togl)
{
   float dir[3], from[4], cur_from[4], cur_dir[4], cur[3];
   float speed, h, p, sh, ch, sp, cp, radius;
   float diff_x, diff_y, diff_z;
   int twist, cur_twist;

	struct cbData *cb = (struct cbData *) Togl_GetClientData( togl );
	struct flyData *fly = &(cb->fly);

	static int draw_all = 0;

	if (!cb->mouse.buttonAny) {
		if (draw_all == 1) {
			draw_all = 0;
/*			Tcl_SetVar(cb->interp, "autoc", "1", TCL_LEAVE_ERR_MSG);
			Ndraw_all_cmd(cb->nv_data, cb->interp, NULL, NULL);
*/
			Ndraw_all_together_cmd(cb->nv_data, cb->interp, 1, NULL);
		}
		return;
	}

	GS_get_from(cur_from);
	GS_get_viewdir(cur_dir);
	twist = cur_twist = GS_get_twist();

	p=asin(cur_dir[Z]);
	h=atan2(-cur_dir[X], -cur_dir[Y]);

	speed = fly->scale[TOGL_MOVE] * fly->valuator[0];

	if (!fly->twist) /* in case of "twist" doesn't change heading */
		h += fly->scale[TOGL_TURN] * fly->valuator[1];

	if (!fly->lateral) /* in case of "lateral" doesn't change pitch */
		p -= fly->scale[TOGL_TURN] * fly->valuator[2];

	h = fmod (h + pi, double_pi) - pi;

	if (p < -quasi_half_pi) p = -quasi_half_pi;	/* Internal flythrough tcl callbacks */
	else if (p > quasi_half_pi) p = quasi_half_pi;

	sh = sin(h);	ch = cos(h);
	sp = sin(p);	cp = cos(p);

	dir[X] = - sh * cp;
	dir[Y] = - ch * cp;
	dir[Z] = sp;

    switch (fly->mode) {
		case TOGL_FLY_BASIC:
		case TOGL_FLY_SIMPLE:
			if (fly->lateral) {
				from[X] = cur_from[X] + speed * dir[Y];
				from[Y] = cur_from[Y] - speed * dir[X];
				from[Z] = cur_from[Z] + fly->scale[TOGL_MOVE] * fly->valuator[2];
			} else {
				from[X] = cur_from[X] + speed * dir[X];
				from[Y] = cur_from[Y] + speed * dir[Y];
				from[Z] = cur_from[Z] + speed * dir[Z];
			}

			if (fly->twist) {
				twist = cur_twist + (int)((fly->scale[TOGL_TURN] * fly->valuator[1] * 1800.0) / pi);
				if(twist > 890)twist=890; else if(twist<-890)twist=-890;
			}

		break;
		case TOGL_FLY_ORBIT:
			cur[X] = cur_from[X] - fly->center[X];
			cur[Y] = cur_from[Y] - fly->center[Y];
			cur[Z] = cur_from[Z] - fly->center[Z];

			GS_v3mag(cur, &radius);
			radius -= speed;
			if (radius < 0.0f) radius = 0.0f;

			from[X] = - radius * dir[X] + fly->center[X];
			from[Y] = - radius * dir[Y] + fly->center[Y];
			from[Z] = - radius * dir[Z] + fly->center[Z];
		break;
    }

	diff_x = fabs(cur_dir[X] - dir[X]);
	diff_y = fabs(cur_dir[Y] - dir[Y]);
	diff_z = fabs(cur_dir[Z] - dir[Z]);

	if ( /* something has changed */
		(diff_x > QUASI_ZERO)	||	(diff_y > QUASI_ZERO) 	|| (diff_z > QUASI_ZERO) ||
		(cur_from[X] != from[X])||	(cur_from[Y] != from[Y])|| (cur_from[Z] != from[Z]) ||
		(cur_twist != twist)
	) {
		GS_moveto(from);

		/* Accomodates for up vector "jumps" when pitch changes sign */
		if (p > 0.0f) 	GS_set_twist(twist + 1800);
		else 			GS_set_twist(twist);

		GS_set_viewdir(dir);		/* calls gsd_set_view */

	    GS_set_draw(GSD_BACK); /* needs to be BACK to avoid flickering */
		GS_clear(cb->nv_data->BGcolor);

		if (cb->coarse_draw) {
			GS_set_draw(GSD_BACK);
			GS_set_draw(GSD_BACK);
			GS_ready_draw();
			GS_alldraw_wire();
			GS_done_draw();
			/* display_cb (togl); */
			flythrough_postdraw_cb();
			draw_all = 1;
		}
		else {
			/* Draws without clearng buffer at each map type */
			Ndraw_all_together_cmd(cb->nv_data, cb->interp, 1, NULL);
			draw_all = 0;
		}
		/* prepare twist for next frame GS_get_twist() call */
		if (p > 0.0f) 	GS_set_twist(twist);
	}
}

/*******************************************************************************
	Ndraw_all override Tcl Command
*******************************************************************************/
int surf_draw_all_together(Nv_data * dc, Tcl_Interp * interp)
{
    int i, nsurfs;
    int sortSurfs[MAX_SURFS], sorti[MAX_SURFS];
    int *surf_list;
    float x, y, z;
    int num, w;

/* Get position for Light 1 */
    num = 1;
    x = dc->light[num].x;
    y = dc->light[num].y;
    z = dc->light[num].z;
    w = dc->light[num].z;

	surf_list = GS_get_surf_list(&nsurfs);
    sort_surfs_max(surf_list, sortSurfs, sorti, nsurfs);
    G_free (surf_list);

/* re-initialize lights */
    GS_setlight_position(num, x, y, z, w);
    num = 2;
    GS_setlight_position(num, 0., 0., 1., 0);

    for (i = 0; i < nsurfs; i++) {
	if (check_blank(interp, sortSurfs[i]) == 0) {
	    GS_draw_surf(sortSurfs[i]);
	}
    }

    /* GS_draw_cplane_fence params will change - surfs aren't used anymore */
    for (i = 0; i < MAX_CPLANES; i++) {
	if (dc->Cp_on[i])
	    GS_draw_cplane_fence(sortSurfs[0], sortSurfs[1], i);
    }
    return (TCL_OK);
}

int vect_draw_all_together(Nv_data * data, Tcl_Interp * interp)
{
    int i, nvects;
    int *vect_list;

    GS_set_cancel(0);
    vect_list = GV_get_vect_list(&nvects);

    for (i = 0; i < nvects; i++) {
	if (check_blank(interp, vect_list[i]) == 0) {
	    GV_draw_vect(vect_list[i]);
	}
    }
    G_free (vect_list);

    GS_set_cancel(0);
    return (TCL_OK);
}

int site_draw_all_together(Nv_data * data, Tcl_Interp * interp)
{
    int i, nsites;
    int *site_list;

    GS_set_cancel(0);

    site_list = GP_get_site_list(&nsites);

    for (i = 0; i < nsites; i++) {
	if (check_blank(interp, site_list[i]) == 0) {
	    GP_draw_site(site_list[i]);
	}
    }
    G_free (site_list);

    GS_set_cancel(0);
    return (TCL_OK);
}

int vol_draw_all_cmd(Nv_data * data, Tcl_Interp * interp,	/* Current interpreter. */
		       int argc,	/* Number of arguments. */
		       char **argv	/* Argument strings. */
    )
{
    int i, nvols;
    int *vol_list;

	GS_set_cancel(0);
    vol_list = GVL_get_vol_list(&nvols);

    for (i = 0; i < nvols; i++) {
	if (check_blank(interp, vol_list[i]) == 0) {
	    GVL_draw_vol(vol_list[i]);
	}
    }
    G_free (vol_list);


    GS_set_cancel(0);
    return (TCL_OK);
}

int Ndraw_all_together_cmd(Nv_data * data, Tcl_Interp * interp,	/* Current interpreter. */
		       int argc,	/* Number of arguments. */
		       char **argv	/* Argument strings. */
    )
{
	const char *buf_surf, *buf_vect, *buf_site, *buf_vol;
	const char *buf_north_arrow, *arrow_x, *buf_label, *buf_legend;
	const char *buf_fringe;
	const char* buf_is_drawing = Tcl_GetVar(interp, "is_drawing", TCL_GLOBAL_ONLY);
	const char* EMPTYSTRING="";
	if (buf_is_drawing && atoi(buf_is_drawing))
		return (TCL_OK);

	Tcl_SetVar(interp, "is_drawing", "1", TCL_GLOBAL_ONLY);

	GS_set_draw(GSD_BACK); /* needs to be BACK to avoid flickering */
	GS_clear(data->BGcolor);
	GS_ready_draw();

	buf_surf = Tcl_GetVar(interp, "surface", TCL_GLOBAL_ONLY);
	buf_vect = Tcl_GetVar(interp, "vector", TCL_GLOBAL_ONLY);
	buf_site = Tcl_GetVar(interp, "sites", TCL_GLOBAL_ONLY);
	buf_vol = Tcl_GetVar(interp, "volume", TCL_GLOBAL_ONLY);
	buf_north_arrow = Tcl_GetVar(interp, "n_arrow", TCL_GLOBAL_ONLY);
        arrow_x = Tcl_GetVar(interp, "n_arrow_x", TCL_GLOBAL_ONLY);
        buf_label = Tcl_GetVar(interp, "labels", TCL_GLOBAL_ONLY);
        buf_legend = Tcl_GetVar(interp, "legend", TCL_GLOBAL_ONLY);
        buf_fringe = Tcl_GetVar(interp, "fringe", TCL_GLOBAL_ONLY);

	if (buf_surf && atoi(buf_surf) == 1)
		surf_draw_all_together(data, interp);
	if (buf_vect && atoi(buf_vect) == 1)
		vect_draw_all_together(data, interp);
	if (buf_site && atoi(buf_site) == 1)
		site_draw_all_together(data, interp);
	if (buf_vol  &&atoi(buf_vol) == 1)
	    	vol_draw_all_cmd(data, interp, argc, argv);
	
	GS_done_draw();
        GS_set_draw(GSD_BACK);

        if ( buf_north_arrow == NULL ) {
	 buf_north_arrow = EMPTYSTRING; 
	}
        if ( arrow_x == NULL ) {
	 arrow_x = EMPTYSTRING; 
	}
        if ( buf_fringe == NULL ) {
	 buf_fringe = EMPTYSTRING; 
	}
        if ( buf_label == NULL ) {
	 buf_label = EMPTYSTRING; 
	}
        if ( buf_legend == NULL ) {
	 buf_legend = EMPTYSTRING; 
	}
        
        /* Draw decorations */
        
	/* North Arrow */
        if (atoi(buf_north_arrow) == 1 && atoi(arrow_x) != 999 ) {
                const char *arrow_y, *arrow_z, *arrow_len;
                float coords[3], len;

                arrow_y = Tcl_GetVar(interp, "n_arrow_y", TCL_GLOBAL_ONLY);
                arrow_z = Tcl_GetVar(interp, "n_arrow_z", TCL_GLOBAL_ONLY);
                arrow_len = Tcl_GetVar(interp, "n_arrow_size", TCL_GLOBAL_ONLY);
                coords[0] = atoi(arrow_x);
                coords[1] = atoi(arrow_y);
                coords[2] = atoi(arrow_z);
                len = atof(arrow_len);

                FontBase = load_font(TOGL_BITMAP_HELVETICA_18);
                gsd_north_arrow(coords, len, FontBase);
        }
        
        /* fringe */
        if (atoi(buf_fringe) == 1) {
                const char *fringe_ne, *fringe_nw, *fringe_se, *fringe_sw;
                const char *surf_id;
                int flags[4], id;

                fringe_ne = Tcl_GetVar(interp, "fringe_ne", TCL_GLOBAL_ONLY);
                fringe_nw = Tcl_GetVar(interp, "fringe_nw", TCL_GLOBAL_ONLY);
                fringe_se = Tcl_GetVar(interp, "fringe_se", TCL_GLOBAL_ONLY);
                fringe_sw = Tcl_GetVar(interp, "fringe_sw", TCL_GLOBAL_ONLY);
                flags[0] = atoi(fringe_nw);
                flags[1] = atoi(fringe_ne);
                flags[2] = atoi(fringe_sw);
                flags[3] = atoi(fringe_se);
                surf_id = Tcl_GetVar2(interp, "Nv_", "CurrSurf", TCL_GLOBAL_ONLY);
                id = atoi(surf_id);

                GS_draw_fringe(id, flags);
        }
        
        /* Legend and/or labels */
        if (atoi(buf_label) == 1 || atoi(buf_legend) == 1)
                        GS_draw_all_list();

        Tcl_SetVar(interp, "is_drawing", "0", TCL_GLOBAL_ONLY);
        flythrough_postdraw_cb();

        return (TCL_OK);
}


/*******************************************************************************
	Nset/Nget_viewdir Tcl Commands
*******************************************************************************/
int Nset_viewdir_cmd(Nv_data * data, Tcl_Interp * interp, int argc, char **argv)
{
    float dir[3];
	if (argc != 4) return (TCL_ERROR);

	dir[0]=atof(argv[1]);
	dir[1]=atof(argv[2]);
	dir[2]=atof(argv[3]);

	GS_set_viewdir(dir);

	return TCL_OK;
}


int Nget_viewdir_cmd(Nv_data * data, Tcl_Interp * interp,	/* Current interpreter. */
		   int argc,	/* Number of arguments. */
		   char **argv	/* Argument strings. */
    )
{
    float dir[3];
    char x[32], y[32], z[32];
    char *list[4];

    GS_get_viewdir(dir);
	sprintf(x, "%f", dir[0]);
	sprintf(y, "%f", dir[1]);
	sprintf(z, "%f", dir[2]);

	list[0] = x;
	list[1] = y;
	list[2] = z;
	list[3] = NULL;

	Tcl_SetResult(interp, Tcl_Merge(3, list), TCL_VOLATILE);

    return (TCL_OK);
}


