/*
    This file is part of the FElt finite element analysis package.
    Copyright (C) 1993-2000 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:	vfe.c							*
 *									*
 * Description:	This file contains the function definitions for the	*
 *		visualization of the finite element structures.		*
 ************************************************************************/

# include <stdio.h>
# include <string.h>
# include <X11/Intrinsic.h>
# include <X11/StringDefs.h>
# include <X11/Xaw/Viewport.h>
# include "Drawing.h"
# include "Canvas.h"
# include "fe.h"
# include "vfe.h"
# include "error.h"
# include "problem.h"
# include "objects.h"
# include "allocate.h"
# include "globals.h"
# include "procedures.h"

# define Valid(x)	(appearance.x != UnspecifiedValue)
# define Present(x)	(appearance.x != NULL)


extern CanvasDialog	canvas_d;
static FigureAttributes	attributes;


/************************************************************************
 * Function:	DrawDisplayList						*
 *									*
 * Description:	Draws the display list from the appearance structure.	*
 ************************************************************************/

static void DrawDisplayList ( )
{
    unsigned  i;
    unsigned  j;
    Figure    figure;
    Point     points [1024];
    FigInfo  *info;


    /* In case no colors are fonts are specified. */

    figure = NULL;

    if (DW_SetForeground (drawing, canvas -> tool_color) == False)
	DW_SetForeground (drawing, "black");

    if (DW_SetFont (drawing, canvas -> tool_font) == False)
	DW_SetFont (drawing, "fixed");

    for (i = 0; i < appearance.num_figures; i ++) {
	info = &appearance.figures [i];

	if (info -> color)
	    DW_SetForeground (drawing, info -> color);


	switch (info -> type) {
	case RECTANGLE:
	    figure = DW_DrawRectangle (drawing, True, info -> x, info -> y,
					info -> width, info -> height);
	    TreeInsert (figure_tree, figure);
	    break;

	case POLYLINE:
	    if (info -> num_points == 2) {
		figure = DW_DrawLine (drawing, info -> points [0].x,
					info -> points [0].y,
					info -> points [1].x,
					info -> points [1].y);

	    } else if (info -> num_points > 2) {
		for (j = 0; j < info -> num_points; j ++) {
		    points [j].x = info -> points [j].x;
		    points [j].y = info -> points [j].y;
		}

		figure = DW_DrawPolygon (drawing, True, points,
					info -> num_points);
	    }

	    TreeInsert (figure_tree, figure);
	    break;

	case TEXT:
	    if (info -> font)
	        DW_SetFont (drawing, info -> font);

	    figure = DW_DrawText (drawing, True, info -> x, info -> y,
					info -> text);
	    TreeInsert (figure_tree, figure);
	    break;

	case ARC:
	    figure = DW_DrawArc (drawing, True, info -> x, info -> y,
					info -> width, info -> height,
					info -> start, info -> length);
	    TreeInsert (figure_tree, figure);
	    break;
	}
    }
}


/************************************************************************
 * Function:	DrawProblem						*
 *									*
 * Description: Draw the problem.  We try to present a suitable viewing	*
 *		of the problem if the appearance structure is not	*
 *		presence.  If only some of the appearance structure is	*
 *		valid then we try to compromise and keep what		*
 *		information is valid.					*
 ************************************************************************/

void DrawProblem (z)
    double   z;
{
    unsigned  i;
    Node      n;
    float     xmin, xmax;
    float     ymin, ymax;
    float     scale;
    float     xrange;
    float     yrange;
    Dimension height;
    Dimension width;
    Cardinal  count;
    Boolean   new_limits;
    Arg       arglist [10];


    /* Compute the maximum and minimum values. */

    xmin = 1;
    xmax = -1;
    ymin = 1;
    ymax = -1;

    for (i = 1; i <= problem.num_nodes; i ++) {
	n = problem.nodes [i];

	if (n -> z == z)
	    if (xmin > xmax) {
		xmin = xmax = n -> x;
		ymin = ymax = n -> y;
	    } else {
		if (n -> x < xmin)
		    xmin = n -> x;
		else if (n -> x > xmax)
		    xmax = n -> x;
		if (n -> y < ymin)
		    ymin = n -> y;
		else if (n -> y > ymax)
		    ymax = n -> y;
	    }
    }

    if (xmin > xmax && problem.num_nodes)
	error ("No nodes lie within the plane z = %g.", z);


    /* Expand the minimum and maximum a bit. */

    if (xmin >= xmax) {
	xmin -= .1 * (ymax - ymin);
	xmax += .1 * (ymax - ymin);
    }

    if (ymin >= ymax) {
	ymin -= .1 * (xmax - xmin);
	ymax += .1 * (xmax - xmin);
    }


    if (xmax - xmin < ymax - ymin)
	canvas -> snap_size = canvas -> grid_size = (ymax - ymin) / 10;
    else
	canvas -> snap_size = canvas -> grid_size = (xmax - xmin) / 10;

    xrange = xmax - xmin;
    xmin -= .05 * xrange;
    xmax += .05 * xrange;
    xrange = xmax - xmin;

    yrange = ymax - ymin;
    ymin -= .05 * yrange;
    ymax += .05 * yrange;
    yrange = ymax - ymin;

    new_limits = False;


    /* If the x-axis limits are valid, then use them. */

    if (Valid (x_min) && Valid (x_max))
	if (appearance.x_min < appearance.x_max) {
	    xmin = appearance.x_min;
	    xmax = appearance.x_max;
	    new_limits = True;
	}


    /* If the y-axis limits are valid, then use them. */

    if (Valid (y_min) && Valid (y_max))
	if (appearance.y_min < appearance.y_max) {
	    ymin = appearance.y_min;
	    ymax = appearance.y_max;
	    new_limits = True;
	}


    /* Check the grid size and recompute it if necessary. */

    if (Valid (grid_size))
	canvas -> grid_size = appearance.grid_size;
    else if (new_limits) {
	if (xmax - xmin < ymax - ymin)
	    canvas -> grid_size = (ymax - ymin) / 10;
	else
	    canvas -> grid_size = (xmax - xmin) / 10;
    }


    /* Check the snap size and recompute it if necessary. */

    if (Valid (snap_size))
	canvas -> snap_size = appearance.snap_size;
    else if (new_limits) {
	if (xmax - xmin < ymax - ymin)
	    canvas -> snap_size = (ymax - ymin) / 10;
	else
	    canvas -> snap_size = (xmax - xmin) / 10;
    }


    count = 0;
    XtSetArg (arglist [count], XtNwidth, &width); count ++;
    XtSetArg (arglist [count], XtNheight, &height); count ++;
    XtGetValues (viewport, arglist, count);


    /* If the width, height, and scale are ALL valid, then use them. */

    if (Valid (width) && Valid (height) && Valid (scale)) {
	scale = appearance.scale;
	width = appearance.width;
	height = appearance.height;
    } else {
	if (xrange / width > yrange / height) {
	    scale = width / xrange;
	    ymax += (xrange - yrange) / 2;
	    ymin -= (xrange - yrange) / 2;
	} else {
	    scale = height / yrange;
	    xmax += (yrange - xrange) / 2;
	    xmin -= (yrange - xrange) / 2;
	}
    }


    /* Set up the canvas dialog. */

    canvas -> xmin = xmin;
    canvas -> xmax = xmax;
    canvas -> ymin = ymin;
    canvas -> ymax = ymax;
    canvas -> scale = scale;

    if (Valid (node_numbers))
	canvas -> node_numbers = appearance.node_numbers;

    if (Valid (element_numbers))
	canvas -> element_numbers = appearance.element_numbers;

    if (Present (node_color))
	canvas -> node_color = strdup (appearance.node_color);

    if (Present (element_color))
	canvas -> element_color = strdup (appearance.element_color);

    if (Present (label_font))
	canvas -> label_font = strdup (appearance.label_font);

    if (Present (tool_color))
	canvas -> tool_color = strdup (appearance.tool_color);

    if (Present (tool_font))
	canvas -> tool_font = strdup (appearance.tool_font);

    CanvasDialogSet (canvas_d);


    if (Valid (grid) && appearance.grid != canvas -> grid)
	ToggleGridStatus ( );

    if (Valid (snap) && appearance.snap != canvas -> snap)
	ToggleSnapStatus ( );


    /* Draw the problem. */

    for (i = 1; i <= problem.num_elements; i ++)
	if (problem.elements [i] != NULL)
	    DrawElement (problem.elements [i], i == 1 ? True : False);

    for (i = 1; i <= problem.num_nodes; i ++)
	if (problem.nodes [i] != NULL)
	    DrawNode (problem.nodes [i], i == 1 ? True : False);

    DrawDisplayList ( );

    count = 0;
    XtSetArg (arglist [count], XtNwidth, width); count ++;
    XtSetArg (arglist [count], XtNheight, height); count ++;
    XtSetValues (viewport, arglist, count);

    XawViewportSetCoordinates (viewport, -appearance.x_pos, -appearance.y_pos);
    InitAppearance ( );
}


/************************************************************************
 * Function:	DestroyProblem						*
 *									*
 * Description:	Destroy the current problem invocation.			*
 ************************************************************************/

void DestroyProblem (material_op)
    ItemDestructor material_op;
{
    (void) TreeSetDestructor (problem.node_tree, (ItemDestructor)
		DestroyNode);
    (void) TreeDestroy (problem.node_tree);

    (void) TreeSetDestructor (problem.element_tree, (ItemDestructor)
		DestroyElement);
    (void) TreeDestroy (problem.element_tree);

    (void) TreeSetDestructor (problem.force_tree, (ItemDestructor)
		DestroyForce);
    (void) TreeDestroy (problem.force_tree);

    (void) TreeSetDestructor (problem.material_tree, (ItemDestructor)
		material_op);
    (void) TreeDestroy (problem.material_tree);

    (void) TreeSetDestructor (problem.constraint_tree, (ItemDestructor)
		DestroyConstraint);
    (void) TreeDestroy (problem.constraint_tree);

    (void) TreeSetDestructor (problem.distributed_tree, (ItemDestructor)
		DestroyDistributed);
    (void) TreeDestroy (problem.distributed_tree);
}


/************************************************************************
 * Function:	setnodenum						*
 *									*
 * Description:	Sets the node numbering for a specified node.		*
 ************************************************************************/

static int setnodenum (item)
    Item item;
{
    static char	     number [10];
    FigureAttributes attr;
    Node	     node;
    Drawn	     drawn;
    float	     x;
    float	     y;


    node = (Node) item;
    drawn = (Drawn) node -> aux;

    if (drawn -> figure == NULL)
	return 0;

    if (drawn -> label == NULL) {
	x = node -> x;
	y = node -> y;
	sprintf (number, " %d", node -> number);
	drawn -> label = DW_DrawText (drawing, True, x, y, number);
	attr.user_data = (char *) node;
	DW_SetAttributes (drawing, drawn -> label, DW_FigureUserData, &attr);
    }

    DW_SetAttributes (drawing, drawn -> label, DW_FigureVisible, &attributes);
    return 0;
}


/************************************************************************
 * Function:	SetNodeNumbering					*
 *									*
 * Description:	Set the node numbering status.				*
 ************************************************************************/

void SetNodeNumbering (value)
    int value;
{
    if (DW_SetForeground (drawing, canvas -> node_color) == False)
	(void) DW_SetForeground (drawing, "black");

    if (DW_SetFont (drawing, canvas -> label_font) == False)
	(void) DW_SetFont (drawing, "fixed");

    attributes.visible = value;
    DW_SetAutoRedraw (drawing, False);
    (void) TreeSetIterator (problem.node_tree, setnodenum);
    (void) TreeIterate (problem.node_tree);
    DW_SetAutoRedraw (drawing, True);
}


/************************************************************************
 * Function:	setelementnum						*
 *									*
 * Description:	Sets the element numbering for a specified element.	*
 ************************************************************************/

static int setelementnum (item)
    Item item;
{
    static char	     number [10];
    FigureAttributes attr;
    Element	     element;
    Drawn	     drawn;
    float	     x;
    float	     y;


    element = (Element) item;
    drawn = (Drawn) element -> aux;

    if (drawn -> figure == NULL)
	return 0;

    if (drawn -> label == NULL) {
	ComputeCenter (element, &x, &y);
	sprintf (number, "%d", element -> number);
	drawn -> label = DW_DrawText (drawing, True, x, y, number);
	attr.user_data = (char *) element;
	DW_SetAttributes (drawing, drawn -> label, DW_FigureUserData, &attr);
    }

    DW_SetAttributes (drawing, drawn -> label, DW_FigureVisible, &attributes);
    return 0;
}


/************************************************************************
 * Function:	SetElementNumbering					*
 *									*
 * Description:	Set the element numbering status.			*
 ************************************************************************/

void SetElementNumbering (value)
    int value;
{
    if (DW_SetForeground (drawing, canvas -> element_color) == False)
	(void) DW_SetForeground (drawing, "black");

    if (DW_SetFont (drawing, canvas -> label_font) == False)
	(void) DW_SetFont (drawing, "fixed");

    attributes.visible = value;
    DW_SetAutoRedraw (drawing, False);
    (void) TreeSetIterator (problem.element_tree, setelementnum);
    (void) TreeIterate (problem.element_tree);
    DW_SetAutoRedraw (drawing, True);
}

static int RecolorNode (item)
   Item		item;
{
   Node			n;
   FigureAttributes	attrib;
   Drawn		drawn;

   n = (Node) item;
   drawn = (Drawn) n -> aux;

   if (n -> force && n -> force -> color) 
      attrib.color = n -> force -> color; 
   else if (n -> constraint -> color) 
      attrib.color = n -> constraint -> color; 
   else 
      attrib.color = canvas -> node_color;

   if (drawn -> figure)
      DW_SetAttributes (drawing, drawn -> figure, DW_FigureColor, &attrib);
   if (drawn -> label)
      DW_SetAttributes (drawing, drawn -> label, DW_FigureColor, &attrib);

   return 0;
}

static int RecolorElement (item)
   Item		item;
{
   FigureAttributes	attrib;
   Element		e;
   Drawn		drawn;

   e = (Element) item;
   drawn = (Drawn) e -> aux;

   if (e -> numdistributed && e -> distributed[1] -> color) 
      attrib.color = e -> distributed[1] -> color; 
   else if (e -> material -> color) 
      attrib.color = e -> material -> color; 
   else 
      attrib.color = canvas -> element_color;

   if (drawn -> figure)
      DW_SetAttributes (drawing, drawn -> figure, DW_FigureColor, &attrib);
   if (drawn -> label)
      DW_SetAttributes (drawing, drawn -> label, DW_FigureColor, &attrib);

   return 0;
} 

/************************************************************************
 * Function:	RecolorCanvas						*
 *									*
 * Description:	Resets the color attribute of all nodes and elements	*
 *		based on possibly new values of object coloring		*
 ************************************************************************/

void RecolorCanvas ( )
{
   DW_SetAutoRedraw (drawing, False);

   TreeSetIterator (problem.node_tree, RecolorNode);
   TreeIterate (problem.node_tree);

   TreeSetIterator (problem.element_tree, RecolorElement);
   TreeIterate (problem.element_tree);

   DW_SetAutoRedraw (drawing, True);

   return;   
}


syntax highlighted by Code2HTML, v. 0.9.1