/* 
 * Wicket.c - wicket abstract data type.
 * 
 * Author:  L. Van Warren
 * Date:    May 1984
 * Copyright (c) 1984 L. Van Warren * All Rights Reserved
 * 
 */

#include "Van.h"
/*****************************************************************
 * TAG( asnWicket )
 * 
 * Assign a value to a vertex.
 * 
 * Inputs:
 *  A kind, position, normal and colors.
 * Outputs:
 *  A wicket whose components are the above inputs.
 * Assumptions:
 *  See Wicket.h
 * Algorithm:
 *  Von Neumann assignment.
 */

Wicket
asnWicket(int kind, Vec pos, Vec cmp, Vec clr, Vec ntuv)
{
    Wicket a;

    a.pos   = pos;
    a.cmp   = cmp;
    a.clr   = clr;
    a.kind  = kind;
    a.ntuv  = ntuv; /* no/ni t, u and v */

    return(a);
}


Vrt
lrpVrt2(Vrt a, Vrt b, double s)
{
    Vrt    c;
    
    c.kind = VRT_KIND;
    c.pos  = lrpVec(a.pos, b.pos, s);
    c.cmp  = lrpVec(a.cmp, b.cmp, s);
    c.clr  = lrpVec(a.clr, b.clr, s);
    c.ntuv = lrpVec(a.ntuv, b.ntuv, s);

    return(c);
}

Vrt
lrpVrt3(Vrt a, Vrt b, Vrt c, Vec d_pos)
{
    Vrt    d;
    Vec    tmp, b_minus_a_pos, c_minus_a_pos, d_minus_a_pos;
    double  r, s, t, area_s, area_t, area;
    
    d.kind = VRT_KIND;

    b_minus_a_pos = subVec(b.pos, a.pos);
    c_minus_a_pos = subVec(c.pos, a.pos);

    tmp =
    crsVec
    (
        b_minus_a_pos,
        c_minus_a_pos
    );

    /* Note, areas are actually twice the areas needed, but the 2's cancel. */
    area = sqrt(dotVec(tmp, tmp));

    if(area <= 0.0)
    {
    fprintf(stderr, "lrpVrt3: Bad polygon area %f\n", area);
    }

    tmp =
    crsVec
    (
        b_minus_a_pos,
        d_minus_a_pos = subVec(d_pos, a.pos)
    );

    area_t = sqrt(dotVec(tmp, tmp));

    tmp =
    crsVec
    (
        d_minus_a_pos,
        c_minus_a_pos
    );

    area_s = sqrt(dotVec(tmp, tmp));

    t = area_t/area;
    s = area_s/area;
    r = 1.0 - s - t;

    d.pos = d_pos;

    d.cmp =
    nrmVec
    (
        addVec
        (
        sclVec(r, a.cmp),
        addVec
        (
            sclVec(s, b.cmp),
            sclVec(t, c.cmp)
        )
        )
    );
    d.clr =
    addVec
    (
        sclVec(r, a.clr),
        addVec
        (
        sclVec(s, b.clr),
        sclVec(t, c.clr)
        )
    );
    d.ntuv =
    addVec
    (
        sclVec(r, a.ntuv),
        addVec
        (
        sclVec(s, b.ntuv),
        sclVec(t, c.ntuv)
        )
    );

    return(d);
}


/*****************************************************************
 * TAG( reflected )
 * 
 * Get the reflected component of a ray.
 * Inputs:
 *  Ray rayIn and transfer function xfr.
 * Outputs:
 *  Ray rayOut
 * Algorithm:
 *  See Whitted: "An Improved Illumination Model for Shaded Display"
 */

Ray
reflected(Ray rayIn, Xfr xfr)
{
    Ray rayOut;
    double RDotN;

    /* Equations from "An improved illumination model for shaded display." */

    RDotN = dotVec(rayIn.cmp, xfr.cmp);

    if(RDotN <= 0.0)
    {
	    rayOut.kind = FRNT_REFL;
	    rayOut.cmp = nrmVec(addVec(sclVec(-1.0/RDotN, rayIn.cmp), sclVec(2.0, xfr.cmp)));
    }
    else if(RDotN > 0.0)
    {
	    rayOut.kind = BACK_REFL;
	    rayOut.cmp = nrmVec(addVec(sclVec(1.0/RDotN, rayIn.cmp), sclVec(-2.0, xfr.cmp)));
    }
    
    rayOut.pos = xfr.pos;
    
    rayOut.clr = mulVec(rayIn.clr, xfr.clr);

    return(rayOut);
}

/*****************************************************************
 * TAG( refrRay_cmp )
 * 
 * Get the refracted component of a ray.
 * Inputs:
 *  Normalized components of input ray, rayIn.cmp and
 *  normalized components of surface normal, xfr.cmp and
 *      ratio of outer/inner indices of refraction n_o_n_i.
 * Outputs:
 *  Transmission packet containing outgoing
 *      Ray components and state information, state is:
 *          FRNT_REFR: if Ray hit object on outward normal side.
 *          BACK_REFR:  if Ray hit object on other side.
 *          TOT_INT_REFL:  if total internal reflection occurred.
 * Assumptions:
 *  [None]
 * Algorithm:
 *  See Whitted: "An Improved Illumination Model for Shaded Display"
 */

Ray
refracted(Ray rayIn, Xfr xfr)
{
    double n_o_n_i;
    double RDotN, term_1, term_2, denom;
    Vec NplusRp;
    Vec Rp;
    Vec N;
    Ray rayOut;
    
    n_o_n_i = xfr.ntuv.vX;
    N = xfr.cmp;

    /* ******************************************************************* */
    /* Equations from "An improved illumination model for shaded display." */
    /* ******************************************************************* */

    RDotN = dotVec(rayIn.cmp, N);
    
	if(RDotN <= 0.0) /* Front refracting case. */
    {
	    Rp = sclVec(-1.0/RDotN, rayIn.cmp);
	
	    NplusRp = addVec(N, Rp);
	
	    term_1 = n_o_n_i * n_o_n_i * dotVec(Rp, Rp);
	    term_2 = dotVec(NplusRp, NplusRp);
	
	    denom = term_1 - term_2;
	
	    if(denom >= 0.0)
	    {
	        rayOut.kind = FRNT_REFR;
	        rayOut.cmp = nrmVec(subVec(sclVec(1.0/sqrt(denom), NplusRp), N));
	    }
	    else
	    {
	        rayOut.kind = INTL_REFL;
	        rayOut.cmp   = rayIn.cmp; /* check this */
	    }
    }
    else if(RDotN > 0.0)
    {
    	N = sclVec(-1.0, N); /* flip the normal and use above calculation. */
    	
	    Rp = sclVec(1.0/RDotN, rayIn.cmp); /* flip the sense of the dot product. */
	
	    NplusRp = addVec(N, Rp);
	
	    term_1 = n_o_n_i * n_o_n_i * dotVec(Rp, Rp);
	    term_2 = dotVec(NplusRp, NplusRp);
	
	    denom = term_1 - term_2;
	
	    if(denom >= 0.0)
	    {
	        rayOut.kind = BACK_REFR;
	        rayOut.cmp = nrmVec(subVec(sclVec(1.0/sqrt(denom), NplusRp), N));
	    }
	    else
	    {
	        rayOut.kind = INTL_REFL;
	        rayOut.cmp   = rayIn.cmp; /* check this */
	    }
    }
        
    rayOut.pos = xfr.pos;
    
    rayOut.clr = mulVec(rayIn.clr, xfr.clr);

    return(rayOut);
}

/*****************************************************************
 * TAG( pntOfItr )
 * 
 * Find intersection in x,y,z given Ray and value of t.
 * Inputs:
 *  A ray, a and a parametric value, t.
 * Outputs:
 *  A point in homogeneous 4 space.
 * Assumptions:
 *  [None]
 * Algorithm:
 *  Back substitution into the Ray equation.
 */

Vec
pntOfItr (a, t)
Ray a;
double  t;
{
    return(addVec(sclVec(t, a.cmp), a.pos));
}

/*****************************************************************
 * TAG( formRay )
 * 
 * Form a Ray leaving a surface.
 * Inputs:
 *  An origin position vector, and a component vector.
 * Outputs:
 *  A ray.
 * Assumptions:
 *  [None]
 * Algorithm:
 *  Forms a Ray whose origin is offset slightly from the surface.
 */

Ray
formRay(pos, cmp) Vec pos, cmp;
{
    Ray a;

    a.cmp = nrmVec(cmp);
    a.pos =
    addVec
    (
        sclVec
        (
        ORIGIN_OFFSET, /* What kind of crap is this? */
        a.cmp
        ),
        pos
    );

    return(a);
}

Wicket
fscanWicket(FILE* f)
{
    Wicket ret ;

    fscanf(f, "%*s "); fscanf(f, "%d", &ret.kind) ;
    fscanf(f, "%*s "); ret.pos = fscanVec (f) ;
    fscanf(f, "%*s "); ret.cmp = fscanVec (f) ;
    fscanf(f, "%*s "); ret.clr = fscanVec (f) ;
    fscanf(f, "%*s "); ret.ntuv = fscanVec (f) ;

    return ret ;
}

void
fprintWicket (FILE* f, Wicket inst)
{
    fprintf(f, "kind: %d\n", inst.kind) ;
    fprintf(f, "pos: ");  fprintVec (f, inst.pos) ;
    fprintf(f, "cmp: ");  fprintVec (f, inst.cmp) ;
    fprintf(f, "clr: ");  fprintVec (f, inst.clr) ;
    fprintf(f, "ntuv: "); fprintVec (f, inst.ntuv) ;
}

Wicket
freadWicket(FILE* f)
{
    Wicket ret ;

    fread (&ret, sizeof(ret), 1, f) ;
    return ret ;
}

void
fwriteWicket(FILE* f, Wicket inst)
{
    fwrite (&inst, sizeof(inst), 1, f) ;
}