/* -*- coding: utf-8 -*- */
/* Copyright (C) 2011-2020  José Luis García Pallero, <jgpallero@gmail.com>
 *
 * This file is part of OctCLIP.
 *
 * OctCLIP 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 3 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 software; see the file COPYING.  If not, see
 * <http://www.gnu.org/licenses/>.
 */
/******************************************************************************/
/******************************************************************************/
#define HELPTEXT "\
-*- texinfo -*-\n\
@deftypefn {}{[@var{p},@var{pp},@var{ni},@var{np}] =}_oc_polybool(@var{sub},@var{clip},@var{op},@var{hor})\n\
\n\
@cindex Performs boolean operations between two polygons.\n\
\n\
This function performs boolean operations between two polygons using the\n\
Greiner-Hormann algorithm (http://davis.wpi.edu/~matt/courses/clipping/).\n\
\n\
@var{sub} is a two column matrix containing the X and Y coordinates of the\n\
vertices for the subject polygon (it must be unique, although\n\
self-intersections are permitted).\n\n\
@var{clip} is a two column matrix containing the X and Y coordinates of the\n\
vertices for the clipper polygon(it must be unique, although\n\
self-intersections are permitted).\n\n\
@var{op} is a text string containing the operation to perform between\n\
@var{sub} and @var{clip}. Possible values are:\n\
\n\
@itemize @bullet\n\
@item @var{'AND'}\n\
Intersection of @var{sub} and @var{clip}.\n\n\
@item @var{'OR'}\n\
Union of @var{sub} and @var{clip}.\n\n\
@item @var{'AB'}\n\
Operation @var{sub} - @var{clip}.\n\n\
@item @var{'BA'}\n\
Operation of @var{clip} - @var{sub}.\n\n\
@item @var{'XOR'}\n\
Exclusive disjunction between @var{clip} and @var{sub}. This operation is\n\
performed as the joining of 'AB' and 'BA' consecutively applied\n\
@end itemize\n\
\n\
@var{hor} is an identifier for performing (value 1) or not (value 0) the\n\
searching for holes in the result of the operation OR. When OR is applied\n\
with non convex entities some of the resulting polygons can be actually\n\
holes. Activating this argument the possible holes are identified. If the\n\
operation is other than OR the value of this argument is irrelevant\n\
\n\
For the matrices @var{sub} and @var{clip}, the first point is not needed to\n\
be repeated at the end (but is permitted). Pairs of (NaN,NaN) coordinates in\n\
@var{sub} and/or @var{clip} are omitted, so they are treated as if each one\n\
stored a single polygon, i.e., this function does not admit boolean\n\
operations between multiple polygons of between polygons with holes, although\n\
polygons containing self-intersections are permitted\n\
\n\
The output arguments are:\n\
\n\
@var{p} is a two column matrix containing the X and Y coordinates of the\n\
vertices of the resultant polygon(s). If the result consist of multiple\n\
polygons they are separated by rows os (NaN,NaN) values.\n\n\
@var{pp} is a three-column matrix with a number of rows equal to the number\n\
of polygons stored in the matrix @var{p}. The first column stores the row of\n\
@var{p} where the corresponding polygon starts, the second column the row of\n\
@var{p} where the polygon end, and the third colum is a mark identifying if\n\
the polygon is a hole (value 0) or not (value 1). The values of the third\n\
column are relevant only in the case of the OR operation\n\n\
@var{ni} is the number of intersections between @var{sub} and @var{clip}.\n\n\
@var{np} is the number of perturbed points of the @var{clip} polygon if any\n\
particular case (points in the border of the other polygon) occurs see\n\
http://davis.wpi.edu/~matt/courses/clipping/ for details.\n\
\n\
This function do not check if the dimensions of @var{sub} and @var{clip} are\n\
correct.\n\
\n\
@end deftypefn"
/******************************************************************************/
/******************************************************************************/
#include<octave/oct.h>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include"octclip.h"
/******************************************************************************/
/******************************************************************************/
#define ERRORTEXT 1000
/******************************************************************************/
/******************************************************************************/
DEFUN_DLD(_oc_polybool,args,,HELPTEXT)
{
    //error message
    char errorText[ERRORTEXT+1]="_oc_polybool:\n\t";
    //output list
    octave_value_list outputList;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //checking input arguments
    if(args.length()!=4)
    {
        //error text
        sprintf(&errorText[strlen(errorText)],
                "Incorrect number of input arguments\n\t"
                "See help _oc_polybool");
        //error message
        error(errorText);
    }
    else
    {
        //loop index
        size_t i=0;
        //polygons and operation
        ColumnVector xSubj=args(0).matrix_value().column(0);
        ColumnVector ySubj=args(0).matrix_value().column(1);
        ColumnVector xClip=args(1).matrix_value().column(0);
        ColumnVector yClip=args(1).matrix_value().column(1);
        std::string opchar=args(2).string_value();
        int sfh=args(3).int_value();
        //computation vectors
        double* xA=NULL;
        double* yA=NULL;
        double* xB=NULL;
        double* yB=NULL;
        //double linked lists
        vertPoliClip* polA=NULL;
        vertPoliClip* polB=NULL;
        //operation identifier
        enum GEOC_OP_BOOL_POLIG op=GeocOpBoolInter;
        //output struct
        polig* result=NULL;
        //number of polygons, intersections and perturbations
        size_t nPol=0,nInter=0,nPert=0;
        //number of elements for the output vectors
        size_t nElem=0,posStart=0,posEnd=0,pos=0;
        ////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////
        //pointers to data
        xA = xSubj.fortran_vec();
        yA = ySubj.fortran_vec();
        xB = xClip.fortran_vec();
        yB = yClip.fortran_vec();
        //create double linked lists for subject and clipper polygons
        polA = CreaPoliClip(xA,yA,static_cast<size_t>(xSubj.numel()),1,1);
        polB = CreaPoliClip(xB,yB,static_cast<size_t>(xClip.numel()),1,1);
        //error checking
        if((polB==NULL)||(polB==NULL))
        {
            //free peviously allocated memory
            LibMemPoliClip(polA);
            LibMemPoliClip(polB);
            //error text
            sprintf(&errorText[strlen(errorText)],"Error in memory allocation");
            //error message
            error(errorText);
            //exit
            return outputList;
        }
        ////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////
        //select operation
        if((!strcmp(opchar.c_str(),"AND"))||(!strcmp(opchar.c_str(),"and")))
        {
            op = GeocOpBoolInter;
        }
        else if((!strcmp(opchar.c_str(),"OR"))||(!strcmp(opchar.c_str(),"or")))
        {
            op = GeocOpBoolUnion;
        }
        else if((!strcmp(opchar.c_str(),"AB"))||(!strcmp(opchar.c_str(),"ab")))
        {
            op = GeocOpBoolAB;
        }
        else if((!strcmp(opchar.c_str(),"BA"))||(!strcmp(opchar.c_str(),"ba")))
        {
            op = GeocOpBoolBA;
        }
        else if((!strcmp(opchar.c_str(),"XOR"))||
                (!strcmp(opchar.c_str(),"xor")))
        {
            op = GeocOpBoolXor;
        }
        else
        {
            //free peviously allocated memory
            LibMemPoliClip(polA);
            LibMemPoliClip(polB);
            //error text
            sprintf(&errorText[strlen(errorText)],
                    "The third input argument (op=%s) is not correct",
                    opchar.c_str());
            //error message
            error(errorText);
            //exit
            return outputList;
        }
        ////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////
        //clipping
        result = PoliBoolGreiner(polA,polB,op,GEOC_GREINER_FAC_EPS_PERTURB,sfh,
                                 &nInter,&nPert);
        //error checking
        if(result==NULL)
        {
            //free peviously allocated memory
            LibMemPoliClip(polA);
            LibMemPoliClip(polB);
            //error text
            sprintf(&errorText[strlen(errorText)],"Error in memory allocation");
            //error message
            error(errorText);
            //exit
            return outputList;
        }
        ////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////
        //number or output polygons
        nPol = result->nPolig;
        //output matrix
        Matrix pp(nPol,3);
        //dimensions for the output vectors
        if(nPol)
        {
            //Number of elements
            nElem = result->nElem;
            posStart = 0;
            posEnd = nElem-1;
            if(EsGeocNan(result->x[0]))
            {
                nElem--;
                posStart = 1;
            }
            if(EsGeocNan(result->x[posEnd]))
            {
                nElem--;
                posEnd = result->nElem-2;
            }
            //output polygons data
            for(i=0;i<nPol;i++)
            {
                //positions start in 1
                pp(i,0) = result->posIni[i]+1-posStart;
                pp(i,1) = pp(i,0)+result->nVert[i]-1;
                pp(i,2) = result->atr[i];
            }
        }
        else
        {
            nElem = 0;
        }
        //output matrix
        Matrix p(nElem,2);
        //copy output data
        pos = 0;
        for(i=posStart;i<=posEnd;i++)
        {
            p(pos,0) = result->x[i];
            p(pos,1) = result->y[i];
            pos++;
        }
        ////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////
        //output parameters list
        outputList(0) = p;
        outputList(1) = pp;
        outputList(2) = nInter;
        outputList(3) = nPert;
        ////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////
        //free memory
        LibMemPoliClip(polA);
        LibMemPoliClip(polB);
        LibMemPolig(result);
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //exit
    return outputList;
}
