/*****************************************************************************
*   "Irit" - the 3d (not only polygonal) solid modeller.		     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Aug. 1990   *
******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                *
******************************************************************************
*   Module to interplate internal vertices using vertices on the given       *
* convex boundary OriginalVList. All internal vertices are assumed to be in  *
* the interior of the convex region defined by OriginalVList or on its       *
* boundary.								     *
*****************************************************************************/

#include <math.h>
#include "irit_sm.h"
#include "iritprsr.h"
#include "geomat3d.h"
#include "intrnrml.h"

/* #define DEBUG1          To define polygon (with normals printing routine. */

static void UpdateOneVertexNormal(IPVertexStruct *VUpdate,
				  IPPolygonStruct *OriginalPl);

#ifdef DEBUG1 
static void PrintPolygon(IPPolygonStruct *Pl);
#endif /* DEBUG1 */

/*****************************************************************************
* DESCRIPTION:                                                               M
* For each polygon in PlList update any vertex normal which is zero to an    M
* interpolated value using the Original polygon vertex list OriginalPl.      M
*   All the new vertices are enclosed within the original polygon which      M
* must be convex as well.						     M
*                                                                            *
* PARAMETERS:                                                                M
*   PlList:       List of polygons to update normal for.                     M
*   OriginalPl:   Original polygons PlList was derived from, probably using  M
*                 Boolean operations.                                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   UpdateVerticesNormals, normals                                           M
*****************************************************************************/
void UpdateVerticesNormals(IPPolygonStruct *PlList,
			   IPPolygonStruct *OriginalPl)
{
    IPVertexStruct *V, *VHead;

    while (PlList) {
	V = VHead = PlList -> PVertex;
	do {
	    if (APX_EQ(V -> Normal[0], 0.0) &&
		APX_EQ(V -> Normal[1], 0.0) &&
		APX_EQ(V -> Normal[2], 0.0)) {
		/* This vertex need to be updated. */
		UpdateOneVertexNormal(V, OriginalPl);
	    }

	    V = V -> Pnext;
	}
	while (V != NULL && V != VHead);

	PlList = PlList -> Pnext;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Update one normal of one vertex by a convex blend of normals of          *
* boundary vertices.							     *
*                                                                            *
* PARAMETERS:                                                                *
*   VUpdate:      Vertex to update normal for.                               *
*   OriginalPl:   Using a convex blend of normals from the original polygon. *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void UpdateOneVertexNormal(IPVertexStruct *VUpdate,
				  IPPolygonStruct *OriginalPl)
{
    IPVertexStruct *V,
	*OriginalVList = OriginalPl -> PVertex;

    V = OriginalVList;
    do {
	if (Colinear3Vertices(V, VUpdate, V -> Pnext)) {
	    /* Interpolate the normal according to the edge vertices VUpdate */
	    /* is on and according to the distance of VUpdate to edge ends.  */
	    InterpNrmlBetweenTwo(VUpdate, V, V -> Pnext);
	    return;
	}
	V = V -> Pnext;
    }
    while (V != NULL && V != OriginalVList);

    /* If we are here then the point is not on the polygon boundary and in   */
    /* that case we simply use the polygon normal as an approximation.       */
    PT_COPY(VUpdate -> Normal, OriginalPl -> Plane);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Verify the colinearity of the given three vertices.                        M
*                                                                            *
* PARAMETERS:                                                                M
*   V1, V2, V3: Vertices to test for colinearity.                            M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:        TRUE if colinear, FALSE otherwise.                           M
*                                                                            *
* KEYWORDS:                                                                  M
*   Colinear3Vertices, colinearity                                           M
*****************************************************************************/
int Colinear3Vertices(IPVertexStruct *V1,
		      IPVertexStruct *V2,
		      IPVertexStruct *V3)
{
    RealType l;
    VectorType V12, V23, V;

    if (PT_APX_EQ(V1 -> Coord, V2 -> Coord) ||
	PT_APX_EQ(V2 -> Coord, V3 -> Coord))
	return TRUE;

    PT_SUB(V12, V1 -> Coord, V2 -> Coord);
    PT_SUB(V23, V2 -> Coord, V3 -> Coord);

    /* Make sure the middle point is in fact in the middle. */
    if (V12[0] * V23[0] < -IRIT_UEPS ||
        V12[1] * V23[1] < -IRIT_UEPS ||
	V12[2] * V23[2] < -IRIT_UEPS)
	return FALSE;

    GMVecCrossProd(V, V12, V23);

    l = PT_LENGTH(V);

    return APX_EQ(l, 0.0);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Update Normal of the middle vertex V, assumed to be between V1 and V2.   M
*                                                                            *
* PARAMETERS:                                                                M
*   V:          Vertex that its normal is to be updated.                     M
*   V1, V2:     Edge V is assumed to be on so that the two normals of V1     M
*               and V2 can be blended to form the normal of V.               M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   InterpNrmlBetweenTwo, normals                                            M
*****************************************************************************/
void InterpNrmlBetweenTwo(IPVertexStruct *V,
			  IPVertexStruct *V1,
			  IPVertexStruct *V2)
{
    RealType t1, t2;
    VectorType Vec1, Vec2;

    PT_SUB(Vec1, V -> Coord, V1 -> Coord);
    PT_SUB(Vec2, V -> Coord, V2 -> Coord);
    t1 = PT_LENGTH(Vec1);
    t2 = PT_LENGTH(Vec2);

    PT_COPY(Vec1, V1 -> Normal);
    PT_COPY(Vec2, V2 -> Normal);
    PT_SCALE(Vec1, t2);
    PT_SCALE(Vec2, t1);
    PT_ADD(V -> Normal, Vec1, Vec2);

    PT_NORMALIZE(V -> Normal);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Update normal of the middle vertex V, assumed to be between V1 and V2.   M
*                                                                            *
* PARAMETERS:                                                                M
*   Pt:         Middle position at which a normal is to be ucomputed.        M
*   Normal:     Where resulting vector is to be placed.                      M
*   V1, V2:     Edge V is assumed to be on so that the two normals of V1     M
*               and V2 can be blended to form the normal of V.               M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   InterpNrmlBetweenTwo2, normals                                           M
*****************************************************************************/
void InterpNrmlBetweenTwo2(PointType Pt,
			   VectorType Normal,
			   IPVertexStruct *V1,
			   IPVertexStruct *V2)
{
    RealType t1, t2;
    VectorType Vec1, Vec2;

    PT_SUB(Vec1, Pt, V1 -> Coord);
    PT_SUB(Vec2, Pt, V2 -> Coord);
    t1 = PT_LENGTH(Vec1);
    t2 = PT_LENGTH(Vec2);

    PT_COPY(Vec1, V1 -> Normal);
    PT_COPY(Vec2, V2 -> Normal);
    PT_SCALE(Vec1, t2);
    PT_SCALE(Vec2, t1);
    PT_ADD(Normal, Vec1, Vec2);

    PT_NORMALIZE(Normal);
}

#ifdef DEBUG1 

/*****************************************************************************
* DESCRIPTION:                                                               *
* Prints the content of the given polygon, to standard output.		     *
*                                                                            *
* PARAMETERS:                                                                *
*   Pl:         Polygon to print to stdout.                                  *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void PrintPolygon(IPPolygonStruct *Pl)
{
    IPVertexStruct *V = Pl -> Coord,
		 *VHead = V;

    do {
	printf("    %10lg %10lg %10lg (%10lg %10lg %10lg)",
	       V -> Coord[0], V -> Coord[1], V -> Coord[2],
	       V -> Normal[0], V -> Normal[1], V -> Normal[2]);
	if (IS_INTERNAL_EDGE(V))
	    printf(" (Internal)\n");
	else
	    printf("\n");
	V = V -> Pnext;
    }
    while (V!= NULL && V != VHead);
}

#endif /* DEBUG1 */
