/*
    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:	vfe.c							*
 *									*
 * Description:	This file contains the function definitions for the	*
 *		visualization of the finite element structures.		*
 ************************************************************************/

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

extern CanvasDialog canvas_d;

/************************************************************************
   Function:	DrawProblem
   Description:	Convert a felt structured problem to a velvet structured
		problem.
 ************************************************************************/

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;
    Arg       arglist [10];


    /* Check if there is anything to do. */

    if (numnodes == 0 && numelts == 0) {
	node = NULL;
	element = NULL;
	return;
    }


    /* Compute the maximum and minimum values. */

    xmin = 1;
    xmax = -1;

    for (i = 1; i <= numnodes; i ++) {
	n = node [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)
	error ("No nodes lie within the plane z=%g.", z);


    /* Reconfigure the widget. */

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

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

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

    canvas -> snap_size = canvas -> grid_size;

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

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

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

    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;
    }

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

    CanvasDialogSet (canvas_d);

    count = 0;
    XtSetArg (arglist [count], XtNxScale, Float2Arg (scale)); count ++;
    XtSetArg (arglist [count], XtNyScale, Float2Arg (scale)); count ++;
    XtSetValues (drawing, arglist, count);


    /* Draw the problem. */

    for (i = 1; i <= numelts; i ++)
	if (element [i] != NULL)
	    DrawElement (element [i], i == 1 ? True : False);

    for (i = 1; i <= numnodes; i ++)
	if (node [i] != NULL)
	    DrawNode (node [i], i == 1 ? True : False);


    /* Deallocate the arrays of pointers to nodes and elements. */

    ZeroOffset (node);
    Deallocate (node);
    ZeroOffset (element);
    Deallocate (element);

    node = NULL;
    element = NULL;
}


/************************************************************************
   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);
}

static FigureAttributes attributes;

static int setnodenum (item)
    Item item;
{
    static char number [10];
    FigureAttributes attr;
    Node node = (Node) item;
    Drawn drawn = (Drawn) node -> aux;

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

    if (drawn -> label == NULL) {
	sprintf (number, " %d", node -> number);
	drawn -> label = DW_DrawText (drawing, True, node->x, node->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.
 ************************************************************************/

void SetNodeNumbering (value)
    Boolean 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);
}


static int setelementnum (item)
    Item item;
{
    static char number [10];
    FigureAttributes attr;
    Element element = (Element) item;
    float x, y;
    Drawn 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.
 ************************************************************************/

void SetElementNumbering (value)
    Boolean 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);
}
