/* -*- coding: utf-8 -*- */
/**
\ingroup geom
@{
\file segmento.c
\brief Definición de funciones para la realización de cálculos con segmentos.
\author José Luis García Pallero, jgpallero@gmail.com
\date 22 de abril de 2011
\copyright
Copyright (c) 2011-2013, 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/segmento.h"
/******************************************************************************/
/******************************************************************************/
int TresPuntosColineales2D(const double xA,
                           const double yA,
                           const double xB,
                           const double yB,
                           const double xC,
                           const double yC)
{
    //utilizamos la macro de posición de punto con respecto a una recta, aunque
    //no es una macro robusta
    //son colineales si el resultado es 0.0
    //calculamos y salimos de la función
    return (POS_PTO_RECTA_2D(xA,yA,xB,yB,xC,yC)==0.0);
}
/******************************************************************************/
/******************************************************************************/
int PuntoEntreDosPuntos2DColin(const double x,
                               const double y,
                               const double xA,
                               const double yA,
                               const double xB,
                               const double yB)
{
    //código de salida
    int cod=0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //distinguimos entre segmento vertical y no vertical
    if(xA!=xB)
    {
        //segmento no vertical, utilizamos las coordenadas X
        cod = ((xA<x)&&(x<xB))||((xA>x)&&(x>xB));
    }
    else
    {
        //segmento vertical, utilizamos las coordenadas Y
        cod = ((yA<y)&&(y<yB))||((yA>y)&&(y>yB));
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return cod;
}
/******************************************************************************/
/******************************************************************************/
int PtoComunSegmParalelos2D(const double xA,
                            const double yA,
                            const double xB,
                            const double yB,
                            const double xC,
                            const double yC,
                            const double xD,
                            const double yD,
                            double* x,
                            double* y)
{
    //variable de salida
    int cod=GEOC_SEG_NO_INTERSEC;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si los puntos son colineales
    if(!TresPuntosColineales2D(xA,yA,xB,yB,xC,yC))
    {
        //los segmentos son paralelos, pero no se cortan
        return GEOC_SEG_NO_INTERSEC;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si los segmentos son el mismo
    if(((xA==xC)&&(yA==yC)&&(xB==xD)&&(yB==yD))||
       ((xA==xD)&&(yA==yD)&&(xB==xC)&&(yB==yC)))
    {
        //coordenadas de salida
        *x = xA;
        *y = yA;
        //los segmentos son el mismo
        return GEOC_SEG_INTERSEC_MISMO_SEG;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si algún punto está entre medias del otro segmento
    if(PuntoEntreDosPuntos2DColin(xA,yA,xC,yC,xD,yD))
    {
        //el punto A está en el segmento CD
        *x = xA;
        *y = yA;
        //salimos
        return GEOC_SEG_INTERSEC_COLIN;
    }
    else if(PuntoEntreDosPuntos2DColin(xC,yC,xA,yA,xB,yB))
    {
        //el punto C está en el segmento AB
        *x = xC;
        *y = yC;
        //salimos
        return GEOC_SEG_INTERSEC_COLIN;
    }
    else if(PuntoEntreDosPuntos2DColin(xB,yB,xC,yC,xD,yD))
    {
        //el punto B está en el segmento CD
        *x = xB;
        *y = yB;
        //salimos
        return GEOC_SEG_INTERSEC_COLIN;
    }
    else if(PuntoEntreDosPuntos2DColin(xD,yD,xA,yA,xB,yB))
    {
        //el punto D está en el segmento AB
        *x = xD;
        *y = yD;
        //salimos
        return GEOC_SEG_INTERSEC_COLIN;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si sólo comparten el extremo A
    if(((xA==xC)&&(yA==yC))||((xA==xD)&&(yA==yD)))
    {
        //coordenadas de salida
        *x = xA;
        *y = yA;
        //los segmentos comparten un extremo
        return GEOC_SEG_INTERSEC_EXTREMOS_COLIN;
    }
    //comprobamos si sólo comparten el extremo B
    if(((xB==xC)&&(yB==yC))||((xB==xD)&&(yB==yD)))
    {
        //coordenadas de salida
        *x = xB;
        *y = yB;
        //los segmentos comparten un extremo
        return GEOC_SEG_INTERSEC_EXTREMOS_COLIN;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return cod;
}
/******************************************************************************/
/******************************************************************************/
int IntersecSegmentos2D(const double xA,
                        const double yA,
                        const double xB,
                        const double yB,
                        const double xC,
                        const double yC,
                        const double xD,
                        const double yD,
                        double* x,
                        double* y)
{
    //centroide del conjunto de puntos implicados
    double xc=0.0,yc=0.0;
    //coordenadas reducidas al centroide
    double xAc=0.0,yAc=0.0,xBc=0.0,yBc=0.0,xCc=0.0,yCc=0.0,xDc=0.0,yDc=0.0;
    //parámetros de las ecuaciones
    double s=0.0,t=0.0,num=0.0,den=0.0;
    //variable de salida
    int cod=GEOC_SEG_NO_INTERSEC;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //si los rectángulos son disjuntos, los segmentos no se tocan
    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)))
    {
        //código de salida
        cod = GEOC_SEG_NO_INTERSEC;
        //salimos de la función
        return cod;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //calculamos el centroide del conjunto de puntos de trabajo
    xc = (xA+xB+xC+xD)/4.0;
    yc = (yA+yB+yC+yD)/4.0;
    //reducimos las coordenadas de trabajo al centroide, para que los números
    //sean más pequeños y evitar posibles errores de desbordamiento
    xAc = xA-xc;
    xBc = xB-xc;
    xCc = xC-xc;
    xDc = xD-xc;
    yAc = yA-yc;
    yBc = yB-yc;
    yCc = yC-yc;
    yDc = yD-yc;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //a partir de aquí, trabajamos con las coordenadas reducidas al centroide
    //calculamos el denominador
    den = xAc*(yDc-yCc)+xBc*(yCc-yDc)+xDc*(yBc-yAc)+xCc*(yAc-yBc);
    //si el denominador es 0.0, los segmentos son paralelos
    if(den==0.0)
    {
        //calculamos el punto común
        cod = PtoComunSegmParalelos2D(xAc,yAc,xBc,yBc,xCc,yCc,xDc,yDc,x,y);
        //deshacemos el cambio del centroide
        *x += xc;
        *y += yc;
        //salimos de la función
        return cod;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //calculamos el numerador
    num = xAc*(yDc-yCc)+xCc*(yAc-yDc)+xDc*(yCc-yAc);
    //un extremo de un segmento puede estar encima del otro segmento, pero los
    //segmentos no son colineales
    if((num==0.0)||(num==den))
    {
        //comprobamos si se tocan en un extremo
        if(((xAc==xCc)&&(yAc==yCc))||((xAc==xDc)&&(yAc==yDc))||
           ((xBc==xCc)&&(yBc==yCc))||((xBc==xDc)&&(yBc==yDc)))
        {
            //asignamos la variable de salida
            cod = GEOC_SEG_INTERSEC_EXTREMOS_NO_COLIN;
        }
        else
        {
            //asignamos la variable de salida
            cod = GEOC_SEG_INTERSEC_EXTREMO_NO_COLIN;
        }
    }
    //calculamos el parámetro s
    s = num/den;
    //calculamos de nuevo el numerador
    num = -(xAc*(yCc-yBc)+xBc*(yAc-yCc)+xCc*(yBc-yAc));
    //un extremo de un segmento puede estar encima del otro segmento, pero los
    //segmentos no son colineales
    if((num==0.0)||(num==den))
    {
        //comprobamos si se tocan en un extremo
        if(((xAc==xCc)&&(yAc==yCc))||((xAc==xDc)&&(yAc==yDc))||
           ((xBc==xCc)&&(yBc==yCc))||((xBc==xDc)&&(yBc==yDc)))
        {
            //asignamos la variable de salida
            cod = GEOC_SEG_INTERSEC_EXTREMOS_NO_COLIN;
        }
        else
        {
            //asignamos la variable de salida
            cod = GEOC_SEG_INTERSEC_EXTREMO_NO_COLIN;
        }
    }
    //calculamos el parámetro t
    t = num/den;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si estamos ante una intersección pura y dura o los segmentos
    //no se cortan
    if((s>0.0)&&(s<1.0)&&(t>0.0)&&(t<1.0))
    {
        //asignamos la variable de salida
        cod = GEOC_SEG_INTERSEC;
    }
    else if((s<0.0)||(s>1.0)||(t<0.0)||(t>1.0))
    {
        //asignamos la variable de salida
        cod = GEOC_SEG_NO_INTERSEC;
    }
    //calculamos las coordenadas del punto intersección y deshacemos el cambio
    //del centroide
    *x = xc+xAc+s*(xBc-xAc);
    *y = yc+yAc+s*(yBc-yAc);
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return cod;
}
/******************************************************************************/
/******************************************************************************/
int IntersecSegmentos2DSimple(const double xA,
                              const double yA,
                              const double xB,
                              const double yB,
                              const double xC,
                              const double yC,
                              const double xD,
                              const double yD)
{
    //centroide del conjunto de puntos implicados
    double xc=0.0,yc=0.0;
    //coordenadas reducidas al centroide
    double xAc=0.0,yAc=0.0,xBc=0.0,yBc=0.0,xCc=0.0,yCc=0.0,xDc=0.0,yDc=0.0;
    //identificadores de posición
    double posA=0.0,posB=0.0,posC=0.0,posD=0.0;
    //identificadores de punto enmedio de un segmento
    int enmA=0,enmB=0,enmC=0,enmD=0;
    //variable de salida (por defecto, no hay intersección)
    int cod=GEOC_SEG_NO_INTERSEC;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //si los rectángulos son disjuntos, los segmentos no se tocan
    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)))
    {
        //salimos de la función (usamos el valor por defecto de no intersección
        return cod;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //calculamos el centroide del conjunto de puntos de trabajo
    xc = (xA+xB+xC+xD)/4.0;
    yc = (yA+yB+yC+yD)/4.0;
    //reducimos las coordenadas de trabajo al centroide, para que los números
    //sean más pequeños y evitar posibles errores de desbordamiento
    xAc = xA-xc;
    xBc = xB-xc;
    xCc = xC-xc;
    xDc = xD-xc;
    yAc = yA-yc;
    yBc = yB-yc;
    yCc = yC-yc;
    yDc = yD-yc;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //calculamos las posiciones de los puntos con respecto a las rectas
    posA = POS_PTO_RECTA_2D(xAc,yAc,xCc,yCc,xDc,yDc);
    posB = POS_PTO_RECTA_2D(xBc,yBc,xCc,yCc,xDc,yDc);
    posC = POS_PTO_RECTA_2D(xCc,yCc,xAc,yAc,xBc,yBc);
    posD = POS_PTO_RECTA_2D(xDc,yDc,xAc,yAc,xBc,yBc);
    //comprobamos si hay tres puntos alineados, que dan lugar a casos especiales
    if((posA==0.0)||(posB==0.0)||(posC==0.0)||(posD==0.0))
    {
        //comprobamos si alguno de los extremos son coincidentes
        if(((xA==xC)&&(yA==yC))||((xA==xD)&&(yA==yD))||
           ((xB==xC)&&(yB==yC))||((xB==xD)&&(yB==yD)))
        {
            //código de salida
            cod = GEOC_SEG_INTERSEC;
        }
        else
        {
            //comprobamos si A está en el segmento CD, pero no es un extremo
            if(posA==0.0)
            {
                enmA = PuntoEntreDosPuntos2DColin(xAc,yAc,xCc,yCc,xDc,yDc);
            }
            //comprobamos si B está en el segmento CD, pero no es un extremo
            if(posB==0.0)
            {
                enmB = PuntoEntreDosPuntos2DColin(xBc,yBc,xCc,yCc,xDc,yDc);
            }
            //comprobamos si C está en el segmento AB, pero no es un extremo
            if(posC==0.0)
            {
                enmC = PuntoEntreDosPuntos2DColin(xCc,yCc,xAc,yAc,xBc,yBc);
            }
            //comprobamos si C está en el segmento AB, pero no es un extremo
            if(posD==0.0)
            {
                enmD = PuntoEntreDosPuntos2DColin(xDc,yDc,xAc,yAc,xBc,yBc);
            }
            //si hay algún punto enmedio de algún segmento, existe intersección
            if(enmA||enmB||enmC||enmD)
            {
                //código de salida
                cod = GEOC_SEG_INTERSEC;
            }
        }
    }
    else
    {
        //para que ocurra intersección pura, las rectas han de dividirse
        //mutuamente en dos, es decir, los puntos de cada una han de estar uno a
        //cada lado de la otra
        if((((posA<0.0)&&(posB>0.0))||((posA>0.0)&&(posB<0.0)))&&
           (((posC<0.0)&&(posD>0.0))||((posC>0.0)&&(posD<0.0))))
        {
            //código de salida
            cod = GEOC_SEG_INTERSEC;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return cod;
}
/******************************************************************************/
/******************************************************************************/
int CodIntSeg2DCodIntSeg2DSimple(const int cod2D)
{
    //variable de salida
    int sal=GEOC_SEG_NO_INTERSEC;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos los posibles valores de entrada
    switch(cod2D)
    {
        case GEOC_SEG_NO_INTERSEC:
            //no hay intersección
            sal = GEOC_SEG_NO_INTERSEC;
            break;
        case GEOC_SEG_INTERSEC:
            //intersección pura
            sal = GEOC_SEG_INTERSEC;
            break;
        case GEOC_SEG_INTERSEC_EXTREMO_NO_COLIN:
            //el vértice de un segmento se apoya en el otro segmento, aunque no
            //en alguno de sus vértices
            sal = GEOC_SEG_INTERSEC;
            break;
        case GEOC_SEG_INTERSEC_EXTREMOS_NO_COLIN:
            //los segmentos comparten un vértice, pero no son colineales
            sal = GEOC_SEG_INTERSEC;
            break;
        case GEOC_SEG_INTERSEC_EXTREMOS_COLIN:
            //los segmentos comparten un vértice y son colineales
            sal = GEOC_SEG_INTERSEC;
            break;
        case GEOC_SEG_INTERSEC_MISMO_SEG:
            //ambos son el mismo segmento
            sal = GEOC_SEG_INTERSEC;
            break;
        case GEOC_SEG_INTERSEC_COLIN:
            //los segmentos se solapan
            sal = GEOC_SEG_INTERSEC;
            break;
        default:
            sal = GEOC_SEG_NO_INTERSEC;
            break;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return sal;
}
/******************************************************************************/
/******************************************************************************/
/** @} */
/******************************************************************************/
/******************************************************************************/
/* 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; */
