/*****************************************************************************
* Converts polylines to polygons.					     *
******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                *
******************************************************************************
* Written by:  Bassarab Dmitri & Plavnik Michael       Ver 0.2, Apr. 1995    *
*****************************************************************************/

#include "program.h"
#include "intrnrml.h"
#include "geomat3d.h"

#define VERTEX_1(p) ((p) -> PVertex)
#define VERTEX_2(p) (VERTEX_1(p) -> Pnext)
#define VERTEX_3(p) (VERTEX_2(p) -> Pnext)
#define VERTEX_4(p) (VERTEX_3(p) -> Pnext)

static void ComputeZDepth(IPVertexStruct *Vertex);
static IPPolygonStruct *OffZet(IPVertexStruct *Vertex, RealType k);
static int LineCross(IPVertexStruct *v1, IPVertexStruct *v2, PointType p);
static void MiterJoin(IPPolygonStruct *p, IPPolygonStruct *q);

static RealType ZNear, ZFar;                              /* Depth of scene. */

/*****************************************************************************
* DESCRIPTION:                                                               *
*    Builds new polygon on segment of line defined by			     *
*    2 vertices (vertex and it's next)                                       *
*                                                                            *
*          1+---------+3                                                     *
*           |         |                                                      *
*    Vertex + - - - - + Vertex -> PNext                                      *
*           |         |                                                      *
*          2+-------- +4                                                     *
*                                                                            *
*    Offset at each vertex (width) depends on it Z depth                     *
*                                                                            *
* PARAMETERS:                                                                *
*   Vertex: IN, base vertex of future polygon                                *
*   k:      IN, width factor                                                 *
*                                                                            *
* RETURN VALUE:                                                              *
*   IPPolygonStruct *: newbie polygon                                        *
*****************************************************************************/
static IPPolygonStruct *OffZet(IPVertexStruct *Vertex, RealType k)
{
    IPPolygonStruct *p;
    int i;
    RealType Width, Length;
    PointType d;

    PT_SUB(d, Vertex -> Pnext -> Coord, Vertex -> Coord);
    Length = sqrt(SQR(d[X]) + SQR(d[Y]));
    if (APX_EQ(Length, 0))
	return NULL;                             /* Segment has zero length. */
    p = IPAllocPolygon(0, NULL, NULL);
    for (i = 0; i < 4; i++)
        p -> PVertex = IPAllocVertex(0, p, p -> PVertex);
    Width = (Vertex -> Coord[Z] - ZFar) * k + Options.PllMinW;
    PT_COPY(VERTEX_1(p) -> Coord, Vertex -> Coord);
    VERTEX_1(p) -> Coord[X] -= Width * d[Y] / Length;
    VERTEX_1(p) -> Coord[Y] += Width * d[X] / Length;
    PT_COPY(VERTEX_4(p) -> Coord, Vertex -> Coord);
    VERTEX_4(p) -> Coord[X] += Width * d[Y] / Length;
    VERTEX_4(p) -> Coord[Y] -= Width * d[X] / Length;
    Width = (Vertex -> Pnext -> Coord[Z] - ZFar) * k + Options.PllMinW;
    PT_COPY(VERTEX_2(p) -> Coord, Vertex -> Pnext -> Coord);
    VERTEX_2(p) -> Coord[X] -= Width * d[Y] / Length;
    VERTEX_2(p) -> Coord[Y] += Width * d[X] / Length;
    PT_COPY(VERTEX_3(p) -> Coord, Vertex -> Pnext -> Coord);
    VERTEX_3(p) -> Coord[X] += Width * d[Y] / Length;
    VERTEX_3(p) -> Coord[Y] -= Width * d[X] / Length;
    return p;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Computes point of intersection between two lines in XY plane             *
*   defined by two vertices (v? and it's next)                               *
*                                                                            *
* PARAMETERS:                                                                *
*   v1: IN, start of first line segment                                      *
*   v2: IN, start of second line segment                                     *
*   p:  OUT, point of intersection                                           *
*                                                                            *
* RETURN VALUE:                                                              *
*   int: boolean, does point of intersection exist                           *
*****************************************************************************/
static int LineCross(IPVertexStruct *v1, IPVertexStruct *v2, PointType p)
{
    RealType dX1, dY1, dX2, dY2, Det, A1, A2;

    dX1 = v1 -> Pnext -> Coord[X] - v1 -> Coord[X];
    dY1 = v1 -> Pnext -> Coord[Y] - v1 -> Coord[Y];
    dX2 = v2 -> Pnext -> Coord[X] - v2 -> Coord[X];
    dY2 = v2 -> Pnext -> Coord[Y] - v2 -> Coord[Y];
    Det = dY1 * (-dX2) - dY2 * (-dX1);
    if (APX_EQ(Det, 0)) 
	return FALSE;                      /* Line projections are parallel. */
    A1 = v1 -> Coord[X] * dY1 - v1 -> Coord[Y] * dX1;
    A2 = v2 -> Coord[X] * dY2 - v2 -> Coord[Y] * dX2;
    p[X] = (A1 * (-dX2) - A2 * (-dX1)) / Det;
    p[Y] = (dY1 * A2 - dY2 * A1) / Det;
    return TRUE;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*    Joins two polygons having 4 vertices by extending their sides           *
*    up to point of intersection.                                            *
*                                                                            *
*    1+-------+2 <-> 1+------+2                                              *
*     |   p   |       |  q   |                                               *
*    4+-------+3 <-> 4+------+3                                              *
*                                                                            *
* PARAMETERS:                                                                *
*   p: IN, pointer to first polygon                                          *
*   q: IN, pointer to second polygon                                         *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void MiterJoin(IPPolygonStruct *p, IPPolygonStruct *q)
{
    PointType x;                           /* Point of intersection (maybe). */

    if (!p || !q || !LineCross(VERTEX_1(p), VERTEX_1(q), x))
	return;                           /* Sides of polygons are parallel. */
    VERTEX_2(p) -> Coord[X] = VERTEX_1(q) -> Coord[X] = x[X];
    VERTEX_2(p) -> Coord[Y] = VERTEX_1(q) -> Coord[Y] = x[Y];
    LineCross(VERTEX_3(p), VERTEX_3(q), x);
    VERTEX_3(p) -> Coord[X] = VERTEX_4(q) -> Coord[X] = x[X];
    VERTEX_3(p) -> Coord[Y] = VERTEX_4(q) -> Coord[Y] = x[Y];
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Computes depth of the scene with respect to Z axes. Globals ZNear and    *
*   ZFar are updated as side effect.                                         *
*                                                                            *
* PARAMETERS:                                                                *
*   Vertex: IN, pointer to vertex contained in scene.                        *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void ComputeZDepth(IPVertexStruct *Vertex)
{
    if (Vertex -> Coord[Z] NEAR_THAN ZNear)
        ZNear = Vertex -> Coord[Z];
    if (Vertex -> Coord[Z] FURTHER_THAN ZFar)
        ZFar = Vertex -> Coord[Z];
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Converts all polylines to polygons. Width of newbie polygons             M
*   depends on pllMaxW & pllMinW global parameters and depth of it           M
*   vertices.                                                                *
*                                                                            *
* PARAMETERS:                                                                M
*   Object: IN, list of objects including polylines                          M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   Polyline2Polygons                                                        M
*****************************************************************************/
void Polyline2Polygons(IPObjectStruct *Object)
{
    IPPolygonStruct *PolyLine, *First,
	*p = NULL;
    IPVertexStruct *Vertex;
    RealType k;

    ZNear = FURTHEST_Z;
    ZFar  = NEAREST_Z;
    IritPrsrForEachVertex(Object, ComputeZDepth);

    k = APX_EQ(ZNear, ZFar) ?
        0 : (Options.PllMaxW - Options.PllMinW) / (ZNear - ZFar);

    for ( ; Object != NULL; Object = Object -> Pnext) { 
	if (IP_IS_POLYLINE_OBJ(Object)) {
	    IP_SET_POLYGON_OBJ(Object);
	    AttrSetObjectIntAttrib(Object, "_NO_SHADE", TRUE);
		    
	    PolyLine = Object -> U.Pl;          /* Cut off reference to list */
	    Object -> U.Pl = NULL;                          /* of polylines. */

	    for( ; PolyLine; PolyLine = PolyLine -> Pnext) {
		First = NULL;
		Vertex = PolyLine -> PVertex;
			    
		for( ; Vertex && Vertex -> Pnext; Vertex = Vertex -> Pnext)
		    if (!First)
			/* First segment - first polygon. */
			First = p = OffZet(Vertex, k);
		    else
		        if ((p -> Pnext = OffZet(Vertex, k)) != NULL)
			    p = p -> Pnext;

		if (!First)  		              /* Polyline too short. */
		    continue;
			    
		/* Unable to compute normal. */
		for (p = First; p; p = p -> Pnext) {
		    p -> Plane[X] =  0;
		    p -> Plane[Y] =  0;
		    p -> Plane[Z] = -1;
		    p -> Plane[3] =  0;
		    IritPrsrUpdateVrtxNrml(p, p -> Plane);
		}

		/* Join each two sequential polygons. */
		for (p = First; p && p -> Pnext; p = p -> Pnext)
		    MiterJoin(p, p -> Pnext);

		/* Get last vertex of polyline. */
		Vertex = IritPrsrGetLastVrtx(PolyLine -> PVertex);
		/* Is it closed? */
		if (PT_APX_EQ(PolyLine -> PVertex -> Coord, Vertex -> Coord))
		    /* Close sequence of polygons. */
		    MiterJoin(p, First);

		if (!Object -> U.Pl)
		    Object -> U.Pl = First;
		else
		    IritPrsrGetLastPoly(Object -> U.Pl) -> Pnext = First;
	    }
	}
    }
}
