 /*
  * Khoros: $Id: text_3D.c,v 1.1 1991/05/10 15:56:56 khoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: text_3D.c,v 1.1 1991/05/10 15:56:56 khoros Exp $";
#endif

 /*
  * $Log: text_3D.c,v $
 * Revision 1.1  1991/05/10  15:56:56  khoros
 * Initial revision
 *
  */ 


/*
 *----------------------------------------------------------------------
 *
 * Copyright 1990, University of New Mexico.  All rights reserved.
 * 
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as too the sui-
 * tability and operability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 * 
 * UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 * NESS.  IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHAT-
 * SOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *----------------------------------------------------------------------
 */

#include "unmcopyright.h"	 /* Copyright 1990 by UNM */
#include "X3D.h"

/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>	    	    file name: text_3D.c                      <<<<
   >>>>                                                       <<<<
   >>>>                 3D Text Utilities    	      	      <<<<
   >>>>                                                       <<<<
   >>>>			X3D_set_text()			      <<<<
   >>>>			X3D_draw_text()			      <<<<
   >>>>			X3D_clear_text()	      	      <<<<
   >>>>			_X3D_get_radians()	      	      <<<<
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */


/************************************************************
*
*  MODULE NAME: X3D_set_text
*
*      PURPOSE: sets justification, spacing, aspect ratio for drawing text
*
*        INPUT: justify  - may be one of 
*			   LeftJustify, CenterJustify, RightJustify
*		spacing  - world coordinate amount of spacing between characters
*		aspect   - width:height aspect ratio .8 => width is .8 of height
*
*       OUTPUT: none
*
*    CALLED BY: application program
*
*   WRITTEN BY:	Mark Young
*
*************************************************************/


X3D_set_text (id, justify, spacing, aspect)

int	id, justify;
Real	spacing, aspect;
{
        X3DGraphics     *graphics;

        if (!(graphics = _X3D_get_graphics(id)))
        {
           fprintf (stderr,"X3D_set_text:");
           fprintf (stderr,"\t unknown graphics id %d\n",id);
           return;
        }

	graphics->justify = justify;
	graphics->spacing = spacing;
	graphics->aspect  = aspect;
}




/************************************************************
*
*  MODULE NAME: X3D_draw_text
*
*      PURPOSE: Draws text into a 3D window
*
*        INPUT: id - the xvgraphics id
*		coord - coordinate describing lower left hand corner of text
*		vector - vector describing direction for text to be drawn
*		width  - width of a single character
*		height - height of a single character
*		depth  - depth of a single character
*		string - the string to be drawn
*		size   - number of characters in the string 
*               fg     - the foreground color
*		
*       OUTPUT: draws 3D text 
*
*    CALLED BY: application program
*
*   WRITTEN BY: Mark Young
*
*************************************************************/


X3D_draw_text (id, coord, vector, width, height, depth, string, size, fg)

int     id, size;
Coord   coord;
Vector  vector;
Real	width, height, depth;
char    *string;
XColor  *fg;
{
	char		ch;
	int		i, j, found, seg_num;
        X3DGraphics     *graphics, *text;
	XSegment	segments[512];
	Matrix_2D	matrix1, matrix2;
	X3DFont		*font, *_X3D_get_font();

	short		x, y, dc_width, dc_height;
	Coord		wc_min, wc_max, tmp;
	Real		x1, x2, y1, y2, xmin, xmax, ymin,
			ymax, xcenter, ycenter, theta, spacing;
	double		_X3D_get_radians();

        if (!(graphics = _X3D_get_graphics(id)))
        {
           fprintf (stderr,"X2D_draw_text: ");
           fprintf (stderr,"\t unknown graphics id\n");
           return;
        }

	/*
	 *  Get the graphics font.
	 */
	if (graphics->font != NULL)
	   font = graphics->font;
	else
	{
	   /*
	    *  The graphics font did not exist, so try to use the default
	    *  font.
	    */
	   font = _X3D_get_font(DefaultFont);
	   if (font == NULL)
	   {
              fprintf (stderr,"X3D_draw_text: ");
              fprintf (stderr,"\t unknown font type\n");
              return;
	   }
	}

        if (!(text = _X3D_get_graphics(TEXT_ID)))
        {
           fprintf (stderr,"X3D_draw_text: ");
           fprintf (stderr,"\t unknown graphics id\n");
           return;
        }

	/* now set the scale back to linear so the text is drawn correctly */
	text->win_type  = graphics->win_type;
	text->device    = graphics->device;
	text->pfile      = graphics->pfile;
	text->ifile      = graphics->ifile;
	text->display   = graphics->display;
	text->workspace = graphics->workspace;
	text->wc_min = font->wc_min;
	text->wc_max = font->wc_max;

	tmp = coord;
	if(!(X3D_convert_point_wc_to_dc(id, tmp, &x1, &y1)))
	   return;

	tmp.x += vector.x;
        tmp.y += vector.y;
        tmp.z += vector.z;
        if(!(X3D_convert_point_wc_to_dc(id, tmp, &x2, &y2)))
           return;

        x1 = x2 - x1;
        y1 = y1 - y2;
	theta   = (float) _X3D_get_radians((double) x1, (double) y1);
	spacing	= graphics->spacing;
	width  *= graphics->aspect;
	depth  *= graphics->aspect;

	/*
	 *  Get the wc bounding box for the first character to be drawn
	 *  in.  The point in which we start drawing the first character
	 *  from depends on the string justification specified by the user
	 *  (in the "X3D_set_text" routine).
	 */
	switch (graphics->justify)
	{
	   case LeftJustify:
		wc_min.x = coord.x - width/2.0;
		break;

	   case CenterJustify:
		wc_min.x = coord.x - (width * size/2.0) - (width * spacing *
			   size/2.0);
		break;

	   case RightJustify:
		wc_min.x = (coord.x - width/2.0) - (width * (size -1)) -
			   (width * spacing * (size -1));
		break;

	   default:
	 	wc_min.x = coord.x - width/2.0;
		break;
	}
	wc_min.z = coord.z - height/2.0;
	wc_min.y = coord.y - depth/2.0;
	wc_max.x = wc_min.x + width;
	wc_max.y = wc_min.y + depth;
	wc_max.z = wc_min.z + height;

	/*
	 *  Get the dc extent for this character.
	 */
	if(!(X3D_convert_point_wc_to_dc(id, wc_min, &xmin, &ymin)))
	   return;

	if(!(X3D_convert_point_wc_to_dc(id, wc_max, &xmax, &ymax)))
	   return;

	x	  = xmin + 0.5;
	dc_width  = (xmax - xmin) + 0.5;
	if (graphics->device == X11 || graphics->device == IMPRESS)
	{
	   y = ymax;
           dc_height = ymin - ymax;
	}
	else
	{
	   y = ymin;
	   dc_height = ymax - ymin;
	}

	/*
	 *  Change the orientation of the characters so that they
	 *  follow the path of the vector.  This is done by translating
	 *  the characters to the origin, and then rotating it by theta
	 *  degrees.
	 */
	theta = 180 * (theta/XV_PI);
	if(!(X3D_convert_point_wc_to_dc(id, coord, &xcenter, &ycenter)))
	   return;

	X2D_matrix_set_translate( -xcenter, -ycenter, matrix1);
	X2D_matrix_set_rotate(theta, matrix2);
	_X2D_matrix_mult(matrix1, matrix2, matrix1);
	X2D_matrix_set_translate(xcenter, ycenter, matrix2);
	_X2D_matrix_mult(matrix1, matrix2, matrix1);

	/*
	 *  For each character in the string, find the corresponding
	 *  font entry.  Then get the character's dc min and max for
	 *  for that character, update the "text" graphics structure
	 *  and draw it.  
	 */
	X3D_draw[graphics->device].draw_color(graphics, fg, FALSE);
	for (i = 0; i < size; i++)
	{
	    /*
	     *  Find the corresponding font entry for the desired
	     *  character.
	     */
	    found = FALSE; j = 0;
	    while (j < font->num  && (!found))
	    {
		if (font->character[j].ch == string[i])
		   found = TRUE;
		else
		   j++;
	    }

	    /*
	     *  Check to see if we found the character.  If not leave a
	     *  blank space in place of the character.
	     */
	    if (found == TRUE)
	    {
	       /*
		*  Update the text structure with this new dc information.
		*  Then multiply the rotation matrix with the text trans-
		*  formation matrix.  This will orient the character before
		*  transforming it.
		*/
	       X2D_set_window(TEXT_ID, x, y, dc_width, dc_height);
	       _X2D_matrix_mult(text->matrix2D, matrix1, text->matrix2D);

	       /*
		*  Convert the character to dc value segments and then
		*  draw them.
		*/
	       if(!(_X2D_convert_wc_to_dc_seg(text, font->character[j].points, 
					 segments, font->character[j].num)))
		   return;

	       seg_num = (font->character[j].num)/2;
	       X3D_draw[graphics->device].segments(graphics, segments, seg_num);
	    }

	    /*
	     *  Increment the wc bounding box for the next character to be
	     *  drawn in.  We do this by making the new x & y min the old
	     *  x & y max and adding in the character spacing (specified
	     *  by the application program thru the "X3D_set_text" routine).
	     */
	    x += (short) dc_width + spacing * dc_width;
	}
}

/************************************************************
*
*  MODULE NAME: _X3D_get_radians
*
*      PURPOSE: Takes an (x,y) point and returns the associated radians
*
*        INPUT: x, y - the (x,y) point (vector)
*
*	OUTPUT: the radian equivalent
*        
*    WRITTEN BY: Mark Young
*
*************************************************************/

double _X3D_get_radians(x, y)

double	x, y;
{
	double theta;

	if (x == 0)
	{
	   if (y > 0)
	      return(_X3D_DegToRads(90.0));
	   else
	      return(_X3D_DegToRads(270.0));
	}
	else if (x < 0)
	   theta = XV_PI - atan((y/(fabs(x))));

	else
	   theta = atan( y/x);

	return(theta);
}



/************************************************************
*
*  MODULE NAME: X3D_clear_text
*
*      PURPOSE: Clears text from a 3D window
*
*        INPUT: id - the xvgraphics id
*		coord - coordinate describing lower left hand corner of text
*		vector - vector describing direction for text to be cleared
*		width  - width of a single character
*		height - height of a single character
*		size   - number of characters in the string 
*               bg     - the background color
*		
*       OUTPUT: clears text 
*
*    CALLED BY: application program
*
*   WRITTEN BY: Mark Young
*
*************************************************************/

X3D_clear_text(id, coord, vector, width, height, size, bg)

int     id, size;
Coord   coord;
Vector  vector;
Real    width, height;
XColor  *bg;
{
        X3DGraphics     *graphics;
        Matrix_2D       matrix1, matrix2;
	XPoint          points[4];


	Coord		wc_min, wc_max, tmp;
        Real            xmin, xmax, ymin, ymax, dumb, delta_width,
                        xcenter, ycenter, theta, spacing, dummy,
                        x1, x2, y1, y2; 
	double		_X3D_get_radians();

        if (!(graphics = _X3D_get_graphics(id)))
        {
           fprintf (stderr,"X2D_clear_text:");
           fprintf (stderr,"\t unknown graphics id %d\n",id);
           return;
        }

        tmp = coord;
        if(!(X3D_convert_point_wc_to_dc(id, tmp, &x1, &y1)))
	   return;

        tmp.x += vector.x;
        tmp.y += vector.y;
        tmp.z += vector.z;
        if(!(X3D_convert_point_wc_to_dc(id, tmp, &x2, &y2)))
	   return;

        x1 = x2 - x1;
        y1 = y1 - y2;

        theta   = (float) _X3D_get_radians((double)x1, (double)y1);
        spacing = graphics->spacing;

        /*
         *  Get the wc bounding box for the first character to be drawn
         *  in.  The point in which we start drawing the first character
         *  from depends on the string justification specified by the user
         *  (in the "X3D_set_text" routine).
         */
        switch (graphics->justify)
        {
           case LeftJustify:
                wc_min.x = coord.x - width/2.0;
                break;

           case CenterJustify:
                wc_min.x = coord.x - (width * size/2.0) - (width * spacing *
			   size/2.0);
                break;

           case RightJustify:
                wc_min.x = (coord.x - width/2.0) - (width * (size -1)) - 
			   (width * spacing * (size -1));
                break;

           default:
		wc_min.x = coord.x - width/2.0;
                break;
        }
        wc_max.x = wc_min.x + width;
        wc_min.y = wc_max.y = coord.y;
        wc_min.z = wc_max.z = coord.z;

        /*
         *  Get the dc extent for this character.
         */
        if(!(X3D_convert_point_wc_to_dc(id, wc_min, &xmin, &ymax)))
	   return;

        if(!(X3D_convert_point_wc_to_dc(id, wc_max, &xmax, &ymin)))
	   return;

        delta_width = (xmax - xmin);
        wc_min.x = MIN(xmin,xmax);
        wc_max.x = MAX(xmin,xmax) + delta_width*((size -1) + (size -1)*spacing);

        wc_min.y = MIN(ymin,ymax) - fabs((double) delta_width/(2.0 * graphics->aspect));
        wc_max.y = MAX(ymin,ymax) + fabs((double) delta_width/(2.0 * graphics->aspect));


        /*
         *  Change the orientation of the characters so that they
         *  follow the path of the vector.  This is done by translating
         *  the characters to the origin, and then rotating it by theta
         *  degrees.
         */
        theta = 180 * (theta/XV_PI);
        if(!(X3D_convert_point_wc_to_dc(id, coord, &xcenter, &ycenter)))
	   return;

        X2D_matrix_set_translate( -xcenter, -ycenter, matrix1);
        X2D_matrix_set_rotate(theta, matrix2);
        _X2D_matrix_mult(matrix1, matrix2, matrix1);

        X2D_matrix_set_translate(xcenter, ycenter, matrix2);
        _X2D_matrix_mult(matrix1, matrix2, matrix1);

	
	tmp.x = wc_min.x;
	tmp.y = wc_min.y;
	_X2D_vector_mult(tmp, matrix1, x1, y1, dummy);
	points[0].x = (short) (x1 + 0.5);
	points[0].y = (short) (y1 + 0.5);

	tmp.x = wc_max.x;
	tmp.y = wc_min.y;
	_X2D_vector_mult(tmp, matrix1, x1, y1, dummy);
	points[1].x = (short) (x1 + 0.5);
	points[1].y = (short) (y1 + 0.5);

	tmp.x = wc_max.x;
	tmp.y = wc_max.y;
	_X2D_vector_mult(tmp, matrix1, x1, y1, dummy);
	points[2].x = (short) (x1 + 0.5);
	points[2].y = (short) (y1 + 0.5);

	tmp.x = wc_min.x;
	tmp.y = wc_max.y;
	_X2D_vector_mult(tmp, matrix1, x1, y1, dummy);
	points[3].x = (short) (x1 + 0.5);
	points[3].y = (short) (y1 + 0.5);

	X3D_draw[graphics->device].fill_color(graphics, bg, FALSE);
        X3D_draw[graphics->device].fill_polygon(graphics, points, 4, Convex,
			CoordModeOrigin, FALSE);
}
