/* -*- coding: utf-8 -*- */
/**
\ingroup geom
@{
\file dpeuckerp.c
\brief Definición de funciones para el aligerado de polilíneas en el plano
       basadas en el algoritmo de Douglas-Peucker.
\author José Luis García Pallero, jgpallero@gmail.com
\note Este fichero contiene funciones paralelizadas con OpenMP.
\date 04 de julio de 2011
\copyright
Copyright (c) 2011-2020, José Luis García Pallero. All rights reserved.
\par
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
\par
- Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this
  list of conditions and the following disclaimer in the documentation and/or
  other materials provided with the distribution.
- Neither the name of the copyright holders nor the names of its contributors
  may be used to endorse or promote products derived from this software without
  specific prior written permission.
\par
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/******************************************************************************/
/******************************************************************************/
#include"libgeoc/dpeuckerp.h"
/******************************************************************************/
/******************************************************************************/
int GeocParOmpDpeuckerp(char version[])
{
    //comprobamos si hay paralelización
#if defined(_OPENMP)
    //comprobamos si hay que extraer versión
    if(version!=NULL)
    {
        //calculamos la versión
        VersionOpenMP(_OPENMP,version);
    }
    //salimos de la función
    return 1;
#else
    if(version!=NULL)
    {
        //utilizamos la variable version para que no dé warming al compilar
        strcpy(version,"");
    }
    //salimos de la función
    return 0;
#endif
}
/******************************************************************************/
/******************************************************************************/
void DouglasPeuckerOriginalPlano(const double* x,
                                 const double* y,
                                 const size_t nPtos,
                                 const size_t incX,
                                 const size_t incY,
                                 const double tol,
                                 const size_t posIni,
                                 const size_t posFin,
                                 char* usados)
{
    //índice para recorrer bucles
    size_t i=0;
    //distancia de los puntos al segmento base
    double dist=0.0;
    //valor absoluto de la tolerancia
    double atol=fabs(tol);
    //posición de la distancia máxima
    size_t pos=0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos casos especiales
    if((nPtos<=2)||(atol==0.0))
    {
        //se usan todos los puntos
        for(i=0;i<nPtos;i++)
        {
            usados[i] = 1;
        }
        //salimos de la función
        return;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //sólo continuamos si los puntos extremos no están seguidos
    if(posIni!=(posFin-1))
    {
        //calculamos la distancia máxima de los puntos de trabajo al segmento
        //formado por los extremos
        dist = DouglasPeuckerDistMaxPlano(x,y,incX,incY,posIni,posFin,&pos);
        //comprobamos si esa distancia está fuera de tolerancia
        if(dist>atol)
        {
            //indicamos que el punto se usa
            usados[pos] = 1;
            //paralelización con OpenMP
#if defined(_OPENMP)
#pragma omp parallel sections default(none) \
 shared(nPtos,incX,x,incY,y,posIni,posFin,pos,tol,usados)
#endif
{
#if defined(_OPENMP)
#pragma omp section
#endif
            //aplicamos el algoritmo a la parte anterior al punto de trabajo
            DouglasPeuckerOriginalPlano(x,y,nPtos,incX,incY,tol,posIni,pos,
                                        usados);
#if defined(_OPENMP)
#pragma omp section
#endif
            //aplicamos el algoritmo a la parte posterior al punto de trabajo
            DouglasPeuckerOriginalPlano(x,y,nPtos,incX,incY,tol,pos,posFin,
                                        usados);
} // --> fin del #pragma omp parallel sections
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return;
}
/******************************************************************************/
/******************************************************************************/
size_t* DouglasPeuckerRobustoPlano(const double* x,
                                   const double* y,
                                   const size_t nPtos,
                                   const size_t incX,
                                   const size_t incY,
                                   const double tol,
                                   const int paralelizaTol,
                                   const enum GEOC_DPEUCKER_ROBUSTO robusto,
                                   const size_t nSegRobOrig,
                                   const size_t nSegRobAuto,
                                   size_t* nPtosSal)
{
    //índices para recorrer bucles
    size_t i=0,j=0;
    //valor absoluto de la tolerancia
    double atol=fabs(tol);
    //variable indicadora de punto en tolerancia
    int entol=0;
    //identificador de caso especial
    int hayCasoEspecial=0;
    //identificadores de utilización de algoritmos semi robustos
    int robOrig=0,robAuto=0;
    //número de elementos de trabajo internos del vector de salida
    size_t nElem=0;
    //identificador de paralelización
    int paraleliza=0;
    //vector de salida
    size_t* sal=NULL;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos casos especiales
    sal = CasosEspecialesAligeraPolilinea(x,y,nPtos,incX,incY,atol,nPtosSal,
                                          &hayCasoEspecial);
    //comprobamos si ha habido algún caso especial
    if(hayCasoEspecial)
    {
        //comprobamos si ha ocurrido algún error de asignación de memoria
        if(nPtos&&(sal==NULL))
        {
            //mensaje de error
            GEOC_ERROR("Error de asignación de memoria");
            //salimos de la función
            return NULL;
        }
        else
        {
            //salimos de la función
            return sal;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si se utiliza algoritmo de intersección con línea original
    if((robusto==GeocDPeuckerRobSi)||(robusto==GeocDPeuckerRobOrig))
    {
        robOrig = 1;
    }
    //comprobamos si se utiliza algoritmo de intersección con línea generada
    if((robusto==GeocDPeuckerRobSi)||(robusto==GeocDPeuckerRobAuto))
    {
        robAuto = 1;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
#if defined(_OPENMP)
    //comprobamos si hay más de un procesador
    if(omp_get_num_procs()>1)
    {
        //indicamos que hay paralelización
        paraleliza = 1;
    }
#endif
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //inicializamos el indicador interno de tamaño del vector
    nElem = GEOC_DPEUCKER_BUFFER_PTOS;
    //asignamos memoria para el vector de salida
    sal = (size_t*)malloc(nElem*sizeof(size_t));
    //comprobamos los posibles errores
    if(sal==NULL)
    {
        //mensaje de error
        GEOC_ERROR("Error de asignación de memoria");
        //salimos de la función
        return NULL;
    }
    //indicamos que el primer punto siempre se usa
    *nPtosSal = 1;
    sal[0] = 0;
    //puntos de trabajo para iniciar los cálculos
    i = 0;
    j = 2;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //entramos en un bucle mientras no hayamos llegado hasta el último punto
    while(j<nPtos)
    {
        //comprobamos si los puntos intermedios están en tolerancia
        //sólo paralelizamos si el número de puntos intermedios es mayor que uno
        if(paraleliza&&paralelizaTol&&((j-i-1)>1))
        {
            //aplicamos el algoritmo en paralelo
            entol = DouglasPeuckerPuntosEnTolPlanoOMP(x,y,incX,incY,atol,i,j,
                                                      i+1,j-1);
        }
        else
        {
            //aplicamos el algoritmo en serie
            entol = DouglasPeuckerPuntosEnTolPlanoSerie(x,y,incX,incY,atol,i,j,
                                                        i+1,j-1);
        }
        //comprobamos si todos los puntos están en tolerancia
        if(entol)
        {
            //pasamos al siguiente punto como extremo del segmento
            j++;
        }
        else
        {
            //el punto final será el anterior al actual, ya que con el actual
            //hay al menos un vértice fuera de tolerancia y con el anterior se
            //comprobó en el paso previo del bucle que no había ningún vértice
            //fuera
            j--;
            //aplicación del algoritmo de intersección con puntos originales
            if(robOrig)
            {
                //aplicamos el algoritmo
                DouglasPeuckerRobIntersecOrigPlano(x,y,nPtos,incX,incY,
                                                   nSegRobOrig,i,&j);
            }
            //aplicación del algoritmo de auto intersección
            if(robAuto)
            {
                //aplicamos el algoritmo
                DouglasPeuckerRobAutoIntersecPlano(x,y,incX,incY,i,&j,sal,
                                                   *nPtosSal,nSegRobAuto);
            }
            //añadimos al contador el nuevo punto
            (*nPtosSal)++;
            //comprobamos si hay que reasignar memoria
            if((*nPtosSal)>nElem)
            {
                //añadimos otro grupo de puntos
                nElem += GEOC_DPEUCKER_BUFFER_PTOS;
                //asignamos memoria para el vector de salida
                sal = (size_t*)realloc(sal,nElem*sizeof(size_t));
                //comprobamos los posibles errores
                if(sal==NULL)
                {
                    //mensaje de error
                    GEOC_ERROR("Error de asignación de memoria");
                    //salimos de la función
                    return NULL;
                }
            }
            //añadimos el punto al vector de salida
            sal[(*nPtosSal)-1] = j;
            //actualizamos los índices de los puntos de trabajo
            i = j;
            j = i+2;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si hay que añadir el último punto
    if((sal[(*nPtosSal)-1]!=(nPtos-1))&&
       ((x[sal[(*nPtosSal)-1]*incX]!=x[(nPtos-1)*incX])||
        (y[sal[(*nPtosSal)-1]*incY]!=y[(nPtos-1)*incY])))
    {
        //añadimos al contador el último punto
        (*nPtosSal)++;
        //comprobamos si hay que reasignar memoria
        if((*nPtosSal)>nElem)
        {
            //añadimos otro grupo de puntos
            nElem += GEOC_DPEUCKER_BUFFER_PTOS;
            //asignamos memoria para el vector de salida
            sal = (size_t*)realloc(sal,nElem*sizeof(size_t));
            //comprobamos los posibles errores
            if(sal==NULL)
            {
                //mensaje de error
                GEOC_ERROR("Error de asignación de memoria");
                //salimos de la función
                return NULL;
            }
        }
        //asignamos el último punto
        sal[(*nPtosSal)-1] = nPtos-1;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si el vector de salida tiene demasiada memoria asignada
    if(nElem>(*nPtosSal))
    {
        //ajustamos el tamaño del vector de salida
        sal = (size_t*)realloc(sal,(*nPtosSal)*sizeof(size_t));
        //comprobamos los posibles errores
        if(sal==NULL)
        {
            //mensaje de error
            GEOC_ERROR("Error de asignación de memoria");
            //salimos de la función
            return NULL;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return sal;
}
/******************************************************************************/
/******************************************************************************/
int DouglasPeuckerPuntosEnTolPlanoOMP(const double* x,
                                      const double* y,
                                      const size_t incX,
                                      const size_t incY,
                                      const double tol,
                                      const size_t posBaseIni,
                                      const size_t posBaseFin,
                                      const size_t posPtoIni,
                                      const size_t posPtoFin)
{
    //índice para recorrer bucles
    size_t i=0;
    //valor absoluto de la tolerancia
    double atol=fabs(tol);
    //identificador de punto fuera de tolerancia
    int ftol=0;
    //coordenadas de los vértices de trabajo
    double xIni=0.0,yIni=0.0,xFin=0.0,yFin=0.0,xTrab=0.0,yTrab=0.0;
    //longitud de la base y parámetros de rotación para el plano
    double dx=0.0,sA=0.0,cA=0.0;
    //distancia calculada
    double dist=0.0;
    //variable de salida
    int entol=1;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos salida rápida
    if((posBaseIni+1)>=posBaseFin)
    {
        //salimos de la función
        return entol;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //coordenadas del primer punto de la base
    xIni = x[posBaseIni*incX];
    yIni = y[posBaseIni*incY];
    //coordenadas del segundo punto de la base, referidas al primero
    xFin = x[posBaseFin*incX]-xIni;
    yFin = y[posBaseFin*incY]-yIni;
    //calculamos la longitud de la base y la rotación
    DouglasPeuckerParamRotaBase(xFin,yFin,&sA,&cA,&dx);
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //paralelización con OpenMP
#if defined(_OPENMP)
#pragma omp parallel for default(none) \
 shared(posPtoIni,posPtoFin,incX,x,incY,y,xIni,yIni,dx,sA,cA,atol) \
 private(i,xTrab,yTrab,dist) \
 reduction(+:ftol)
#endif
    //recorremos los puntos de trabajo
    for(i=posPtoIni;i<=posPtoFin;i++)
    {
        //sólo calculo si no se ha encontrado ningún punto fuera de tolerancia
        //en este hilo
        if(!ftol)
        {
            //extraemos las coordenadas del vértice de trabajo y las referimos
            //al punto inicial de la base
            xTrab = x[i*incX]-xIni;
            yTrab = y[i*incY]-yIni;
            //calculamos la distancia del punto a la base
            dist = DouglasPeuckerDistMaxPlanoAux(dx,sA,cA,xTrab,yTrab);
            //comprobamos si está fuera de tolerancia
            if(dist>atol)
            {
                //aumentamos el indicador de fuera de tolerancia
                ftol++;
            }
        }
    } // --> fin del #pragma omp parallel for
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si hay algún punto fuera de tolerancia
    if(ftol)
    {
        //indicamos que hay algún punto que no está en tolerancia
        entol = 0;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return entol;
}
/******************************************************************************/
/******************************************************************************/
int DouglasPeuckerPuntosEnTolPlanoSerie(const double* x,
                                        const double* y,
                                        const size_t incX,
                                        const size_t incY,
                                        const double tol,
                                        const size_t posBaseIni,
                                        const size_t posBaseFin,
                                        const size_t posPtoIni,
                                        const size_t posPtoFin)
{
    //índice para recorrer bucles
    size_t i=0;
    //valor absoluto de la tolerancia
    double atol=fabs(tol);
    //coordenadas de los vértices
    double xIni=0.0,yIni=0.0,xFin=0.0,yFin=0.0,xTrab=0.0,yTrab=0.0;
    //longitud de la base y parámetros de rotación para el plano
    double dx=0.0,sA=0.0,cA=0.0;
    //distancia calculada
    double dist=0.0;
    //variable de salida
    int entol=1;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos salida rápida
    if((posBaseIni+1)>=posBaseFin)
    {
        //salimos de la función
        return entol;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //coordenadas del primer punto de la base
    xIni = x[posBaseIni*incX];
    yIni = y[posBaseIni*incY];
    //coordenadas del segundo punto de la base, referidas al primero
    xFin = x[posBaseFin*incX]-xIni;
    yFin = y[posBaseFin*incY]-yIni;
    //calculamos la longitud de la base y la rotación
    DouglasPeuckerParamRotaBase(xFin,yFin,&sA,&cA,&dx);
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //recorremos los puntos a chequear
    for(i=posPtoIni;i<=posPtoFin;i++)
    {
        //extraemos las coordenadas del vértice de trabajo y las referimos al
        //punto inicial de la base
        xTrab = x[i*incX]-xIni;
        yTrab = y[i*incY]-yIni;
        //calculamos la distancia del punto a la base
        dist = DouglasPeuckerDistMaxPlanoAux(dx,sA,cA,xTrab,yTrab);
        //comprobamos si está fuera de tolerancia
        if(dist>atol)
        {
            //indicamos que estamos fuera de tolerancia
            entol = 0;
            //salimos del bucle
            break;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return entol;
}
/******************************************************************************/
/******************************************************************************/
void DouglasPeuckerRobIntersecOrigPlano(const double* x,
                                        const double* y,
                                        const size_t nPtos,
                                        const size_t incX,
                                        const size_t incY,
                                        const size_t segAUsar,
                                        const size_t posIni,
                                        size_t* posFin)
{
    //índice para recorrer bucles
    size_t i=0;
    //coordenadas del segmento base
    double xA=0.0,yA=0.0,xB=0.0,yB=0.0;
    //posición de parada para comprobar la intersección de segmentos/arcos
    size_t posParada=0;
    //identificación de paralelización
    int paraleliza=0;
    //variable identificadora de existencia de corte de segmentos
    int corte=0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //si los puntos de inicio y fin son contiguos, hay salida rápida
    if((posIni+1)>=(*posFin))
    {
        //salimos de la función
        return;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
#if defined(_OPENMP)
    //comprobamos si hay más de un procesador
    if(omp_get_num_procs()>1)
    {
        //indicamos que hay paralelización
        paraleliza = 1;
    }
#endif
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //posición de parada para el chequeo de segmentos/arcos
    posParada = ((segAUsar==0)||(segAUsar>=(nPtos-(*posFin))))
                ? nPtos-1
                : (*posFin)+segAUsar;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //coordenadas del punto inicial del segmento/arco base (no cambian)
    xA = x[posIni*incX];
    yA = y[posIni*incY];
    //construimos todos los segmentos/arcos base posibles
    for(i=(*posFin);i>posIni;i--)
    {
        //comprobamos si estamos ante el punto posterior al inicial
        if(i==(posIni+1))
        {
            //este punto es el siguiente al punto inicial del segmento/arco base
            *posFin = i;
            //salimos del bucle
            break;
        }
        //coordenadas del punto final del segmento base
        xB = x[i*incX];
        yB = y[i*incY];
        //comprobamos si hay que paralelizar
        if(paraleliza)
        {
            //calculamos en paralelo
            corte = DouglasPeuckerRobIntersecOrigPlanoOMP(xA,yA,xB,yB,x,y,incX,
                                                          incY,i,posParada);
        }
        else
        {
            //calculamos en serie
            corte = DouglasPeuckerRobIntersecOrigPlanoSerie(xA,yA,xB,yB,x,y,
                                                            incX,incY,i,
                                                            posParada);
        }
        //comprobamos si no ha habido ninguna intersección
        if(!corte)
        {
            //indicamos el índice del vértice final
            *posFin = i;
            //salimos del bucle de segmentos base
            break;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return;
}
/******************************************************************************/
/******************************************************************************/
int DouglasPeuckerRobIntersecOrigPlanoOMP(const double xA,
                                          const double yA,
                                          const double xB,
                                          const double yB,
                                          const double* x,
                                          const double* y,
                                          const size_t incX,
                                          const size_t incY,
                                          const size_t posIni,
                                          const size_t posFin)
{
    //índice para recorrer bucles
    size_t i=0;
    //coordenadas de los segmentos de trabajo
    double xC=0.0,yC=0.0,xD=0.0,yD=0.0;
    //variable de salida
    int corte=0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //paralelización con OpenMP
#if defined(_OPENMP)
#pragma omp parallel for default(none) \
 shared(posIni,posFin,incX,x,incY,y,xA,xB,yA,yB) \
 private(i,xC,yC,xD,yD) \
 reduction(+:corte)
#endif
    //recorremos los puntos de trabajo
    for(i=posIni;i<posFin;i++)
    {
        //sólo realizo cálculos si no se ha encontrado ningún corte en este hilo
        if(!corte)
        {
            //puntos inicial y final del siguiente segmento/arco de trabajo
            xC = x[i*incX];
            yC = y[i*incY];
            xD = x[(i+1)*incX];
            yD = y[(i+1)*incY];
            //sigo si los rectángulos que encierran a los segmentos se cortan
            if(!GEOC_RECT_DISJUNTOS(GEOC_MIN(xA,xB),GEOC_MAX(xA,xB),
                                    GEOC_MIN(yA,yB),GEOC_MAX(yA,yB),
                                    GEOC_MIN(xC,xD),GEOC_MAX(xC,xD),
                                    GEOC_MIN(yC,yD),GEOC_MAX(yC,yD)))
            {
                //comprobamos si hay intersección
                corte += DouglasPeuckerRobIntersecPlano(xA,yA,xB,yB,xC,yC,xD,yD,
                                                        posIni,i);
            }
        }
    } // --> fin del #pragma omp parallel for
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return corte;
}
/******************************************************************************/
/******************************************************************************/
int DouglasPeuckerRobIntersecOrigPlanoSerie(const double xA,
                                            const double yA,
                                            const double xB,
                                            const double yB,
                                            const double* x,
                                            const double* y,
                                            const size_t incX,
                                            const size_t incY,
                                            const size_t posIni,
                                            const size_t posFin)
{
    //índice para recorrer bucles
    size_t i=0;
    //coordenadas de los segmentos de trabajo
    double xC=0.0,yC=0.0,xD=0.0,yD=0.0;
    //variable de salida
    int corte=0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //recorremos los puntos de trabajo
    for(i=posIni;i<posFin;i++)
    {
        //puntos inicial y final del siguiente segmento/arco de trabajo
        xC = x[i*incX];
        yC = y[i*incY];
        xD = x[(i+1)*incX];
        yD = y[(i+1)*incY];
        //seguimos si los rectángulos que encierran a los segmentos se cortan
        if(!GEOC_RECT_DISJUNTOS(GEOC_MIN(xA,xB),GEOC_MAX(xA,xB),
                                GEOC_MIN(yA,yB),GEOC_MAX(yA,yB),
                                GEOC_MIN(xC,xD),GEOC_MAX(xC,xD),
                                GEOC_MIN(yC,yD),GEOC_MAX(yC,yD)))
        {
            //comprobamos si hay intersección
            corte = DouglasPeuckerRobIntersecPlano(xA,yA,xB,yB,xC,yC,xD,yD,
                                                   posIni,i);
        }
        //si ha habido intersección de segmentos/arcos, salimos del bucle
        if(corte)
        {
            //salimos del bucle
            break;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return corte;
}
/******************************************************************************/
/******************************************************************************/
void DouglasPeuckerRobAutoIntersecPlano(const double* x,
                                        const double* y,
                                        const size_t incX,
                                        const size_t incY,
                                        const size_t posIni,
                                        size_t* posFin,
                                        const size_t* posAlig,
                                        const size_t nPosAlig,
                                        const size_t segAUsar)
{
    //índice para recorrer bucles
    size_t i=0;
    //coordenadas del segmento base
    double xA=0.0,yA=0.0,xB=0.0,yB=0.0;
    //posición de parada para comprobar la intersección de segmentos/arcos
    size_t posParada=0;
    //identificación de paralelización
    int paraleliza=0;
    //variable identificadora de existencia de corte de segmentos
    int corte=0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //si los puntos de inicio y fin son contiguos, hay salida rápida
    if((posIni+1)>=(*posFin))
    {
        //salimos de la función
        return;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
#if defined(_OPENMP)
    //comprobamos si hay más de un procesador
    if(omp_get_num_procs()>1)
    {
        //indicamos que hay paralelización
        paraleliza = 1;
    }
#endif
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //posición de parada en el vector posAlig para el chequeo de segmentos/arcos
    posParada = ((segAUsar==0)||(segAUsar>=(nPosAlig-1)))
                ? 0
                : nPosAlig-1-segAUsar;
    //coordenadas del punto inicial del segmento base (no cambian)
    xA = x[posIni*incX];
    yA = y[posIni*incY];
    //construimos todos los segmentos base posibles
    for(i=(*posFin);i>posIni;i--)
    {
        //comprobamos si estamos ante el punto posterior al inicial
        if(i==(posIni+1))
        {
            //este punto es el siguiente al punto inicial del segmento base
            *posFin = i;
            //salimos del bucle
            break;
        }
        //coordenadas del punto final del segmento base
        xB = x[i*incX];
        yB = y[i*incY];
        //comprobamos si hay que paralelizar
        if(paraleliza)
        {
            //calculamos en paralelo
            corte = DouglasPeuckerRobAutoIntersecPlanoOMP(xA,yA,xB,yB,x,y,incX,
                                                          incY,posAlig,nPosAlig,
                                                          nPosAlig-1,posParada);
        }
        else
        {
            //calculamos en serie
            corte = DouglasPeuckerRobAutoIntersecPlanoSerie(xA,yA,xB,yB,x,y,
                                                            incX,incY,posAlig,
                                                            nPosAlig,nPosAlig-1,
                                                            posParada);
        }
        //comprobamos si no ha habido ninguna intersección
        if(!corte)
        {
            //indicamos el índice del vértice final
            *posFin = i;
            //salimos del bucle de segmentos base
            break;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return;
}
/******************************************************************************/
/******************************************************************************/
int DouglasPeuckerRobAutoIntersecPlanoOMP(const double xA,
                                          const double yA,
                                          const double xB,
                                          const double yB,
                                          const double* x,
                                          const double* y,
                                          const size_t incX,
                                          const size_t incY,
                                          const size_t* posAlig,
                                          const size_t nPosAlig,
                                          const size_t posIni,
                                          const size_t posFin)
{
    //índice para recorrer bucles
    size_t i=0;
    //coordenadas de los segmentos de trabajo
    double xC=0.0,yC=0.0,xD=0.0,yD=0.0;
    //variable de salida
    int corte=0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //paralelización con OpenMP
#if defined(_OPENMP)
#pragma omp parallel for default(none) \
 shared(posIni,posFin,incX,x,posAlig,incY,y,xA,xB,yA,yB,nPosAlig) \
 private(i,xC,yC,xD,yD) \
 reduction(+:corte)
#endif
    //recorremos los puntos de trabajo
    for(i=posIni;i>posFin;i--)
    {
        //sólo realizo cálculos si no se ha encontrado ningún corte en este hilo
        if(!corte)
        {
            //puntos inicial y final del siguiente segmento/arco de trabajo
            xC = x[posAlig[i]*incX];
            yC = y[posAlig[i]*incY];
            xD = x[posAlig[i-1]*incX];
            yD = y[posAlig[i-1]*incY];
            //seguimos si los rectángulos que encierran a los segmentos se cortan
            if(!GEOC_RECT_DISJUNTOS(GEOC_MIN(xA,xB),GEOC_MAX(xA,xB),
                                    GEOC_MIN(yA,yB),GEOC_MAX(yA,yB),
                                    GEOC_MIN(xC,xD),GEOC_MAX(xC,xD),
                                    GEOC_MIN(yC,yD),GEOC_MAX(yC,yD)))
            {
                //comprobamos si hay intersección
                corte += DouglasPeuckerRobIntersecPlano(xB,yB,xA,yA,xC,yC,xD,yD,
                                                        posAlig[nPosAlig-1],
                                                        posAlig[i]);
            }
        }
    } // --> fin del #pragma omp parallel for
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return corte;
}
/******************************************************************************/
/******************************************************************************/
int DouglasPeuckerRobAutoIntersecPlanoSerie(const double xA,
                                            const double yA,
                                            const double xB,
                                            const double yB,
                                            const double* x,
                                            const double* y,
                                            const size_t incX,
                                            const size_t incY,
                                            const size_t* posAlig,
                                            const size_t nPosAlig,
                                            const size_t posIni,
                                            const size_t posFin)
{
    //índice para recorrer bucles
    size_t i=0;
    //coordenadas de los segmentos de trabajo
    double xC=0.0,yC=0.0,xD=0.0,yD=0.0;
    //variable de salida
    int corte=0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //recorremos los puntos de trabajo
    for(i=posIni;i>posFin;i--)
    {
        //puntos inicial y final del siguiente segmento/arco de trabajo
        xC = x[posAlig[i]*incX];
        yC = y[posAlig[i]*incY];
        xD = x[posAlig[i-1]*incX];
        yD = y[posAlig[i-1]*incY];
        //seguimos si los rectángulos que encierran a los segmentos se cortan
        if(!GEOC_RECT_DISJUNTOS(GEOC_MIN(xA,xB),GEOC_MAX(xA,xB),
                                GEOC_MIN(yA,yB),GEOC_MAX(yA,yB),
                                GEOC_MIN(xC,xD),GEOC_MAX(xC,xD),
                                GEOC_MIN(yC,yD),GEOC_MAX(yC,yD)))
        {
            //comprobamos si hay intersección
            corte = DouglasPeuckerRobIntersecPlano(xB,yB,xA,yA,xC,yC,xD,yD,
                                                   posAlig[nPosAlig-1],
                                                   posAlig[i]);
        }
        //si ha habido intersección de segmentos/arcos, salimos del bucle
        if(corte)
        {
            //salimos del bucle
            break;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return corte;
}
/******************************************************************************/
/******************************************************************************/
int DouglasPeuckerRobIntersecPlano(const double xA,
                                   const double yA,
                                   const double xB,
                                   const double yB,
                                   const double xC,
                                   const double yC,
                                   const double xD,
                                   const double yD,
                                   const size_t posFinAB,
                                   const size_t posIniCD)
{
    //variables auxiliares
    double xAux=0.0,yAux=0.0;
    //identificador de intersección
    int inter=GEOC_DPEUCKER_NO_INTERSEC;
    //variable de salida
    int corte=0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //seguimos si los rectángulos que encierran a los segmentos se cortan
    if(!GEOC_RECT_DISJUNTOS(GEOC_MIN(xA,xB),GEOC_MAX(xA,xB),
                            GEOC_MIN(yA,yB),GEOC_MAX(yA,yB),
                            GEOC_MIN(xC,xD),GEOC_MAX(xC,xD),
                            GEOC_MIN(yC,yD),GEOC_MAX(yC,yD)))
    {
        //compruebo si los dos segmentos son contiguos
        if(posFinAB==posIniCD)
        {
            //compruebo intersección con la función completa (lenta)
            inter = IntersecSegmentos2D(xA,yA,xB,yB,xC,yC,xD,yD,
                                        &xAux,&yAux);
            //compruebo si es la sucesión de segmento inicial+final
            if((inter!=GEOC_SEG_INTERSEC_MISMO_SEG)&&
               (inter!=GEOC_SEG_INTERSEC_COLIN))
            {
                //en este caso, no hay intersección
                inter = GEOC_DPEUCKER_NO_INTERSEC;
            }
        }
        else
        {
            //compruebo intersección con la función simple (rápida)
            inter = IntersecSegmentos2DSimple(xA,yA,xB,yB,xC,yC,xD,yD);
        }
        //unificamos los identificadores de intersección
        if(!((inter==GEOC_SEG_NO_INTERSEC)||(inter==GEOC_DPEUCKER_NO_INTERSEC)))
        {
            //hay intersección
            corte = 1;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return corte;
}
/******************************************************************************/
/******************************************************************************/
double DouglasPeuckerDistMaxPlano(const double* x,
                                  const double* y,
                                  const size_t incX,
                                  const size_t incY,
                                  const size_t posIni,
                                  const size_t posFin,
                                  size_t* pos)
{
    //índice para recorrer bucles
    size_t i=0;
    //coordenadas de los extremos del segmento base y del punto de trabajo
    double xIni=0.0,yIni=0.0,xFin=0.0,yFin=0.0,xP=0.0,yP=0.0;
    //razones trigonométricas del ángulo de giro del sistema de coordenadas
    double sA=0.0,cA=0.0;
    //longitudes auxiliares
    double dx=0.0,lonAux=0.0;
    //variable de salida
    double lon=-1.0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si los dos puntos están seguidos
    if((posIni+1)==posFin)
    {
        //la posición que devolvemos es la del punto inicial
        *pos = posIni;
        //la distancia devuelta es -1.0
        return lon;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //coordenadas del primer punto de la base
    xIni = x[posIni*incX];
    yIni = y[posIni*incY];
    //coordenadas del segundo punto de la base, referidas al primero
    xFin = x[posFin*incX]-xIni;
    yFin = y[posFin*incY]-yIni;
    //calculamos la longitud de la base y la rotación
    DouglasPeuckerParamRotaBase(xFin,yFin,&sA,&cA,&dx);
    //recorremos los puntos entre los extremos
    for(i=posIni+1;i<posFin;i++)
    {
        //coordenadas del punto de trabajo referidas al punto inicial de la base
        xP = x[i*incX]-xIni;
        yP = y[i*incY]-yIni;
        //calculamos la distancia del punto de trabajo al segmento base
        lonAux = DouglasPeuckerDistMaxPlanoAux(dx,sA,cA,xP,yP);
        //comprobamos si la distancia calculada es mayor que la anterior
        if(lonAux>lon)
        {
            //actualizamos la distancia máxima
            lon = lonAux;
            //guardamos la posición del punto
            *pos = i;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return lon;
}
/******************************************************************************/
/******************************************************************************/
void DouglasPeuckerParamRotaBase(const double xBase2RB1,
                                 const double yBase2RB1,
                                 double* sAlfa,
                                 double* cAlfa,
                                 double* lonBase)
{
    //álgulo de rotación
    double alfa=0.0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //ángulo a rotar el sistema de coordenadas para llevar el eje X a coincidir
    //con el segmento base
    alfa = atan2(yBase2RB1,xBase2RB1);
    //calculamos las razones trigonométricas del ángulo de rotación
    *sAlfa = sin(alfa);
    *cAlfa = cos(alfa);
    //la longitud del segmento base será el valor absoluto de la coordenada X
    //del extremo final del segmento base en el sistema de coordenadas rotado
    *lonBase = fabs((*cAlfa)*xBase2RB1+(*sAlfa)*yBase2RB1);
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return;
}
/******************************************************************************/
/******************************************************************************/
double DouglasPeuckerDistMaxPlanoAux(const double lonBase,
                                     const double sAlfa,
                                     const double cAlfa,
                                     const double xVertRB1,
                                     const double yVertRB1)
{
    //coordenadas del vértice de trabajo en el sistema rotado
    double xVert1=0.0,yVert1=0.0;
    //variable de salida
    double lon=0.0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //transformamos el vértice de trabajo al nuevo sistema de coordenadas
    xVert1 = cAlfa*xVertRB1+sAlfa*yVertRB1;
    yVert1 = -sAlfa*xVertRB1+cAlfa*yVertRB1;
    //comprobamos la posición del punto con respecto al segmento base
    if((xVert1>=0.0)&&(xVert1<=lonBase))
    {
        //el punto de trabajo está entre los extremos del segmento base
        //su distancia hasta él es el valor absoluto de su coordenada Y en
        //el sistema rotado
        lon = fabs(yVert1);
    }
    else if(xVert1<0.0)
    {
        //el punto de trabajo está a la izquierda del punto inicial de la
        //base, luego su distancia hasta el segmento será la distancia
        //hasta dicho punto inicial
        //Konrad Ebisch (2002), A correction to the Douglas-Peucker line
        //generalization algorithm, Computers and Geosciences, vol. 28,
        //págs. 995 a 997
        lon = sqrt(xVert1*xVert1+yVert1*yVert1);
    }
    else
    {
        //el punto de trabajo está a la derecha del punto final de la base,
        //luego su distancia hasta el segmento será la distancia hasta dicho
        //punto final
        //Konrad Ebisch (2002), A correction to the Douglas-Peucker line
        //generalization algorithm, Computers and Geosciences, vol. 28,
        //págs. 995 a 997
        lon = sqrt((xVert1-lonBase)*(xVert1-lonBase)+yVert1*yVert1);
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return lon;
}
/******************************************************************************/
/******************************************************************************/
/** @} */
/******************************************************************************/
/******************************************************************************/
/* kate: encoding utf-8; end-of-line unix; syntax c; indent-mode cstyle; */
/* kate: replace-tabs on; space-indent on; tab-indents off; indent-width 4; */
/* kate: line-numbers on; folding-markers on; remove-trailing-space on; */
/* kate: backspace-indents on; show-tabs on; */
/* kate: word-wrap-column 80; word-wrap-marker-color #D2D2D2; word-wrap off; */
