/*****************************************************************************
* Applies coordinate system transformations to each object and global context*
******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                *
******************************************************************************
* Written by:  Bassarab Dmitri & Plavnik Michael       Ver 0.2, Apr. 1995    *
*****************************************************************************/

#include "program.h"
#include "debug.h"

static void Transform(IPVertexStruct *Vertex);
static void MapToScreen(IPVertexStruct *Vertex);
static void ReverseVertexNormal(IPVertexStruct *v);
static void ReversePlaneNormal(IPPolygonStruct *Poly);

static MatrixType ScreenMat;			    /* Map to screen matrix. */

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Apply transformation defined in global view matrix to a vertex coordi-   *
*   nates. Sets reciprotory of homogenious coordinate to computed value.     *
*                                                                            *
* PARAMETERS:                                                                *
*   Vertex: IN OUT, pointer to transofrmee.                                  *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void Transform(IPVertexStruct *Vertex)
{
    RealType Homo[4], w;
    
    PT_COPY(Homo, Vertex -> Coord);
    Homo[3] = 1.0;
    MatMultWVecby4by4(Homo, Homo, Context.ViewMat);
    if (Homo[3] < EPSILON) {
	if (Homo[3] < -EPSILON)	{
	    static int
		WasMessage = FALSE;

	    if (!WasMessage) {
		fprintf(stderr, "Error: negative homogeneous values in view!\n");
		WasMessage = TRUE;
	    }
	}
	else {
	    fprintf(stderr, "Error: bad perspective matrix!\n");
	    exit(11);
	}
    }
    w = 1 / Homo[3];
    PT_SCALE(Homo, w);
    PT_COPY(Vertex -> Coord, Homo);
    AttrSetRealAttrib(&Vertex -> Attrs, "_1/W", w);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Apply transformation defined in global centerX, centerY and scale vars.  *
*   That is actualy 3D to 2D of viewing plane projection.                    *
*                                                                            *
* PARAMETERS:                                                                *
*   Vertex: IN OUT, pointer to transformee.                                  *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void MapToScreen(IPVertexStruct *Vertex)
{
    MatMultVecby4by4(Vertex -> Coord, Vertex -> Coord, ScreenMat);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Set vertex normal in opposite direction.                                 *
*                                                                            *
* PARAMETERS:                                                                *
*   v:       IN OUT, pointer to vertex object.                               *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void ReverseVertexNormal(IPVertexStruct *v)
{
    PT_SCALE(v -> Normal, -1.0);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Sets plane equation to have normal defined in opposite direction.        *
*                                                                            *
* PARAMETERS:                                                                *
*   v:       IN OUT, pointer to vertex object.                               *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void ReversePlaneNormal(IPPolygonStruct *Poly)
{
    PT_SCALE(Poly -> Plane, -1.0);
    Poly -> Plane[3] = -Poly -> Plane[3];
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Performs necessary transformation of coordinate system as described in   M
*   irit matrices and generates properly global "all" and "inv" matrices.    M
*   All objects in the system are transformed in accordance. Also clipping   M 
*   and polyline to polygon conversions are performed. Vertex attributes are M 
*   inserted to enable incremental algorithm style.                          M
*                                                                            *
* PARAMETERS:                                                                M
*   Objects: IN, pointer to Irit objects tree.                               M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPObjectStruct *: pointer to the Irit objects tree after transformations.M
*                                                                            *
* KEYWORDS:                                                                  M
*   Map, coordinate transformations, homogenious coordinate, clipping        M
*****************************************************************************/
IPObjectStruct *Map(IPObjectStruct *Objects)
{
    MAPPING_MESSAGE();

    MatGenUnitMat(Context.ViewMat);
    
    if (IritPrsrWasViewMat)
        MatMultTwo4by4(Context.ViewMat, Context.ViewMat, IritPrsrViewMat);
    
    if (IritPrsrWasPrspMat) {
        MatMultTwo4by4(Context.ViewMat, Context.ViewMat, IritPrsrPrspMat);
        Context.Viewer[X] = Context.Viewer[Y] = Context.Viewer[Z] = 0.0;
    }
    if (!MatInverseMatrix(Context.ViewMat, Context.InvMat)) {
        fprintf(stderr, "Error: non-invertable matrix.\n");
        exit(11);
    }
    IritPrsrForEachVertex(Objects, Transform);

    if (Options.Polylines)
        Polyline2Polygons(Objects);
    
    if (Options.NormalReverse){
        IritPrsrForEachVertex(Objects, ReverseVertexNormal);
        IritPrsrForEachPoly(Objects, ReversePlaneNormal);
    }

    MatGenMatScale(Options.XSize / 2, Options.YSize / 2, 1, ScreenMat);
    MatMultTwo4by4(Context.ViewMat, Context.ViewMat, ScreenMat);
    IritPrsrForEachVertex(Objects, MapToScreen);
    
    MatGenMatTrans(Options.XSize / 2, Options.YSize / 2, 0., ScreenMat);
    MatMultTwo4by4(Context.ViewMat, Context.ViewMat, ScreenMat);
    IritPrsrForEachVertex(Objects, MapToScreen);

    if (!MatInverseMatrix(Context.ViewMat, Context.InvMat)) {
        fprintf(stderr, "Error: non-invertable matrix.\n");
        exit(11);
    }

    MatMultVecby4by4(Context.Viewer, Context.Viewer, Context.InvMat);

    if (!IS_VIEWER_POINT()) {           /* Only vectors need to be adjusted. */
        static PointType
	    Zero = { 0.0, 0.0, 0.0 };

        MatMultVecby4by4(Zero, Zero, Context.InvMat);
        PT_SUB(Context.Viewer, Context.Viewer, Zero);
        PT_NORMALIZE(Context.Viewer);
    }
    return Objects;
}
