/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file:     graphXO_1d.c                                                   */
/*                                                                          */
/*                                                                          */
/* description:  simple graphical routines in 1d                            */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  authors:   Alfred Schmidt                                               */
/*             Zentrum fuer Technomathematik                                */
/*             Fachbereich 3 Mathematik/Informatik                          */
/*             Universitaet Bremen                                          */
/*             Bibliothekstr. 2                                             */
/*             D-28359 Bremen, Germany                                      */
/*                                                                          */
/*             Kunibert G. Siebert                                          */
/*             Institut fuer Mathematik                                     */
/*             Universitaet Augsburg                                        */
/*             Universitaetsstr. 14                                         */
/*             D-86159 Augsburg, Germany                                    */
/*                                                                          */
/*  http://www.mathematik.uni-freiburg.de/IAM/ALBERTA                       */
/*                                                                          */
/*  (c) by A. Schmidt and K.G. Siebert (1996-2003)                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------*/
/*---  get min and max coordinates                                       ---*/
/*--------------------------------------------------------------------------*/
typedef struct min_max {
  float xmin_1d[2], xmax_1d[2];
} MIN_MAX;

const REAL_B vertices_b[N_VERTICES_1D] = {
  INIT_BARY_1D(1,0),
  INIT_BARY_1D(0,1)
};

static void xminmax_fct_1d(const EL_INFO *el_info, void *data)
{
  FUNCNAME("xminmax_fct_1d");
  MIN_MAX *ud = (MIN_MAX *)data;
  int i, j;
  PARAMETRIC *parametric = el_info->mesh->parametric;
  
  if (parametric && parametric->init_element(el_info, parametric)) 
  {
    REAL_D     world[N_VERTICES_1D];

    parametric->coord_to_world(el_info, NULL, N_VERTICES_1D, vertices_b, world);

    for (i = 0; i < N_VERTICES_1D; i++) 
    {
      for (j = 0; j < DIM_OF_WORLD; j++)
      {
	ud->xmin_1d[j] = MIN(ud->xmin_1d[j], world[i][j]);
	ud->xmax_1d[j] = MAX(ud->xmax_1d[j], world[i][j]);
      }
    }
  }
  else 
  {
    TEST_FLAG(FILL_COORDS, el_info);

    for (i = 0; i < N_VERTICES_1D; i++)
    {
      for (j = 0; j < DIM_OF_WORLD; j++)
      {
	ud->xmin_1d[j] = MIN(ud->xmin_1d[j], el_info->coord[i][j]);
	ud->xmax_1d[j] = MAX(ud->xmax_1d[j], el_info->coord[i][j]);
      }    
    }
  }

  return;
}

static GRAPH_WINDOW graph_open_window_1d(const char *title, const char *geom, 
					 REAL *world, MESH *mesh)
{
  OGL_WINDOW *winO = NULL;
  MIN_MAX    mm[1];
  int        i;
  char       geometry[16];
  float      xdiam[2] = { 0.0, };

  if (world)
  {
    mm->xmin_1d[0] = world[0];
    mm->xmax_1d[0] = world[1];
    mm->xmin_1d[1] = world[2];
    mm->xmax_1d[1] = world[3];
    xdiam[0] = MAX(mm->xmax_1d[0] - mm->xmin_1d[0], 1.E-10);
    xdiam[1] = MAX(mm->xmax_1d[1] - mm->xmin_1d[1], 1.E-10);
  }
  else if (mesh)
  {
    for (i = 0; i < DIM_OF_WORLD; i++)
      mm->xmax_1d[i] = -(mm->xmin_1d[i] = 1.0E10);
    mesh_traverse(mesh, -1, CALL_LEAF_EL|FILL_COORDS, xminmax_fct_1d, mm);

    mm->xmin_1d[1] = -1.0;
    mm->xmax_1d[1] = 1.0;

    for (i = 0; i < DIM_OF_WORLD; i++)
    {
      xdiam[i] = MAX(mm->xmax_1d[i] - mm->xmin_1d[i], 1.E-10);
      mm->xmin_1d[i] -= 0.1*xdiam[i];
      mm->xmax_1d[i] += 0.1*xdiam[i];
      xdiam[i] *= 1.2;
    }
  }
  else
  {
    for (i = 0; i < DIM_OF_WORLD; i++)
    {
      mm->xmin_1d[i] = -1.0;
      mm->xmax_1d[i] =  1.0;
      xdiam[i] =  2.0;
    }
  }

  if (!title) title = "ALBERTAgraphics";

  if (!geom)
  {
    REAL size = 400.0, xsize, ysize;

    if (xdiam[0] >= xdiam[1])
    {
      xsize = size;
      ysize = size*xdiam[1]/xdiam[0];
    }
    else
    {
      xsize = size*xdiam[0]/xdiam[1];
      ysize = size;
    }
    snprintf(geometry, 16, "%dx%d", (int)xsize, (int)ysize);
    geom = geometry;
  }

  if ((winO = OGL_create_window(title, geom)))
  {
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    for (i = 0; i < MAX(DIM_OF_WORLD,2); i++) {
      winO->xmin[i] = mm->xmin_1d[i];
      winO->xmax[i] = mm->xmax_1d[i];
    }
    glOrtho(winO->xmin[0], winO->xmax[0],
	    winO->xmin[1], winO->xmax[1],
	    -1.0, 1.0);

    return((GRAPH_WINDOW) winO);
  }

  return(NULL);
}

static void graph_close_window_1d(GRAPH_WINDOW win)
{
  OGL_destroy_window((OGL_WINDOW *)win);
  return;
}

static void graph_clear_window_1d(GRAPH_WINDOW win, const GRAPH_RGBCOLOR c)
{
  OGL_clear_window((OGL_WINDOW *)win, c);
  return;
}

static void graph_mesh_1d(GRAPH_WINDOW win, MESH *mesh,
			  const GRAPH_RGBCOLOR c, FLAGS flag)
{
  REAL_D          world[N_VERTICES_1D];
  REAL            coord[N_VERTICES_1D][2];
  const float     *color;
  PARAMETRIC *parametric = NULL;
  
  if (!win || !mesh)  return;

  /* Standardfarbe setzen */
  color = c ? c : rgb_black;

  parametric = mesh->parametric;

  OGL_set_std_window((OGL_WINDOW *) win);

  glLineWidth(1);
  glPointSize(3);

  TRAVERSE_FIRST(mesh, -1, CALL_LEAF_EL|FILL_COORDS|FILL_BOUND) {
    BNDRY_TYPE wb;

    glColor3fv(color);
    if (flag & GRAPH_MESH_ELEMENT_MARK) {
      if (el_info->el->mark > 0)
	glColor3fv(rgb_red);
      else if (el_info->el->mark < 0)
	glColor3fv(rgb_blue);
    }

    glBegin(GL_LINE_STRIP);
    if (parametric && parametric->init_element(el_info, parametric)) {
      parametric->coord_to_world(el_info,
				 NULL, N_VERTICES_1D, vertices_b, world);
    } else {
      world[0][0] = el_info->coord[0][0];
      world[1][0] = el_info->coord[1][0];
    }
    coord[0][0] = world[0][0];
    coord[1][0] = world[1][0];
    coord[0][1] = coord[1][1] = 0.0;

    glVertex2dv(coord[0]);
    glVertex2dv(coord[1]);
    glEnd();
/*--------------------------------------------------------------------------*/
/*--- draw element boundaries                                            ---*/
/*--------------------------------------------------------------------------*/
    if ((wb = wall_bound(el_info, 1)) != INTERIOR) {
      if ((S_CHAR)wb > 0)
	glColor3fv(rgb_blue);
      else if ((S_CHAR)wb < 0)
	glColor3fv(rgb_red);

      glPointSize(5);
      glBegin(GL_POINTS);
      glVertex2dv(coord[0]);
      glEnd();
    }

    if ((S_CHAR)(wb = wall_bound(el_info, 0)) > 0)
      glColor3fv(rgb_blue);
    else if ((S_CHAR)wb < 0)
      glColor3fv(rgb_red);
    else
      glColor3fv(color);

    glBegin(GL_POINTS);
    glVertex2dv(coord[1]);
    glEnd();
  } TRAVERSE_NEXT();

  glFlush();
  return;
}

static void graph_drv_1d(GRAPH_WINDOW win, const DOF_REAL_VEC *uh,
			 REAL min, REAL max, int refine,const GRAPH_RGBCOLOR c)
{
  MESH            *mesh;
  REAL_D          world[N_VERTICES_1D];
  double          coord[2], scal;
  PARAMETRIC      *parametric = NULL;
  int             i,n_refine;
  static int      n_refines[6] = {1, 2, 4, 8, 16, 32};
  const BAS_FCTS  *bas_fcts;
  REAL            uh_val;

  if (!win || !uh)  return;

  parametric = uh->fe_space->mesh->parametric;
  if (parametric)
    WARNING("This function is not tested for parametric elements\n"); 

  if (min >= max)
  {
    min = dof_min(uh);
    max = dof_max(uh);
  }
  min = ABS(min);
  max = ABS(max);

  scal = min == max ? 0.9 : 0.9/MAX(min,max);

  bas_fcts = uh->fe_space->bas_fcts;
  if (refine < 0) refine = bas_fcts->degree-1;
  if (refine <= 0) 
      n_refine = 0;
  else if (refine > 5)
      refine = 5;
  n_refine = n_refines[refine];

  OGL_set_std_window((OGL_WINDOW *) win);

  glLineWidth(1);
  glColor3fv(c ? c : rgb_black);

  glBegin(GL_LINE_STRIP);
  mesh = uh->fe_space->mesh;

  TRAVERSE_FIRST(mesh, -1, CALL_LEAF_EL|FILL_COORDS) {
    const EL_REAL_VEC *uh_el = fill_el_real_vec(NULL, el_info->el, uh);

    if (parametric && parametric->init_element(el_info, parametric))  {
      parametric->coord_to_world(el_info, NULL, N_VERTICES_1D, vertices_b, world);
    } else {
      world[0][0] = el_info->coord[0][0];
      world[1][0] = el_info->coord[1][0];
    }     

    for (i = 0; i < n_refine+1; ++i) {
	REAL_B lambda;
	lambda[1] = ((REAL)i)/((REAL)n_refine);
	lambda[0] = 1.0 - lambda[1];
	uh_val = eval_uh(lambda, uh_el, bas_fcts);
	coord[0] = lambda[0] * world[0][0] + lambda[1] * world[1][0];
	coord[1] = scal*uh_val;
	glVertex2dv(coord);
    }
    
  } TRAVERSE_NEXT();

  glEnd();
  glFlush();
}

static void graph_drv_d_1d(GRAPH_WINDOW win, const DOF_REAL_D_VEC *uh,
			   REAL min, REAL max, int refine,
			   const GRAPH_RGBCOLOR c)
{
  MESH           *mesh;
  REAL_D         world[N_VERTICES_1D];
  double         coord[2], scal;
  PARAMETRIC     *parametric = NULL;
  int            i,n_refine;
  static int     n_refines[6] = {1, 2, 4, 8, 16, 32};
  const BAS_FCTS *bas_fcts;
  const REAL     *uh_val;

  if (!win || !uh)  return;

  parametric = uh->fe_space->mesh->parametric;
  if (parametric) 
    WARNING("This function is not tested for parametric elements\n"); 

  if (min >= max)
  {
    min = dof_min_d(uh);
    max = dof_max_d(uh);
  }

  scal = min == max ? 0.9 : 0.9/max;

  bas_fcts = uh->fe_space->bas_fcts;
  if (refine < 0) refine = bas_fcts->degree-1;
  if (refine <= 0) 
      n_refine = 0;
  else if (refine > 5)
      refine = 5;
  n_refine = n_refines[refine];

  OGL_set_std_window((OGL_WINDOW *) win);

  glLineWidth(1);
  glColor3fv(c ? c : rgb_black);

  glBegin(GL_LINE_STRIP);
  mesh = uh->fe_space->mesh;

  TRAVERSE_FIRST(mesh, -1, CALL_LEAF_EL|FILL_COORDS) {
    const EL_REAL_D_VEC *uh_d_el = fill_el_real_d_vec(NULL, el_info->el, uh);

    if (parametric && parametric->init_element(el_info, parametric)) {
      parametric->coord_to_world(el_info,
				 NULL, N_VERTICES_1D, vertices_b, world);
    } else  {
      world[0][0] = el_info->coord[0][0];
      world[1][0] = el_info->coord[1][0];
    }     

    for (i = 0; i < n_refine+1; ++i) {
	REAL_B lambda;
	lambda[1] = ((REAL)i)/((REAL)n_refine);
	lambda[0] = 1.0 - lambda[1];
	uh_val = eval_uh_d(NULL, lambda, uh_d_el, bas_fcts);
	coord[0] = lambda[0] * world[0][0] + lambda[1] * world[1][0];
	coord[1] = scal*NORM_DOW(uh_val);
	glVertex2dv(coord);
    }

  } TRAVERSE_NEXT();

  glEnd();
  glFlush();
  return;
}

/*--------------------------------------------------------------------------*/

static void graph_el_est_1d(GRAPH_WINDOW win, MESH *mesh,
			    REAL (*get_est)(EL *),
			    REAL min, REAL max, const GRAPH_RGBCOLOR c)
{
  TRAVERSE_STACK  *stack;
  const EL_INFO   *el_info;
  double          coord[2], scal, est;
  REAL_D          world[N_VERTICES_1D];
  PARAMETRIC      *parametric = NULL;

  if (!win || !mesh || !get_est)  return;

  parametric = mesh->parametric;
  if (parametric) 
    WARNING("This function is not tested for parametric elements\n"); 

  stack  = get_traverse_stack();

  if (min >= max)
  {
    el_info = traverse_first(stack, mesh, -1, CALL_LEAF_EL);
    while (el_info)
    {
      est = (*get_est)(el_info->el);
      min = MIN(min,est);
      max = MAX(max,est);

      el_info = traverse_next(stack, el_info);
    }
  }
  min = ABS(min);
  max = ABS(max);
  scal = min == max ? 0.9 : 0.9/MAX(min,max);

  OGL_set_std_window((OGL_WINDOW *) win);

  glLineWidth(1);
  glColor3fv(c ? c : rgb_black);

  el_info = traverse_first(stack, mesh, -1, CALL_LEAF_EL|FILL_COORDS);
  while (el_info)
  {
    est = (*get_est)(el_info->el);

    if (parametric && parametric->init_element(el_info, parametric)) 
    {
      parametric->coord_to_world(el_info,
				 NULL, N_VERTICES_1D, vertices_b, world);
    }
    else 
    {
      world[0][0] = el_info->coord[0][0];
      world[1][0] = el_info->coord[1][0];
    } 
    
    glBegin(GL_LINE_STRIP);
    coord[0] = world[0][0];
    coord[1] = scal*est;
    glVertex2dv(coord);
    coord[0] = world[1][0];
    coord[1] = scal*est;
    glVertex2dv(coord);
    glEnd();

    el_info = traverse_next(stack, el_info);
  }
  free_traverse_stack(stack);
  glFlush();
  return;
}
