/*
    Apophysis Plugin

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

typedef struct
{
    double dc_triangle_scatter_area, A;
    int dc_triangle_zero_edges;
} Variables;

#include "apoplugin.h"

APO_PLUGIN("dc_triangle");
APO_VARIABLES(
    VAR_REAL(dc_triangle_scatter_area, 0.0),
    VAR_INTEGER_RANGE(dc_triangle_zero_edges, 0, 1, 0)
);

int PluginVarPrepare(Variation* vp)
{
    VAR(A) = VAR(dc_triangle_scatter_area) < -1 ? -1 :
             VAR(dc_triangle_scatter_area) > 1 ? 1 :
             VAR(dc_triangle_scatter_area);
    return TRUE; 
}

/* This function is called during calculation.
   You must call the argument "vp"
*/
int PluginVarCalc(Variation* vp)
{
    // set up triangle
    const double 
      xx = TM(a), xy = TM(b),  // X
      yx = TM(c) * -1, yy = TM(d) * -1,  // Y
      ox = TM(e), oy = TM(f),  // O
      px = FTx - ox, py = FTy - oy; // P
    
    // calculate dot products
    const double dot00 = xx * xx + xy * xy; // X * X
    const double dot01 = xx * yx + xy * yy; // X * Y
    const double dot02 = xx * px + xy * py; // X * P
    const double dot11 = yx * yx + yy * yy; // Y * Y
    const double dot12 = yx * px + yy * py; // Y * P
    
    // calculate barycentric coordinates
    const double denom = (dot00 * dot11 - dot01 * dot01);
    const double num_u = (dot11 * dot02 - dot01 * dot12);
    const double num_v = (dot00 * dot12 - dot01 * dot02);
    
    // u, v must not be constant
    double u = num_u / denom;
    double v = num_v / denom;
    int inside = 0, f = 1;
    
    // case A - point escapes edge XY
    if (u + v > 1) { f = -1;
       if (u > v) { u = u>1?1:u; v = 1-u; }
       else       { v = v>1?1:v; u = 1-v; } }
           
    // case B - point escapes either edge OX or OY
    else if ((u < 0) || (v < 0)) { 
       u = u<0?0:u>1?1:u; v = v<0?0:v>1?1:v; }
       
    // case C - point is in triangle
    else inside = 1;
    
    // handle outside points
    if (VAR(dc_triangle_zero_edges) && !inside) u = v = 0;
    else if (!inside) {
        u = (u + random01() * VAR(A) * f);
        v = (v + random01() * VAR(A) * f);
        u = u<-1?-1:u>1?1:u; v = v<-1?-1:v>1?1:v;
        
        if ((u + v > 1) && (VAR(A) > 0))  
           if (u > v) { u = u>1?1:u; v = 1-u; }       
           else       { v = v>1?1:v; u = 1-v; } 
    }

    // set output
    FPx += VVAR * (ox + u * xx + v * yx);
    FPy += VVAR * (oy + u * xy + v * yy);
    FPz += VVAR * FTz;
    TC   = fmod(fabs(u+v),1.0);
    
    // done
    return TRUE;
}
