/*------------------------------------------------------------------------*
  Filename....: frame_otyp.c


		 COPYRIGHT (C) 2001.  All Rights Reserved.
		 auto*des*sys, Inc., Columbus, Ohio, USA.
 *------------------------------------------------------------------------*/

#include "frame.h"



/************************************************************************
	Frame otype uuid callback function
*************************************************************************/
static fzrt_error_td frame_otyp_uuid(
	fzrt_UUID_td			uuid 		
	)
{

	plugin_stuff.fzrt_base_funcs.fzrt_UUID_copy( FRAME_OTYP_ID, uuid );
	return(FZRT_NOERR);
}

/************************************************************************
	Frame otype info callback function
*************************************************************************/
static fzrt_error_td frame_otyp_info(
	fzrt_int					*size,
	fzrt_int					*flags
	)
{

	*size  = sizeof(frame_otyp_parms_td);
	*flags = 0;
	FZ_SETBIT(*flags,FZ_OTYP_HANDLE_RVRS);
	return(FZRT_NOERR);
}

/************************************************************************
	Frame init callback function
*************************************************************************/
static fzrt_error_td frame_otyp_init(
	fzrt_int			windex,
	fz_objt_ptr		objt,
	fzrt_ptr		parm
	)
{
	frame_otyp_parms_td		*frame;
	fzrt_error_td			err = FZRT_NOERR;
	
	if(parm != NULL)
	{
		frame = (frame_otyp_parms_td *)parm;
		frame->radius    	= _frame_tool_opts ? _frame_tool_opts->radius : 10.0;
		frame->base_obj 	= NULL;
	}

	return(err);
}

/************************************************************************
	Frame finit callback function
*************************************************************************/
static fzrt_error_td frame_otyp_finit (
	fzrt_int			windex,
	fz_objt_ptr		objt,
	fzrt_ptr		parm
	)
{
	frame_otyp_parms_td		*frame;
	frame = (frame_otyp_parms_td *)parm;

	if ( frame->base_obj ) plugin_stuff.model_funcs.fz_objt_indp_finit(windex,&frame->base_obj);

	return(FZRT_NOERR);
}

/************************************************************************
	Frame name callback function
*************************************************************************/
static fzrt_error_td frame_otyp_name(
	fzrt_int			windex,		
	fz_objt_ptr		objt,
	fzrt_ptr		parm,		
	char			*str,
	fzrt_int			max_str
	)
{
	if(str) 
	{	plugin_stuff.fzrt_base_funcs.fzrt_fzr_get_string(plugin_stuff.fz_rsrc_ref_func, FRAME_STR_ID, FRAME_STR_OTYP_NAME, str);
	}
	return(FZRT_NOERR);
}

/************************************************************************
	Frame transform callback function
*************************************************************************/
static fzrt_error_td frame_otyp_tform(
	fzrt_int			windex,
	fz_objt_ptr		objt,
	fzrt_ptr		parm,
	fz_mat4x4_td	*tform
	)
{
	frame_otyp_parms_td			*frame;
	fz_xyz_td						scl;
	fzrt_error_td				rv = FZRT_NOERR;
	
	if(parm != NULL)
	{
		frame = (frame_otyp_parms_td *)parm;

		if ( frame->base_obj != NULL ) 
		{	plugin_stuff.model_funcs.fz_objt_edit_transform_objt(windex,frame->base_obj,tform);
		}
		
		plugin_stuff.math_funcs.fz_math_4x4_mat_to_trl_scl_rot(tform,NULL,&scl,NULL);
		frame->radius *= (scl.x + scl.y + scl.z) / 3.0;
	}
	
	return(rv);
}

/************************************************************************
	Check whether the given radius allows the generation of the frame.
	Optionally, return the maximum allowable radius (smallest segment 
	length in the base object)
*************************************************************************/
fzrt_boolean frame_otyp_check_radius(
	fzrt_int					windex,
	fz_objt_ptr				base_obj,
	double					radius,
	double					*max_radius
	)
{
	fzrt_int			i,nsegt,pindx1,pindx2;
	fz_xyz_td		pnt1,pnt2;
	double			diam_square,max_diam_square,dist;
	fzrt_boolean	rv = TRUE;
	
	diam_square = (radius * 2.0) * (radius * 2.0);
	max_diam_square = DBL_MAX;
	
	plugin_stuff.model_funcs.fz_objt_get_segt_count(windex,base_obj,FZ_OBJT_MODEL_TYPE_UNSPEC,&nsegt);
	for(i = 0; i < nsegt; i++)
	{	
		plugin_stuff.model_funcs.fz_objt_segt_get_start_pindx(windex,base_obj,i,FZ_OBJT_MODEL_TYPE_UNSPEC,&pindx1);
		plugin_stuff.model_funcs.fz_objt_segt_get_end_pindx(windex,base_obj,i,FZ_OBJT_MODEL_TYPE_UNSPEC,&pindx2);
		if ( pindx2 == -1 ) continue;	/* IGNORE DUMMY SEGMENT AT END OF OPEN WIRES */

		plugin_stuff.model_funcs.fz_objt_point_get_xyz(windex,base_obj,pindx1,FZ_OBJT_MODEL_TYPE_UNSPEC,&pnt1);
		plugin_stuff.model_funcs.fz_objt_point_get_xyz(windex,base_obj,pindx2,FZ_OBJT_MODEL_TYPE_UNSPEC,&pnt2);
		
		dist = FZ_MATH_3D_DIST_PT_PT_SQUARED(&pnt1,&pnt2);
		if ( dist < diam_square ) 
		{	rv = FALSE;
			if ( max_radius == NULL ) break;
		}
		
		if ( max_radius != NULL )
		{	if ( dist < max_diam_square ) max_diam_square = dist;
		}
	}

	if ( max_radius ) *max_radius = sqrt(max_diam_square) * 0.5;

	return(rv);
}

/************************************************************************
	Frame regen callback function
*************************************************************************/
static fzrt_error_td frame_otyp_regen(
	fzrt_int				windex,
	fz_objt_ptr			obj,	
	fzrt_ptr			parm	
	)
{
	fzrt_error_td			rv = FZRT_NOERR;
	fz_xyz_td				rxyz,pnt1,pnt2,vec;
	fz_mat4x4_td			mat,mat2;
	fz_map_plane_td			map_plane;
	fz_plane_equ_td			equc;
	fzrt_int					i,n,ncord,nsegt,pindx1,pindx2,rindx,nobjs;
	fz_objt_ptr				*temp_objs,obj1,obj2;
	fz_enty_list_ptr 		ent_list;
	fz_attr_refn_ptr		refn;
	
	frame_otyp_parms_td		*frame;

	if(parm != NULL)
	{
		ent_list	= NULL;
		refn		= NULL;
		temp_objs	= NULL;
		nobjs 		= 0;
		
		/* CREATE A MINIMAL REFINEMENT */
		plugin_stuff.attr_funcs.fz_objt_attr_refn_init(windex,FZ_ATTR_REFN_INIT_MINIMAL,&refn);
		
		frame = (frame_otyp_parms_td *)parm;
		
		plugin_stuff.model_funcs.fz_objt_get_point_count(windex,frame->base_obj,FZ_OBJT_MODEL_TYPE_UNSPEC,&ncord);
		plugin_stuff.model_funcs.fz_objt_get_segt_count(windex,frame->base_obj,FZ_OBJT_MODEL_TYPE_UNSPEC,&nsegt);

	
		/* CHECK FOR VALID RADIUS */
		if ( frame_otyp_check_radius(windex,frame->base_obj,frame->radius,NULL) == TRUE )
		{
			rxyz.x = frame->radius;
			rxyz.y = frame->radius;
			rxyz.z = frame->radius;
			
			n = ncord + nsegt;
			
			plugin_stuff.fuim_funcs.fz_fuim_curs_wait(FZ_FUIM_CURS_WAIT_START);	/* INIT THE CLOCK CURSOR */
			
			if (plugin_stuff.fzrt_mem_funcs.fzrt_new_ptr(sizeof(fz_objt_ptr) * n, (fzrt_ptr*)&temp_objs) == FZRT_NOERR &&
				temp_objs != NULL)
			{
				for(i = 0; i < n; i++) temp_objs[i] = NULL;
				obj1 = NULL;
				obj2 = NULL;
				
				
				plugin_stuff.model_funcs.fz_objt_list_create(&ent_list);
				
				/* CREATE SPHERICAL JOINTS */
				for(i = 0; i < ncord; i++)
				{	plugin_stuff.model_funcs.fz_objt_point_get_xyz(windex,frame->base_obj,i,FZ_OBJT_MODEL_TYPE_UNSPEC,&pnt1);
					if((rv = plugin_stuff.model_funcs.fz_objt_cnstr_sphr(windex,&rxyz,&pnt1,NULL,NULL,refn,&temp_objs[nobjs++])) != FZRT_NOERR ) break;
					plugin_stuff.fuim_funcs.fz_fuim_curs_wait(FZ_FUIM_CURS_WAIT_TURN);
					
					/* CHECK FOR USER CANCEL */
					if ( plugin_stuff.fuim_funcs.fz_fuim_key_cancel())
					{	rv = -1;
						break;
					}
				}
				
				/* CREATE CYLINDRICAL POLES */
				if ( rv == FZRT_NOERR )
				{
					for(i = 0; i < nsegt; i++)
					{	
						plugin_stuff.model_funcs.fz_objt_segt_get_reverse(windex,frame->base_obj,i,FZ_OBJT_MODEL_TYPE_UNSPEC,&rindx);
						if ( rindx > i ) continue;
						plugin_stuff.model_funcs.fz_objt_segt_get_next(windex,frame->base_obj,i,FZ_OBJT_MODEL_TYPE_UNSPEC,&rindx);
						if ( rindx == -1 ) continue;
						
						plugin_stuff.fuim_funcs.fz_fuim_curs_wait(FZ_FUIM_CURS_WAIT_TURN);
						
						plugin_stuff.model_funcs.fz_objt_segt_get_start_pindx(windex,frame->base_obj,i,FZ_OBJT_MODEL_TYPE_UNSPEC,&pindx1);
						plugin_stuff.model_funcs.fz_objt_segt_get_end_pindx(windex,frame->base_obj,i,FZ_OBJT_MODEL_TYPE_UNSPEC,&pindx2);
						plugin_stuff.model_funcs.fz_objt_point_get_xyz(windex,frame->base_obj,pindx1,FZ_OBJT_MODEL_TYPE_UNSPEC,&pnt1);
						plugin_stuff.model_funcs.fz_objt_point_get_xyz(windex,frame->base_obj,pindx2,FZ_OBJT_MODEL_TYPE_UNSPEC,&pnt2);
						
						if((rv = plugin_stuff.math_funcs.fz_math_3d_create_unit_vec(&pnt1,&pnt2,&vec)) == FZRT_NOERR )
						{
							/* CREATE THE CYLINDER */
							rxyz.x = frame->radius;
							rxyz.y = frame->radius;
							rxyz.z = FZ_MATH_3D_DIST_PT_PT(&pnt1,&pnt2);
							if((rv = plugin_stuff.model_funcs.fz_objt_cnstr_cyln(windex,&rxyz,NULL,NULL,NULL,refn,&obj2)) != FZRT_NOERR ) break;

							temp_objs[nobjs++] = obj2;

							/* ROTATE CYLINDER TO ALIGN WITH SEGMENT */
							equc.a = vec.x;
							equc.b = vec.y;
							equc.c = vec.z;
							equc.d = 0.0;
							plugin_stuff.math_funcs.fz_math_3d_map_plane_from_equc(&equc,&map_plane);
							plugin_stuff.math_funcs.fz_math_3d_map_plane_get_matrix(&map_plane,&mat);
							plugin_stuff.math_funcs.fz_math_4x4_set_trl_mat(&mat2,&pnt1);
							plugin_stuff.math_funcs.fz_math_4x4_multiply_mat_mat(&mat,&mat2,&mat);
							plugin_stuff.model_funcs.fz_objt_edit_transform_objt(windex,obj2,&mat);
						}
					
						/* CHECK FOR USER CANCEL */
						if ( plugin_stuff.fuim_funcs.fz_fuim_key_cancel())
						{	rv = -1;
							break;
						}
					}

					/* BOOLEAN EVERYTHING TOGETHER */
					if ( rv == FZRT_NOERR )
					{	if((rv = plugin_stuff.model_funcs.fz_objt_edit_bool_union_multi(windex,temp_objs,nobjs,TRUE,ent_list)) == FZRT_NOERR )
						{	plugin_stuff.model_funcs.fz_objt_list_count(ent_list, &n);
							if ( n != 1 )
							{	rv = plugin_stuff.fzrt_base_funcs.fzrt_error_set(FRAME_STR_EXEC_ERR, FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_PLUGIN, plugin_stuff.frame_otyp_plugin_runtime_id );
							}
							plugin_stuff.model_funcs.fz_objt_list_get_objt(ent_list,0,&obj1);
						}
					}

					/* DELETE ORIGINAL PIPES AND POLES */
					for(i = 0; i < nobjs; i++)
					{	if (temp_objs[i]) 
						{	plugin_stuff.model_funcs.fz_objt_edit_delete_objt(windex,temp_objs[i]);
						}
					}
				}
				
				/* COPY THE BOOLEAN UNION TO THE FINAL OBJECT */
				if ( rv == FZRT_NOERR )
				{	plugin_stuff.model_funcs.fz_objt_edit_copy_objt_geom(windex,obj1,obj);
				
					/* REFACET WITH OBJECT'S REAL REFINEMENT */
					plugin_stuff.model_funcs.fz_objt_edit_regen_facets(windex,obj,NULL);
				}
				
				/* DELETE THE BOOLEAN UNION OBJECT */
				if ( obj1 != NULL ) plugin_stuff.model_funcs.fz_objt_edit_delete_objt(windex,obj1);
			}
			plugin_stuff.fuim_funcs.fz_fuim_curs_wait(FZ_FUIM_CURS_WAIT_END);	/* END CLOCK CURSOR */

			/* CLEAN UP */
			if ( ent_list != NULL 	) plugin_stuff.model_funcs.fz_objt_list_delete(&ent_list);
			if ( refn != NULL 		) plugin_stuff.attr_funcs.fz_objt_attr_refn_finit(windex,&refn);
			if (temp_objs != NULL	) plugin_stuff.fzrt_mem_funcs.fzrt_dispose_ptr( (fzrt_ptr) temp_objs );
		}
		else
		{	rv = plugin_stuff.fzrt_base_funcs.fzrt_error_set(FRAME_STR_RADIUS_TOO_LARGE, FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_PLUGIN, plugin_stuff.frame_otyp_plugin_runtime_id );
		}
	}

	return(rv);
}



enum
{
	FRAME_OTYP_STACK_PVIEW_DATA = 0,
	FRAME_OTYP_STACK_PVIEW_OPTS,
	FRAME_OTYP_STACK_PVIEW_DRES,
	FRAME_OTYP_STACK_PVIEW_MAX
};

enum
{
	FRAME_OTYP_ID_UPDATE_BUTTON = 0,
	FRAME_OTYP_ID_MAX
};


typedef struct frame_otyp_pview_data_td
{	
	fzrt_int					src_windex;
	fzrt_int					dst_windex;
	fz_objt_ptr				src_obj;
	fz_objt_ptr				dst_obj;
	frame_otyp_parms_td		frame_parms;
	fzrt_boolean			dirty;
} frame_otyp_pview_data_td;

/************************************************************************
	load callback function for the 3d preview of the frame object
*************************************************************************/
static fzrt_error_td frame_otyp_fuim_load_func(fz_fuim_tmpl_ptr fuim_tmpl,fzrt_int src_windex, fzrt_int dst_windex)
{
	fzrt_int						err=FZRT_NOERR;
	frame_otyp_pview_data_td	*pview_data;

	plugin_stuff.fuim_funcs.fz_fuim_stack_get_ptr(fuim_tmpl, FRAME_OTYP_STACK_PVIEW_DATA, (void**)&pview_data);

	if(pview_data->src_obj != NULL)
	{	
		pview_data->dst_windex = dst_windex;

		if( plugin_stuff.model_funcs.fz_objt_edit_copy_objt_to_windex(src_windex,pview_data->src_obj,dst_windex, TRUE, &pview_data->dst_obj) == FZRT_NOERR )
			plugin_stuff.model_funcs.fz_objt_add_objt_to_project(dst_windex,pview_data->dst_obj);
	}
	
	return(err);
}

/*************************************************************************
	new value callback function for the frame object edit dialog
**************************************************************************/
static fzrt_boolean frame_otyp_fuim_newval(fz_fuim_tmpl_ptr fuim_tmpl, void *data_ptr)
{
	frame_otyp_pview_data_td	*pview_data;
	frame_otyp_parms_td			*parm_data;
	fz_attr_refn_ptr			refn;	
	
	plugin_stuff.fuim_funcs.fz_fuim_stack_get_ptr(fuim_tmpl, FRAME_OTYP_STACK_PVIEW_DATA, (void**)&pview_data);

	pview_data->dirty = TRUE;

	/* SET RADIUS */
	plugin_stuff.model_funcs.fz_objt_parm_get_data(pview_data->dst_windex,pview_data->dst_obj,(fzrt_ptr*)&parm_data);
	parm_data->radius = pview_data->frame_parms.radius;

	/* SET REFINEMENT */
	plugin_stuff.fuim_funcs.fz_fuim_stack_get(fuim_tmpl, FRAME_OTYP_STACK_PVIEW_DRES,&refn);
	plugin_stuff.attr_funcs.fz_objt_attr_set_objt_refn(pview_data->dst_windex,pview_data->dst_obj,refn,FALSE);

	return (TRUE);
}

/*************************************************************************
	reset callback function for the frame object edit dialog
**************************************************************************/
static fzrt_boolean  frame_otyp_fuim_reset(fz_fuim_tmpl_ptr fuim_tmpl, void *data_ptr)
{
	frame_otyp_pview_data_td		*pview_data;

	plugin_stuff.fuim_funcs.fz_fuim_stack_get_ptr(fuim_tmpl, FRAME_OTYP_STACK_PVIEW_DATA, (void**)&pview_data);

	return(TRUE);
}

/*************************************************************************
	OK callback function for the frame object edit dialog
**************************************************************************/
static fzrt_boolean frame_otyp_fuim_ok(fz_fuim_tmpl_ptr fuim_tmpl, void *data_ptr)
{
	frame_otyp_pview_data_td		*pview_data;

	plugin_stuff.fuim_funcs.fz_fuim_stack_get_ptr(fuim_tmpl, FRAME_OTYP_STACK_PVIEW_DATA, (void**)&pview_data);

	if ( pview_data->dirty )
	{	plugin_stuff.model_funcs.fz_objt_edit_parm_regen(pview_data->dst_windex,pview_data->dst_obj);
	}
	
	plugin_stuff.model_funcs.fz_objt_edit_copy_objt_data_to_windex(pview_data->dst_windex,
		pview_data->dst_obj, pview_data->src_windex,
		pview_data->src_obj, TRUE);

	return(TRUE);
}


/*************************************************************************
	finit callback function for the frame object edit dialog
**************************************************************************/
static fzrt_boolean  frame_otyp_fuim_finit(fz_fuim_tmpl_ptr fuim_tmpl, void *data_ptr)
{
	fz_attr_refn_ptr			refn;	
	frame_otyp_pview_data_td	*pview_data;

	plugin_stuff.fuim_funcs.fz_fuim_stack_get_ptr(fuim_tmpl, FRAME_OTYP_STACK_PVIEW_DATA, (void**)&pview_data);
	plugin_stuff.fuim_funcs.fz_fuim_stack_get(fuim_tmpl, FRAME_OTYP_STACK_PVIEW_DRES, &refn);
	plugin_stuff.attr_funcs.fz_objt_attr_refn_finit(pview_data->src_windex,&refn);

	return(TRUE);
}

/************************************************************************
	item callback for the frame object edit dialog
*************************************************************************/
static short frame_otyp_tmpl_item_func(fz_fuim_tmpl_ptr fuim_tmpl, fzrt_int action, short item_id, void *func_data, fz_fuim_type_td *rv_data)
{
	frame_otyp_pview_data_td	*pview_data;
	short						rv = FALSE;
	
	plugin_stuff.fuim_funcs.fz_fuim_stack_get_ptr(fuim_tmpl, FRAME_OTYP_STACK_PVIEW_DATA, (void**)&pview_data);
	switch (action)
	{
		case FZ_FUIM_ACTN_GET_ACTIV :
			switch ( item_id )
			{
				case FRAME_OTYP_ID_UPDATE_BUTTON :
					rv_data->fuim_short = pview_data->dirty ? 0 : 255;
					rv = TRUE;
				break;
			}
		break;
		
		case FZ_FUIM_ACTN_HIT :
			switch ( item_id )
			{
				case FRAME_OTYP_ID_UPDATE_BUTTON :

					plugin_stuff.model_funcs.fz_objt_edit_parm_regen(pview_data->dst_windex,pview_data->dst_obj);
					pview_data->dirty = FALSE;
					rv = TRUE;
				break;
			}
		break;
	}
	
	return (rv);
}


/************************************************************************
	Frame edit callback function
*************************************************************************/
static fzrt_error_td		frame_otyp_iface_tmpl(
	fzrt_int					windex,
	fz_fuim_tmpl_ptr		fuim_tmpl,
	fzrt_ptr				objt_ptr
	)
{
	fzrt_error_td				err = FZRT_NOERR;
	short						tab_group,g1,g2,g3,gg;
	char						str[256];
	fz_objt_ptr					obj;
	frame_otyp_parms_td			*frame;
	fz_fuim_pview_opts_ptr		pview_opts;
	frame_otyp_pview_data_td	pview_data,*pview_data_ptr;
	fz_attr_refn_ptr			refn;
	double						max_radius;
	fz_tag_td					obj_tag;
	
	obj = (fz_objt_ptr)objt_ptr;
	plugin_stuff.model_funcs.fz_objt_ptr_to_tag(windex, obj,&obj_tag);
	plugin_stuff.model_funcs.fz_objt_parm_get_data(windex,obj,(fzrt_ptr*)&frame);

	plugin_stuff.fzrt_base_funcs.fzrt_fzr_get_string(plugin_stuff.fz_rsrc_ref_func, FRAME_STR_ID, FRAME_STR_EDIT_TITLE, str);
	if((err = plugin_stuff.fuim_funcs.fz_fuim_tmpl_init(fuim_tmpl, str, 0, FRAME_OTYP_ID, 0)) == FZRT_NOERR)
	{
		/* CHECK FOR MAX RADIUS */
		if( frame_otyp_check_radius(windex,frame->base_obj,frame->radius,&max_radius) == FALSE )
		{	frame->radius = max_radius;
		}

		pview_data.src_obj = obj;
		pview_data.dst_obj = NULL;
		pview_data.src_windex = windex;
		pview_data.dst_windex = -1;
		pview_data.frame_parms = *frame;
		pview_data.dirty = FALSE;
		
		

		plugin_stuff.attr_funcs.fz_objt_attr_refn_init(windex,FZ_ATTR_REFN_INIT_DEFAULT,&refn);
		plugin_stuff.attr_funcs.fz_objt_attr_get_objt_refn(windex,obj,NULL,refn);
	
		plugin_stuff.fuim_funcs.fz_fuim_tmpl_set_new_value_func(fuim_tmpl, frame_otyp_fuim_newval, NULL);
		plugin_stuff.fuim_funcs.fz_fuim_tmpl_set_ok_func(fuim_tmpl,frame_otyp_fuim_ok, NULL);
		plugin_stuff.fuim_funcs.fz_fuim_tmpl_set_reset_func(fuim_tmpl, frame_otyp_fuim_reset, NULL);
		plugin_stuff.fuim_funcs.fz_fuim_tmpl_set_finit_func(fuim_tmpl, frame_otyp_fuim_finit, NULL);

		g1 = plugin_stuff.fuim_funcs.fz_fuim_new_group(fuim_tmpl, FZ_FUIM_ROOT, FZ_FUIM_NONE, FZ_FUIM_FLAG_HORZ, NULL);
			g2 = plugin_stuff.fuim_funcs.fz_fuim_new_group(fuim_tmpl, g1, FZ_FUIM_NONE, FZ_FUIM_FLAG_NONE, NULL);

				plugin_stuff.fuim_funcs.fz_fuim_pview_opts_init(&pview_opts,windex); 
				plugin_stuff.fuim_funcs.fz_fuim_pview_opts_set_load_func(pview_opts, frame_otyp_fuim_load_func);
				plugin_stuff.fuim_funcs.fz_fuim_pview_create(fuim_tmpl, g2, FZ_FUIM_NONE, pview_opts);
				plugin_stuff.fuim_funcs.fz_fuim_stack_put(fuim_tmpl, FRAME_OTYP_STACK_PVIEW_DATA, sizeof(pview_data), &pview_data);
				plugin_stuff.fuim_funcs.fz_fuim_stack_put(fuim_tmpl, FRAME_OTYP_STACK_PVIEW_OPTS, sizeof(pview_opts), &pview_opts);
				plugin_stuff.fuim_funcs.fz_fuim_stack_put(fuim_tmpl, FRAME_OTYP_STACK_PVIEW_DRES, sizeof(fz_attr_refn_ptr), &refn);
				plugin_stuff.fuim_funcs.fz_fuim_stack_get_ptr(fuim_tmpl, FRAME_OTYP_STACK_PVIEW_DATA, (void**)&pview_data_ptr);
				frame = &pview_data_ptr->frame_parms;

			g2 = plugin_stuff.fuim_funcs.fz_fuim_new_group(fuim_tmpl, g1, FZ_FUIM_NONE, FZ_FUIM_FLAG_NONE, NULL);
			
			tab_group = plugin_stuff.fuim_funcs.fz_fuim_new_tab_group(fuim_tmpl,g2,FZ_FUIM_NONE,FZ_FUIM_FLAG_NONE,NULL,NULL);
			
				plugin_stuff.fzrt_base_funcs.fzrt_fzr_get_string(plugin_stuff.fz_rsrc_ref_func, FRAME_STR_ID, FRAME_STR_OPTIONS, str);
				gg = plugin_stuff.fuim_funcs.fz_fuim_new_text_static(fuim_tmpl,tab_group,FZ_FUIM_NONE,FZ_FUIM_FLAG_NONE,str, NULL, NULL);			

					plugin_stuff.fzrt_base_funcs.fzrt_fzr_get_string(plugin_stuff.fz_rsrc_ref_func, FRAME_STR_ID, FRAME_STR_RADIUS, str);
//					plugin_stuff.fuim_funcs.fz_fuim_new_text_static_edit(fuim_tmpl,gg,FZ_FUIM_NONE,str,FZ_FUIM_NONE,FZ_FUIM_FLAG_NONE, NULL, NULL,&g3);			
					plugin_stuff.fuim_funcs.fz_fuim_new_text_static_edit_anim(fuim_tmpl,gg,FZ_FUIM_NONE,str,FZ_FUIM_NONE,FZ_FUIM_FLAG_NONE, NULL, NULL,
																			  0,windex,FZ_FUIM_ANIM_OBJECT_TYPE,&obj_tag,FRAME_PARM_RADIUS_UUID,&g3);			
					plugin_stuff.fuim_funcs.fz_fuim_item_range_double(fuim_tmpl, g3, &frame->radius, 0.0, max_radius, FZ_FUIM_FORMAT_FLOAT_DISTANCE, 
								FZ_FUIM_RANGE_MIN | FZ_FUIM_RANGE_MAX | FZ_FUIM_RANGE_MAX_INCL );
  
  			plugin_stuff.fuim_funcs.fz_fuim_disp_res_surf_group(fuim_tmpl,tab_group,refn);
				
			plugin_stuff.fzrt_base_funcs.fzrt_fzr_get_string(plugin_stuff.fz_rsrc_ref_func, FRAME_STR_ID, FRAME_STR_UPDATE, str);
			g3 = plugin_stuff.fuim_funcs.fz_fuim_new_button(fuim_tmpl,g2,FRAME_OTYP_ID_UPDATE_BUTTON, FZ_FUIM_FLAG_NONE,str,frame_otyp_tmpl_item_func, NULL);
	}
	
	return (err);
}


/************************************************************************
	Frame copy callback function
*************************************************************************/
static  fzrt_error_td  frame_otyp_copy (
	fzrt_int			src_windex,
	fz_objt_ptr		src_objt,	
	fzrt_ptr		src_parm,	
	fzrt_int			dst_windex,	
	fz_objt_ptr		dst_objt,	
	fzrt_ptr		dst_parm	
	)
{
	frame_otyp_parms_td		*src_frame,*dst_frame;
	fzrt_zone_ptr			zone_ptr;
	fzrt_error_td			rv = FZRT_NOERR;

	src_frame = (frame_otyp_parms_td *)src_parm;
	dst_frame = (frame_otyp_parms_td *)dst_parm;

	dst_frame->radius   = src_frame->radius;
	if ( src_frame->base_obj != NULL )
	{
		plugin_stuff.model_funcs.fz_objt_get_zone_ptr(dst_windex,dst_objt,&zone_ptr);
		dst_frame->base_obj = NULL;
		if((rv = plugin_stuff.model_funcs.fz_objt_indp_init(dst_windex,zone_ptr,&dst_frame->base_obj)) == FZRT_NOERR)
		{	rv = plugin_stuff.model_funcs.fz_objt_edit_copy_objt_geom(src_windex,src_frame->base_obj,dst_frame->base_obj);
		}
	}

	return(rv);
}


/*************************************************************************
	Frame copy nested control objects callback function
*************************************************************************/
static fzrt_error_td	frame_otyp_copy_cntl_objts(	
	fzrt_int				windex,			
	fz_objt_ptr			obj,			
	fzrt_ptr			parm,			
	fzrt_int				*nobj,			
	fz_objt_ptr			*cntrl_objs
	)
{
	frame_otyp_parms_td		*frame;
	fzrt_error_td			rv = FZRT_NOERR;
	
	frame = (frame_otyp_parms_td *)parm;
	
	*nobj = 1;
	if ( cntrl_objs != NULL )
	{	rv = plugin_stuff.model_funcs.fz_objt_parm_nested_extract(windex,obj,frame->base_obj,cntrl_objs[0]);
	}
	
	return(rv);
}

/************************************************************************
	Frame io stream callback function
*************************************************************************/
static fzrt_error_td frame_otyp_iost(	
	fzrt_int					windex,			
	fz_objt_ptr				obj,			
	fzrt_ptr				parm,			
	fz_iost_ptr				iost, 			
	fz_iost_dir_td_enum		dir,			
	fzpl_vers_td * const 	version, 		
	fzrt_unsigned_int 			size			
	)
{
	fzrt_error_td			err = FZRT_NOERR;
	fzrt_zone_ptr			zone_ptr;
	frame_otyp_parms_td		*frame;

	frame = (frame_otyp_parms_td *)parm;

	if ( dir == FZ_IOST_WRITE ) *version = 0;
	
	if((err = plugin_stuff.iost_funcs.fz_iost_double(iost,&frame->radius,1)) == FZRT_NOERR )
	{	
		if ( dir == FZ_IOST_READ )
		{	
			plugin_stuff.model_funcs.fz_objt_get_zone_ptr(windex,obj,&zone_ptr);
			err = plugin_stuff.model_funcs.fz_objt_indp_init(windex,zone_ptr,&frame->base_obj);
		}
		
		if ( err == FZRT_NOERR && frame->base_obj != NULL )
		{	err = plugin_stuff.model_funcs.fz_objt_io(windex,frame->base_obj,iost,dir);
		}
	}

	return(err);
}

/************************************************************************
	Callbacks that provide information about this object type's
	parameters.  These callbacks are useful, in particular, for object
	animation allowing formZ to query/change the parameters that can be
	animated.
*************************************************************************/


enum
{
	FRAME_PARM_RADIUS,
	
	FRAME_PARM_MAX
};

/************************************************************************
	parameter count callback function
*************************************************************************/
static fzrt_error_td frame_otyp_parm_count(
	fzrt_int			*count	
	)
{
	fzrt_error_td err = FZRT_NOERR;
	
	*count = FRAME_PARM_MAX;
	
	return err;
}

/************************************************************************
	parameter get info callback function
*************************************************************************/
static fzrt_error_td  frame_otyp_parm_get_info(
	fzrt_int						parm_indx,			
	fzrt_UUID_td				parm_uuid,			
	fz_string_td				parm_name,			
	fz_type_enum				*parm_type,			
	fz_fuim_format_int_enum		*parm_format_int,	
	fz_fuim_format_float_enum	*parm_format_float,	
	fz_fuim_item_type_enum		*parm_fuim_item,
	fzrt_int						*parm_range,		
	fz_type_td					*parm_range_min,	
	fz_type_td					*parm_range_max,	
	fzrt_int						*flags				
	)
{
	fzrt_error_td	err = FZRT_NOERR;
	char			str[256];
	double			dval;
	
	switch(parm_indx)
	{
		case FRAME_PARM_RADIUS:
			if (parm_uuid)
				plugin_stuff.fzrt_base_funcs.fzrt_UUID_copy(FRAME_PARM_RADIUS_UUID, parm_uuid);
			if (parm_name)
			{
				plugin_stuff.fzrt_base_funcs.fzrt_fzr_get_string(plugin_stuff.fz_rsrc_ref_func, FRAME_STR_ID, FRAME_STR_RADIUS, str);
				strcpy(parm_name, str);
			}
			if (parm_type)
				*parm_type = FZ_TYPE_DOUBLE;
			if (parm_format_int)
				*parm_format_int = FZ_FUIM_FORMAT_INT_DEFAULT;
			if (parm_format_float)
				*parm_format_float = FZ_FUIM_FORMAT_FLOAT_DISTANCE;
			if (parm_range)
				*parm_range = FZ_FUIM_RANGE_MIN;
			if (parm_range_min)
			{
				dval = 0;
				plugin_stuff.type_funcs.fz_type_set_double(&dval, parm_range_min);
			}
			if (flags)
			{	*flags = 0;
				FZ_SETBIT(*flags,FZ_OTYP_PARM_ANIM_LEVEL1_BIT);
			}
			if ( parm_fuim_item ) *parm_fuim_item = FZ_FUIM_ITEM_TEXT;
		break;
		
	}
	
	return err;
}

/************************************************************************
	parameter get state str callback function
*************************************************************************/
static fzrt_error_td  frame_otyp_parm_get_state_str(
	fzrt_UUID_td		parm_uuid,	
	fzrt_int				which_state,
	fz_string_td		str			
	)
{
	fzrt_error_td	err = FZRT_NOERR;
	
	return err;
}
	

/************************************************************************
	parameter get callback function
*************************************************************************/
static fzrt_error_td  frame_otyp_parm_get(
	fzrt_int				windex,		
	fz_objt_ptr			objt,		
	fzrt_UUID_td		parm_uuid,
	fz_type_td			*data		
	)
{
	fzrt_error_td			err = FZRT_NOERR;
	frame_otyp_parms_td		*frame;
	double					dval;

	plugin_stuff.model_funcs.fz_objt_parm_get_data(windex,objt,(fzrt_ptr*)&frame);
	
	if (plugin_stuff.fzrt_base_funcs.fzrt_UUID_is_equal(parm_uuid, FRAME_PARM_RADIUS_UUID))
	{
		dval = frame->radius;
		plugin_stuff.type_funcs.fz_type_set_double(&dval, data);
	}
	
	return err;
}
	

/************************************************************************
	parameter set callback function
*************************************************************************/
static fzrt_error_td  frame_otyp_parm_set(
	fzrt_int				windex,		
	fz_objt_ptr			objt,		
	fzrt_UUID_td		parm_uuid,
	fz_type_td			*data		
	)
{
	fzrt_error_td			err = FZRT_NOERR;
	frame_otyp_parms_td		*frame;
	double					dval;

	plugin_stuff.model_funcs.fz_objt_parm_get_data(windex,objt,(fzrt_ptr*)&frame);

	if (plugin_stuff.fzrt_base_funcs.fzrt_UUID_is_equal(parm_uuid, FRAME_PARM_RADIUS_UUID))
	{
		plugin_stuff.type_funcs.fz_type_get_double(data, &dval);
		if (dval > 0)
			frame->radius = dval;
		else
			err = -1;
	}
	
	if (err == -1)
		err = plugin_stuff.fzrt_base_funcs.fzrt_error_set(FZRT_BAD_PARAM_ERROR, FZRT_ERROR_SEVERITY_ERROR,FZRT_ERROR_CONTEXT_PLUGIN, 0);
	
	return err;
	
}


#define FUIM_FRAME_STACK_UUID FRAME_OTYP_PLUGIN_ID
enum
{
	FUIM_FRAME_STACK_OBJT_LIST = 0,
	FUIM_FRAME_STACK_NOBJT,
	FUIM_FRAME_STACK_WINDEX,
	
	FUIM_FRAME_STACK_MAX
};

enum
{
	FRAME_ID_RADIUS = 0,
	FRAME_ID_MAX
};
static fzrt_int	_fuim_frame_ids[FRAME_ID_MAX];

/************************************************************************
	item callback for multi editing
*************************************************************************/
static short fuim_frame_multi_item_func(fz_fuim_tmpl_ptr fuim_tmpl, fzrt_int action, short item_id, void *func_data, fz_fuim_type_td *rv_data)
{
	fz_objt_ptr			*objts,obj;
	fzrt_int				i,nobjt,windex;
	double				fval;
	frame_otyp_parms_td	*frame;
	short				rv = FALSE;
	
	static	fzrt_int	nested = 0;
	
	
	switch (action)
	{
		case FZ_FUIM_ACTN_GET_VALUE:
			plugin_stuff.fuim_funcs.fz_fuim_stack_uuid_get(fuim_tmpl, FUIM_FRAME_STACK_UUID, FUIM_FRAME_STACK_OBJT_LIST,  &objts);
			plugin_stuff.fuim_funcs.fz_fuim_stack_uuid_get(fuim_tmpl, FUIM_FRAME_STACK_UUID, FUIM_FRAME_STACK_NOBJT,      &nobjt);
			plugin_stuff.fuim_funcs.fz_fuim_stack_uuid_get(fuim_tmpl, FUIM_FRAME_STACK_UUID, FUIM_FRAME_STACK_WINDEX,     &windex);

			obj = objts[0];
			plugin_stuff.model_funcs.fz_objt_parm_get_data(windex,obj,(fzrt_ptr*)&frame);
			
			if ( item_id == _fuim_frame_ids[FRAME_ID_RADIUS] )  fval = frame->radius;
			
			for(i = 1; i < nobjt; i++)
			{
				obj = objts[i];
				plugin_stuff.model_funcs.fz_objt_parm_get_data(windex,obj,(fzrt_ptr*)&frame);
				
				if ( item_id == _fuim_frame_ids[FRAME_ID_RADIUS] && fabs(fval - frame->radius) > FZ_MATH_TOL_LIN  ) 
				{
					rv = 0x8000 | TRUE;
					break;
				}
			}
			
		break;
			
		case FZ_FUIM_ACTN_PRE_NEW_VALUE	:
			nested++;
			rv = TRUE;
		break;
			
		case FZ_FUIM_ACTN_POST_NEW_VALUE	:
			nested--;
			rv = TRUE;
		break;
			
		case FZ_FUIM_ACTN_NEW_VALUE:
			
			if ( nested == 1 )
			{
				plugin_stuff.fuim_funcs.fz_fuim_stack_uuid_get(fuim_tmpl, FUIM_FRAME_STACK_UUID, FUIM_FRAME_STACK_OBJT_LIST,  &objts);
				plugin_stuff.fuim_funcs.fz_fuim_stack_uuid_get(fuim_tmpl, FUIM_FRAME_STACK_UUID, FUIM_FRAME_STACK_NOBJT,      &nobjt);
				plugin_stuff.fuim_funcs.fz_fuim_stack_uuid_get(fuim_tmpl, FUIM_FRAME_STACK_UUID, FUIM_FRAME_STACK_WINDEX,     &windex);
				
				obj = objts[0];
				plugin_stuff.model_funcs.fz_objt_parm_get_data(windex,obj,(fzrt_ptr*)&frame);
				
				if ( item_id == _fuim_frame_ids[FRAME_ID_RADIUS] )  fval = frame->radius;
				
				for(i = 1; i < nobjt; i++)
				{
					obj = objts[i];
					plugin_stuff.model_funcs.fz_objt_parm_get_data(windex,obj,(fzrt_ptr*)&frame);
					
					if ( item_id == _fuim_frame_ids[FRAME_ID_RADIUS] ) frame->radius = fval;
				}
			}
			rv = TRUE;
		break;
	}
	
	return(rv);
}

/************************************************************************
	Object type parameter edit function
 *************************************************************************/
static fzrt_error_td  frame_otyp_prop_palt (
	fzrt_int				windex,		
	fz_objt_ptr			*objts,		
	fzrt_int				nobjt,		
	fz_fuim_tmpl_ptr	fuim_tmpl,	
	short				parent
	)
{
	
	fz_string_td		str;
	short				g1;
	fz_objt_ptr			obj;
	frame_otyp_parms_td	*frame;
	double				max_radius,max_radius2;
	fzrt_int				i;
	
	max_radius2 = DBL_MAX;
	for(i = 0; i < nobjt; i++)
	{
		obj = objts[i];
		plugin_stuff.model_funcs.fz_objt_parm_get_data(windex,obj,(fzrt_ptr*)&frame);
		
		/* CHECK FOR MAX RADIUS */
		if( frame_otyp_check_radius(windex,frame->base_obj,frame->radius,&max_radius) == FALSE )
		{	frame->radius = max_radius;
		}
		
		if ( max_radius2 > max_radius ) max_radius2 = max_radius;
	}
	
	obj = objts[0];
	plugin_stuff.model_funcs.fz_objt_parm_get_data(windex,obj,(fzrt_ptr*)&frame);
	
	plugin_stuff.fuim_funcs.fz_fuim_stack_uuid_put(fuim_tmpl, FUIM_FRAME_STACK_UUID, FUIM_FRAME_STACK_OBJT_LIST,  sizeof(objts),	&objts);
	plugin_stuff.fuim_funcs.fz_fuim_stack_uuid_put(fuim_tmpl, FUIM_FRAME_STACK_UUID, FUIM_FRAME_STACK_NOBJT,      sizeof(nobjt),    &nobjt);
	plugin_stuff.fuim_funcs.fz_fuim_stack_uuid_put(fuim_tmpl, FUIM_FRAME_STACK_UUID, FUIM_FRAME_STACK_WINDEX,     sizeof(windex),   &windex);
	
	plugin_stuff.fzrt_base_funcs.fzrt_fzr_get_string(plugin_stuff.fz_rsrc_ref_func, FRAME_STR_ID, FRAME_STR_RADIUS, str);
	plugin_stuff.fuim_funcs.fz_fuim_new_text_static_edit(fuim_tmpl,parent,FZ_FUIM_NONE,str,FZ_FUIM_NONE,FZ_FUIM_FLAG_NONE, fuim_frame_multi_item_func, NULL,&g1);			
	plugin_stuff.fuim_funcs.fz_fuim_item_range_double(fuim_tmpl, g1, &frame->radius, 0.0, max_radius2, FZ_FUIM_FORMAT_FLOAT_DISTANCE, 
													  FZ_FUIM_RANGE_MIN | FZ_FUIM_RANGE_MAX | FZ_FUIM_RANGE_MAX_INCL );

	_fuim_frame_ids[FRAME_ID_RADIUS] = g1;
	
	
	return(FZRT_NOERR);
}

/************************************************************************
	Fills the fset structure with the object type callback functions.
*************************************************************************/
fzrt_error_td frame_otyp_get_callback_fset ( const fzpl_fset_def_ptr fset_def, fzpl_fset_td * const fset )
{
	fzrt_error_td			err = FZRT_NOERR;
	fz_otyp_cbak_fset		*otyp_fset;

	if ( fset_def && fset )
	{
		plugin_stuff.fsf->fzpl_fset_def_check ( 	fset_def, 
											FZ_OTYP_CBAK_FSET_VERSION, 
											FZPL_TYPE_STRING(fz_otyp_cb_fset), 
											sizeof ( fz_otyp_cbak_fset ), 
											FZPL_VERSION_OP_NEWER );

		if ( !err )
		{
			otyp_fset = (fz_otyp_cbak_fset *)fset;

			otyp_fset->fz_otyp_cbak_name			= frame_otyp_name;
			otyp_fset->fz_otyp_cbak_uuid			= frame_otyp_uuid;
			otyp_fset->fz_otyp_cbak_info			= frame_otyp_info;
			otyp_fset->fz_otyp_cbak_init			= frame_otyp_init;
			otyp_fset->fz_otyp_cbak_finit			= frame_otyp_finit;
			otyp_fset->fz_otyp_cbak_tform			= frame_otyp_tform;
			otyp_fset->fz_otyp_cbak_regen			= frame_otyp_regen;
			otyp_fset->fz_otyp_cbak_iface_tmpl		= frame_otyp_iface_tmpl;
			otyp_fset->fz_otyp_cbak_io				= frame_otyp_iost;
			otyp_fset->fz_otyp_cbak_copy			= frame_otyp_copy;
			otyp_fset->fz_otyp_cbak_copy_cntl_objts	= frame_otyp_copy_cntl_objts;
			otyp_fset->fz_otyp_cbak_parm_count			= frame_otyp_parm_count;
			otyp_fset->fz_otyp_cbak_parm_get_info2		= frame_otyp_parm_get_info;
			otyp_fset->fz_otyp_cbak_parm_get_state_str	= frame_otyp_parm_get_state_str;
			otyp_fset->fz_otyp_cbak_parm_get			= frame_otyp_parm_get;
			otyp_fset->fz_otyp_cbak_parm_set			= frame_otyp_parm_set;
			otyp_fset->fz_otyp_cbak_prop_palt			= frame_otyp_prop_palt;
		}
	}
	else
		return ( plugin_stuff.fzrt_base_funcs.fzrt_error_set ( FZRT_BAD_PARAM_ERROR, FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_FZRT, 0 ) );

	return err;
}
