/*
    This file is part of the FElt finite element analysis package.
    Copyright (C) 1993 Jason I. Gobat and Darren C. Atkinson

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/****************************************************************************
 * 
 * File:	structure.c
 *
 * Description:	Contains routines to visualize structural problems in velvet
 *		Both 2d and 3d line drawing are supported ... displaced
 * 		shapes are drawn over the original shape.
 *
 ****************************************************************************/

# include <stdio.h>
# include <math.h>
# include <X11/Xlib.h>
# include <X11/Xutil.h>
# include <X11/Intrinsic.h>
# include <X11/StringDefs.h>
# include <X11/Xaw/Form.h>
# include <X11/Xaw/Dialog.h>
# include <X11/Shell.h>
# include "Solution.h"
# include "fe.h"
# include "Drawing.h"
# include "allocate.h"

extern Widget toplevel;

# define MaxNodesPerElement 	12

static double x_min3d, y_min3d, z_min3d;
static double xscale3d, yscale3d, zscale3d;

static double transform [4][4];

static void Setup3D ( ),
            Convert3Dto2D ( ),
            VisMultiplyMatrices  ( ),
            MatrixRotationX   ( ),
            MatrixRotationY   ( ),
            MatrixRotationZ   ( ),
            CreateScaleMatrix ( ),
            CreateUnitMatrix  ( );

static Widget	structShell;
static Widget	struct_dw;
static int     	first_time = 1;

static unsigned	OkayButton = 1;
static unsigned SaveButton = 2;

static void	ButtonActions ();

void CreateStructureShell ()
{
   Arg			arglist [10];
   Cardinal		count;
   Widget		formwidget,buttonwidget;

   structShell = XtCreatePopupShell ("structShell",transientShellWidgetClass,
                               toplevel,NULL, 0);

   count = 0;
   XtSetArg (arglist [count], XtNborderWidth, 0); count++;
   formwidget = XtCreateManagedWidget ("form", formWidgetClass, 
                                       structShell, arglist, count);

   count = 0;
   XtSetArg (arglist [count], XtNgrid, False); count++;
   XtSetArg (arglist [count], XtNtop, XawChainTop); count++;
   XtSetArg (arglist [count], XtNbottom, XawChainBottom); count++;
   XtSetArg (arglist [count], XtNborderWidth, 3); count++;
   struct_dw = XtCreateManagedWidget ("struct_dw", drawingWidgetClass, 
                                      formwidget, arglist, count);

   count = 0;
   XtSetArg (arglist [count], XtNlabel, ""); count++;
   XtSetArg (arglist [count], XtNborderWidth, 0); count++;
   XtSetArg (arglist [count], XtNtop, XawChainBottom); count++;
   XtSetArg (arglist [count], XtNbottom, XawChainBottom); count++;
   XtSetArg (arglist [count], XtNleft, XawChainLeft); count++;
   XtSetArg (arglist [count], XtNright, XawChainLeft); count++;
   XtSetArg (arglist [count], XtNfromVert, struct_dw); count++;

   buttonwidget = XtCreateManagedWidget ("buttons", dialogWidgetClass,
                                         formwidget, arglist, count);

   XawDialogAddButton (buttonwidget,"dismiss",ButtonActions,&OkayButton);
   XawDialogAddButton (buttonwidget,"save",ButtonActions,&SaveButton);

   return; 
}

void VisualizeStructure (element, numelts)
   Element	*element;
   unsigned	numelts;
{
   Point	points [MaxNodesPerElement];
   unsigned	n;
   unsigned	i,j;
   float	x_max, x_min,
		y_max, y_min;
   float	maxX, minX, 
                maxY, minY, 
                Xscale, Yscale;
   float	x,y;
   float	x1,x2,y1,y2;
   Dimension	width, height;

   if (first_time) {
      CreateStructureShell ();
      first_time = 0;
   }

   x_max = x_min = element [1] -> node[1] -> x +
             (element[1] -> node[1] -> dx[1]*solution -> magnify); 
   y_max = y_min = element [1] -> node[1] -> y + 
             (element[1] -> node[1] -> dx[2]*solution -> magnify); 

   for (i = 1 ; i <= numelts ; i++) {
      for (j = 1 ; j <= element[i] -> definition -> numnodes ; j++) {

         x = element[i] -> node[j] -> x + 
            (element[i] -> node[j] -> dx[1] *solution -> magnify);
         y = element[i] -> node[j] -> y + 
            (element[i] -> node[j] -> dx[2] *solution -> magnify);

         if (x > x_max)
            x_max = x;
         else if (x < x_min)
            x_min = x;

         if (y > y_max) 
            y_max = y;
         else if (y < y_min) 
            y_min = y;

         x = element[i] -> node[j] -> x;
         y = element[i] -> node[j] -> y; 

         if (x > x_max)
            x_max = x;
         else if (x < x_min)
            x_min = x;

         if (y > y_max) 
            y_max = y;
         else if (y < y_min) 
            y_min = y;
      }
   }

   minX = x_min - 0.05*(x_max - x_min); 
   minY = y_min - 0.05*(y_max - y_min);
   maxX = x_max + 0.05*(x_max - x_min);
   maxY = y_max + 0.05*(y_max - y_min);
   
   InitializeDrawingShell (structShell, struct_dw, minX, maxX, minY, maxY,
                           &Xscale, &Yscale, &width, &height);

   DW_RemoveAll (struct_dw);

   DW_SetAutoRedraw (struct_dw, False);

   DW_SetForeground (struct_dw, "yellow");
   for (i = 1 ; i <= numelts ; i++) {
      n = element[i] -> definition -> numnodes;
      if (n > 2) {
         for (j = 1 ; j <= n ; j++) {
            points [j-1].x = element [i] -> node[j] -> x;
            points [j-1].y = element [i] -> node[j] -> y;
         }
         points [n].x = element[i] -> node[1] -> x;
         points [n].y = element[i] -> node[1] -> y;

         DW_DrawPolygon (struct_dw, True, points, n+1);
      } else 
         DW_DrawLine (struct_dw, element[i] -> node[1] -> x,
                      element[i] -> node[1] -> y, element[i] -> node[2] -> x,
                      element[i] -> node[2] -> y); 
   }

   DW_SetForeground (struct_dw, "black");
   for (i = 1 ; i <= numelts ; i++) {
      n = element[i] -> definition -> numnodes;
      if (n > 2) {
         for (j = 1 ; j <= n ; j++) {
            points [j-1].x = element [i] -> node[j] -> x +
                             (element[i] -> node[j] -> dx[1] * solution -> magnify);
            points [j-1].y = element [i] -> node[j] -> y +
                             (element[i] -> node[j] -> dx[2] * solution -> magnify);
         }
         points [n].x = element[i] -> node[1] -> x +
                        (element[i] -> node[1] -> dx[1] * solution -> magnify);
         points [n].y = element[i] -> node[1] -> y +
                        (element[i] -> node[1] -> dx[2] * solution -> magnify);

         DW_DrawPolygon (struct_dw, True, points, n+1);
      } else {
         x1 = element[i] -> node[1] -> x +
              (element[i] -> node[1] -> dx[1] * solution -> magnify); 
         x2 = element[i] -> node[2] -> x +
              (element[i] -> node[2] -> dx[1] * solution -> magnify); 
         y1 = element[i] -> node[1] -> y +
              (element[i] -> node[1] -> dx[2] * solution -> magnify); 
         y2 = element[i] -> node[2] -> y +
              (element[i] -> node[2] -> dx[2] * solution -> magnify); 

         DW_DrawLine (struct_dw, x1, y1, x2, y2);
      }
   }
   DW_SetAutoRedraw (struct_dw, True);
}

void VisualizeStructure3D (element, numelts)
   Element	*element;
   unsigned	numelts;
{
   Point	points [MaxNodesPerElement];
   unsigned	i,j;
   float	maxX, minX, 
                maxY, minY, 
                maxZ, minZ, 
		Xscale, Yscale;
   float	xdiff, ydiff;
   float	x,y,z,
		sx,sy;
   Dimension	width, height;
   struct s_pair {
      float	xd[MaxNodesPerElement], yd[MaxNodesPerElement];
      float    	x[MaxNodesPerElement], y[MaxNodesPerElement]; 
   } *s_element;

   if (first_time) {
      CreateStructureShell ();
      first_time = 0;
   }

   s_element = Allocate (struct s_pair, numelts);
   if (s_element == NULL)
      Fatal ("allocation error creating 3d plot");

   UnitOffset (s_element);

   maxX = minX = element[1] -> node[1] -> x +
                 (element[1] -> node[1] -> dx[1] * solution -> magnify);
   maxY = minY = element[1] -> node[1] -> y +
                 (element[1] -> node[1] -> dx[2] * solution -> magnify);
   maxZ = minZ = element[1] -> node[1] -> z +
                 (element[1] -> node[1] -> dx[3] * solution -> magnify);

   for (i = 1 ; i <= numelts ; i++) {
      for (j = 1 ; j <= element[i] -> definition -> numnodes; j++) {

         x = element [i] -> node[j] -> x +
             (element [i] -> node[j] -> dx[1] * solution -> magnify);
         y = element [i] -> node[j] -> y +
             (element [i] -> node[j] -> dx[2] * solution -> magnify);
         z = element [i] -> node[j] -> z +
             (element [i] -> node[j] -> dx[3] * solution -> magnify);

         if (x > maxX) maxX = x;
         else if (x < minX) minX = x;

         if (y > maxY) maxY = y;
         else if (y < minY) minY = y;

         if (z > maxZ) maxZ = z;
         else if (z < minZ) minY = z;

         x = element [i] -> node[j] -> x;
         y = element [i] -> node[j] -> y;
         z = element [i] -> node[j] -> z;

         if (x > maxX) maxX = x;
         else if (x < minX) minX = x;

         if (y > maxY) maxY = y;
         else if (y < minY) minY = y;

         if (z > maxZ) maxZ = z;
         else if (z < minZ) minY = z;
      }
   }

   Setup3D (minX,maxX,minY,maxY,minZ,maxZ);

   xdiff = maxX - minX;
   ydiff = maxY - minY;

   for (i = 1 ; i <= numelts ; i++) {
      for (j = 1 ; j <= element [i] -> definition -> numnodes ; j++) {

         x = element [i] -> node[j] -> x +
             (element [i] -> node[j] -> dx[1] * solution -> magnify);
         y = element [i] -> node[j] -> y +
             (element [i] -> node[j] -> dx[2] * solution -> magnify);
         z = element [i] -> node[j] -> z +
             (element [i] -> node[j] -> dx[3] * solution -> magnify);
         
         Convert3Dto2D (x, y, z, xdiff, ydiff, &sx, &sy);

         if (i == 1 && j == 1) {
            maxX = minX = sx;
            maxY = minY = sy;
         }
         else {
            if (sx > maxX) maxX = sx;
            else if (sx < minX) minX = sx;

            if (sy > maxY) maxY = sy;
            else if (sy < minY) minY = sy;
         } 
           
         s_element[i].xd[j] = sx;
         s_element[i].yd[j] = sy;

         x = element [i] -> node[j] -> x;
         y = element [i] -> node[j] -> y;
         z = element [i] -> node[j] -> z;
         
         Convert3Dto2D (x, y, z, xdiff, ydiff, &sx, &sy);

         if (i == 1 && j == 1) {
            maxX = minX = sx;
            maxY = minY = sy;
         }
         else {
            if (sx > maxX) maxX = sx;
            else if (sx < minX) minX = sx;

            if (sy > maxY) maxY = sy;
            else if (sy < minY) minY = sy;
         } 
           
         s_element[i].x[j] = sx;
         s_element[i].y[j] = sy;
      }
   } 

   maxX += 0.05*(maxX - minX);
   maxY += 0.05*(maxY - minY);
   minY -= 0.05*(maxY - minY);
   minX -= 0.05*(maxX - minX);

   InitializeDrawingShell (structShell, struct_dw, minX, maxX, minY, maxY,
                           &Xscale, &Yscale, &width, &height);

   DW_RemoveAll (struct_dw);
    
   DW_SetForeground (struct_dw, "yellow");
   for (i = 1 ; i <= numelts ; i++) {
      if (element [i] -> definition -> numnodes > 2) {
         for (j = 1 ; j <= element[i] -> definition -> numnodes ; j++) {
            points [j-1].x = s_element[i].x[j];
            points [j-1].y = s_element[i].y[j];
         }
         points [j].x = s_element[i].x[1];
         points [j].y = s_element[i].y[1];

         DW_DrawPolygon (struct_dw, True, points, j+1);
      } else
         DW_DrawLine (struct_dw, s_element[i].x[1], s_element[i].y[1],
            s_element[i].x[2], s_element[i].y[2]); 
   }

   DW_SetForeground (struct_dw, "black");
   for (i = 1 ; i <= numelts ; i++) {
      if (element [i] -> definition -> numnodes > 2) {
         for (j = 1 ; j <= element[i] -> definition -> numnodes ; j++) {
            points [j-1].x = s_element[i].xd[j];
            points [j-1].y = s_element[i].yd[j];
         }
         points [j].x = s_element[i].xd[1];
         points [j].y = s_element[i].yd[1];

         DW_DrawPolygon (struct_dw, True, points, j+1);
      } else
         DW_DrawLine (struct_dw, s_element[i].xd[1], s_element[i].yd[1],
            s_element[i].xd[2], s_element[i].yd[2]); 
   }
   
   ZeroOffset (s_element);
   Deallocate (s_element);
}

static void ButtonActions (w, client_data, call_data)
   Widget	w;
   XtPointer	client_data,
		call_data;
{
   unsigned	selected = *(unsigned *) client_data;

   if (selected == OkayButton) 
      XtPopdown (structShell);
   else if (selected == SaveButton)
      SaveWidgetXWD (struct_dw);
}                       

static void Convert3Dto2D (x, y, z, xdiff, ydiff, xt, yt)
   double 	x, y, z;
   float	xdiff, ydiff;
   float 	*xt, *yt;
{
   int 		i,j;
   double 	v[4], res[4],		     
	  	w = transform[3][3];

   v[0] = (x - x_min3d)*xscale3d - 1.0;
   v[1] = (y - y_min3d)*yscale3d - 1.0;
   v[2] = (z - z_min3d)*zscale3d - 1.0;
   v[3] = 1.0;

   for (i = 0; i < 2; i++) {	             
       res[i] = transform[3][i];     
       for (j = 0; j < 3; j++) 
          res[i] += v[j] * transform[j][i];
   }

   for (i = 0; i < 3; i++) 
      w += v[i] * transform[i][3];

   if (w == 0) 
      w = 0.00001;

   *xt = ((res[0] * 10.0 / w));
   *yt = ((res[1] * 10.0*ydiff/xdiff / w));
}

static void Setup3D (min_x, max_x, min_y, max_y, min_z, max_z)
   double 	min_x, max_x;
   double 	min_y, max_y;
   double 	min_z, max_z;
{
   double 	ztemp, temp;
   double 	mat [4][4];

	/*
	 * perform the rotations 
	 */

   MatrixRotationY (solution -> yrot, transform);
   MatrixRotationX (solution -> xrot, mat);
   VisMultiplyMatrices (transform, transform, mat);
   MatrixRotationZ (solution -> zrot, mat);
   VisMultiplyMatrices (transform, transform, mat);
   CreateScaleMatrix (0.5, 0.5, 0.5, mat);
   VisMultiplyMatrices (transform, transform, mat);

	/*
	 * fudge min / max so the z direction will scale
	 */

   ztemp = (max_z - min_z) / (2.0 * solution -> zscale);
   temp = (max_z + min_z) / 2.0;
   min_z = temp - ztemp;
   max_z = temp + ztemp;

	/* 
	 * set the global variables 
	 */

   x_min3d = min_x;
   y_min3d = min_y;
   z_min3d = min_z;

   xscale3d = 2.0/(max_x - min_x);
   yscale3d = 2.0/(max_y - min_y);
   if (max_z - min_z > 0)
      zscale3d = 2.0/(max_z - min_z);
   else
      zscale3d = 0.0;
}

static void CreateUnitMatrix (mat)
   double 	mat[4][4];
{
   unsigned	i, j;

   for (i = 0; i < 4; i++) {
      for (j = 0; j < 4; j++) {

         if (i == j)
            mat[i][j] = 1.0;
         else
            mat[i][j] = 0.0;
      }
   }
}

static void CreateScaleMatrix (sx, sy, sz, mat)
   double 	sx, sy, sz;
   double 	mat[4][4];
{
    CreateUnitMatrix(mat);                              

    mat[0][0] = sx;
    mat[1][1] = sy;
    mat[2][2] = sz;
}

static void MatrixRotationX (theta, mat)
   double 	theta;
   double 	mat[4][4];
{
   theta *= M_PI / 180.0;

   CreateUnitMatrix(mat);                              
   mat[1][1] = cos(theta);
   mat[1][2] = -sin(theta);
   mat[2][1] = sin(theta);
   mat[2][2] = cos(theta);
}

static void MatrixRotationY (theta, mat)
   double 	theta;
   double 	mat[4][4];
{
   theta *= M_PI / 180.0;

   CreateUnitMatrix(mat);                             
   mat[0][0] = cos(theta);
   mat[0][2] = -sin(theta);
   mat[2][0] = sin(theta);
   mat[2][2] = cos(theta);
}

static void MatrixRotationZ (theta, mat)
   double 	theta;
   double 	mat[4][4];
{
   theta *= M_PI / 180.0;

   CreateUnitMatrix(mat);                            
   mat[0][0] = cos(theta);
   mat[0][1] = -sin(theta);
   mat[1][0] = sin(theta);
   mat[1][1] = cos(theta);
}

static void VisMultiplyMatrices (result, mat_a, mat_b)
   double 	result [4][4], 
           	mat_a [4][4], 
           	mat_b [4][4];
{
   unsigned	i,j,k;
   double 	temp[4][4];

   for (i = 0; i < 4; i++) {
      for (j = 0; j < 4; j++) {
         temp[i][j] = 0;
         for (k = 0; k < 4; k++) 
            temp[i][j] += mat_a[i][k] * mat_b[k][j];
      }
   }

   for (i = 0; i < 4; i++) 
      for (j = 0; j < 4; j++) 
         result[i][j] = temp[i][j];
}
