/*****************************************************************************
*   "Irit" - the 3d (not only polygonal) solid modeller.		     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, May 1995    *
******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                *
******************************************************************************
*   Module to compute the offset of a polygon/line, in the XY plame.	     *
*****************************************************************************/

#include <stdio.h>
#include <math.h>
#include "irit_sm.h"
#include "geomat3d.h"
#include "allocate.h"

#define MINIMUM_MITTER_SCALE 0.01		   /* A mitter scale of 100. */

static RealType GMPolyOffsetAmountConstant(RealType *Coord);

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Default offset amount estimating routine - returns a constant scaling    *
* factor of one.                                                             *
*                                                                            *
* PARAMETERS:                                                                *
*   Coord:    Of point as XYZ values.                                        *
*                                                                            *
* RETURN VALUE:                                                              *
*   RealType:  scaling factor of offset amount.                              *
*****************************************************************************/
static RealType GMPolyOffsetAmountConstant(RealType *Coord)
{
    return 1.0;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Sets the offset amount to be a function of the depth Z value by scaling  M
* with 1/Z.                                                                  M
*                                                                            *
* PARAMETERS:                                                                M
*   Coord:    Of point as XYZ values.                                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   RealType:  scaling factor of offset amount.                              M
*                                                                            *
* SEE ALSO:								     M
*   GMPolyOffset							     M
*                                                                            *
* KEYWORDS:                                                                  M
*   GMPolyOffsetAmountDepth                                                  M
*****************************************************************************/
RealType GMPolyOffsetAmountDepth(RealType *Coord)
{
    return 1.0 / (IRIT_EPS + fabs(Coord[2] + 10.0));
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Computes the offset of a given polygon/line in the XY plane by Ofst.     M
*                                                                            M
*                                                                            *
* PARAMETERS:                                                                M
*   Poly:        To compute its offset in the XY plane.                      M
*   IsPolygon:   TRUE for a polygon, FALSE for a polyline.                   M
*   Ofst:        Amount of offset.				             M
*   AmountFunc:  Scale the offset amount according to this function. A NULL  M
*		 here will use a constant scaling factor of one.	     M
*                                                                            *
* RETURN VALUE:                                                              M
*   IPPolygonStruct *:   Offset of Poly by Ofst amount.                      M
*                                                                            *
* SEE ALSO:								     M
*   GMPolyOffsetAmountDepth						     M
*                                                                            *
* KEYWORDS:                                                                  M
*   GMPolyOffset                                                             M
*****************************************************************************/
IPPolygonStruct *GMPolyOffset(IPPolygonStruct *Poly,
			      int IsPolygon,
			      RealType Ofst,
			      GMPolyOffsetAmountFuncType AmountFunc)
{
    RealType R;
    VectorType Dir, DirAux, PrevDir;
    IPPolygonStruct
        *PolyOfst = IPAllocPolygon(Poly -> Tags,
				   CopyVertexList(Poly -> PVertex), NULL);
    IPVertexStruct
	*V = Poly -> PVertex,
	*Vnext = V -> Pnext,
	*VO = PolyOfst -> PVertex;

    if (AmountFunc == NULL)
	AmountFunc = GMPolyOffsetAmountConstant;

    PT_SUB(PrevDir, Vnext -> Coord, V -> Coord);
    PrevDir[2] = 0.0; 				     /* Make sure Z is zero. */
    PT_NORMALIZE(PrevDir);
    if (!IsPolygon) {
	/* Handle the first vertex, if a polyline. */
	R = AmountFunc(VO -> Coord) * Ofst;
	VO -> Coord[0] += PrevDir[1] * R;
	VO -> Coord[1] -= PrevDir[0] * R;
    }

    /* Iterate through the rest of the points.  For a polygon the list is    */
    /* assumed circular.  For a polyline the list is assumed NULL terminated.*/
    V = Vnext;
    Vnext = Vnext -> Pnext;
    VO = VO -> Pnext;
    if (Vnext != NULL) {
	do {
	    PT_SUB(Dir, Vnext -> Coord, V -> Coord);
	    Dir[2] = 0.0; 			     /* Make sure Z is zero. */
	    PT_NORMALIZE(Dir);
	    PT_ADD(DirAux, Dir, PrevDir);
	    PT_NORMALIZE(DirAux);

	    /* Compute the mitter joint's scaling factor of 1/sin(alpha). */
	    R = sin(acos(-DOT_PROD(Dir, PrevDir)) / 2.0);
	    if (R < MINIMUM_MITTER_SCALE)
	        R = MINIMUM_MITTER_SCALE;

	    R = AmountFunc(VO -> Coord) * Ofst / R;
	    VO -> Coord[0] += DirAux[1] * R;
	    VO -> Coord[1] -= DirAux[0] * R;

	    V = Vnext;
	    Vnext = Vnext -> Pnext;
	    VO = VO -> Pnext;

	    PT_COPY(PrevDir, Dir);
	}
	while (Vnext != NULL && V != Poly -> PVertex -> Pnext);
    }
    else
        PT_COPY(Dir, PrevDir);

    if (!IsPolygon) {
	/* Do the last point. */
	R = AmountFunc(VO -> Coord) * Ofst;
	VO -> Coord[0] += Dir[1] * R;
	VO -> Coord[1] -= Dir[0] * R;
    }
    else
	IritPrsrUpdatePolyPlane(PolyOfst);

    return PolyOfst;
}


