/*****************************************************************************
*   "Irit" - the 3d (not only polygonal) solid modeller.		     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Aug. 1990   *
******************************************************************************
*   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 */

/*****************************************************************************
*   For each polygon in PlList update any vertex normal which is zero to an  *
*interpolated value using the Original polygon vertex list OriginalVList.    *
*   All the new vertices are enclosed within the original polygon which      *
* must be convex as well.						     *
*****************************************************************************/
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;
    }
}

/*****************************************************************************
*   Update one vertex normal be a convex blend of boundary vertices normals  *
*****************************************************************************/
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);
}

/*****************************************************************************
*   Returns TRUE iff the 3 vertices are colinear.			     *
*****************************************************************************/
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] < -EPSILON ||
        V12[1] * V23[1] < -EPSILON ||
	V12[2] * V23[2] < -EPSILON)
	return FALSE;

    GMVecCrossProd(V, V12, V23);

    l = PT_LENGTH(V);

    return APX_EQ(l, 0.0);
}

/*****************************************************************************
*   Update Normal of the middle vertex V, assumed to be between V1 and V2.   *
*****************************************************************************/
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);
}

/*****************************************************************************
*   Copute Normal from middle position of Pt Between V1 and V2.		     *
*****************************************************************************/
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 

/*****************************************************************************
*   Print the content of the given polygon, to standard output.		     *
*****************************************************************************/
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 */
