/* GTS - Library for the manipulation of triangulated surfaces
 * Copyright (C) 1999 Stéphane Popinet
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <math.h>
#include "gts.h"

static void triangle_destroy (GtsObject * object)
{
  GtsTriangle * triangle = GTS_TRIANGLE (object);
  GtsEdge * e1 = triangle->e1;
  GtsEdge * e2 = triangle->e2;
  GtsEdge * e3 = triangle->e3;

  e1->triangles = g_slist_remove (e1->triangles, triangle);
  if (!GTS_OBJECT_DESTROYED (e1) &&
      !gts_allow_floating_edges && e1->triangles == NULL)
    gts_object_destroy (GTS_OBJECT (e1));
  
  e2->triangles = g_slist_remove (e2->triangles, triangle);
  if (!GTS_OBJECT_DESTROYED (e2) &&
      !gts_allow_floating_edges && e2->triangles == NULL)
    gts_object_destroy (GTS_OBJECT (e2));
  
  e3->triangles = g_slist_remove (e3->triangles, triangle);
  if (!GTS_OBJECT_DESTROYED (e3) &&
      !gts_allow_floating_edges && e3->triangles == NULL)
    gts_object_destroy (GTS_OBJECT (e3));

  (* GTS_OBJECT_CLASS (gts_triangle_class ())->parent_class->destroy) (object);
}

static void triangle_class_init (GtsObjectClass * klass)
{
  klass->destroy = triangle_destroy;
}

static void triangle_init (GtsTriangle * triangle)
{
  triangle->e1 = triangle->e2 = triangle->e3 = NULL;
}

/**
 * gts_triangle_class:
 *
 * Returns: the #GtsTriangleClass.
 */
GtsTriangleClass * gts_triangle_class (void)
{
  static GtsTriangleClass * klass = NULL;

  if (klass == NULL) {
    GtsObjectClassInfo triangle_info = {
      "GtsTriangle",
      sizeof (GtsTriangle),
      sizeof (GtsTriangleClass),
      (GtsObjectClassInitFunc) triangle_class_init,
      (GtsObjectInitFunc) triangle_init,
      (GtsArgSetFunc) NULL,
      (GtsArgGetFunc) NULL
    };
    klass = gts_object_class_new (gts_object_class (), 
				  &triangle_info);
  }

  return klass;
}

/**
 * gts_triangle_set:
 * @triangle: a #GtsTriangle.
 * @e1: a #GtsEdge.
 * @e2: another #GtsEdge touching @e1.
 * @e3: another #GtsEdge touching both @e1 and @e2.
 *
 * Sets the edge of @triangle to @e1, @e2 and @e3 while checking that they
 * define a valid triangle.
 */
void gts_triangle_set (GtsTriangle * triangle, 
		       GtsEdge * e1, 
		       GtsEdge * e2,
		       GtsEdge * e3)
{
  g_return_if_fail (e1 != NULL);
  g_return_if_fail (e2 != NULL);
  g_return_if_fail (e3 != NULL);
  g_return_if_fail (e1 != e2 && e1 != e3 && e2 != e3);

  triangle->e1 = e1;
  triangle->e2 = e2;
  triangle->e3 = e3;

  if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v1)
    g_return_if_fail (gts_segment_connect (GTS_SEGMENT (e3), 
					   GTS_SEGMENT (e1)->v2, 
					   GTS_SEGMENT (e2)->v2));
  else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v1)
    g_return_if_fail (gts_segment_connect (GTS_SEGMENT (e3), 
					   GTS_SEGMENT (e1)->v1, 
					   GTS_SEGMENT (e2)->v2));
  else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v2)
    g_return_if_fail (gts_segment_connect (GTS_SEGMENT (e3), 
					   GTS_SEGMENT (e1)->v1, 
					   GTS_SEGMENT (e2)->v1));
  else if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v2)
    g_return_if_fail (gts_segment_connect (GTS_SEGMENT (e3), 
					   GTS_SEGMENT (e1)->v2, 
					   GTS_SEGMENT (e2)->v1));
  else
    g_assert_not_reached ();

  e1->triangles = g_slist_prepend (e1->triangles, triangle);
  e2->triangles = g_slist_prepend (e2->triangles, triangle);
  e3->triangles = g_slist_prepend (e3->triangles, triangle);
}

/**
 * gts_triangle_new:
 * @klass: a #GtsTriangleClass.
 * @e1: a #GtsEdge.
 * @e2: another #GtsEdge touching @e1.
 * @e3: another #GtsEdge touching both @e1 and @e2.
 *
 * Returns: a new #GtsTriangle having @e1, @e2 and @e3 as edges.
 */
GtsTriangle * gts_triangle_new (GtsTriangleClass * klass,
				GtsEdge * e1,
				GtsEdge * e2,
				GtsEdge * e3)
{
  GtsTriangle * t;

  t = GTS_TRIANGLE (gts_object_new (GTS_OBJECT_CLASS (klass)));
  gts_triangle_set (t, e1, e2, e3);
  
  return t;
}

/**
 * gts_triangle_vertex_opposite:
 * @t: a #GtsTriangle.
 * @e: a #GtsEdge used by @t.
 *
 * This function fails if @e is not an edge of @t.
 * 
 * Returns: a #GtsVertex, vertex of @t which does not belong to @e.
 */
GtsVertex * gts_triangle_vertex_opposite (GtsTriangle * t, GtsEdge * e)
{
  g_return_val_if_fail (t != NULL, NULL);
  g_return_val_if_fail (e != NULL, NULL);

  if (t->e1 == e) {
    GtsVertex * v = GTS_SEGMENT (t->e2)->v1;
    if (v != GTS_SEGMENT (e)->v1 && v != GTS_SEGMENT (e)->v2)
      return v;
    return GTS_SEGMENT (t->e2)->v2;
  }
  if (t->e2 == e) {
    GtsVertex * v = GTS_SEGMENT (t->e1)->v1;
    if (v != GTS_SEGMENT (e)->v1 && v != GTS_SEGMENT (e)->v2)
      return v;
    return GTS_SEGMENT (t->e1)->v2;
  }
  if (t->e3 == e) {
    GtsVertex * v = GTS_SEGMENT (t->e2)->v1;
    if (v != GTS_SEGMENT (e)->v1 && v != GTS_SEGMENT (e)->v2)
      return v;
    return GTS_SEGMENT (t->e2)->v2;
  }
  g_assert_not_reached ();
  return NULL;
}

/**
 * gts_triangle_edge_opposite:
 * @t: a #GtsTriangle.
 * @v: a #GtsVertex of @t.
 *
 * Returns: the edge of @t opposite @v or %NULL if @v is not a vertice of @t.
 */
GtsEdge * gts_triangle_edge_opposite (GtsTriangle * t, GtsVertex * v)
{
  GtsSegment * s1, * s2, * s3;

  g_return_val_if_fail (t != NULL, NULL);
  g_return_val_if_fail (v != NULL, NULL);

  s1 = GTS_SEGMENT (t->e1);
  s2 = GTS_SEGMENT (t->e2);

  if (s1->v1 != v && s1->v2 != v) {
    if (s2->v1 != v && s2->v2 != v)
      return NULL;
    return t->e1;
  }
  if (s2->v1 != v && s2->v2 != v)
    return t->e2;
  s3 = GTS_SEGMENT (t->e3);
  g_assert (s3->v1 != v && s3->v2 != v);
  return t->e3;
}

/**
 * gts_triangles_angle:
 * @t1: a #GtsTriangle.
 * @t2: a #GtsTriangle.
 *
 * Returns: the value (in radians) of the angle between @t1 and @t2.
 */
gdouble gts_triangles_angle (GtsTriangle * t1,
			     GtsTriangle * t2)
{
  gdouble nx1, ny1, nz1, nx2, ny2, nz2;
  gdouble pvx, pvy, pvz;
  gdouble theta;

  g_return_val_if_fail (t1 != NULL && t2 != NULL, 0.0);

  gts_triangle_normal (t1, &nx1, &ny1, &nz1);
  gts_triangle_normal (t2, &nx2, &ny2, &nz2);

  pvx = ny1*nz2 - nz1*ny2;
  pvy = nz1*nx2 - nx1*nz2;
  pvz = nx1*ny2 - ny1*nx2;

  theta = atan2 (sqrt (pvx*pvx + pvy*pvy + pvz*pvz), 
		 nx1*nx2 + ny1*ny2 + nz1*nz2) - M_PI;
  return theta < - M_PI ? theta + 2.*M_PI : theta;
}

/**
 * gts_triangles_are_compatible:
 * @t1: a #GtsTriangle.
 * @t2: a #GtsTriangle.
 * @e: a #GtsEdge used by both @t1 and @t2.
 *
 * Checks if @t1 and @t2 have compatible orientations i.e. if @t1 and
 * @t2 can be part of the same surface without conflict in the surface
 * normal orientation.
 *
 * Returns: %TRUE if @t1 and @t2 are compatible, %FALSE otherwise.
 */
gboolean gts_triangles_are_compatible (GtsTriangle * t1, 
				       GtsTriangle * t2,
				       GtsEdge * e)
{
  GtsEdge * e1 = NULL, * e2 = NULL;

  g_return_val_if_fail (t1 != NULL, FALSE);
  g_return_val_if_fail (t2 != NULL, FALSE);
  g_return_val_if_fail (e != NULL, FALSE);

  if (t1->e1 == e) e1 = t1->e2;
  else if (t1->e2 == e) e1 = t1->e3;
  else if (t1->e3 == e) e1 = t1->e1;
  else
    g_assert_not_reached ();
  if (t2->e1 == e) e2 = t2->e2;
  else if (t2->e2 == e) e2 = t2->e3;
  else if (t2->e3 == e) e2 = t2->e1;
  else
    g_assert_not_reached ();
  if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v1 || 
      GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v2 || 
      GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v1 || 
      GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v2)
    return FALSE;
  return TRUE;
}

/**
 * gts_triangle_area:
 * @t: a #GtsTriangle.
 *
 * Returns: the area of the triangle @t.
 */
gdouble gts_triangle_area (GtsTriangle * t)
{
  gdouble x, y, z;
  
  g_return_val_if_fail (t != NULL, 0.0);
  
  gts_triangle_normal (t, &x, &y, &z);
  
  return sqrt (x*x + y*y + z*z)/2.;
}

/**
 * gts_triangle_perimeter:
 * @t: a #GtsTriangle.
 *
 * Returns: the perimeter of the triangle @t.
 */
gdouble gts_triangle_perimeter (GtsTriangle * t)
{
  GtsVertex * v;

  g_return_val_if_fail (t != NULL, 0.0);

  v = gts_triangle_vertex (t);
  return 
    gts_point_distance (GTS_POINT (GTS_SEGMENT (t->e1)->v1), 
			GTS_POINT (GTS_SEGMENT (t->e1)->v2)) +
    gts_point_distance (GTS_POINT (GTS_SEGMENT (t->e1)->v1), 
			GTS_POINT (v)) +
    gts_point_distance (GTS_POINT (GTS_SEGMENT (t->e1)->v2), 
			GTS_POINT (v));
}

/* perimeter of the equilateral triangle of area unity */
#define GOLDEN_PERIMETER 4.5590141139 

/**
 * gts_triangle_quality:
 * @t: a #GtsTriangle.
 *
 * The quality of a triangle is defined as the ratio of the square
 * root of its surface area to its perimeter relative to this same
 * ratio for an equilateral triangle with the same area. The quality
 * is then one for an equilateral triangle and tends to zero for a
 * very stretched triangle.
 *
 * Returns: the quality of the triangle @t.
 */
gdouble gts_triangle_quality (GtsTriangle * t)
{
  gdouble perimeter;

  g_return_val_if_fail (t != NULL, 0.0);

  perimeter = gts_triangle_perimeter (t);
  return perimeter > 0.0 ?
    GOLDEN_PERIMETER*sqrt (gts_triangle_area (t))/perimeter :
    0.0;
}

/**
 * gts_triangle_normal:
 * @t: a #GtsTriangle.
 * @x: the x coordinate of the normal.
 * @y: the y coordinate of the normal.
 * @z: the z coordinate of the normal.
 *
 * Computes the coordinates of the oriented normal of @t as the
 * cross-product of two edges, using the left-hand rule. The normal is
 * not normalized.  If this triangle is part of a closed and oriented
 * surface, the normal points to the outside of the surface.  
 */
void gts_triangle_normal (GtsTriangle * t, 
			  gdouble * x, 
			  gdouble * y, 
			  gdouble * z)
{
  GtsVertex * v1, * v2 = NULL, * v3 = NULL;
  GtsPoint * p1, * p2, * p3;
  gdouble x1, y1, z1, x2, y2, z2;

  g_return_if_fail (t != NULL);

  v1 = GTS_SEGMENT (t->e1)->v1;
  if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v1) {
    v2 = GTS_SEGMENT (t->e2)->v2;
    v3 = GTS_SEGMENT (t->e1)->v2;
  }
  else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v2) {
    v2 = GTS_SEGMENT (t->e1)->v2;
    v3 = GTS_SEGMENT (t->e2)->v1;
  }
  else if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v2) {
    v2 = GTS_SEGMENT (t->e2)->v1;
    v3 = GTS_SEGMENT (t->e1)->v2;
  }
  else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v1) {
    v2 = GTS_SEGMENT (t->e1)->v2;
    v3 = GTS_SEGMENT (t->e2)->v2;
  }
  else {
    fprintf (stderr, "t: %p t->e1: %p t->e2: %p t->e3: %p t->e1->v1: %p t->e1->v2: %p t->e2->v1: %p t->e2->v2: %p t->e3->v1: %p t->e3->v2: %p\n",
	 t, t->e1, t->e2, 
	 t->e3, GTS_SEGMENT (t->e1)->v1, GTS_SEGMENT (t->e1)->v2, 
	 GTS_SEGMENT (t->e2)->v1, GTS_SEGMENT (t->e2)->v2, 
	 GTS_SEGMENT (t->e3)->v1, GTS_SEGMENT (t->e3)->v2);
    g_assert_not_reached ();
  }

  p1 = GTS_POINT (v1);
  p2 = GTS_POINT (v2);
  p3 = GTS_POINT (v3);

  x1 = p2->x - p1->x;
  y1 = p2->y - p1->y;
  z1 = p2->z - p1->z;

  x2 = p3->x - p1->x;
  y2 = p3->y - p1->y;
  z2 = p3->z - p1->z;

  *x = y1*z2 - z1*y2;
  *y = z1*x2 - x1*z2;
  *z = x1*y2 - y1*x2;
}

/**
 * gts_triangle_orientation:
 * @t: a #GtsTriangle.
 * 
 * Checks for the orientation of the plane (x,y) projection of a
 * triangle. See gts_point_orientation() for details. This function
 * is geometrically robust.
 *
 * Returns: a number depending on the orientation of the vertices of @t.
 */
gdouble gts_triangle_orientation (GtsTriangle * t)
{
  GtsVertex * v1, * v2 = NULL, * v3 = NULL;

  g_return_val_if_fail (t != NULL, 0.0);

  v1 = GTS_SEGMENT (t->e1)->v1;
  if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v1) {
    v2 = GTS_SEGMENT (t->e2)->v2;
    v3 = GTS_SEGMENT (t->e1)->v2;
  }
  else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v2) {
    v2 = GTS_SEGMENT (t->e1)->v2;
    v3 = GTS_SEGMENT (t->e2)->v1;
  }
  else if (GTS_SEGMENT (t->e1)->v1 == GTS_SEGMENT (t->e2)->v2) {
    v2 = GTS_SEGMENT (t->e2)->v1;
    v3 = GTS_SEGMENT (t->e1)->v2;
  }
  else if (GTS_SEGMENT (t->e1)->v2 == GTS_SEGMENT (t->e2)->v1) {
    v2 = GTS_SEGMENT (t->e1)->v2;
    v3 = GTS_SEGMENT (t->e2)->v2;
  }
  else
    g_assert_not_reached ();
  return gts_point_orientation (GTS_POINT (v1), 
				GTS_POINT (v2), 
				GTS_POINT (v3));
}

/**
 * gts_triangle_revert:
 * @t: a #GtsTriangle.
 * 
 * Changes the orientation of triangle @t, turning it inside out.
 */
void gts_triangle_revert (GtsTriangle * t)
{
  GtsEdge * e;

  g_return_if_fail (t != NULL);

  e = t->e1;
  t->e1 = t->e2;
  t->e2 = e;
}

/**
 * gts_triangles_from_edges:
 * @edges: a list of #GtsEdge.
 *
 * Builds a list of unique triangles which have one of their edges in @edges.
 * 
 * Returns: the list of triangles.
 */
GSList * gts_triangles_from_edges (GSList * edges)
{
  GHashTable * hash;
  GSList * triangles = NULL, * i;

  hash = g_hash_table_new (NULL, NULL);
  i = edges;
  while (i) {
    GSList * j = GTS_EDGE (i->data)->triangles;
    while (j) {
      GtsTriangle * t = j->data;
      if (g_hash_table_lookup (hash, t) == NULL) {
	triangles = g_slist_prepend (triangles, t);
	g_hash_table_insert (hash, t, i);
      }
      j = j->next;
    }
    i = i->next;
  }
  g_hash_table_destroy (hash);

  return triangles;
}

/**
 * gts_triangle_vertices_edges:
 * @t: a #GtsTriangle.
 * @e: a #GtsEdge belonging to the edges of @t or %NULL.
 * @v1: a #GtsVertex used by @t.
 * @v2: a #GtsVertex used by @t.
 * @v3: a #GtsVertex used by @t.
 * @e1: a #GtsEdge used by @t.
 * @e2: a #GtsEdge used by @t.
 * @e3: a #GtsEdge used by @t.
 *
 * Given @t and @e, returns @v1, @v2, @v3, @e1, @e2 and @e3. @e1
 * has @v1 and @v2 as vertices, @e2 has @v2 and @v3 as vertices
 * and @e3 has @v3 and @v1 as vertices. @v1, @v2 and @v3 respects
 * the orientation of @t. If @e is not NULL, @e1 and @e are
 * identical.
 */
void gts_triangle_vertices_edges (GtsTriangle * t, 
				  GtsEdge * e,
				  GtsVertex ** v1, 
				  GtsVertex ** v2, 
				  GtsVertex ** v3,
				  GtsEdge ** e1,
				  GtsEdge ** e2,
				  GtsEdge ** e3)
{
  GtsEdge * ee1, * ee2;

  g_return_if_fail (t != NULL);
  
  if (e == t->e1 || e == NULL) {
    *e1 = ee1 = t->e1; *e2 = ee2 = t->e2; *e3 = t->e3;    
  }
  else if (e == t->e2) {
    *e1 = ee1 = e; *e2 = ee2 = t->e3; *e3 = t->e1;
  }
  else if (e == t->e3) {
    *e1 = ee1 = e; *e2 = ee2 = t->e1; *e3 = t->e2;
  }
  else {
    g_assert_not_reached ();
    ee1 = ee2 = NULL; /* to avoid complaints from the compiler */
  }
  if (GTS_SEGMENT (ee1)->v2 == GTS_SEGMENT (ee2)->v1) {
    *v1 = GTS_SEGMENT (ee1)->v1; 
    *v2 = GTS_SEGMENT (ee1)->v2; 
    *v3 = GTS_SEGMENT (ee2)->v2;
  }
  else if (GTS_SEGMENT (ee1)->v2 == GTS_SEGMENT (ee2)->v2) {
    *v1 = GTS_SEGMENT (ee1)->v1; 
    *v2 = GTS_SEGMENT (ee1)->v2; 
    *v3 = GTS_SEGMENT (ee2)->v1;
  }
  else if (GTS_SEGMENT (ee1)->v1 == GTS_SEGMENT (ee2)->v1) {
    *v1 = GTS_SEGMENT (ee1)->v2; 
    *v2 = GTS_SEGMENT (ee1)->v1; 
    *v3 = GTS_SEGMENT (ee2)->v2;
  }
  else if (GTS_SEGMENT (ee1)->v1 == GTS_SEGMENT (ee2)->v2) {
    *v1 = GTS_SEGMENT (ee1)->v2; 
    *v2 = GTS_SEGMENT (ee1)->v1; 
    *v3 = GTS_SEGMENT (ee2)->v1;
  }
  else
    g_assert_not_reached ();
}

/* sqrt(3) */
#define SQRT3 1.73205080757

/**
 * gts_triangle_enclosing:
 * @klass: the class of the new triangle.
 * @points: a list of #GtsPoint.
 * @scale: a scaling factor (must be larger than one).
 * 
 * Builds a new triangle (including new vertices and edges) enclosing
 * the plane projection of all the points in @points. This triangle is
 * equilateral and encloses a rectangle defined by the maximum and
 * minimum x and y coordinates of the points. @scale is an homothetic
 * scaling factor. If equal to one, the triangle encloses exactly the
 * enclosing rectangle.
 * 
 * Returns: a new #GtsTriangle.  
 */
GtsTriangle * gts_triangle_enclosing (GtsTriangleClass * klass,
				      GSList * points, gdouble scale)
{
  gdouble xmax, xmin, ymax, ymin;
  gdouble xo, yo, r;
  GtsVertex * v1, * v2, * v3;
  GtsEdge * e1, * e2, * e3;

  if (points == NULL)
    return NULL;
  
  xmax = xmin = GTS_POINT (points->data)->x;
  ymax = ymin = GTS_POINT (points->data)->y;
  points = points->next;
  while (points) {
    GtsPoint * p = points->data;
    if (p->x > xmax) xmax = p->x;
    else if (p->x < xmin) xmin = p->x;
    if (p->y > ymax) ymax = p->y;
    else if (p->y < ymin) ymin = p->y;    
    points = points->next;
  }
  xo = (xmax + xmin)/2.;
  yo = (ymax + ymin)/2.;
  r = scale*sqrt((xmax - xo)*(xmax - xo) + (ymax - yo)*(ymax - yo));
  if (r == 0.0) r = scale;
  v1 = gts_vertex_new (gts_vertex_class (),
		       xo + r*SQRT3, yo - r, 0.0);
  v2 = gts_vertex_new (gts_vertex_class (),
		       xo, yo + 2.*r, 0.0);
  v3 = gts_vertex_new (gts_vertex_class (),
		       xo - r*SQRT3, yo - r, 0.0);
  e1 = gts_edge_new (gts_edge_class (), v1, v2);
  e2 = gts_edge_new (gts_edge_class (), v2, v3);
  e3 = gts_edge_new (gts_edge_class (), v3, v1);
  return gts_triangle_new (gts_triangle_class (), e1, e2, e3);
}

/**
 * gts_triangle_neighbor_number:
 * @t: a #GtsTriangle.
 *
 * Returns: the number of triangles neighbors of @t.
 */
guint gts_triangle_neighbor_number (GtsTriangle * t)
{
  GSList * i;
  guint nn = 0;
  GtsEdge * ee[4], ** e = ee;
  
  g_return_val_if_fail (t != NULL, 0);

  ee[0] = t->e1; ee[1] = t->e2; ee[2] = t->e3; ee[3] = NULL;
  while (*e) {
    i = (*e++)->triangles;
    while (i) {
      GtsTriangle * t1 = i->data;
      if (t1 != t)
	nn++;
      i = i->next;
    }
  }
  return nn;
}

/**
 * gts_triangle_neighbors:
 * @t: a #GtsTriangle.
 *
 * Returns: a list of #GtsTriangle neighbors of @t.
 */
GSList * gts_triangle_neighbors (GtsTriangle * t)
{
  GSList * i, * list = NULL;
  GtsEdge * ee[4], ** e = ee;
  
  g_return_val_if_fail (t != NULL, NULL);

  ee[0] = t->e1; ee[1] = t->e2; ee[2] = t->e3; ee[3] = NULL;
  while (*e) {
    i = (*e++)->triangles;
    while (i) {
      GtsTriangle * t1 = i->data;
      if (t1 != t)
	list = g_slist_prepend (list, t1);
      i = i->next;
    }
  }
  return list;
}

/**
 * gts_triangles_common_edge:
 * @t1: a #GtsTriangle.
 * @t2: a #GtsTriangle.
 *
 * Returns: a #GtsEdge common to both @t1 and @t2 or %NULL if @t1 and @t2
 * do not share any edge.
 */
GtsEdge * gts_triangles_common_edge (GtsTriangle * t1,
				     GtsTriangle * t2)
{
  g_return_val_if_fail (t1 != NULL, NULL);
  g_return_val_if_fail (t2 != NULL, NULL);

  if (t1->e1 == t2->e1 || t1->e1 == t2->e2 || t1->e1 == t2->e3)
    return t1->e1;
  if (t1->e2 == t2->e1 || t1->e2 == t2->e2 || t1->e2 == t2->e3)
    return t1->e2;
  if (t1->e3 == t2->e1 || t1->e3 == t2->e2 || t1->e3 == t2->e3)
    return t1->e3;
  return NULL;
}

/**
 * gts_triangle_is_duplicate:
 * @t: a #GtsTriangle.
 *
 * Returns: a #GtsTriangle different from @t but sharing all its edges 
 * with @t or %NULL if there is none.
 */
GtsTriangle * gts_triangle_is_duplicate (GtsTriangle * t)
{
  GSList * i;
  GtsEdge * e2, * e3;

  g_return_val_if_fail (t != NULL, NULL);

  e2 = t->e2;
  e3 = t->e3;
  i = t->e1->triangles;
  while (i) {
    GtsTriangle * t1 = i->data;
    if (t1 != t && 
	(t1->e1 == e2 || t1->e2 == e2 || t1->e3 == e2) &&
	(t1->e1 == e3 || t1->e2 == e3 || t1->e3 == e3))
      return t1;
    i = i->next;
  }
  
  return NULL;
}

/**
 * gts_triangle_use_edges:
 * @e1: a #GtsEdge.
 * @e2: a #GtsEdge.
 * @e3: a #GtsEdge.
 *
 * Returns: a #GtsTriangle having @e1, @e2 and @e3 as edges or %NULL if @e1,
 * @e2 and @e3 are not part of any triangle.
 */
GtsTriangle * gts_triangle_use_edges (GtsEdge * e1,
				      GtsEdge * e2,
				      GtsEdge * e3)
{
  GSList * i;
  
  g_return_val_if_fail (e1 != NULL, NULL);
  g_return_val_if_fail (e2 != NULL, NULL);
  g_return_val_if_fail (e3 != NULL, NULL);

  i = e1->triangles;
  while (i) {
    GtsTriangle * t = i->data;
    if ((t->e1 == e2 && (t->e2 == e3 || t->e3 == e3)) ||
	(t->e2 == e2 && (t->e1 == e3 || t->e3 == e3)) ||
	(t->e3 == e2 && (t->e1 == e3 || t->e2 == e3)))
      return t;
    i = i->next;
  }
  
  return NULL;
}

/**
 * gts_triangle_is_ok:
 * @t: a #GtsTriangle.
 *
 * Returns: %TRUE if @t is a non-degenerate, non-duplicate triangle,
 * %FALSE otherwise.
 */
gboolean gts_triangle_is_ok (GtsTriangle * t)
{
  g_return_val_if_fail (t != NULL, FALSE);
  g_return_val_if_fail (t->e1 != NULL, FALSE);
  g_return_val_if_fail (t->e2 != NULL, FALSE);
  g_return_val_if_fail (t->e3 != NULL, FALSE);
  g_return_val_if_fail (t->e1 != t->e2 && t->e1 != t->e3 && t->e2 != t->e3, 
			FALSE);
  g_return_val_if_fail (gts_segments_touch (GTS_SEGMENT (t->e1), 
					    GTS_SEGMENT (t->e2)),
			FALSE);
  g_return_val_if_fail (gts_segments_touch (GTS_SEGMENT (t->e1), 
					    GTS_SEGMENT (t->e3)), 
			FALSE);
  g_return_val_if_fail (gts_segments_touch (GTS_SEGMENT (t->e2), 
					    GTS_SEGMENT (t->e3)), 
			FALSE);
  g_return_val_if_fail (GTS_SEGMENT (t->e1)->v1 != GTS_SEGMENT (t->e1)->v2, 
			FALSE);
  g_return_val_if_fail (GTS_SEGMENT (t->e2)->v1 != GTS_SEGMENT (t->e2)->v2, 
			FALSE);
  g_return_val_if_fail (GTS_SEGMENT (t->e3)->v1 != GTS_SEGMENT (t->e3)->v2, 
			FALSE);
  g_return_val_if_fail (GTS_OBJECT (t)->reserved == NULL, FALSE);
  g_return_val_if_fail (!gts_triangle_is_duplicate (t), FALSE);
  return TRUE;
}

/**
 * gts_triangle_vertices:
 * @t: a #GtsTriangle.
 * @v1: a pointer on a #GtsVertex.
 * @v2: a pointer on a #GtsVertex.
 * @v3: a pointer on a #GtsVertex.
 *
 * Fills @v1, @v2 and @v3 with the oriented set of vertices, summits of @t.
 */
void gts_triangle_vertices (GtsTriangle * t,
			    GtsVertex ** v1, GtsVertex ** v2, GtsVertex ** v3)
{
  GtsSegment * e1, * e2;

  g_return_if_fail (t != NULL);
  g_return_if_fail (v1 != NULL && v2 != NULL && v3 != NULL);

  e1 = GTS_SEGMENT (t->e1);
  e2 = GTS_SEGMENT (t->e2);

  if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v1) {
    *v1 = GTS_SEGMENT (e1)->v1; 
    *v2 = GTS_SEGMENT (e1)->v2; 
    *v3 = GTS_SEGMENT (e2)->v2;
  }
  else if (GTS_SEGMENT (e1)->v2 == GTS_SEGMENT (e2)->v2) {
    *v1 = GTS_SEGMENT (e1)->v1; 
    *v2 = GTS_SEGMENT (e1)->v2; 
    *v3 = GTS_SEGMENT (e2)->v1;
  }
  else if (GTS_SEGMENT (e1)->v1 == GTS_SEGMENT (e2)->v1) {
    *v1 = GTS_SEGMENT (e1)->v2; 
    *v2 = GTS_SEGMENT (e1)->v1; 
    *v3 = GTS_SEGMENT (e2)->v2;
  }
  else {
    *v1 = GTS_SEGMENT (e1)->v2; 
    *v2 = GTS_SEGMENT (e1)->v1; 
    *v3 = GTS_SEGMENT (e2)->v1;
  }
}

/**
 * gts_triangle_circumcircle_center:
 * @t: a #GtsTriangle.
 * @point_class: a #GtsPointClass.
 *
 * Returns: a new #GtsPoint, center of the circumscribing circle of @t or
 * %NULL if the circumscribing circle is not defined.
 */
GtsPoint * gts_triangle_circumcircle_center (GtsTriangle * t,
					     GtsPointClass * point_class)
{
  GtsVertex * v1, * v2, * v3;
  gdouble xa, ya, xb, yb, xc, yc;
  gdouble xd, yd, xe, ye;
  gdouble xad, yad, xae, yae;
  gdouble det;
  
  g_return_val_if_fail (t != NULL, NULL);
  g_return_val_if_fail (point_class != NULL, NULL);

  gts_triangle_vertices (t, &v1, &v2, &v3);

  xa = GTS_POINT (v1)->x; ya = GTS_POINT (v1)->y;
  xb = GTS_POINT (v2)->x; yb = GTS_POINT (v2)->y;
  xc = GTS_POINT (v3)->x; yc = GTS_POINT (v3)->y;
  xd = (xa + xb)/2.; yd = (ya + yb)/2.;
  xe = (xa + xc)/2.; ye = (ya + yc)/2.;
  xad = xd - xa; yad = yd - ya;
  xae = xe - xa; yae = ye - ya;
  det = xad*yae - xae*yad;
  if (det == 0.)
    return NULL;
  return gts_point_new (point_class,
			(yae*yad*(yd - ye) + xad*yae*xd - xae*yad*xe)/det,
			-(xae*xad*(xd - xe) + yad*xae*yd - yae*xad*ye)/det,
			0.);
}

/* square of the maximum area ratio admissible */
#define AREA_RATIO_MAX2 1e8

static gboolean points_are_folded (GtsPoint * A,
				   GtsPoint * B,
				   GtsPoint * C,
				   GtsPoint * D,
				   gdouble max)
{
  GtsVector AB, AC, AD;
  GtsVector n1, n2;
  gdouble nn1, nn2, n1n2;

  gts_vector_init (AB, A, B);
  gts_vector_init (AC, A, C);
  gts_vector_init (AD, A, D);
  gts_vector_cross (n1, AB, AC);
  gts_vector_cross (n2, AD, AB);

  nn1 = gts_vector_scalar (n1, n1);
  nn2 = gts_vector_scalar (n2, n2);
  if (nn1 >= AREA_RATIO_MAX2*nn2 || nn2 >= AREA_RATIO_MAX2*nn1)
    return TRUE;
  n1n2 = gts_vector_scalar (n1, n2);
  if (n1n2 > 0.)
    return FALSE;
  if (n1n2*n1n2/(nn1*nn2) > max)
    return TRUE;
  return FALSE;
}

static GtsVertex * triangle_use_vertices (GtsTriangle * t,
					  GtsVertex * A, 
					  GtsVertex * B)
{
  GtsVertex 
    * v1 = GTS_SEGMENT (t->e1)->v1, 
    * v2 = GTS_SEGMENT (t->e1)->v2, 
    * v3 = gts_triangle_vertex (t);

  if (v1 == A) {
    if (v2 == B)
      return v3;
    g_assert (v3 == B);
    return v2;
  }
  if (v2 == A) {
    if (v1 == B)
      return v3;
    g_assert (v3 == B);
    return v1;
  }
  if (v3 == A) {
    if (v1 == B)
      return v2;
    g_assert (v2 == B);
    return v1;
  }
  g_assert_not_reached ();
  return NULL;
}

/**
 * gts_triangles_are_folded:
 * @triangles: a list of #GtsTriangle.
 * @A: a #GtsVertex.
 * @B: another #GtsVertex.
 * @max: the maximum value of the square of the cosine of the angle between
 * two triangles.
 *
 * Given a list of triangles sharing @A and @B as vertices, checks if any
 * two triangles in the list make an angle larger than a given value defined
 * by @max.
 * 
 * Returns: %TRUE if any pair of triangles in @triangles makes an angle larger 
 * than the maximum value, %FALSE otherwise.
 */
gboolean gts_triangles_are_folded (GSList * triangles,
				   GtsVertex * A, GtsVertex * B,
				   gdouble max)
{
  GSList * i;

  g_return_val_if_fail (A != NULL, TRUE);
  g_return_val_if_fail (B != NULL, TRUE);

  i = triangles;
  while (i) {
    GtsVertex * C = triangle_use_vertices (i->data, A, B);
    GSList * j = i->next;    
    while (j) {
      GtsVertex * D = triangle_use_vertices (j->data, A, B);
      if (points_are_folded (GTS_POINT (A), 
			     GTS_POINT (B), 
			     GTS_POINT (C), 
			     GTS_POINT (D), 
			     max))
	return TRUE;
      j = j->next;
    }
    i = i->next;
  }
  return FALSE;
}

/**
 * gts_triangle_is_stabbed:
 * @t: a #GtsTriangle.
 * @p: a #GtsPoint.
 * @orientation: a pointer or %NULL.
 *
 * Returns: one of the vertices of @t, one of the edges of @t or @t if
 * any of these are stabbed by the ray starting at @p (included) and
 * ending at (@p->x, @p->y, +infty), %NULL otherwise. If the ray is
 * contained in the plane of the triangle %NULL is also returned. If
 * @orientation is not %NULL, it is set to the value of the
 * orientation of @p relative to @t (as given by
 * gts_point_orientation_3d()).  
 */
GtsObject * gts_triangle_is_stabbed (GtsTriangle * t, 
				     GtsPoint * p,
				     gdouble * orientation)
{
  GtsVertex * v1, * v2, * v3, * inverted = NULL;
  GtsEdge * e1, * e2, * e3, * tmp;
  gdouble o, o1, o2, o3;

  g_return_val_if_fail (t != NULL, NULL);
  g_return_val_if_fail (p != NULL, NULL);

  gts_triangle_vertices_edges (t, NULL, &v1, &v2, &v3, &e1, &e2, &e3);
  o = gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), GTS_POINT (v3));
  if (o == 0.)
    return NULL;
  if (o < 0.) {
    inverted = v1;
    v1 = v2;
    v2 = inverted;
    tmp = e2;
    e2 = e3;
    e3 = tmp;
  }
  o = gts_point_orientation_3d (GTS_POINT (v1),
				GTS_POINT (v2), 
				GTS_POINT (v3), 
				p);
  if (o < 0.)
    return NULL;
  o1 = gts_point_orientation (GTS_POINT (v1), GTS_POINT (v2), p);
  if (o1 < 0.)
    return NULL;
  o2 = gts_point_orientation (GTS_POINT (v2), GTS_POINT (v3), p);
  if (o2 < 0.)
    return NULL;
  o3 = gts_point_orientation (GTS_POINT (v3), GTS_POINT (v1), p);
  if (o3 < 0.)
    return NULL;  
  if (orientation) *orientation = inverted ? -o : o;
  if (o1 == 0.) {
    if (o2 == 0.)
      return GTS_OBJECT (v2);
    if (o3 == 0.)
      return GTS_OBJECT (v1);
    return GTS_OBJECT (e1);
  }
  if (o2 == 0.) {
    if (o3 == 0.)
      return GTS_OBJECT (v3);
    return GTS_OBJECT (e2);
  }
  if (o3 == 0.)
    return GTS_OBJECT (e3);
  return GTS_OBJECT (t);
}

/**
 * gts_triangle_interpolate_height:
 * @t: a #GtsTriangle.
 * @p: a #GtsPoint.
 *
 * Fills the z-coordinate of point @p belonging to the plane
 * projection of triangle @t with the linearly interpolated value of
 * the z-coordinates of the vertices of @t.
 */
void gts_triangle_interpolate_height (GtsTriangle * t, GtsPoint * p)
{
  GtsPoint * p1, * p2, * p3;
  gdouble x1, x2, y1, y2, det;

  g_return_if_fail (t != NULL);
  g_return_if_fail (p != NULL);

  p1 = GTS_POINT (GTS_SEGMENT (t->e1)->v1);
  p2 = GTS_POINT (GTS_SEGMENT (t->e1)->v2);
  p3 = GTS_POINT (gts_triangle_vertex (t));

  x1 = p2->x - p1->x;
  y1 = p2->y - p1->y;
  x2 = p3->x - p1->x;
  y2 = p3->y - p1->y;
  det = x1*y2 - x2*y1;
  if (det == 0.)
    p->z = (p1->z + p2->z + p3->z)/3.;
  else {
    gdouble x = p->x - p1->x;
    gdouble y = p->y - p1->y;
    gdouble a = (x*y2 - y*x2)/det;
    gdouble b = (y*x1 - x*y1)/det;

    p->z = (1. - a - b)*p1->z + a*p2->z + b*p3->z;
  }
}
