/*<html><pre>*/
#include <../source/TAL/objbase.h>

#ifdef	_MSC_VER
#pragma warning(disable:4056)
#pragma warning(disable:4244)
#pragma warning(disable:4305)
#endif	/* _MSC_VER */

/*
**	Distance from axis to labels along direction of other axis; it's
**	hard to explain - see fbb_draw_axes().
*/
#define	LD_DIST 		(.1f)

/*
 *		Can be called any time, but especially useful when
 *	OpenGL window with font already selected to be current.
 */
void	V_GLOBAL
fbbInit(functional_bounding_box_t *fbb)
{
	int i;
	vhandle_t fnt;
	if(!fbb)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"fbbInit(NULL): incorrect parameter.");
#endif	/* CHECKED_BUILD */
		return;
	}
	memZero(fbb,sizeof(functional_bounding_box_t));
	fnt=vglGetCurrentFont();
	vec3Set(fbb->mins,-1,-1,-1);
	vec3Set(fbb->maxs,1,1,1);
	vec3Copy(fbb->f_mins,fbb->mins);
	vec3Copy(fbb->f_maxs,fbb->maxs);
	for(i=0;i<3;i++)
	{
		fbb->steps[i]=10;
		fbb->draw_axis[i]=true;
		fbb->draw_ticks[i]=true;
		fbb->draw_grid[i]=true;
		fbb->draw_labels[i]=true;
		fbb->draw_legend[i]=true;
	}
	fbb->smooth_axes=true;
	fbb->smooth_grids=true;
	fbb->smooth_ticks=true;
	vec3Set(fbb->axis_width,2,2,2);
	vec3Set(fbb->grid_width,1,1,1);
	vec3Set(fbb->ticks_size,8,8,8);
#if	0
	/* looks like map of ancient USSR ;) */
	vec4Set(fbb->axis_color[0],1,0,1,0.5f);
	vec4Set(fbb->axis_color[1],1,1,0,0.5f);
	vec4Set(fbb->axis_color[2],0,1,1,0.5f);
	vec4Set(fbb->grid_color[0],1,0,0,0.5f);
	vec4Set(fbb->grid_color[1],0,1,0,0.5f);
	vec4Set(fbb->grid_color[2],0,0,1,0.5f);
	vec4Set(fbb->ticks_color[0],1,0,.5f,0.5f);
	vec4Set(fbb->ticks_color[0],.5f,1,0,0.5f);
	vec4Set(fbb->ticks_color[0],0,.5f,1,.5f);
	vec4Set(fbb->label_color[0],1,.7f,.5f,.5f);
	vec4Set(fbb->label_color[0],.5f,1,.5f,0.5f);
	vec4Set(fbb->label_color[0],.7f,.5f,1,.5f);
	vec4Set(fbb->legend_color[0],0,.7f,.5f,.5f);
	vec4Set(fbb->legend_color[0],.5f,0,.5f,0.5f);
	vec4Set(fbb->legend_color[0],.7f,.5f,0,.5f);
#else
	/* shades of grey; usefull for printing */
	vec4Set(fbb->axis_color[0],.2f,.2f,.2f,.5f);
	vec4Copy(fbb->axis_color[1],fbb->axis_color[0]);
	vec4Copy(fbb->axis_color[2],fbb->axis_color[0]);
	vec4Set(fbb->grid_color[0],.5f,.5f,.5f,.5f);
	vec4Copy(fbb->grid_color[1],fbb->grid_color[0]);
	vec4Copy(fbb->grid_color[2],fbb->grid_color[0]);
	vec4Set(fbb->ticks_color[0],0,0,0,0.5f);
	vec4Copy(fbb->ticks_color[1],fbb->ticks_color[0]);
	vec4Copy(fbb->ticks_color[2],fbb->ticks_color[0]);
	vec4Set(fbb->label_color[0],0,0,0,.5f);
	vec4Copy(fbb->label_color[1],fbb->label_color[0]);
	vec4Copy(fbb->label_color[2],fbb->label_color[0]);
	vec4Set(fbb->legend_color[0],0,0,0,.5f);
	vec4Copy(fbb->legend_color[1],fbb->legend_color[0]);
	vec4Copy(fbb->legend_color[2],fbb->legend_color[0]);
#endif /* 0 */
	for(i=0;i<3;i++)
	{
		fbb->label_font[i]=fnt;
		fbb->legend_font[i]=fnt;
	}
}

/*
 *	Computes 'good'-looking value near given 'ref'.
 *	TODO: reduce it, it looks ugly (also works fine).
 */
vec_t	V_LOCAL
make_readable_float(vec_t ref,int num_digits)
{
	vec_t md,s,power,lower,upper,r1,r2,r3,d1,d2,d3;
	if(num_digits<1)
	  	num_digits=1;
	if(ref>=0)
		s=1;
	else
		s=-1;
	ref*=s;
	power=(vec_t)pow(10,floor(log10(ref)));
	md=(vec_t)pow(10,num_digits-1);
	r1=ref*md/power;
	upper=(vec_t)ceil(r1);
	lower=(vec_t)floor(r1);
	power/=md;
	r1=lower*power;
	r2=upper*power;
	r3=(r1+r2)/2;
	d1=ref-r1;
	d2=r2-ref;
	d3=fabs(r3-ref);
	return	(d1<d2) ? ((d1<d3) ? s*r1 : s*r3) : ((d2<d3) ? s*r2 : s*r3);
}

/*
 *	Computes step, delta and offset for plot.
 */
void	V_LOCAL
compute_range_axis(
	vec_t lower,vec_t upper,			/* range */
	u32_t steps,						/* number of steps (only HINT) */
	vec_t *delta,						/* increnment */
	vec_t *offset,						/* initial offset to start from */
	u32_t *real_steps)					/* adjusted number of steps */
{
	vec_t d,o;
#ifdef	CHECKED_BUILD
	if(!steps)
	{
		conlPuts(LOG_ERROR,
			"compute_range_axis(): zero steps.\n");
		return;
	}
#endif	/* CHECKED_BUILD */
	if(lower>upper)
	{
		vec_t temp=lower;
		lower=upper;
		upper=temp;
	}
	if(delta)
	{
		d=(vec_t)fabs(upper-lower);
		d=make_readable_float(d / steps,1);
		*delta=d;
		if(offset || real_steps)
		{
			if(IsZero(d))
			{
#ifdef			CHECKED_BUILD
				conlPrintf(LOG_ERROR,
					"compute_range_axis(): zero length range (upper %g, lower %d).\n",
					upper,lower);
#endif			/* CHECKED_BUILD */
				if(offset)
					*offset=0;
				if(real_steps)
					*real_steps=steps;
				return;
			}
			lower/=d;
			o=d*ceil(lower);
			if(offset)
				*offset=o;
			if(real_steps)
				*real_steps=(u32_t)floor((upper-o)/d);
		}
	}
}

/*
 *	Returns vertices opposing given along given axis.
 *	Assumes box created by box_from_bounds().
 */
u32_t	V_LOCAL
find_opposing_vertex(u32_t vert,u32_t axis)
{
	V_LOCAL u8_t opverts[8][3] =
	{
		{1,2,4},
		{0,3,5},
		{3,0,6},
		{2,1,7},
		{5,6,0},
		{4,7,1},
		{7,4,2},
		{6,5,3}
	};
	return opverts[vert][axis];
}

/*
 *	Assumes all args correct.
 */
void	V_LOCAL
fbb_draw_axes(
	functional_bounding_box_t *fbb,cam_t *viewer,
	vec3_t origin,vec3_t opposing[3],vec3_t e123[3],
	vec3_t starts[3], vec3_t deltas[3],		/* geom */
	vec3_t f_starts, vec3_t f_deltas,		/* logical */
	/*	vector from origin to start of line on which lies centers
	 *	of series #N of labels */
	vec3_t ld[3],
	u32_t steps[3])	/* number of steps along axes */
{
	int i,j,num_labels;
	vec3_t tick,label;
	vec_t val,delta;
	char buf[128];
	char *legends[3];
	char *def_legends[]={"X","Y","Z"};
	glBlendFunc(GL_SRC_COLOR,GL_ONE_MINUS_SRC_COLOR);
	if(fbb->smooth_axes)
	{
		glEnable(GL_LINE_SMOOTH);
		glEnable(GL_BLEND);
	}
	else
	{
		glDisable(GL_LINE_SMOOTH);
		glDisable(GL_BLEND);
	}
	/* draw axes lines */
	for(i=0;i<3;i++)
	{
		if(!fbb->draw_axis[i])
			continue;
		glLineWidth(fbb->axis_width[i]);
		glBegin(GL_LINES);
			glColor4fv(fbb->axis_color[i]);
			glVertex3fv(origin);
			glVertex3fv(opposing[i]);
		glEnd(); /* GL_LINES */
	}
	if(fbb->smooth_axes)
	{
		glDisable(GL_LINE_SMOOTH);
		glDisable(GL_BLEND);
	}
	/* draw ticks */
	if(fbb->smooth_ticks)
	{
		glEnable(GL_POINT_SMOOTH);
		glEnable(GL_BLEND);
	}
	else
		glDisable(GL_POINT_SMOOTH);
	for(i=0;i<3;i++)
	{
		if(!fbb->draw_ticks[i])
			continue;
		vec3Copy(tick,starts[i]);
		glColor4fv(fbb->ticks_color[i]);
		glPointSize(fbb->ticks_size[i]);
		num_labels=(int)(steps[i])+1;
		glBegin(GL_POINTS);
			for(j=0;j<num_labels;j++)
			{
				glVertex3fv(tick);
				vec3Add(tick,tick,deltas[i]);
			}
		glEnd();	/* GL_POINTS */
	}
	if(fbb->smooth_ticks)
	{
		glDisable(GL_POINT_SMOOTH);
		glDisable(GL_BLEND);
	}
	/* labels */
	/* we won't change blend/smooth settings: they depends on font
	 * parameters and will be adjusted automatically by text drawing
	 * proc. */
	for(i=0;i<3;i++)
	{
		if(!fbb->draw_labels[i])
			continue;
#if 0
		vec3Copy(tick,starts[i]);
#endif
		vec3Copy(label,starts[i]);
		vec3Add(label,label,ld[i]);
		val=f_starts[i];
		delta=f_deltas[i];
		num_labels=(int)(steps[i])+1;
		glColor4fv(fbb->label_color[i]);
		vglSelectFont(fbb->label_font[i]);
		for(j=0;j<num_labels;j++)
		{
			sprintf(buf,"%g",val);
			vglDrawString(label,viewer,buf);
#if 0
			glLineWidth(fbb->grid_width[i]);
			glBegin(GL_LINES);
			glColor4fv(fbb->grid_color[i]);
				glVertex3fv(tick);
				glVertex3fv(label);
			glEnd();	/* GL_LINES */
			vec3Add(tick,tick,deltas[i]);
#endif
			val+=delta;
			vec3Add(label,label,deltas[i]);
		}
	}
	/* legends */
	for(i=0;i<3;i++)
	{
		if(!fbb->draw_legend[i])
			continue;
		legends[i]=fbb->legend[i] ? fbb->legend[i] : def_legends[i];
		/* TODO: make offset adjustable */
		vec3Scale(label,e123[i],LD_DIST);
		vec3Add(label,label,opposing[i]);
		glColor4fv(fbb->legend_color[i]);
		vglDrawString(label,viewer,legends[i]);
	}
	glBlendFunc(GL_ONE,GL_ZERO);
}

void	V_LOCAL
fbb_draw_wall(vec3_t origin,vec3_t opposing[3],
	vec3_t starts[3],vec3_t offsets[3],u32_t steps[3],
	u32_t ind1, u32_t ind2)
{
	u32_t i,num_steps;
	vec3_t p1,p2,delta;
	vec3_t fourth;
	vec3Sub(fourth,opposing[ind1],origin);
	vec3Add(fourth,fourth,opposing[ind2]);
	/* draw wall bounds */
	glBegin(GL_LINE_STRIP);
		glVertex3fv(opposing[ind1]);
		glVertex3fv(fourth);
		glVertex3fv(opposing[ind2]);
	glEnd();	/* GL_LINE_STRIP */
	/* draw grid lines - along first direction: */
	num_steps=steps[ind1]+1;
	vec3Copy(p1,starts[ind1]);
	vec3Sub(p2,p1,origin);
	vec3Add(p2,opposing[ind2],p2);
	vec3Copy(delta,offsets[ind1]);
	glBegin(GL_LINES);
	for(i=0;i<num_steps;i++)
	{
		glVertex3fv(p1);
		glVertex3fv(p2);
		vec3Add(p1,p1,delta);
		vec3Add(p2,p2,delta);
	}
	glEnd();	/* GL_LINES */
	/* ... and along second: */
	num_steps=steps[ind2]+1;
	vec3Copy(p1,starts[ind2]);
	vec3Sub(p2,p1,origin);
	vec3Add(p2,opposing[ind1],p2);
	vec3Copy(delta,offsets[ind2]);
	glBegin(GL_LINES);
	for(i=0;i<num_steps;i++)
	{
		glVertex3fv(p1);
		glVertex3fv(p2);
		vec3Add(p1,p1,delta);
		vec3Add(p2,p2,delta);
	}
	glEnd();	/* GL_LINES */
}

/*
 *	Creates vertices of box with opposite corners 'mins' and 'maxs'.
 *	Odd lies in plane 'x=mins[0]'; even - in plane 'x=maxs[0]'.
 *	0,1,4,5 - in plane 'y=mins[1]'; other four - in plane 'y=maxs[1]'.
 *	First four - in plane 'z=mins[2]'; other - in plane 'z=maxs[2]'.
 */
void	V_LOCAL
box_from_corners(vec3_t verts[],vec3_t mins,vec3_t maxs)
{
	/* z = mins[2] */
	vec3Set(verts[0],mins[0],mins[1],mins[2]);
	vec3Set(verts[1],maxs[0],mins[1],mins[2]);
	vec3Set(verts[2],mins[0],maxs[1],mins[2]);
	vec3Set(verts[3],maxs[0],maxs[1],mins[2]);
	/* z = maxs[2] */
	vec3Set(verts[4],mins[0],mins[1],maxs[2]);
	vec3Set(verts[5],maxs[0],mins[1],maxs[2]);
	vec3Set(verts[6],mins[0],maxs[1],maxs[2]);
	vec3Set(verts[7],maxs[0],maxs[1],maxs[2]);
}

void	V_GLOBAL
fbbDraw(functional_bounding_box_t *fbb)
{
	u32_t i,opt;
	u32_t steps[3];
	vec_t in_len,ts,td;
	/* vec_t nd, dist; */
	vec3_t box[8];
	vec3_t starts[3], deltas[3], e123[3], opposing[3];
	vec3_t f_starts,f_deltas;
	vec3_t ld[3];
	if(!fbb)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"fbbDraw(NULL): incorrect parameter.");
#endif	/* CHECKED_BUILD */
		return;
	}
	if(!fbb->cam)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,
			"fbbDraw(): you must set camera prior to draw.");
#endif	/* CHECKED_BUILD */
		return;
	}
	/* make geometry box */
	box_from_corners(box,fbb->mins,fbb->maxs);
	/* determine axes corner */
	opt=0;
	/*
		Idea was good, but SCALES not working: provide method to 
		recompute logical-space offsets (or pass draw directions to
		fbb_draw_axes()).
	vec3Sub(delta,fbb->cam->origin,box[0]);
	nd=vec3Dot(delta,delta);
	for(i=1;i<8;i++)
	{
		vec3Sub(delta,fbb->cam->origin,box[i]);
		dist=vec3Dot(delta,delta);
		if(dist>nd)
		{
			opt=i;
			nd=dist;
		}
	}
	*/
	/* TODO: check if corner point visible */
	/* compute offsets and increments along all axes */
	for(i=0;i<3;i++)
	{
		steps[i]=fbb->steps[i];
		compute_range_axis(fbb->f_mins[i],fbb->f_maxs[i],steps[i],
			&f_deltas[i],&f_starts[i],&steps[i]);
		vec3Copy(opposing[i],box[find_opposing_vertex(opt,i)]);
		in_len=fbb->f_maxs[i]-fbb->f_mins[i];
		ts=(f_starts[i]-fbb->f_mins[i])/in_len;
		td=f_deltas[i]/in_len;
		vec3Sub(e123[i],opposing[i],box[opt]);
		vec3Scale(deltas[i],e123[i],td);
		vec3Scale(starts[i],e123[i],ts);
		vec3Add(starts[i],starts[i],box[opt]);
	}
	for(i=0;i<3;i++)
	{
		vec3_t sum;
		vec3Add(sum,e123[(i+1) % 3],e123[(i+2) % 3]);
		vec3Scale(ld[i],sum,- LD_DIST / 1.4142);
	}
	vec3Scale(ld[2],ld[2],0.71f);
	/* draw axes, ticks, labels and legends */
	fbb_draw_axes(fbb,fbb->cam,box[opt],opposing,e123,
		starts,deltas,f_starts,f_deltas,ld,steps);
	/* draw walls */
	if(fbb->smooth_grids)
	{
		glEnable(GL_LINE_SMOOTH);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	}
	else
	{
		glDisable(GL_LINE_SMOOTH);
		glDisable(GL_BLEND);
	}
	for(i=0;i<3;i++)
	{
		if(!fbb->draw_grid[i])
			continue;
		glLineWidth(fbb->grid_width[i]);
		glColor4fv(fbb->grid_color[i]);
		fbb_draw_wall(box[opt],opposing,
			starts,deltas,steps,i,(i+1)%3);
	}
	if(fbb->smooth_grids)
	{
		glDisable(GL_LINE_SMOOTH);
		glDisable(GL_BLEND);
	}
	glBlendFunc(GL_ONE,GL_ZERO);
}

/* Returns vertices opposing given along given axis.
 * Assumes rect created by rect_from_bounds(). */
u32_t	V_LOCAL
find_opposing_vertex_2d(u32_t vert,u32_t axis)
{
	V_LOCAL u8_t opverts[4][2] =
	{
		{1,2},
		{0,3},
		{3,0},
		{2,1}
	};
	return opverts[vert][axis];
}

/*
 *	Assumes all args correct.
 */
void	V_LOCAL
fbb_draw_axes_2d(
	functional_bounding_box_t *fbb,cam_t *viewer,
	vec3_t origin,vec3_t opposing[2],vec3_t e12[2],
	vec3_t starts[2], vec3_t deltas[2],		/* geom */
	vec3_t f_starts, vec3_t f_deltas,		/* logical */
	/*	vector from origin to start of line on which lies centers
	 *	of series #N of labels */
	vec3_t ld[2],
	u32_t steps[2])	/* number of steps along axes */
{
	int i,j,num_labels;
	vec3_t tick,label;
	vec_t val,delta;
	char buf[128];
	char *legends[2], *def_legends[2]={"X","Y"};
	glBlendFunc(GL_SRC_COLOR,GL_ONE_MINUS_SRC_COLOR);
	if(fbb->smooth_axes)
	{
		glEnable(GL_LINE_SMOOTH);
		glEnable(GL_BLEND);
	}
	else
	{
		glDisable(GL_LINE_SMOOTH);
		glDisable(GL_BLEND);
	}
	/* draw axes lines */
	for(i=0;i<2;i++)
	{
		if(!fbb->draw_axis[i])
			continue;
		glLineWidth(fbb->axis_width[i]);
		glBegin(GL_LINES);
			glColor4fv(fbb->axis_color[i]);
			glVertex3fv(origin);
			glVertex3fv(opposing[i]);
		glEnd(); /* GL_LINES */
	}
	if(fbb->smooth_axes)
	{
		glDisable(GL_LINE_SMOOTH);
		glDisable(GL_BLEND);
	}
	/* draw ticks */
	if(fbb->smooth_ticks)
	{
		glEnable(GL_POINT_SMOOTH);
		glEnable(GL_BLEND);
	}
	else
		glDisable(GL_POINT_SMOOTH);
	for(i=0;i<2;i++)
	{
		if(!fbb->draw_ticks[i])
			continue;
		vec3Copy(tick,starts[i]);
		glColor4fv(fbb->ticks_color[i]);
		glPointSize(fbb->ticks_size[i]);
		num_labels=(int)(steps[i])+1;
		glBegin(GL_POINTS);
			for(j=0;j<num_labels;j++)
			{
				glVertex3fv(tick);
				vec3Add(tick,tick,deltas[i]);
			}
		glEnd();	/* GL_POINTS */
	}
	if(fbb->smooth_ticks)
	{
		glDisable(GL_POINT_SMOOTH);
		glDisable(GL_BLEND);
	}
	/* labels */
	for(i=0;i<2;i++)
	{
		if(!fbb->draw_labels[i])
			continue;
		vec3Copy(label,starts[i]);
		vec3Add(label,label,ld[i]);
		val=f_starts[i];
		delta=f_deltas[i];
		num_labels=(int)(steps[i])+1;
		glColor4fv(fbb->label_color[i]);
		vglSelectFont(fbb->label_font[i]);
		for(j=0;j<num_labels;j++)
		{
			sprintf(buf,"%g",val);
			vglDrawString(label,viewer,buf);
			val+=delta;
			vec3Add(label,label,deltas[i]);
		}
	}
	/* legends */
	for(i=0;i<2;i++)
	{
		if(!fbb->draw_legend[i])
			continue;
		legends[i]=fbb->legend[i] ? fbb->legend[i] : def_legends[i];
		/* TODO: make offset adjustable */
		vec3Scale(label,e12[i],LD_DIST);
		vec3Add(label,label,opposing[i]);
		glColor4fv(fbb->legend_color[i]);
		vglDrawString(label,viewer,legends[i]);
	}
	glBlendFunc(GL_ONE,GL_ZERO);
}

void	V_LOCAL
fbb_draw_one_wall(vec3_t origin,vec3_t opposing[2],
	vec3_t starts[2],vec3_t offsets[2],u32_t steps[2])
{
	u32_t i,num_steps;
	vec3_t p1,p2,delta;
	vec3_t fourth;
	vec3Sub(fourth,opposing[0],origin);
	vec3Add(fourth,fourth,opposing[1]);
	/* draw wall bounds */
	glBegin(GL_LINE_STRIP);
		glVertex3fv(opposing[0]);
		glVertex3fv(fourth);
		glVertex3fv(opposing[1]);
	glEnd();	/* GL_LINE_STRIP */
	/* draw grid lines - along first direction: */
	num_steps=steps[0]+1;
	vec3Copy(p1,starts[0]);
	vec3Sub(p2,p1,origin);
	vec3Add(p2,opposing[1],p2);
	vec3Copy(delta,offsets[0]);
	glBegin(GL_LINES);
	for(i=0;i<num_steps;i++)
	{
		glVertex3fv(p1);
		glVertex3fv(p2);
		vec3Add(p1,p1,delta);
		vec3Add(p2,p2,delta);
	}
	glEnd();	/* GL_LINES */
	/* ... and along second: */
	num_steps=steps[1];
	vec3Copy(p1,starts[1]);
	vec3Sub(p2,p1,origin);
	vec3Add(p2,opposing[0],p2);
	vec3Copy(delta,offsets[1]);
	glBegin(GL_LINES);
	for(i=0;i<num_steps;i++)
	{
		glVertex3fv(p1);
		glVertex3fv(p2);
		vec3Add(p1,p1,delta);
		vec3Add(p2,p2,delta);
	}
	glEnd();	/* GL_LINES */
}

/*
 *	Creates vertices of rect with opposite corners 'mins' and 'maxs'.
 *	Odd lies on line 'x=mins[0]'; even - on line 'x=maxs[0]'.
 *	First two - on line 'y=mins[1]'; other - on line 'y=maxs[1]'.
 */
void	V_LOCAL
rect_from_corners(vec3_t verts[],vec3_t mins,vec3_t maxs)
{
	/* y = mins[1] */
	vec3Set(verts[0],mins[0],mins[1],0);
	vec3Set(verts[1],maxs[0],mins[1],0);
	/* y = maxs[1] */
	vec3Set(verts[2],mins[0],maxs[1],0);
	vec3Set(verts[3],maxs[0],maxs[1],0);
}

void	V_GLOBAL
fbbDraw2D(functional_bounding_box_t *fbb)
{
	u32_t i,opt;
	u32_t steps[2];
	vec_t in_len,ts,td;
	/* vec_t nd, dist; */
	vec3_t rect[4];
	vec3_t starts[2], deltas[2], e12[2], opposing[2];
	vec3_t f_starts,f_deltas;
	vec3_t ld[2];
	if(!fbb)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"fbbDraw2D(NULL): incorrect parameter.");
#endif	/* CHECKED_BUILD */
		return;
	}
	if(!fbb->cam)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,
			"fbbDraw2D(): you must set camera prior to draw.");
#endif	/* CHECKED_BUILD */
		return;
	}
	/* make geometry rect */
	rect_from_corners(rect,fbb->mins,fbb->maxs);
	/* determine axes corner */
	opt=0;
	/*
		see above ...
	*/
	/* compute offsets and increments along all axes */
	for(i=0;i<2;i++)
	{
		steps[i]=fbb->steps[i];
		compute_range_axis(fbb->f_mins[i],fbb->f_maxs[i],steps[i],
			&f_deltas[i],&f_starts[i],&steps[i]);
		vec3Copy(opposing[i],rect[find_opposing_vertex_2d(opt,i)]);
		in_len=fbb->f_maxs[i]-fbb->f_mins[i];
		ts=(f_starts[i]-fbb->f_mins[i])/in_len;
		td=f_deltas[i]/in_len;
		vec3Sub(e12[i],opposing[i],rect[opt]);
		vec3Scale(deltas[i],e12[i],td);
		vec3Scale(starts[i],e12[i],ts);
		vec3Add(starts[i],starts[i],rect[opt]);
	}
	vec3Scale(ld[0],e12[1],-1 * LD_DIST);
	vec3Scale(ld[1],e12[0],-1 * LD_DIST);
	/* draw axes, ticks, labels and legends */
	fbb_draw_axes_2d(fbb,fbb->cam,rect[opt],opposing,e12,
		starts,deltas,f_starts,f_deltas,ld,steps);
	/* draw walls */
	if(fbb->draw_grid[0])
	{
    	if(fbb->smooth_grids)
    	{
    		glEnable(GL_LINE_SMOOTH);
    		glEnable(GL_BLEND);
    		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    	}
    	else
    	{
    		glDisable(GL_LINE_SMOOTH);
    		glDisable(GL_BLEND);
    	}
		glLineWidth(fbb->grid_width[0]);
		glColor4fv(fbb->grid_color[0]);
		fbb_draw_one_wall(rect[opt],opposing,starts,deltas,steps);
    	if(fbb->smooth_grids)
    	{
    		glDisable(GL_LINE_SMOOTH);
    		glDisable(GL_BLEND);
    	}
	}
	glBlendFunc(GL_ONE,GL_ZERO);
}

/*</pre></html>*/
/*EOF*/
