/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file:     level_common.c                                                 */
/*                                                                          */
/* description:  routines for extracting level set of a piecewise linear    */
/*               fe-function, 2d part                                       */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*            file includes ../Common/level_common.c                        */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  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.alberta-fem.de/                                              */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*  (c) by A. Schmidt and K.G. Siebert (1996-2004)                          */
/*                                                                          */
/*     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    */
/*     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.                         */
/*--------------------------------------------------------------------------*/

REAL level_element_det_2d(const REAL_D coord[N_VERTICES_1D])
{
  REAL_D  diff;
  int     i;

  for (i = 0; i < DIM_OF_WORLD; i++)
    diff[i] = coord[0][i] - coord[1][i];
  
  return(sqrt(SCP_DOW(diff,diff)));
}

void level_coord_to_world_2d(const REAL_D coord[N_VERTICES_1D],
			     const REAL_B lambda,
			     REAL_D world)
{
  int      k;

  for (k = 0; k < DIM_OF_WORLD; k++)
    world[k] = (lambda[0]*coord[0][k] + lambda[1]*coord[1][k]);
}

void level_coord_to_el_coord_2d(const REAL_B v_lambda[N_VERTICES_1D],
				const REAL_B lambda,
				REAL_B el_lambda)
{
  int     k;

  for (k = 0; k < N_VERTICES_2D; k++)
    el_lambda[k] = (lambda[0]*v_lambda[0][k] + lambda[1]*v_lambda[1][k]);
}

static int   n_el = 0;

static void new_edge_2d(const EL_INFO *el_info, REAL v[], 
			REAL_B lambda[], int face)
{
  int      n, j, k;
  REAL_D   coord[N_VERTICES_1D];

  if (init_element &&
      !(*init_element)(el_info, v, 2, face, (const REAL_B *)lambda))
    return;

  n_el++;

  for (n = 0; n < 2; n++)
  {
    for (k = 0; k < DIM_OF_WORLD; k++)
    {
      coord[n][k] = 0.0;
      for (j = 0; j < N_VERTICES_2D; j++)
	coord[n][k] += lambda[n][j]*el_info->coord[j][k];
    }
  }

  if (cal_element)  (*cal_element)(el_info, v, 0, face, (const REAL_B *) lambda,
				   (const REAL_D *) coord);

  return;
}

static void level_fct_2d(const EL_INFO *el_info, void *dummy)
{
  FUNCNAME("level_fct");
  static REAL_B lambdaT[N_VERTICES_2D] = {{1,0,0},{0,1,0},{0,0,1}};
  static REAL_B lambdae[N_EDGES_2D][N_VERTICES_1D] =
    {{{0,1,0},{0,0,1}},
     {{0,0,1},{1,0,0}},
     {{1,0,0},{0,1,0}}};
  REAL        s;
  REAL_B      v;
  REAL_BB     lambda;
  int         i, j, i1, i2;
  int         n_p = 0, n_m = 0, n_0 = 0;
  int         index_p[N_VERTICES_2D], index_m[N_VERTICES_2D], index_0[N_VERTICES_2D];

  (*get_real_vec)(v, el_info->el, level);

/****************************************************************************/
/*  count the number of positve, negative and zero values at the vertices   */
/****************************************************************************/

  for (i = 0; i < N_VERTICES_2D; i++) 
  {
    if (v[i] - level_value > small)
      index_p[n_p++] = i;
    else if (v[i] - level_value < -small)
      index_m[n_m++] = i;
    else
      index_0[n_0++] = i;
  }

  TEST_EXIT(n_p + n_m + n_0 == N_VERTICES_2D,
	    "n_p + n_m + n_0 = %d != N_VERTICES_2D\n", n_p + n_m + n_0);

  switch(n_0)
  {
  case 3:  
/****************************************************************************/
/*  all three edges belong to the level:                                    */
/*    =>  three edges                                                       */
/****************************************************************************/
    for (i = 0; i < N_NEIGH_2D; i++)
	new_edge_2d(el_info, v, lambdae[i], i);
    break;
  case 2:
/****************************************************************************/
/*  one edge belongs to the level:                                          */
/*    =>  one edge   (if not collected by the neighbour!)                   */
/****************************************************************************/

    if (n_p)
      i = index_p[0];
    else
      i = index_m[0];
    new_edge_2d(el_info, v, lambdae[i], i);

    break;
  case 1:
/****************************************************************************/
/*  one of the other vertices is positive and the other negativ             */
/*    => one (interior) edge                                                */
/*  otherwise the zero level is only a vertex => nothing to do              */
/****************************************************************************/

    if (n_p == 1  &&  n_m  == 1)
    {
      i1 = index_p[0];
      i2 = index_m[0];

      s = (level_value - v[i1])/(v[i2] - v[i1]);
      for (j = 0; j < N_VERTICES_2D; j++)
      {
	lambda[0][j] = lambdaT[index_0[0]][j];
	lambda[1][j] = (1.0 - s)*lambdaT[i1][j] + s*lambdaT[i2][j];
      }

      new_edge_2d(el_info, v, lambda, -1);
    }
    break;
  case 0:
/****************************************************************************/
/* all other cases:                                                         */
/*   3 positive vertices: nothing                                           */
/*   2 positive vertices and 1 negative vertex: 1 interior edge             */
/*   1 positive vertex and 2 negative vertices: 1 interior edge             */
/*   3 negative vertices: nothing                                           */
/****************************************************************************/

    switch (n_p)
    {
    case 2:
      i1 = index_m[0];

      for (i = 0; i < 2; i++)
      {
	i2 = index_p[i];
	s = (level_value - v[i1])/(v[i2] - v[i1]);
	for (j = 0; j < N_VERTICES_2D; j++)
	  lambda[i][j] = (1.0 - s)*lambdaT[i1][j] + s*lambdaT[i2][j];
      }
      new_edge_2d(el_info, v, lambda, -1);

      break;
    case 1:
      i1 = index_p[0];

      for (i = 0; i < 2; i++)
      {
	i2 = index_m[i];
	s = (level_value - v[i1])/(v[i2] - v[i1]);
	for (j = 0; j < N_VERTICES_2D; j++)
	  lambda[i][j] = (1.0 - s)*lambdaT[i1][j] + s*lambdaT[i2][j];
      }
      new_edge_2d(el_info, v, lambda, -1);

      break;
    }
    break;
  }
  return;
}

#if 0 /* cH: I'm just too lazy ATM. */

#if DIM_OF_WORLD == 2
#define X(x)  ((x-x_min)*diam_x_1)
#define Y(y)  ((y-y_min)*diam_y_1)

static FILE  *psFile = NULL;
static REAL  x_min, x_max, y_min, y_max, diam_x_1, diam_y_1;
static int   (*is_domain)(const EL *);

static int ps_init(const EL_INFO *el_info, REAL v[N_VERTICES], int N,
		   int f, const REAL lambda[][DIM+1])
{
  EL  *n;

  el_info->el->mark = 0;
  if (f >= 0)
  {
    if ((n = NEIGH(el_info->el,el_info)[f]))
    {
      if (!n->mark)  return(0);
      if (is_domain && !is_domain(el_info->el) && !is_domain(n)) return(0);
    }
    else
    {
      if (is_domain && !is_domain(el_info->el)) return(0);
    }
  }

  return(1);
}

static void ps_write(const EL_INFO *el_info, REAL v[N_VERTICES], int i, int f,
		     const REAL lambda[DIM][DIM+1], const REAL_D coord[DIM])
{
  int     in0, in1;

  el_info->el->mark = 0;

  in0 = (coord[0][0] > x_min  &&  coord[0][0] < x_max);
  in0 = in0 && (coord[0][1] > y_min  &&  coord[0][1] < y_max);
  in1 = (coord[1][0] > x_min  &&  coord[1][0] < x_max);
  in1 = in1 && (coord[1][1] > y_min  &&  coord[1][1] < y_max);

  if (in0 || in1)
  {
    fprintf(psFile,"newpath\n");

    fprintf(psFile,"%f %f m\n", X(coord[0][0]), Y(coord[0][1])); 
    fprintf(psFile,"%f %f l\n", X(coord[1][0]), Y(coord[1][1])); 

    fprintf(psFile,"stroke\n");
  }
}

void write_ps_level(MESH *mesh, const char *filename, const char *mode,
		    const char *title, const REAL x[2], const REAL y[2], 
		    int keepaspect, const DOF_REAL_VEC *level, REAL value, 
		    int (*is_d)(const EL *), int draw_bound)
{
  FUNCNAME("write_ps_level");
  int      i;
  MACRO_EL *mel;
  const float *c = rgb_black;  /*--- to be done, color as argument  ---*/

  if (mesh==NULL) return;

  x_max = y_max = -LARGE;
  x_min = y_min = LARGE;
  for (mel = mesh->first_macro_el; mel; mel = mel->next)
  {
    for (i = 0; i < N_VERTICES; i++)
    {
      x_max = MAX(x_max,mel->coord[i][0]);
      y_max = MAX(y_max,mel->coord[i][1]);
      x_min = MIN(x_min,mel->coord[i][0]);
      y_min = MIN(y_min,mel->coord[i][1]);
    }
  }
  if (x && x[0] != x[1])
  {
    REAL xx;
    xx = MAX(x[1], x_min);
    x_max = MIN(x_max,xx);
    xx = MIN(x[0], x_max);
    x_min = MAX(x_min,xx);
  }

  if (y && y[0] != y[1])
  {
    REAL yy;
    yy = MAX(y[1], y_min);
    y_max = MIN(y_max,yy);
    yy = MIN(y[0], y_max);
    y_min = MAX(y_min,yy);
  }
  diam_x_1 = x_max - x_min ? 1.0/(x_max - x_min) : 1.0;
  diam_y_1 = y_max - y_min ? 1.0/(y_max - y_min) : 1.0;
  if (keepaspect)
    diam_x_1 = diam_y_1 = MIN(diam_x_1, diam_y_1);

#if 0
  MSG("x in [%le,%le], diam_x = %le\n", x_min, x_max, 1.0/diam_x_1);
  MSG("y in [%le,%le], diam_y = %le\n", y_min, y_max, 1.0/diam_y_1);
#endif

  if (!(psFile = fopen(filename, mode)))
  {
    MSG("cannot open PS file for writing\n");
    return;
  }

  if (!strcmp(mode, "w"))   /*  new file  */
  {
    fprintf(psFile,"%%!PS-Adobe-2.0 EPSF-1.2\n");
    fprintf(psFile,"%%%%Creator: ALBERTAPostScript level writer\n");
    fprintf(psFile,"%%%%Title: %s\n", title ? title : "ALBERTAOutput");
    fprintf(psFile,"%%%%Pages: 1\n");
    fprintf(psFile,"%%%%DocumentFonts: Times-Roman\n");
    fprintf(psFile,"%%%%BoundingBox: 200 200 %.3f %.3f\n", 
	    200.0*(1.0 + X(x_max)), 200.0*(1.0 + Y(y_max)));
    fprintf(psFile,"%%%%EndComments\n");
    fprintf(psFile,"%%begin(plot)\n");
    fprintf(psFile,"200 200 translate\n");
    fprintf(psFile,"200 200 scale %% 72 = 1in, 200 ~ 7cm\n");
    fprintf(psFile,"/mm { 0.01132 mul } def\n");
    fprintf(psFile,"/m { moveto } def\n");
    fprintf(psFile,"/l { lineto } def\n");
    fprintf(psFile," %.3f %.3f m\n", 0.0, 0.0);
    fprintf(psFile," %.3f %.3f l\n", X(x_max), 0.0);
    fprintf(psFile," %.3f %.3f l\n", X(x_max), Y(y_max));
    fprintf(psFile," %.3f %.3f l\n", 0.0, Y(y_max));
    fprintf(psFile,"closepath\n");
    if (!draw_bound) fprintf(psFile, "%%");
    fprintf(psFile,"gsave 0.5 mm setlinewidth stroke grestore\n");
    fprintf(psFile,"clip\n");
    fprintf(psFile, "0.25 mm setlinewidth 1 setlinejoin 1 setlinecap\n");
    fprintf(psFile," 0 0 0 setrgbcolor\n");
    fprintf(psFile,
	    "/Times-Roman findfont 5 mm scalefont setfont 0 setgray\n");
#if 0
    fprintf(psFile,"%%newpath\n");
    fprintf(psFile,"%% 0.  1. m\n");
    fprintf(psFile,"%% 0.  0. l\n");
    fprintf(psFile,"%% 1.  0. l\n");
    fprintf(psFile,"%% 1.  1. l\n");
    fprintf(psFile,"%% 0.  1. l\n");
    fprintf(psFile,"%%stroke\n");
#endif
    fprintf(psFile,"%% ENDE DES PROLOGS XXXXXXXXXXXXXXXXXXX\n");

    if (title) {
      fprintf(psFile,"0.1 1.05 m\n (%s) show\n",title);
    }

    if (draw_bound)
    {
      fprintf(psFile,"%% begin domains boundary\n");
      for (mel = mesh->first_macro_el; mel; mel = mel->next)
      {
	for (i = 0; i < N_EDGES; i++)
	{
	  if (!IS_INTERIOR(mel->boundary[i]))
	  {
	    fprintf(psFile,"newpath\n");
	    fprintf(psFile,"%f %f m\n", 
		    X(mel->coord[(i+1)%3][0]), Y(mel->coord[(i+1)%3][1])); 
	    fprintf(psFile,"%f %f l\n", 
		    X(mel->coord[(i+2)%3][0]), Y(mel->coord[(i+2)%3][1])); 
	    fprintf(psFile,"stroke\n");
	  }
	}
      }
      fprintf(psFile,"%% end domains boundary\n");
    }
    fprintf(psFile,"%% first level\n");
  }
  else
  {
    fprintf(psFile,"%% next level\n");
  }
  fprintf(psFile, "%% set color\n");
  fprintf(psFile, "%8.5f %8.5f %8.5f setrgbcolor\n", c[0], c[1], c[2]);

  is_domain = is_d;
  set_element_mark(mesh, CALL_LEAF_EL, 1);
  find_level(mesh, FILL_COORDS|FILL_NEIGH, level, value, ps_init, ps_write);
  set_element_mark(mesh, CALL_LEAF_EL, 0);

/*   fprintf(psFile,"showpage end\n"); */
  fclose(psFile);
  psFile = NULL;
}

#endif /* DOW == 2 */

#endif /* 0 */

