/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                        Intel License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's 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.
//
//   * The name of Intel Corporation may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// 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 the Intel Corporation or contributors 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.
//
//M*/

/* Reconstruction of contour skeleton */
#include "_cvaux.h"
#include <time.h>

#define NEXT_SEQ(seq,seq_first) ((seq) == (seq_first) ? seq->v_next : seq->h_next)
#define SIGN(x) ( x<0 ? -1:( x>0 ? 1:0 ) )

const float LEE_CONST_ZERO = 1e-6f;
const float LEE_CONST_DIFF_POINTS = 1e-2f;
const float LEE_CONST_ACCEPTABLE_ERROR = 1e-4f;

/****************************************************************************************\
*                                    Auxiliary struct definitions                                 *
\****************************************************************************************/

template<class T>
struct CvLeePoint
{
    T x,y;
};

typedef CvLeePoint<float> CvPointFloat;
typedef CvLeePoint<float> CvDirection;

struct CvVoronoiSiteInt;
struct CvVoronoiEdgeInt;
struct CvVoronoiNodeInt;
struct CvVoronoiParabolaInt;
struct CvVoronoiChainInt;
struct CvVoronoiHoleInt;

struct CvVoronoiDiagramInt
{
    CvSeq* SiteSeq;
    CvSeq* EdgeSeq;
    CvSeq* NodeSeq;
    CvSeq* ChainSeq;
    CvSeq* ParabolaSeq;
    CvSeq* DirectionSeq;
    CvSeq* HoleSeq;
    CvVoronoiSiteInt* reflex_site;
    CvVoronoiHoleInt* top_hole;
};

struct CvVoronoiStorageInt
{
    CvMemStorage* SiteStorage;
    CvMemStorage* EdgeStorage;
    CvMemStorage* NodeStorage;
    CvMemStorage* ChainStorage;
    CvMemStorage* ParabolaStorage;
    CvMemStorage* DirectionStorage;
    CvMemStorage* HoleStorage;
};

struct CvVoronoiNodeInt
{
    CvPointFloat  node;
    float         radius;
};

struct CvVoronoiSiteInt
{
    CvVoronoiNodeInt* node1;
    CvVoronoiNodeInt* node2;
    CvVoronoiEdgeInt* edge1;
    CvVoronoiEdgeInt* edge2;
    CvVoronoiSiteInt* next_site;
    CvVoronoiSiteInt* prev_site;
    CvDirection* direction;
};

struct CvVoronoiEdgeInt
{
    CvVoronoiNodeInt* node1;
    CvVoronoiNodeInt* node2;
    CvVoronoiSiteInt* site;
    CvVoronoiEdgeInt* next_edge;
    CvVoronoiEdgeInt* prev_edge;
    CvVoronoiEdgeInt* twin_edge;
    CvVoronoiParabolaInt* parabola;
    CvDirection*  direction;
};

struct CvVoronoiParabolaInt
{
    float map[6];
    float a;
    CvVoronoiNodeInt* focus;
    CvVoronoiSiteInt* directrice;
};

struct CvVoronoiChainInt
{
    CvVoronoiSiteInt * first_site;
    CvVoronoiSiteInt * last_site;
    CvVoronoiChainInt* next_chain;
};

struct CvVoronoiHoleInt
{
    CvSeq* SiteSeq;
    CvSeq* ChainSeq;
    CvVoronoiSiteInt* site_top;
    CvVoronoiSiteInt* site_nearest;
    CvVoronoiSiteInt* site_opposite;
    CvVoronoiNodeInt* node;
    CvVoronoiHoleInt* next_hole;
    bool error;
    float x_coord;
};

typedef CvVoronoiSiteInt* pCvVoronoiSite;
typedef CvVoronoiEdgeInt* pCvVoronoiEdge;
typedef CvVoronoiNodeInt* pCvVoronoiNode;
typedef CvVoronoiParabolaInt* pCvVoronoiParabola;
typedef CvVoronoiChainInt* pCvVoronoiChain;
typedef CvVoronoiHoleInt* pCvVoronoiHole;
typedef CvPointFloat* pCvPointFloat;
typedef CvDirection* pCvDirection; 

/****************************************************************************************\
*                                    Function definitions                                *
\****************************************************************************************/

/*F///////////////////////////////////////////////////////////////////////////////////////
//    Author:  Andrey Sobolev
//    Name:    _cvLee
//    Purpose: Compute Voronoi Diagram for one given polygon with holes
//    Context:
//    Parameters:
//      ContourSeq : in, vertices of polygon.
//      VoronoiDiagramInt : in&out, pointer to struct, which contains the 
//                       description of Voronoi Diagram.
//      VoronoiStorage: in, storage for Voronoi Diagram.
//      contour_type: in, type of vertices.
//                    The possible values are CV_LEE_INT,CV_LEE_FLOAT,CV_LEE_DOUBLE.
//      contour_orientation: in, orientation of polygons.
//                           = 1, if contour is left - oriented in left coordinat system
//                           =-1, if contour is left - oriented in right coordinat system
//      attempt_number: in, number of unsuccessful attemts made by program to compute 
//                          the Voronoi Diagram befor return the error
//
//    Returns: 1, if Voronoi Diagram was succesfully computed 
//             0, if some error occures
//F*/
static int  _cvLee(CvSeq* ContourSeq,
                      CvVoronoiDiagramInt* pVoronoiDiagramInt,
                      CvMemStorage* VoronoiStorage,
                      CvLeeParameters contour_type,
                      int contour_orientation,
                      int attempt_number);

/*F///////////////////////////////////////////////////////////////////////////////////////
//    Author:  Andrey Sobolev
//    Name:    _cvConstuctSites
//    Purpose : Compute sites for given polygon with holes
//                     (site is an edge of polygon or a reflex vertex).
//    Context:
//    Parameters:
//            ContourSeq : in, vertices of polygon
//       pVoronoiDiagram : in, pointer to struct, which contains the 
//                          description of Voronoi Diagram
//           contour_type: in, type of vertices.  The possible values are 
//                          CV_LEE_INT,CV_LEE_FLOAT,CV_LEE_DOUBLE.
//    contour_orientation: in, orientation of polygons.
//                           = 1, if contour is left - oriented in left coordinat system
//                           =-1, if contour is left - oriented in right coordinat system
//     Return: 1, if sites were succesfully constructed 
//             0, if some error occures
//F*/
static int _cvConstuctSites(CvSeq* ContourSeq,
                            CvVoronoiDiagramInt* pVoronoiDiagram,
                            CvLeeParameters contour_type,
                            int contour_orientation);

/*F///////////////////////////////////////////////////////////////////////////////////////
//    Author:  Andrey Sobolev
//    Name:    _cvConstructChains
//    Purpose : Compute chains for given polygon with holes.
//    Context:
//    Parameters:
//       pVoronoiDiagram : in, pointer to struct, which contains the 
//                          description of Voronoi Diagram
//     Return: 1, if chains were succesfully constructed 
//             0, if some error occures
//F*/
static int _cvConstructChains(CvVoronoiDiagramInt* pVoronoiDiagram);

/*F///////////////////////////////////////////////////////////////////////////////////////
//    Author:  Andrey Sobolev
//    Name:    _cvConstructSkeleton
//    Purpose: Compute skeleton for given collection of sites, using Lee algorithm
//    Context:
//    Parameters:
//      VoronoiDiagram : in, pointer to struct, which contains the 
//                       description of Voronoi Diagram.
//    Returns: 1, if skeleton was succesfully computed 
//             0, if some error occures
//F*/
static int _cvConstructSkeleton(CvVoronoiDiagramInt* pVoronoiDiagram);

/*F///////////////////////////////////////////////////////////////////////////////////////
//    Author:  Andrey Sobolev
//    Name:    _cvConstructSiteTree
//    Purpose: Construct tree of sites (by analogy with contour tree).
//    Context:
//    Parameters:
//      VoronoiDiagram : in, pointer to struct, which contains the 
//                       description of Voronoi Diagram.
//    Returns: 
//F*/
static void _cvConstructSiteTree(CvVoronoiDiagramInt* pVoronoiDiagram);

/*F///////////////////////////////////////////////////////////////////////////////////////
//    Author:   Andrey Sobolev
//    Name:     _cvReleaseVoronoiStorage
//    Purpose : Function realease storages. 
//                  The storages are divided into two groups:
//                  SiteStorage, EdgeStorage, NodeStorage form the first group;
//                  ChainStorage,ParabolaStorage,DirectionStorage,HoleStorage form the second group.
//    Context:
//    Parameters:
//        pVoronoiStorage: in,
//        group1,group2: in, if group1<>0 then storages from first group released   
//                           if group2<>0 then storages from second group released
//    Return    :
//F*/
static void _cvReleaseVoronoiStorage(CvVoronoiStorageInt* pVoronoiStorage, int group1, int group2);

/*F///////////////////////////////////////////////////////////////////////////////////////
//  Author:  Andrey Sobolev
//  Name:  _cvConvert
//  Purpose :  Function convert internal representation of VD (via 
//                  structs CvVoronoiSiteInt, CvVoronoiEdgeInt,CvVoronoiNodeInt) into 
//                  external representation of VD (via structs CvVoronoiSite2D, CvVoronoiEdge2D,
//                  CvVoronoiNode2D) 
//    Context:
//    Parameters:
//        VoronoiDiagram: in
//        VoronoiStorage: in
//        change_orientation: in, if = -1 then the convertion is accompanied with change 
//                            of orientation
//
//     Return: 1, if convertion was succesfully completed
//             0, if some error occures
//F*/
/*
static int _cvConvert(CvVoronoiDiagram2D* VoronoiDiagram, 
                      CvMemStorage* VoronoiStorage,
                      int change_orientation);
*/
static int _cvConvert(CvVoronoiDiagram2D* VoronoiDiagram,
                       CvVoronoiDiagramInt VoronoiDiagramInt,
                       CvSet* &NewSiteSeqPrev,
                       CvSeqWriter &NodeWriter,
                       CvSeqWriter &EdgeWriter,
                       CvMemStorage* VoronoiStorage,
                       int change_orientation);

/*F///////////////////////////////////////////////////////////////////////////////////////
//  Author:  Andrey Sobolev
//  Name:  _cvConvertSameOrientation
//  Purpose :  Function convert internal representation of VD (via 
//                  structs CvVoronoiSiteInt, CvVoronoiEdgeInt,CvVoronoiNodeInt) into 
//                  external representation of VD (via structs CvVoronoiSite2D, CvVoronoiEdge2D,
//                  CvVoronoiNode2D) without change of orientation
//    Context:
//    Parameters:
//        VoronoiDiagram: in
//        VoronoiStorage: in
/
//     Return: 1, if convertion was succesfully completed
//             0, if some error occures
//F*/
/*
static int _cvConvertSameOrientation(CvVoronoiDiagram2D* VoronoiDiagram, 
                                      CvMemStorage* VoronoiStorage);
*/
static int _cvConvertSameOrientation(CvVoronoiDiagram2D* VoronoiDiagram,
                       CvVoronoiDiagramInt VoronoiDiagramInt,
                       CvSet* &NewSiteSeqPrev,
                       CvSeqWriter &NodeWriter,
                       CvSeqWriter &EdgeWriter,
                       CvMemStorage* VoronoiStorage);

/*F///////////////////////////////////////////////////////////////////////////////////////
//  Author:  Andrey Sobolev
//  Name:  _cvConvertChangeOrientation
//  Purpose :  Function convert internal representation of VD (via 
//                  structs CvVoronoiSiteInt, CvVoronoiEdgeInt,CvVoronoiNodeInt) into 
//                  external representation of VD (via structs CvVoronoiSite2D, CvVoronoiEdge2D,
//                  CvVoronoiNode2D) with change of orientation
//    Context:
//    Parameters:
//        VoronoiDiagram: in
//        VoronoiStorage: in
/
//     Return: 1, if convertion was succesfully completed
//             0, if some error occures
//F*/
/*
static int _cvConvertChangeOrientation(CvVoronoiDiagram2D* VoronoiDiagram, 
                                      CvMemStorage* VoronoiStorage);
                                      */
static int _cvConvertChangeOrientation(CvVoronoiDiagram2D* VoronoiDiagram,
                       CvVoronoiDiagramInt VoronoiDiagramInt,
                       CvSet* &NewSiteSeqPrev,
                       CvSeqWriter &NodeWriter,
                       CvSeqWriter &EdgeWriter,
                       CvMemStorage* VoronoiStorage);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Compute  sites for external polygon. 
    Arguments 
     pVoronoiDiagram : in, pointer to struct, which contains the 
                        description of Voronoi Diagram
     ContourSeq : in, vertices of polygon
     pReflexSite: out, pointer to reflex site,if any exist,else NULL
     orientation: in, orientation of contour ( = 1 or = -1)
     type:        in, type of vertices. The possible values are (int)1,
                   (float)1,(double)1.
     Return:    1, if sites were succesfully constructed 
                0, if some error occures    : 
    --------------------------------------------------------------------------*/
template<class T>
int _cvConstructExtSites(CvVoronoiDiagramInt* pVoronoiDiagram,
                         CvSeq* ContourSeq,
                         int orientation,
                         T /*type*/);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Compute  sites for internal polygon (for hole). 
    Arguments 
     pVoronoiDiagram : in, pointer to struct, which contains the 
                        description of Voronoi Diagram
     CurrSiteSeq: in, the sequence for sites to be constructed
     CurrContourSeq : in, vertices of polygon
     pTopSite: out, pointer to the most left site of polygon (it is the most left 
                vertex of polygon)
     orientation: in, orientation of contour ( = 1 or = -1)
     type:        in, type of vertices. The possible values are (int)1,
                   (float)1,(double)1.
     Return:    1, if sites were succesfully constructed 
                0, if some error occures    : 
    --------------------------------------------------------------------------*/
template<class T>
int _cvConstructIntSites(CvVoronoiDiagramInt* pVoronoiDiagram,
                         CvSeq* CurrSiteSeq,
                         CvSeq* CurrContourSeq, 
                         pCvVoronoiSite &pTopSite,
                         int orientation,
                         T /*type*/);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Compute the simple chains of sites for external polygon.
    Arguments 
     pVoronoiDiagram : in&out, pointer to struct, which contains the 
                        description of Voronoi Diagram
    
    Return: 1, if chains were succesfully constructed 
            0, if some error occures
    --------------------------------------------------------------------------*/
static int _cvConstructExtChains(CvVoronoiDiagramInt* pVoronoiDiagram);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Compute the simple chains of sites for internal polygon (= hole)
    Arguments 
    pVoronoiDiagram : in, pointer to struct, which contains the 
                        description of Voronoi Diagram
    CurrSiteSeq : in, the sequence of sites
   CurrChainSeq : in,the sequence for chains to be constructed
       pTopSite : in, the most left site of hole

      Return    : 
    --------------------------------------------------------------------------*/
static void _cvConstructIntChains(CvVoronoiDiagramInt* pVoronoiDiagram,
                                  CvSeq* CurrChainSeq,
                                  CvSeq* CurrSiteSeq,
                                  pCvVoronoiSite pTopSite);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Compute the initial Voronoi Diagram for single site
    Arguments 
     pVoronoiDiagram : in, pointer to struct, which contains the 
                        description of Voronoi Diagram
     pSite: in, pointer to site

     Return : 
    --------------------------------------------------------------------------*/
static void _cvConstructEdges(pCvVoronoiSite pSite,CvVoronoiDiagramInt* pVoronoiDiagram);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function moves each node on small random value. The nodes are taken
                    from pVoronoiDiagram->NodeSeq.
    Arguments 
     pVoronoiDiagram : in, pointer to struct, which contains the 
                        description of Voronoi Diagram
     begin,end: in, the first and the last nodes in pVoronoiDiagram->NodeSeq,
                    which moves
     shift: in, the value of maximal shift.
     Return : 
    --------------------------------------------------------------------------*/
static void _cvRandomModification(CvVoronoiDiagramInt* pVoronoiDiagram, int begin, int end, float shift);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Compute the internal Voronoi Diagram for external polygon.
    Arguments 
     pVoronoiDiagram : in, pointer to struct, which contains the 
                        description of Voronoi Diagram
     Return     : 1, if VD was constructed succesfully
                  0, if some error occure
    --------------------------------------------------------------------------*/
static int _cvConstructExtVD(CvVoronoiDiagramInt* pVoronoiDiagram);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Compute the external Voronoi Diagram for each internal polygon (hole).
    Arguments 
     pVoronoiDiagram : in, pointer to struct, which contains the 
                        description of Voronoi Diagram
     Return     : 
    --------------------------------------------------------------------------*/
static void _cvConstructIntVD(CvVoronoiDiagramInt* pVoronoiDiagram);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function joins the Voronoi Diagrams of different 
                    chains into one Voronoi Diagram 
    Arguments 
     pVoronoiDiagram : in, pointer to struct, which contains the 
                        description of Voronoi Diagram
     pChain1,pChain1: in, given chains
     Return     : 1, if joining was succesful
                  0, if some error occure
    --------------------------------------------------------------------------*/
static int _cvJoinChains(pCvVoronoiChain pChain1, 
                         pCvVoronoiChain pChain2,
                         CvVoronoiDiagramInt* pVoronoiDiagram);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function finds the nearest site for top vertex 
                 (= the most left vertex) of each hole
    Arguments 
        pVoronoiDiagram : in, pointer to struct, which contains the 
                         description of Voronoi Diagram
     Return     : 
    --------------------------------------------------------------------------*/
static void _cvFindNearestSite(CvVoronoiDiagramInt* pVoronoiDiagram);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function seeks for site, which has common bisector in
                  final VD with top vertex of given hole. It stores in pHole->opposite_site.
                   The search begins from  Hole->nearest_site and realizes in clockwise 
                   direction around the top vertex of given hole.
    Arguments 
        pVoronoiDiagram : in, pointer to struct, which contains the 
                          description of Voronoi Diagram
          pHole : in, given hole        
     Return     : 1, if the search was succesful
                  0, if some error occure
    --------------------------------------------------------------------------*/
static int _cvFindOppositSiteCW(pCvVoronoiHole pHole, CvVoronoiDiagramInt* pVoronoiDiagram);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function seeks for site, which has common bisector in
                  final VD with top vertex of given hole. It stores in pHole->opposite_site.
                   The search begins from  Hole->nearest_site and realizes in counterclockwise 
                   direction around the top vertex of given hole.
    Arguments 
        pVoronoiDiagram : in, pointer to struct, which contains the 
                        description of Voronoi Diagram
          pHole : in, given hole        
     Return     : 1, if the search was succesful
                  0, if some error occure
    --------------------------------------------------------------------------*/
static int _cvFindOppositSiteCCW(pCvVoronoiHole pHole,CvVoronoiDiagramInt* pVoronoiDiagram);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function merges external VD of hole and internal VD, which was 
                  constructed ealier.
    Arguments 
pVoronoiDiagram : in, pointer to struct, which contains the 
                        description of Voronoi Diagram
          pHole : in, given hole
     Return     : 1, if merging was succesful
                  0, if some error occure
    --------------------------------------------------------------------------*/
static int _cvMergeVD(pCvVoronoiHole pHole,CvVoronoiDiagramInt* pVoronoiDiagram);


/*////////////////////////////////////////////////////////////////////////////////////////
//                               Computation of bisectors                               //
////////////////////////////////////////////////////////////////////////////////////////*/

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Compute the bisector of two sites
    Arguments 
     pSite_left,pSite_right: in, given sites
     pVoronoiDiagram : in, pointer to struct, which contains the 
                        description of Voronoi Diagram
     pEdge      : out, bisector
     Return     : 
    --------------------------------------------------------------------------*/
void _cvCalcEdge(pCvVoronoiSite pSite_left, 
                pCvVoronoiSite pSite_right,
                pCvVoronoiEdge pEdge,
                CvVoronoiDiagramInt* pVoronoiDiagram);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Compute the bisector of point and site
    Arguments 
     pSite      : in, site
     pNode      : in, point
     pVoronoiDiagram : in, pointer to struct, which contains the 
                        description of Voronoi Diagram
     pEdge      : out, bisector
     Return     : 
    --------------------------------------------------------------------------*/
void _cvCalcEdge(pCvVoronoiSite pSite,
                pCvVoronoiNode pNode,
                pCvVoronoiEdge pEdge,
                CvVoronoiDiagramInt* pVoronoiDiagram);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Compute the bisector of point and site
    Arguments 
     pSite      : in, site
     pNode      : in, point
     pVoronoiDiagram : in, pointer to struct, which contains the 
                        description of Voronoi Diagram
     pEdge      : out, bisector
     Return     : 
    --------------------------------------------------------------------------*/
void _cvCalcEdge(pCvVoronoiNode pNode, 
                pCvVoronoiSite pSite,
                pCvVoronoiEdge pEdge,
                CvVoronoiDiagramInt* pVoronoiDiagram);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Compute the direction of bisector of two segments
    Arguments 
     pDirection1: in, direction of first segment
     pDirection2: in, direction of second segment
     pVoronoiDiagram : in, pointer to struct, which contains the 
                        description of Voronoi Diagram
     pEdge      : out, bisector
     Return     : 
    --------------------------------------------------------------------------*/
CV_INLINE
void _cvCalcEdgeLL(pCvDirection pDirection1,
                  pCvDirection pDirection2,
                  pCvVoronoiEdge pEdge,
                  CvVoronoiDiagramInt* pVoronoiDiagram);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Compute the bisector of two points
    Arguments 
     pPoint1, pPoint2: in, given points
     pVoronoiDiagram : in, pointer to struct, which contains the 
                        description of Voronoi Diagram
     pEdge      : out, bisector
     Return     : 
    --------------------------------------------------------------------------*/
CV_INLINE
void _cvCalcEdgePP(pCvPointFloat pPoint1,
                  pCvPointFloat pPoint2,
                  pCvVoronoiEdge pEdge,
                  CvVoronoiDiagramInt* pVoronoiDiagram);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Compute the bisector of segment and point. Since 
                    it is parabola, it is defined by its focus (site - point)
                    and directrice(site-segment)
    Arguments 
     pFocus    : in, point, which defines the focus of parabola
     pDirectrice: in, site - segment, which defines the directrice of parabola 
     pVoronoiDiagram : in, pointer to struct, which contains the 
                        description of Voronoi Diagram
     pEdge      : out, bisector
     Return     : 
    --------------------------------------------------------------------------*/
CV_INLINE
void _cvCalcEdgePL(pCvVoronoiNode pFocus,
                  pCvVoronoiSite pDirectrice,
                  pCvVoronoiEdge pEdge,
                  CvVoronoiDiagramInt* pVoronoiDiagram);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Compute the bisector of segment and point. Since 
                    it is parabola, it is defined by its focus (site - point)
                    and directrice(site-segment)
    Arguments 
     pFocus    : in, point, which defines the focus of parabola
     pDirectrice: in, site - segment, which defines the directrice of parabola 
     pVoronoiDiagram : in, pointer to struct, which contains the 
                        description of Voronoi Diagram
     pEdge      : out, bisector
     Return     : 
    --------------------------------------------------------------------------*/
CV_INLINE
void _cvCalcEdgeLP(pCvVoronoiSite pDirectrice,
                  pCvVoronoiNode pFocus,
                  pCvVoronoiEdge pEdge,
                  CvVoronoiDiagramInt* pVoronoiDiagram);

/*////////////////////////////////////////////////////////////////////////////////////////
//                  Computation of intersections of bisectors                           //
////////////////////////////////////////////////////////////////////////////////////////*/

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes intersection of two edges. Intersection
                    must be the nearest to the marked point on pEdge1
                    (this marked point is pEdge1->node1->node).
    Arguments 
    pEdge1,pEdge2: in, two edges
        pPoint: out, intersection of pEdge1 and pEdge2
        Radius: out, distance between pPoint and sites, assosiated
                    with pEdge1 and pEdge2 (pPoint is situated on the equal
                    distance from site, assosiated with pEdge1 and from
                    site,assosiated with pEdge2)
      Return    : distance between pPoint and marked point on pEdge1 or
                : -1, if edges have no intersections
    --------------------------------------------------------------------------*/
static
float _cvCalcEdgeIntersection(pCvVoronoiEdge pEdge1,
                              pCvVoronoiEdge pEdge2,
                              CvPointFloat* pPoint,
                              float &Radius);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes intersection of two edges. Intersection
                    must be the nearest to the marked point on pEdge1
                    (this marked point is pEdge1->node1->node).
    Arguments 
    pEdge1 : in, straight ray
    pEdge2: in, straight ray or segment
        pPoint: out, intersection of pEdge1 and pEdge2
        Radius: out, distance between pPoint and sites, assosiated
                    with pEdge1 and pEdge2 (pPoint is situated on the equal
                    distance from site, assosiated with pEdge1 and from
                    site,assosiated with pEdge2)
     Return : distance between pPoint and marked point on pEdge1 or
                : -1, if edges have no intersections
    --------------------------------------------------------------------------*/
static
float _cvLine_LineIntersection(pCvVoronoiEdge pEdge1,
                                pCvVoronoiEdge pEdge2,
                                pCvPointFloat  pPoint,
                                float &Radius);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes intersection of two edges. Intersection
                    must be the nearest to the marked point on pEdge1
                    (this marked point is pEdge1->node1->node).
    Arguments 
    pEdge1 : in, straight ray
    pEdge2: in, parabolic ray or segment
        pPoint: out, intersection of pEdge1 and pEdge2
        Radius: out, distance between pPoint and sites, assosiated
                    with pEdge1 and pEdge2 (pPoint is situated on the equal
                    distance from site, assosiated with pEdge1 and from
                    site,assosiated with pEdge2)
      Return    : distance between pPoint and marked point on pEdge1 or
                : -1, if edges have no intersections
    --------------------------------------------------------------------------*/
static
float _cvLine_ParIntersection(pCvVoronoiEdge pEdge1,
                                pCvVoronoiEdge pEdge2,
                                pCvPointFloat  pPoint,
                                float &Radius);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes intersection of two edges. Intersection
                    must be the nearest to the marked point on pEdge1
                    (this marked point is pEdge1->node1->node).
    Arguments 
    pEdge1 : in, straight ray
    pEdge2: in, parabolic segment
        pPoint: out, intersection of pEdge1 and pEdge2
        Radius: out, distance between pPoint and sites, assosiated
                    with pEdge1 and pEdge2 (pPoint is situated on the equal
                    distance from site, assosiated with pEdge1 and from
                    site,assosiated with pEdge2)
      Return    : distance between pPoint and marked point on pEdge1 or
                : -1, if edges have no intersections
    --------------------------------------------------------------------------*/
static
float _cvLine_CloseParIntersection(pCvVoronoiEdge pEdge1,
                                pCvVoronoiEdge pEdge2,
                                pCvPointFloat  pPoint,
                                float &Radius);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes intersection of two edges. Intersection
                    must be the nearest to the marked point on pEdge1
                    (this marked point is pEdge1->node1->node).
    Arguments 
    pEdge1 : in, straight ray
    pEdge2: in, parabolic ray 
        pPoint: out, intersection of pEdge1 and pEdge2
        Radius: out, distance between pPoint and sites, assosiated
                    with pEdge1 and pEdge2 (pPoint is situated on the equal
                    distance from site, assosiated with pEdge1 and from
                    site,assosiated with pEdge2)
      Return    : distance between pPoint and marked point on pEdge1 or
                : -1, if edges have no intersections
    --------------------------------------------------------------------------*/
static
float _cvLine_OpenParIntersection(pCvVoronoiEdge pEdge1,
                                pCvVoronoiEdge pEdge2,
                                pCvPointFloat  pPoint,
                                float &Radius);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes intersection of two edges. Intersection
                    must be the nearest to the marked point on pEdge1
                    (this marked point is pEdge1->node1->node).
    Arguments 
    pEdge1 : in,  parabolic ray
    pEdge2: in,  straight ray or segment
        pPoint: out, intersection of pEdge1 and pEdge2
        Radius: out, distance between pPoint and sites, assosiated
                    with pEdge1 and pEdge2 (pPoint is situated on the equal
                    distance from site, assosiated with pEdge1 and from
                    site,assosiated with pEdge2)
      Return    : distance between pPoint and marked point on pEdge1 or
                : -1, if edges have no intersections
    --------------------------------------------------------------------------*/
static
float _cvPar_LineIntersection(pCvVoronoiEdge pEdge1,
                                pCvVoronoiEdge pEdge2,
                                pCvPointFloat  pPoint,
                                float &Radius);
/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes intersection of two edges. Intersection
                    must be the nearest to the marked point on pEdge1
                    (this marked point is pEdge1->node1->node).
    Arguments 
    pEdge1 : in,  parabolic ray
    pEdge2: in,  straight ray 
        pPoint: out, intersection of pEdge1 and pEdge2
        Radius: out, distance between pPoint and sites, assosiated
                    with pEdge1 and pEdge2 (pPoint is situated on the equal
                    distance from site, assosiated with pEdge1 and from
                    site,assosiated with pEdge2)
      Return    : distance between pPoint and marked point on pEdge1 or
                : -1, if edges have no intersections
    --------------------------------------------------------------------------*/
static
float _cvPar_OpenLineIntersection(pCvVoronoiEdge pEdge1,
                                pCvVoronoiEdge pEdge2,
                                pCvPointFloat  pPoint,
                                float &Radius);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes intersection of two edges. Intersection
                    must be the nearest to the marked point on pEdge1
                    (this marked point is pEdge1->node1->node).
    Arguments 
    pEdge1 : in,  parabolic ray
    pEdge2: in,  straight segment
        pPoint: out, intersection of pEdge1 and pEdge2
        Radius: out, distance between pPoint and sites, assosiated
                    with pEdge1 and pEdge2 (pPoint is situated on the equal
                    distance from site, assosiated with pEdge1 and from
                    site,assosiated with pEdge2)
      Return    : distance between pPoint and marked point on pEdge1 or
                : -1, if edges have no intersections
    --------------------------------------------------------------------------*/
static
float _cvPar_CloseLineIntersection(pCvVoronoiEdge pEdge1,
                                    pCvVoronoiEdge pEdge2,
                                    pCvPointFloat  pPoint,
                                    float &Radius);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes intersection of two edges. Intersection
                    must be the nearest to the marked point on pEdge1
                    (this marked point is pEdge1->node1->node).
    Arguments 
    pEdge1 : in,  parabolic ray
    pEdge2: in,  parabolic ray or segment
        pPoint: out, intersection of pEdge1 and pEdge2
        Radius: out, distance between pPoint and sites, assosiated
                    with pEdge1 and pEdge2 (pPoint is situated on the equal
                    distance from site, assosiated with pEdge1 and from
                    site,assosiated with pEdge2)
      Return    : distance between pPoint and marked point on pEdge1 or
                : -1, if edges have no intersections
    --------------------------------------------------------------------------*/
static
float _cvPar_ParIntersection(pCvVoronoiEdge pEdge1,
                                pCvVoronoiEdge pEdge2,
                                pCvPointFloat  pPoint,
                                float &Radius);


/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes intersection of two edges. Intersection
                    must be the nearest to the marked point on pEdge1
                    (this marked point is pEdge1->node1->node).
    Arguments 
    pEdge1 : in,  parabolic ray
    pEdge2: in,  parabolic ray 
        pPoint: out, intersection of pEdge1 and pEdge2
        Radius: out, distance between pPoint and sites, assosiated
                    with pEdge1 and pEdge2 (pPoint is situated on the equal
                    distance from site, assosiated with pEdge1 and from
                    site,assosiated with pEdge2)
      Return    : distance between pPoint and marked point on pEdge1 or
                : -1, if edges have no intersections
    --------------------------------------------------------------------------*/
static
float _cvPar_OpenParIntersection(pCvVoronoiEdge pEdge1,
                            pCvVoronoiEdge pEdge2,
                            pCvPointFloat  pPoint,
                            float &Radius);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes intersection of two edges. Intersection
                    must be the nearest to the marked point on pEdge1
                    (this marked point is pEdge1->node1->node).
    Arguments 
    pEdge1 : in,  parabolic ray
    pEdge2: in,  parabolic segment
        pPoint: out, intersection of pEdge1 and pEdge2
        Radius: out, distance between pPoint and sites, assosiated
                    with pEdge1 and pEdge2 (pPoint is situated on the equal
                    distance from site, assosiated with pEdge1 and from
                    site,assosiated with pEdge2)
      Return    : distance between pPoint and marked point on pEdge1 or
                : -1, if edges have no intersections
    --------------------------------------------------------------------------*/
static
float _cvPar_CloseParIntersection(pCvVoronoiEdge pEdge1,
                            pCvVoronoiEdge pEdge2,
                            pCvPointFloat  pPoint,
                            float &Radius);

/*////////////////////////////////////////////////////////////////////////////////////////
//                           Subsidiary functions                                       //
////////////////////////////////////////////////////////////////////////////////////////*/

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : 
    Arguments 
        pEdge1  : in
        pEdge2  : out
     Return     : 
    --------------------------------------------------------------------------*/
CV_INLINE
void _cvMakeTwinEdge(pCvVoronoiEdge pEdge2,
                     pCvVoronoiEdge pEdge1);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : 
    Arguments 
        pEdge   : in&out
        pEdge_left_prev : in&out
        pSite_left : in&out
     Return     : 
    --------------------------------------------------------------------------*/
CV_INLINE
void _cvStickEdgeLeftBegin(pCvVoronoiEdge pEdge, 
                          pCvVoronoiEdge pEdge_left_prev,
                          pCvVoronoiSite pSite_left);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : 
    Arguments 
        pEdge   : in&out
        pEdge_right_next : in&out
        pSite_right : in&out
     Return     : 
    --------------------------------------------------------------------------*/
CV_INLINE
void _cvStickEdgeRightBegin(pCvVoronoiEdge pEdge, 
                          pCvVoronoiEdge pEdge_right_next,
                          pCvVoronoiSite pSite_right);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : 
    Arguments 
        pEdge   : in&out
        pEdge_left_next : in&out
        pSite_left : in&out
     Return     : 
    --------------------------------------------------------------------------*/
CV_INLINE
void _cvStickEdgeLeftEnd(pCvVoronoiEdge pEdge, 
                        pCvVoronoiEdge pEdge_left_next,
                        pCvVoronoiSite pSite_left);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : 
    Arguments 
        pEdge   : in&out
        pEdge_right_prev : in&out
        pSite_right : in&out
     Return     : 
    --------------------------------------------------------------------------*/
CV_INLINE
void _cvStickEdgeRightEnd(pCvVoronoiEdge pEdge, 
                         pCvVoronoiEdge pEdge_right_prev,
                         pCvVoronoiSite pSite_right);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : 
    Arguments 
        pEdge_left_cur  : in
        pEdge_left : in
     Return     : 
    --------------------------------------------------------------------------*/
CV_INLINE
void _cvTwinNULLLeft(pCvVoronoiEdge pEdge_left_cur,
                    pCvVoronoiEdge pEdge_left);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : 
    Arguments 
        pEdge_right_cur : in
        pEdge_right : in
     Return     : 
    --------------------------------------------------------------------------*/
CV_INLINE
void _cvTwinNULLRight(pCvVoronoiEdge pEdge_right_cur,
                     pCvVoronoiEdge pEdge_right);


/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : function initializes the struct CvVoronoiNode
    Arguments 
        pNode   : out
         pPoint : in, 
        radius  : in
     Return     : 
    --------------------------------------------------------------------------*/
template <class T> CV_INLINE
void _cvInitVoronoiNode(pCvVoronoiNode pNode,
                       T pPoint, float radius = 0);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : function initializes the struct CvVoronoiSite
    Arguments 
        pSite   : out
         pNode1,pNode2,pPrev_site : in 
     Return     : 
    --------------------------------------------------------------------------*/
CV_INLINE
void _cvInitVoronoiSite(pCvVoronoiSite pSite,
                       pCvVoronoiNode pNode1,
                       pCvVoronoiNode pNode2,
                       pCvVoronoiSite pPrev_site);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : function pushs the element in the end of the sequence
                    end returns its adress
    Arguments 
            Seq : in, pointer to the sequence
           Elem : in, element 
     Return     : pointer to the element in the sequence
    --------------------------------------------------------------------------*/
template <class T> CV_INLINE
T _cvSeqPush(CvSeq* Seq, T pElem);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : function pushs the element in the begin of the sequence
                    end returns its adress
    Arguments 
            Seq : in, pointer to the sequence
           Elem : in, element 
     Return     : pointer to the element in the sequence
    --------------------------------------------------------------------------*/
template <class T> CV_INLINE
T _cvSeqPushFront(CvSeq* Seq, T pElem); 

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : function pushs the element pHole in pHoleHierarchy->HoleSeq
                     so as all elements in this sequence would be normalized
                     according to field .x_coord of element. pHoleHierarchy->TopHole
                     points to hole with smallest x_coord.
    Arguments 
pHoleHierarchy  : in, pointer to the structur
          pHole : in, element 
     Return     : pointer to the element in the sequence
    --------------------------------------------------------------------------*/
CV_INLINE
void _cvSeqPushInOrder(CvVoronoiDiagramInt* pVoronoiDiagram, pCvVoronoiHole pHole);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : function intersects given edge pEdge (and his twin edge)
                    by point pNode on two parts
    Arguments 
        pEdge   : in, given edge
          pNode : in, given point
        EdgeSeq : in
     Return     : one of parts
    --------------------------------------------------------------------------*/
CV_INLINE
pCvVoronoiEdge _cvDivideRightEdge(pCvVoronoiEdge pEdge,
                                 pCvVoronoiNode pNode, 
                                 CvSeq* EdgeSeq);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : function intersects given edge pEdge (and his twin edge)
                    by point pNode on two parts
    Arguments 
        pEdge   : in, given edge
          pNode : in, given point
        EdgeSeq : in
     Return     : one of parts
    --------------------------------------------------------------------------*/
CV_INLINE
pCvVoronoiEdge _cvDivideLeftEdge(pCvVoronoiEdge pEdge,
                                pCvVoronoiNode pNode, 
                                CvSeq* EdgeSeq);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : function pushs the element in the end of the sequence
                    end returns its adress
    Arguments 
          writer: in, writer associated with sequence
          pElem : in, element 
     Return     : pointer to the element in the sequence
    --------------------------------------------------------------------------*/
template<class T> CV_INLINE
T _cvWriteSeqElem(T pElem, CvSeqWriter &writer);

/*////////////////////////////////////////////////////////////////////////////////////////
//                           Mathematical functions                                     //
////////////////////////////////////////////////////////////////////////////////////////*/

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function changes x and y
    Arguments 
            x,y : in&out
      Return    : 
    --------------------------------------------------------------------------*/
template <class T> CV_INLINE
void _cvSwap(T &x, T &y);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes the inverse map to the 
                    given ortogonal affine map
    Arguments 
            A   : in, given ortogonal affine map
            B   : out, inverse map
      Return    : 1, if inverse map exist
                  0, else
    --------------------------------------------------------------------------*/
template <class T> CV_INLINE
int _cvCalcOrtogInverse(T* B, T* A);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes the composition of two affine maps
    Arguments 
            A,B : in, given affine maps
            Result: out, composition of A and B (Result = AB)
      Return    : 
    --------------------------------------------------------------------------*/
template <class T> CV_INLINE
void _cvCalcComposition(T* Result,T* A,T* B);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes the image of point under 
                    given affin map
    Arguments 
            A   : in, affine maps
        pPoint  : in, pointer to point
        pImgPoint:out, pointer to image of point
      Return    : 
    --------------------------------------------------------------------------*/
template<class T> CV_INLINE
void _cvCalcPointImage(pCvPointFloat pImgPoint,pCvPointFloat pPoint,T* A);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes the image of vector under 
                    given affin map
    Arguments 
            A   : in, affine maps
        pVector : in, pointer to vector
        pImgVector:out, pointer to image of vector
      Return    : 
    --------------------------------------------------------------------------*/
template<class T> CV_INLINE
void _cvCalcVectorImage(pCvDirection pImgVector,pCvDirection pVector,T* A);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes the distance between the point 
                    and site. Internal function.
    Arguments 
        pPoint  : in, point
        pSite   : in, site
      Return    : distance
    --------------------------------------------------------------------------*/
CV_INLINE
float _cvCalcDist(pCvPointFloat pPoint, pCvVoronoiSite pSite);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes the distance between two points 
    Arguments 
    pPoint1,pPoint2 : in, two points
      Return    : distance
    --------------------------------------------------------------------------*/
CV_INLINE
float _cvPPDist(pCvPointFloat pPoint1,pCvPointFloat pPoint2);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function computes the distance betwin point and 
                    segment. Internal function. 
    Arguments 
          pPoint: in, point
    pPoint1,pPoint2 : in, segment [pPoint1,pPoint2]
       Return   : distance
    --------------------------------------------------------------------------*/
CV_INLINE
float _cvPLDist(pCvPointFloat pPoint,pCvPointFloat pPoint1,pCvDirection pDirection);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function solves the squar equation with real coefficients
                    T - float or double
    Arguments 
     c2,c1,c0: in, real coefficients of polynom 
               X: out, array of roots
     Return     : number of roots
    --------------------------------------------------------------------------*/
template <class T> 
int _cvSolveEqu2thR(T c2, T c1, T c0, T* X);

/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : Function solves the linear equation with real or complex coefficients
                    T - float or double or complex
    Arguments 
        c1,c0: in, real or complex coefficients of polynom 
               X: out, array of roots
     Return     : number of roots
    --------------------------------------------------------------------------*/
template <class T> CV_INLINE
int _cvSolveEqu1th(T c1, T c0, T* X);

/****************************************************************************************\
*                             Storage Block Increase                                    *
\****************************************************************************************/
/*--------------------------------------------------------------------------
    Author      : Andrey Sobolev
    Description : For each sequence function creates the memory block sufficient to store
                  all elements of sequnce
    Arguments 
        pVoronoiDiagramInt: in, pointer to struct, which contains the 
                            description of Voronoi Diagram.
        vertices_number: in, number of vertices in polygon
     Return     : 
    --------------------------------------------------------------------------*/
void _cvSetSeqBlockSize(CvVoronoiDiagramInt* pVoronoiDiagramInt,int vertices_number)
{
    int N = 2*vertices_number;
    cvSetSeqBlockSize(pVoronoiDiagramInt->SiteSeq,N*pVoronoiDiagramInt->SiteSeq->elem_size);
    cvSetSeqBlockSize(pVoronoiDiagramInt->EdgeSeq,3*N*pVoronoiDiagramInt->EdgeSeq->elem_size);
    cvSetSeqBlockSize(pVoronoiDiagramInt->NodeSeq,5*N*pVoronoiDiagramInt->NodeSeq->elem_size);
    cvSetSeqBlockSize(pVoronoiDiagramInt->ParabolaSeq,N*pVoronoiDiagramInt->ParabolaSeq->elem_size);
    cvSetSeqBlockSize(pVoronoiDiagramInt->DirectionSeq,3*N*pVoronoiDiagramInt->DirectionSeq->elem_size);
    cvSetSeqBlockSize(pVoronoiDiagramInt->ChainSeq,N*pVoronoiDiagramInt->DirectionSeq->elem_size);
    cvSetSeqBlockSize(pVoronoiDiagramInt->HoleSeq,100*pVoronoiDiagramInt->HoleSeq->elem_size);
}

/****************************************************************************************\
*                                    Function realization                               *
\****************************************************************************************/


CV_IMPL int   cvVoronoiDiagramFromContour(CvSeq* ContourSeq,    
                                           CvVoronoiDiagram2D** VoronoiDiagram,
                                           CvMemStorage* VoronoiStorage,
                                           CvLeeParameters contour_type,
                                           int contour_orientation,
                                           int attempt_number)
{
    CV_FUNCNAME( "cvVoronoiDiagramFromContour" );

    __BEGIN__;

    CvSet* SiteSeq = NULL;
    CvSeq* CurContourSeq = NULL;
    CvVoronoiDiagramInt VoronoiDiagramInt;
    CvSeqWriter NodeWriter, EdgeWriter;
    CvMemStorage* storage;

    memset( &VoronoiDiagramInt, 0, sizeof(VoronoiDiagramInt) );

    if( !ContourSeq )
        CV_ERROR( CV_StsBadArg,"Contour sequence is empty" );

    if(!VoronoiStorage)
        CV_ERROR( CV_StsBadArg,"Storage is not initialized" );
   
    if( contour_type < 0 || contour_type > 2)
        CV_ERROR( CV_StsBadArg,"Unsupported parameter: type" );

    if( contour_orientation != 1 &&  contour_orientation != -1)
        CV_ERROR( CV_StsBadArg,"Unsupported parameter: orientation" );

    storage = cvCreateChildMemStorage(VoronoiStorage);
    (*VoronoiDiagram) = (CvVoronoiDiagram2D*)cvCreateSet(0,sizeof(CvVoronoiDiagram2D),sizeof(CvVoronoiNode2D), storage);
    storage = cvCreateChildMemStorage(VoronoiStorage);
    (*VoronoiDiagram)->edges = cvCreateSet(0,sizeof(CvSet),sizeof(CvVoronoiEdge2D), storage);
    cvStartAppendToSeq((CvSeq*)(*VoronoiDiagram)->edges, &EdgeWriter);
    cvStartAppendToSeq((CvSeq*)(*VoronoiDiagram), &NodeWriter);

        for(CurContourSeq = ContourSeq;\
            CurContourSeq != NULL;\
            CurContourSeq = CurContourSeq->h_next)
        {
            if(_cvLee(CurContourSeq, &VoronoiDiagramInt,VoronoiStorage,contour_type,contour_orientation,attempt_number))
    
            {
                if(!_cvConvert(*VoronoiDiagram,VoronoiDiagramInt,SiteSeq,NodeWriter,EdgeWriter,VoronoiStorage,contour_orientation))
                    goto exit;
            }
            else if(CurContourSeq->total >= 3)
                goto exit;
        }

        cvEndWriteSeq(&EdgeWriter);
        cvEndWriteSeq(&NodeWriter);
        if(SiteSeq != NULL)
            return 1;

    
    __END__;
    return 0;
}//end of cvVoronoiDiagramFromContour 

CV_IMPL int   cvVoronoiDiagramFromImage(IplImage* pImage,
                                         CvSeq** ContourSeq,
                                         CvVoronoiDiagram2D** VoronoiDiagram,
                                         CvMemStorage* VoronoiStorage,
                                         CvLeeParameters regularization_method,
                                         float approx_precision)
{
    CV_FUNCNAME( "cvVoronoiDiagramFromContour" );
    int RESULT = 0;
    
    __BEGIN__;

    IplImage* pWorkImage = NULL;
    CvSize image_size;
    int i, multiplicator = 3;
    
    int approx_method;
    CvMemStorage* ApproxContourStorage = NULL;
    CvSeq* ApproxContourSeq = NULL;

    if( !ContourSeq )
        CV_ERROR( CV_StsBadArg,"Contour sequence is not initialized" );

    if( (*ContourSeq)->total != 0)
        CV_ERROR( CV_StsBadArg,"Contour sequence is not empty" );

    if(!VoronoiStorage)
        CV_ERROR( CV_StsBadArg,"Storage is not initialized" );

    if(!pImage)
        CV_ERROR( CV_StsBadArg,"Image is not initialized" );

    if(pImage->nChannels != 1 || pImage->depth != 8)
        CV_ERROR( CV_StsBadArg,"Unsupported image format" );

    if(approx_precision<0 && approx_precision != CV_LEE_AUTO)
        CV_ERROR( CV_StsBadArg,"Unsupported presision value" );
    
    switch(regularization_method)
    {
        case CV_LEE_ERODE:  image_size.width = pImage->width;
                            image_size.height = pImage->height;
                            pWorkImage = cvCreateImage(image_size,8,1);
                            cvErode(pImage,pWorkImage,0,1);
                            approx_method = CV_CHAIN_APPROX_TC89_L1;
                            break;
        case CV_LEE_ZOOM:   image_size.width = multiplicator*pImage->width;
                            image_size.height = multiplicator*pImage->height;
                            pWorkImage = cvCreateImage(image_size,8,1);
                            cvResize(pImage, pWorkImage, CV_INTER_NN);
                            approx_method = CV_CHAIN_APPROX_TC89_L1;
                            break;
        case CV_LEE_NON:    pWorkImage = pImage;
                            approx_method = CV_CHAIN_APPROX_TC89_L1;
                            break;
        default:            CV_ERROR( CV_StsBadArg,"Unsupported regularisation method" );
                            break;

    }

    cvFindContours(pWorkImage, (*ContourSeq)->storage, ContourSeq, \
                            sizeof(CvContour), CV_RETR_CCOMP, approx_method );

    if(pWorkImage && pWorkImage != pImage )
        cvReleaseImage(&pWorkImage);
    
    ApproxContourStorage = cvCreateMemStorage(0);
    if(approx_precision > 0)
    {
        ApproxContourSeq = cvApproxPoly(*ContourSeq, sizeof(CvContour), ApproxContourStorage,\
                                        CV_POLY_APPROX_DP,approx_precision,1);
    
        RESULT = cvVoronoiDiagramFromContour(ApproxContourSeq,VoronoiDiagram,VoronoiStorage,CV_LEE_INT,-1,10);
    }
    else if(approx_precision == CV_LEE_AUTO)
    {
        ApproxContourSeq = *ContourSeq;
        for(i = 1; i < 50; i++)
        {
            RESULT = cvVoronoiDiagramFromContour(ApproxContourSeq,VoronoiDiagram,VoronoiStorage,CV_LEE_INT,-1,1);
            if(RESULT)
                break;
            ApproxContourSeq = cvApproxPoly(ApproxContourSeq, sizeof(CvContour),ApproxContourStorage,\
                                            CV_POLY_APPROX_DP,(float)i,1);
        }
    }
    else
        RESULT = cvVoronoiDiagramFromContour(*ContourSeq,VoronoiDiagram,VoronoiStorage,CV_LEE_INT,-1,10);
/*
    if(ApproxContourSeq)
    {
        cvClearMemStorage( (*ContourSeq)->storage );
        *ContourSeq = cvCreateSeq(0,sizeof(CvSeq),sizeof(CvPoint),(*ContourSeq)->storage);
        CvSeqReader reader;
        CvSeqWriter writer;
        CvPoint Point;
        cvStartAppendToSeq(*ContourSeq, &writer);
        cvStartReadSeq(ApproxContourSeq, &reader);
        for(int i = 0;i < ApproxContourSeq->total;i++)
        {
            CV_READ_SEQ_ELEM(Point,reader);
            Point.y = 600 - Point.y;
            CV_WRITE_SEQ_ELEM(Point,writer);
        }
        cvEndWriteSeq(&writer);
    }
    */

    cvReleaseMemStorage(&ApproxContourStorage);
    
    
    __END__;
    return RESULT;                                         
}//end of cvVoronoiDiagramFromImage

CV_IMPL void cvReleaseVoronoiStorage(CvVoronoiDiagram2D* VoronoiDiagram,
                                     CvMemStorage** pVoronoiStorage)
{
    /*CV_FUNCNAME( "cvReleaseVoronoiStorage" );*/
    __BEGIN__;
    
    CvSeq* Seq;

    if(VoronoiDiagram->storage)
        cvReleaseMemStorage(&VoronoiDiagram->storage);
    for(Seq = (CvSeq*)VoronoiDiagram->sites; Seq != NULL; Seq = Seq->h_next)
        if(Seq->storage)
            cvReleaseMemStorage(&Seq->storage);
    for(Seq = (CvSeq*)VoronoiDiagram->edges; Seq != NULL; Seq = Seq->h_next)
        if(Seq->storage)
            cvReleaseMemStorage(&Seq->storage);

    if(*pVoronoiStorage)
        cvReleaseMemStorage(pVoronoiStorage);

    __END__;
}//end of cvReleaseVoronoiStorage

static int  _cvLee(CvSeq* ContourSeq,
                    CvVoronoiDiagramInt* pVoronoiDiagramInt,
                    CvMemStorage* VoronoiStorage,
                    CvLeeParameters contour_type,
                    int contour_orientation,
                    int attempt_number)
{
    //orientation = 1 for left coordinat system
    //orientation = -1 for right coordinat system
    if(ContourSeq->total<3)
        return 0;

    int attempt = 0;
    CvVoronoiStorageInt VoronoiStorageInt;

    srand((int)time(NULL));

NEXTATTEMPT:
    VoronoiStorageInt.SiteStorage = cvCreateChildMemStorage(VoronoiStorage);
    VoronoiStorageInt.NodeStorage = cvCreateChildMemStorage(VoronoiStorage);
    VoronoiStorageInt.EdgeStorage = cvCreateChildMemStorage(VoronoiStorage);
    VoronoiStorageInt.ParabolaStorage = cvCreateMemStorage(0);
    VoronoiStorageInt.ChainStorage = cvCreateMemStorage(0);
    VoronoiStorageInt.DirectionStorage = cvCreateMemStorage(0);
    VoronoiStorageInt.HoleStorage = cvCreateMemStorage(0);

    pVoronoiDiagramInt->SiteSeq = cvCreateSeq(0,sizeof(CvSeq),sizeof(CvVoronoiSiteInt),VoronoiStorageInt.SiteStorage);
    pVoronoiDiagramInt->NodeSeq = cvCreateSeq(0,sizeof(CvSeq),sizeof(CvVoronoiNodeInt),VoronoiStorageInt.NodeStorage);
    pVoronoiDiagramInt->EdgeSeq = cvCreateSeq(0,sizeof(CvSeq),sizeof(CvVoronoiEdgeInt),VoronoiStorageInt.EdgeStorage);
    pVoronoiDiagramInt->ChainSeq  = cvCreateSeq(0,sizeof(CvSeq),sizeof(CvVoronoiChainInt),VoronoiStorageInt.ChainStorage);
    pVoronoiDiagramInt->DirectionSeq = cvCreateSeq(0,sizeof(CvSeq),sizeof(CvDirection),VoronoiStorageInt.DirectionStorage);
    pVoronoiDiagramInt->ParabolaSeq =  cvCreateSeq(0,sizeof(CvSeq),sizeof(CvVoronoiParabolaInt),VoronoiStorageInt.ParabolaStorage);
    pVoronoiDiagramInt->HoleSeq =  cvCreateSeq(0,sizeof(CvSeq),sizeof(CvVoronoiHoleInt),VoronoiStorageInt.HoleStorage);

    _cvSetSeqBlockSize(pVoronoiDiagramInt,ContourSeq->total);
    
    if(!_cvConstuctSites(ContourSeq, pVoronoiDiagramInt, contour_type,contour_orientation))
    {
        attempt = attempt_number;
        goto FAULT;
    }
    _cvRandomModification(pVoronoiDiagramInt, 0,pVoronoiDiagramInt->NodeSeq->total,0.2f);

    if(!_cvConstructChains(pVoronoiDiagramInt))
    {
        attempt = attempt_number;
        goto FAULT;
    }

    if(!_cvConstructSkeleton(pVoronoiDiagramInt))
        goto FAULT; 

    _cvConstructSiteTree(pVoronoiDiagramInt);
    
//SUCCESS:
    _cvReleaseVoronoiStorage(&VoronoiStorageInt,0,1);
    return 1;

FAULT:
    _cvReleaseVoronoiStorage(&VoronoiStorageInt,1,1);
    if(++attempt < attempt_number)
        goto NEXTATTEMPT;

    return 0;
}// end of _cvLee

static int _cvConstuctSites(CvSeq* ContourSeq,
                            CvVoronoiDiagramInt* pVoronoiDiagram,
                            CvLeeParameters contour_type,
                            int contour_orientation)
{
    pVoronoiDiagram->reflex_site = NULL;
    pVoronoiDiagram->top_hole = NULL;   
    int result = 0;

    switch(contour_type)
    {
        case CV_LEE_INT :    result = _cvConstructExtSites(pVoronoiDiagram,ContourSeq,contour_orientation,(int)1);
                             break;
        case CV_LEE_FLOAT :  result = _cvConstructExtSites(pVoronoiDiagram,ContourSeq,contour_orientation,(float)1);
                             break;
        case CV_LEE_DOUBLE : result = _cvConstructExtSites(pVoronoiDiagram,ContourSeq,contour_orientation,(double)1);
                             break;
        default:             return 0;
    }
    
    if(!result)
        return 0;

    CvSeq* CurSiteSeq; 
    CvVoronoiHoleInt Hole = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,false,0};
    pCvVoronoiSite pTopSite = 0;
    for(CvSeq* CurContourSeq = ContourSeq->v_next;\
        CurContourSeq != NULL;\
        CurContourSeq = CurContourSeq->h_next)
    {
        if(CurContourSeq->total == 0)
            continue;

        CurSiteSeq = cvCreateSeq(0,sizeof(CvSeq),sizeof(CvVoronoiSiteInt),pVoronoiDiagram->SiteSeq->storage);
        switch(contour_type)
        {
            case CV_LEE_INT :   result = _cvConstructIntSites(pVoronoiDiagram,CurSiteSeq,CurContourSeq,pTopSite,contour_orientation,(int)1);
                                break;
            case CV_LEE_FLOAT : result = _cvConstructIntSites(pVoronoiDiagram,CurSiteSeq,CurContourSeq,pTopSite,contour_orientation,(float)1);
                                break;
            case CV_LEE_DOUBLE :result = _cvConstructIntSites(pVoronoiDiagram,CurSiteSeq,CurContourSeq,pTopSite,contour_orientation,(double)1);
                                break;
            default:            result = 0;
        }
        if(!result)
            continue;

        Hole.SiteSeq = CurSiteSeq;
        Hole.site_top = pTopSite;
        Hole.x_coord = pTopSite->node1->node.x;
        Hole.error = false;
        _cvSeqPushInOrder(pVoronoiDiagram, &Hole);
    }
    return 1;
}//end of _cvConstuctSites

static int _cvConstructChains(CvVoronoiDiagramInt* pVoronoiDiagram)
{
    if(!_cvConstructExtChains(pVoronoiDiagram))
        return 0;

    CvSeq* CurrChainSeq;
    for(pCvVoronoiHole pHole = pVoronoiDiagram->top_hole;\
        pHole != NULL; \
        pHole = pHole->next_hole)
        {
            pHole->error = false;
            CurrChainSeq = cvCreateSeq(0,sizeof(CvSeq),sizeof(CvVoronoiChainInt),pVoronoiDiagram->ChainSeq->storage);
            _cvConstructIntChains(pVoronoiDiagram,CurrChainSeq,pHole->SiteSeq,pHole->site_top);
            pHole->ChainSeq = CurrChainSeq;
        }
    return 1;
}//end of _cvConstructChains

static int _cvConstructSkeleton(CvVoronoiDiagramInt* pVoronoiDiagram)
{
    if(!_cvConstructExtVD(pVoronoiDiagram))
        return 0;
    _cvConstructIntVD(pVoronoiDiagram);
    _cvFindNearestSite(pVoronoiDiagram);
    
    float dx,dy;
    int result;
    for(pCvVoronoiHole pHole = pVoronoiDiagram->top_hole;\
        pHole != NULL; pHole = pHole->next_hole)
    {
        if(pHole->error)
            continue;
        dx = pHole->node->node.x - pHole->site_top->node1->node.x;
        dy = pHole->node->node.y - pHole->site_top->node1->node.y;
        
        if(fabs(dy) < 0.01 && dx < 0)
            pHole->site_opposite = pHole->site_nearest;
        else
        {
            if(dy > 0)
                result = _cvFindOppositSiteCCW(pHole,pVoronoiDiagram);
            else
                result = _cvFindOppositSiteCW(pHole,pVoronoiDiagram);

            if(!result)
            {
                pHole->error = true;
                continue;
            }
        }
    
        if(!_cvMergeVD(pHole,pVoronoiDiagram))
            return 0;
    }
    return 1;
}//end of _cvConstructSkeleton

static void _cvConstructSiteTree(CvVoronoiDiagramInt* pVoronoiDiagram)
{
    CvSeq* CurSeq = pVoronoiDiagram->SiteSeq;
    for(pCvVoronoiHole pHole = pVoronoiDiagram->top_hole;\
        pHole != NULL; pHole = pHole->next_hole)
    {
        if(pHole->error)
            continue;
        if(CurSeq == pVoronoiDiagram->SiteSeq)
        {
            CurSeq->v_next = pHole->SiteSeq;
            pHole->SiteSeq->v_prev = CurSeq;
        }
        else
        {
            CurSeq->h_next = pHole->SiteSeq;
            pHole->SiteSeq->h_prev = CurSeq;
            pHole->SiteSeq->v_prev = pVoronoiDiagram->SiteSeq;
        }
        CurSeq = pHole->SiteSeq; 
    }
    CurSeq->h_next = NULL;
}//end of _cvConstructSiteTree

static void _cvRandomModification(CvVoronoiDiagramInt* pVoronoiDiagram, int begin, int end, float shift)
{
    CvSeqReader Reader;
    pCvVoronoiNode pNode;
    const float rnd_const = shift/RAND_MAX;
    int i;

    cvStartReadSeq(pVoronoiDiagram->NodeSeq, &Reader,0);
    for( i = begin; i < end; i++)
    {
        pNode = (pCvVoronoiNode)Reader.ptr;
        pNode->node.x = (float)cvFloor(pNode->node.x) + rand()*rnd_const;
        pNode->node.y = (float)cvFloor(pNode->node.y) + rand()*rnd_const;
        CV_NEXT_SEQ_ELEM( sizeof(CvVoronoiNodeInt), Reader ); 
    }

    for(pCvVoronoiHole pHole = pVoronoiDiagram->top_hole;\
        pHole != NULL;\
        pHole = pHole->next_hole)
    {
        pHole->site_top->node1->node.x = (float)cvFloor(pHole->site_top->node1->node.x);
    }
        
}//end of _cvRandomModification

static void _cvReleaseVoronoiStorage(CvVoronoiStorageInt* pVoronoiStorage, int group1, int group2)
{
    //if group1 = 1 then SiteSeq, NodeSeq, EdgeSeq released
    //if group2 = 1 then DirectionSeq, ParabolaSeq, ChainSeq, HoleSeq released
    if(group1 == 1)
    {
        if(pVoronoiStorage->SiteStorage!=NULL)
            cvReleaseMemStorage(&pVoronoiStorage->SiteStorage);
        if(pVoronoiStorage->EdgeStorage!=NULL)
            cvReleaseMemStorage(&pVoronoiStorage->EdgeStorage);
        if(pVoronoiStorage->NodeStorage!=NULL)
            cvReleaseMemStorage(&pVoronoiStorage->NodeStorage);
    }
    if(group2 == 1)
    {
        if(pVoronoiStorage->ParabolaStorage!=NULL)
            cvReleaseMemStorage(&pVoronoiStorage->ParabolaStorage);
        if(pVoronoiStorage->ChainStorage!=NULL)
            cvReleaseMemStorage(&pVoronoiStorage->ChainStorage);
        if(pVoronoiStorage->DirectionStorage!=NULL)
            cvReleaseMemStorage(&pVoronoiStorage->DirectionStorage);
        if(pVoronoiStorage->HoleStorage != NULL)
            cvReleaseMemStorage(&pVoronoiStorage->HoleStorage);
    }
}// end of _cvReleaseVoronoiStorage

static int _cvConvert(CvVoronoiDiagram2D* VoronoiDiagram,
                       CvVoronoiDiagramInt VoronoiDiagramInt,
                       CvSet* &SiteSeq,
                       CvSeqWriter &NodeWriter,
                       CvSeqWriter &EdgeWriter,
                       CvMemStorage* VoronoiStorage,
                       int contour_orientation)
{
    if(contour_orientation == 1)
        return _cvConvertSameOrientation(VoronoiDiagram,VoronoiDiagramInt,SiteSeq,NodeWriter,
                                        EdgeWriter,VoronoiStorage);
    else
        return _cvConvertChangeOrientation(VoronoiDiagram,VoronoiDiagramInt,SiteSeq,NodeWriter,
                                        EdgeWriter,VoronoiStorage);
}// end of _cvConvert

static int _cvConvertSameOrientation(CvVoronoiDiagram2D* VoronoiDiagram,
                                      CvVoronoiDiagramInt VoronoiDiagramInt,
                                      CvSet* &NewSiteSeqPrev,
                                      CvSeqWriter &NodeWriter,
                                      CvSeqWriter &EdgeWriter,
                                      CvMemStorage* VoronoiStorage)
{
    CvSeq* SiteSeq = VoronoiDiagramInt.SiteSeq;
    CvSeq* EdgeSeq = VoronoiDiagramInt.EdgeSeq;
    CvSeq* NodeSeq = VoronoiDiagramInt.NodeSeq;


    CvMemStorage *NewSiteStorage = cvCreateChildMemStorage(VoronoiStorage);
    CvSet *NewSiteSeq = NULL,*CurrNewSiteSeq = NULL, *PrevNewSiteSeq = NULL;;
    CvSeqWriter SiteWriter; 

    CvVoronoiSite2D NewSite = {{0,0},{0,0},{0,0}},NewSite_prev;
    CvVoronoiSite2D *pNewSite, *pNewSite_prev = &NewSite_prev;
    pCvVoronoiSite pSite,pFirstSite;

    CvVoronoiEdge2D NewEdge = {{0,0},{0,0},{0,0,0,0}};
    CvVoronoiEdge2D *pNewEdge1, *pNewEdge2;
    pCvVoronoiEdge pEdge;

    CvVoronoiNode2D* pNode1, *pNode2;
    CvVoronoiNode2D Node;
    Node.next_free = NULL;

    for(CvSeq* CurrSiteSeq = SiteSeq;\
        CurrSiteSeq != NULL;\
        CurrSiteSeq = NEXT_SEQ(CurrSiteSeq,SiteSeq))
    {
        CurrNewSiteSeq = cvCreateSet(0,sizeof(CvSet),sizeof(CvVoronoiSite2D), NewSiteStorage);
        if(!NewSiteSeq)
            NewSiteSeq = PrevNewSiteSeq = CurrNewSiteSeq;
        else if(PrevNewSiteSeq->v_prev == NULL)
        {
            PrevNewSiteSeq->v_next = (CvSeq*)CurrNewSiteSeq;
            CurrNewSiteSeq->v_prev = (CvSeq*)PrevNewSiteSeq;
        }
        else
        {
            PrevNewSiteSeq->h_next = (CvSeq*)CurrNewSiteSeq;
            CurrNewSiteSeq->h_prev = (CvSeq*)PrevNewSiteSeq;
            CurrNewSiteSeq->v_prev = (CvSeq*)PrevNewSiteSeq->v_prev;
        }
        PrevNewSiteSeq = CurrNewSiteSeq;
        
        pSite = pFirstSite = (pCvVoronoiSite)cvGetSeqElem(CurrSiteSeq, 0);
        while(pSite->prev_site->node1 == pSite->prev_site->node2)\
            pSite = pSite->next_site;
        pFirstSite = pSite;
        
        pNewSite_prev = &NewSite_prev;
        cvStartAppendToSeq((CvSeq*)CurrNewSiteSeq, &SiteWriter);
        do
        {
            pNewSite = _cvWriteSeqElem(&NewSite,SiteWriter); 
            pNewSite->next[0] = pNewSite_prev;
            pNewSite_prev->next[1] = pNewSite;
            pEdge = pSite->edge1;
            if(!pEdge || !pEdge->node1 || !pEdge->node2)
                return 0;

            if(pEdge->site == NULL)
            {
                pNewEdge1 = (CvVoronoiEdge2D*)pEdge->twin_edge;
                pNewEdge1->site[1] = pNewSite;
                pNewSite->node[0] = pNewEdge1->node[0];
            }
            else
            {   
                pNewEdge1 = _cvWriteSeqElem(&NewEdge,EdgeWriter); 
                pNewEdge1->site[0] = pNewSite;

                pNode1 = _cvWriteSeqElem(&Node,NodeWriter);
                pNode2 = _cvWriteSeqElem(&Node,NodeWriter);
                pNode1->pt.x = pEdge->node1->node.x;
                pNode1->pt.y = pEdge->node1->node.y;
                pNode1->radius = pEdge->node1->radius;
                pNode2->pt.x = pEdge->node2->node.x;
                pNode2->pt.y = pEdge->node2->node.y;
                pNode2->radius = pEdge->node2->radius;
                pNewEdge1->node[0] = pNode1;
                pNewEdge1->node[1] = pNode2;

                pNewSite->node[0] = pNewEdge1->node[1];

                if(!pNewEdge1->node[0] || !pNewEdge1->node[1])
                    return 0;
                pEdge->twin_edge->site = NULL;
                pEdge->twin_edge->twin_edge = (pCvVoronoiEdge)pNewEdge1;
            }
            pNewSite->edge[1] = pNewEdge1;
            pEdge = pEdge->prev_edge;
            while((pEdge != NULL && CurrSiteSeq->total>1) ||
                  (pEdge != pSite->edge2 && CurrSiteSeq->total == 1))
            {
                if(pEdge->site == NULL)
                {
                    pNewEdge2 = (CvVoronoiEdge2D*)pEdge->twin_edge;
                    pNewEdge2->site[1] = pNewSite;
                    if(CV_VORONOIEDGE2D_BEGINNODE(pNewEdge1,pNewSite) != pNewEdge2->node[0])
                    {
                        cvFlushSeqWriter(&NodeWriter);
//                      cvSetRemove((CvSet*)VoronoiDiagram,VoronoiDiagram->total-1);
                        pNewEdge1->node[0] = pNewEdge2->node[0];
                    }
                }
                else
                {   
                    pNewEdge2 = _cvWriteSeqElem(&NewEdge,EdgeWriter); 
                    pNewEdge2->site[0] = pNewSite;

                    pNode1 = _cvWriteSeqElem(&Node,NodeWriter);
                    pNode1->pt.x = pEdge->node1->node.x;
                    pNode1->pt.y = pEdge->node1->node.y;
                    pNode1->radius = pEdge->node1->radius;
                    pNewEdge2->node[0] = pNode1;
                
                    if(pNewEdge1->site[0] == pNewSite)
                        pNewEdge2->node[1] = pNewEdge1->node[0];
                    else
                        pNewEdge2->node[1] = pNewEdge1->node[1];
                
                    if(!pNewEdge1->node[0] || !pNewEdge1->node[1])
                        return 0;
                    pEdge->twin_edge->site = NULL;
                    pEdge->twin_edge->twin_edge = (pCvVoronoiEdge)pNewEdge2;
                }
                if(pNewEdge1->site[0] == pNewSite)
                    pNewEdge1->next[2] = pNewEdge2;
                else
                    pNewEdge1->next[3] = pNewEdge2;
            
                if(pNewEdge2->site[0] == pNewSite)
                    pNewEdge2->next[0] = pNewEdge1;
                else
                    pNewEdge2->next[1] = pNewEdge1;

                pEdge = pEdge->prev_edge;
                pNewEdge1 = pNewEdge2;
            }
            pNewSite->edge[0] = pNewEdge1;
            pNewSite->node[1] = pNewEdge1->node[0];

            if(pSite->node1 == pSite->node2 && pSite != pSite->next_site && pNewEdge1->node[0] != pNewEdge1->node[1])
            {
                cvFlushSeqWriter(&NodeWriter);
//              cvSetRemove((CvSet*)VoronoiDiagram,VoronoiDiagram->total-1);
                pNewSite->node[1] = pNewEdge1->node[0] = pNewSite->node[0];
            }
            
            pNewSite_prev = pNewSite;
            pSite = pSite->next_site;
        }while(pSite != pFirstSite);
        pNewSite->node[1] = pNewEdge1->node[1];
        if(pSite == pSite->next_site)
        {
            Node.pt.x = pSite->node1->node.x;
            Node.pt.y = pSite->node1->node.y;
            Node.radius = 0;
            pNewSite->node[0] = pNewSite->node[1] = _cvWriteSeqElem(&Node,NodeWriter);
        }
        
        cvEndWriteSeq(&SiteWriter);
        pNewSite = (CvVoronoiSite2D*)cvGetSetElem(CurrNewSiteSeq, 0);
        pNewSite->next[0] = pNewSite_prev;
        pNewSite_prev->next[1] = pNewSite;
    }

    cvReleaseMemStorage(&(SiteSeq->storage));
    cvReleaseMemStorage(&(EdgeSeq->storage));
    cvReleaseMemStorage(&(NodeSeq->storage));

    if(NewSiteSeqPrev == NULL)
        VoronoiDiagram->sites = NewSiteSeq;
    else
    {
        NewSiteSeqPrev->h_next = (CvSeq*)NewSiteSeq;
        NewSiteSeq->h_prev = (CvSeq*)NewSiteSeqPrev;
    }

    NewSiteSeqPrev = NewSiteSeq;
    return 1;
}//end of _cvConvertSameOrientation

static int _cvConvertChangeOrientation(CvVoronoiDiagram2D* VoronoiDiagram,
                                        CvVoronoiDiagramInt VoronoiDiagramInt,
                                        CvSet* &NewSiteSeqPrev,
                                        CvSeqWriter &NodeWriter,
                                        CvSeqWriter &EdgeWriter,
                                        CvMemStorage* VoronoiStorage)
{
    // pNewSite->edge[1] = pSite->edge2
    // pNewSite->edge[0] = pSite->edge1
    
    CvSeq* SiteSeq = VoronoiDiagramInt.SiteSeq;
    CvSeq* EdgeSeq = VoronoiDiagramInt.EdgeSeq;
    CvSeq* NodeSeq = VoronoiDiagramInt.NodeSeq;


    CvMemStorage *NewSiteStorage = cvCreateChildMemStorage(VoronoiStorage);
    CvSet *NewSiteSeq = NULL,*CurrNewSiteSeq = NULL, *PrevNewSiteSeq = NULL;;
    CvSeqWriter SiteWriter; 

    CvVoronoiSite2D NewSite = {{0,0},{0,0},{0,0}},NewSite_prev;
    CvVoronoiSite2D *pNewSite, *pNewSite_prev = &NewSite_prev;
    pCvVoronoiSite pSite,pFirstSite;

    CvVoronoiEdge2D NewEdge = {{0,0},{0,0},{0,0,0,0}};
    CvVoronoiEdge2D *pNewEdge1, *pNewEdge2;
    pCvVoronoiEdge pEdge;

    CvVoronoiNode2D* pNode1, *pNode2;
    CvVoronoiNode2D Node;
    Node.next_free = NULL;

    for(CvSeq* CurrSiteSeq = SiteSeq;\
        CurrSiteSeq != NULL;\
        CurrSiteSeq = NEXT_SEQ(CurrSiteSeq,SiteSeq))
    {
        CurrNewSiteSeq = cvCreateSet(0,sizeof(CvSet),sizeof(CvVoronoiSite2D), NewSiteStorage);
        if(!NewSiteSeq)
            NewSiteSeq = PrevNewSiteSeq = CurrNewSiteSeq;
        else if(PrevNewSiteSeq->v_prev == NULL)
        {
            PrevNewSiteSeq->v_next = (CvSeq*)CurrNewSiteSeq;
            CurrNewSiteSeq->v_prev = (CvSeq*)PrevNewSiteSeq;
        }
        else
        {
            PrevNewSiteSeq->h_next = (CvSeq*)CurrNewSiteSeq;
            CurrNewSiteSeq->h_prev = (CvSeq*)PrevNewSiteSeq;
            CurrNewSiteSeq->v_prev = (CvSeq*)PrevNewSiteSeq->v_prev;
        }
        PrevNewSiteSeq = CurrNewSiteSeq;
        
        pSite = (pCvVoronoiSite)cvGetSeqElem(CurrSiteSeq, 0);
        while(pSite->next_site->node1 == pSite->next_site->node2)\
            pSite = pSite->next_site;
        pFirstSite = pSite;
        
        pNewSite_prev = &NewSite_prev;
        cvStartAppendToSeq((CvSeq*)CurrNewSiteSeq, &SiteWriter);
        do
        {
            pNewSite = _cvWriteSeqElem(&NewSite,SiteWriter); 
            pNewSite->next[0] = pNewSite_prev;
            pNewSite_prev->next[1] = pNewSite;

            pEdge = pSite->edge2;
            if(!pEdge || !pEdge->node1 || !pEdge->node2)
                return 0;

            if(pEdge->site == NULL)
            {
                pNewEdge1 = (CvVoronoiEdge2D*)pEdge->twin_edge;
                pNewEdge1->site[1] = pNewSite;
                pNewSite->node[0] = pNewEdge1->node[0];
            }
            else
            {   
                pNewEdge1 = _cvWriteSeqElem(&NewEdge,EdgeWriter); 
                pNewEdge1->site[0] = pNewSite;
                
                pNode1 = _cvWriteSeqElem(&Node,NodeWriter);
                pNode2 = _cvWriteSeqElem(&Node,NodeWriter);
                pNode1->pt.x = pEdge->node1->node.x;
                pNode1->pt.y = pEdge->node1->node.y;
                pNode1->radius = pEdge->node1->radius;
                pNode2->pt.x = pEdge->node2->node.x;
                pNode2->pt.y = pEdge->node2->node.y;
                pNode2->radius = pEdge->node2->radius;
                pNewEdge1->node[0] = pNode2;
                pNewEdge1->node[1] = pNode1;

                pNewSite->node[0] = pNewEdge1->node[1];

                if(!pNewEdge1->node[0] || !pNewEdge1->node[1])
                    return 0;
                pEdge->twin_edge->site = NULL;
                pEdge->twin_edge->twin_edge = (pCvVoronoiEdge)pNewEdge1;
            }
            pNewSite->edge[1] = pNewEdge1;
            

            pEdge = pEdge->next_edge;
            while((pEdge != NULL && CurrSiteSeq->total>1) ||
                  (pEdge != pSite->edge1 && CurrSiteSeq->total == 1))
            {
                if(pEdge->site == NULL)
                {
                    pNewEdge2 = (CvVoronoiEdge2D*)pEdge->twin_edge;
                    pNewEdge2->site[1] = pNewSite;
                    if(CV_VORONOIEDGE2D_BEGINNODE(pNewEdge1,pNewSite) != pNewEdge2->node[0])
                    {
                        cvFlushSeqWriter(&NodeWriter);
//                      cvSetRemove((CvSet*)VoronoiDiagram,VoronoiDiagram->total-1);
                        pNewEdge1->node[0] = pNewEdge2->node[0];
                    }
                }
                else
                {   
                    pNewEdge2 = _cvWriteSeqElem(&NewEdge,EdgeWriter); 
                    pNewEdge2->site[0] = pNewSite;

                    pNode2 = _cvWriteSeqElem(&Node,NodeWriter);
                    pNode2->pt.x = pEdge->node2->node.x;
                    pNode2->pt.y = pEdge->node2->node.y;
                    pNode2->radius = pEdge->node2->radius;
                    pNewEdge2->node[0] = pNode2;

                    if(pNewEdge1->site[0] == pNewSite)
                        pNewEdge2->node[1] = pNewEdge1->node[0];
                    else
                        pNewEdge2->node[1] = pNewEdge1->node[1];
                
                    if(!pNewEdge1->node[0] || !pNewEdge1->node[1])
                        return 0;
                    pEdge->twin_edge->site = NULL;
                    pEdge->twin_edge->twin_edge = (pCvVoronoiEdge)pNewEdge2;
                }
                if(pNewEdge1->site[0] == pNewSite)
                    pNewEdge1->next[2] = pNewEdge2;
                else
                    pNewEdge1->next[3] = pNewEdge2;

                if(pNewEdge2->site[0] == pNewSite)
                    pNewEdge2->next[0] = pNewEdge1;
                else
                    pNewEdge2->next[1] = pNewEdge1;

                pEdge = pEdge->next_edge;
                pNewEdge1 = pNewEdge2;
            }
            pNewSite->edge[0] = pNewEdge1;
            pNewSite->node[1] = pNewEdge1->node[0];

            if(pSite->node1 == pSite->node2 && pSite != pSite->next_site && pNewEdge1->node[0] != pNewEdge1->node[1])
            {
                cvFlushSeqWriter(&NodeWriter);
//              cvSetRemove((CvSet*)VoronoiDiagram,VoronoiDiagram->total-1);
                pNewSite->node[1] = pNewEdge1->node[0] = pNewSite->node[0];
            }

            pNewSite_prev = pNewSite;
            pSite = pSite->prev_site;
        }while(pSite != pFirstSite);
        pNewSite->node[1] = pNewEdge1->node[1];
        if(pSite == pSite->next_site)
        {
            Node.pt.x = pSite->node1->node.x;
            Node.pt.y = pSite->node1->node.y;
            Node.radius = 0;
            pNewSite->node[0] = pNewSite->node[1] = _cvWriteSeqElem(&Node,NodeWriter);
        }
        
        cvEndWriteSeq(&SiteWriter);
        pNewSite = (CvVoronoiSite2D*)cvGetSetElem(CurrNewSiteSeq, 0);
        pNewSite->next[0] = pNewSite_prev;
        pNewSite_prev->next[1] = pNewSite;
    }

    cvReleaseMemStorage(&(SiteSeq->storage));
    cvReleaseMemStorage(&(EdgeSeq->storage));
    cvReleaseMemStorage(&(NodeSeq->storage));

    if(NewSiteSeqPrev == NULL)
        VoronoiDiagram->sites = NewSiteSeq;
    else
    {
        NewSiteSeqPrev->h_next = (CvSeq*)NewSiteSeq;
        NewSiteSeq->h_prev = (CvSeq*)NewSiteSeqPrev;
    }
    NewSiteSeqPrev = NewSiteSeq;
    return 1;
}//end of _cvConvert

template<class T>
int _cvConstructExtSites(CvVoronoiDiagramInt* pVoronoiDiagram,
                         CvSeq* ContourSeq,
                         int orientation,
                         T type)
{ 
    const double angl_eps = 0.03;
    CvSeq* SiteSeq = pVoronoiDiagram->SiteSeq;
    CvSeq* NodeSeq = pVoronoiDiagram->NodeSeq;
    //CvSeq* DirectionSeq = pVoronoiDiagram->DirectionSeq;
    CvPointFloat Vertex1,Vertex2,Vertex3;
    CvLeePoint<T> VertexT1,VertexT2,VertexT3;
    
    CvSeqReader ContourReader;
    CvVoronoiSiteInt Site = {NULL,NULL,NULL,NULL,NULL,NULL,NULL};
    CvVoronoiSiteInt SiteTemp = {NULL,NULL,NULL,NULL,NULL,NULL,NULL};
    CvVoronoiNodeInt Node;
    pCvVoronoiNode pNode1,pNode2;
    pCvVoronoiSite pSite = &SiteTemp,pSite_prev = &SiteTemp;
    pCvVoronoiSite pReflexSite = NULL;
    int NReflexSite = 0;
    
    float delta_x_rc, delta_x_cl, delta_y_rc, delta_y_cl;
    float norm_cl,norm_rc, mult_cl_rc;
    float _sin, _cos;
    int i;

    if(orientation == 1)
    {
        cvStartReadSeq(ContourSeq, &ContourReader,0);
        CV_READ_SEQ_ELEM(VertexT1,ContourReader);
        CV_READ_SEQ_ELEM(VertexT2,ContourReader);
    }
    else
    {
        cvStartReadSeq(ContourSeq, &ContourReader,1); 
        CV_REV_READ_SEQ_ELEM(VertexT1,ContourReader);
        CV_REV_READ_SEQ_ELEM(VertexT2,ContourReader);   
    }
    
    Vertex1.x = (float)VertexT1.x;
    Vertex1.y = (float)VertexT1.y;
    Vertex2.x = (float)VertexT2.x;
    Vertex2.y = (float)VertexT2.y;

    _cvInitVoronoiNode(&Node, &Vertex2);
    pNode1 = _cvSeqPush(NodeSeq, &Node);

    delta_x_cl = Vertex2.x - Vertex1.x;
    delta_y_cl = Vertex2.y - Vertex1.y;
    norm_cl = (float)sqrt((double)delta_x_cl*delta_x_cl + delta_y_cl*delta_y_cl);

    for( i = 0;i<ContourSeq->total;i++)
    {
        if(orientation == 1)
        {
            CV_READ_SEQ_ELEM(VertexT3,ContourReader);
        }
        else
        {
            CV_REV_READ_SEQ_ELEM(VertexT3,ContourReader);
        }

        Vertex3.x = (float)VertexT3.x;
        Vertex3.y = (float)VertexT3.y;

        _cvInitVoronoiNode(&Node, &Vertex3);
        pNode2 = _cvSeqPush(NodeSeq, &Node);

        delta_x_rc = Vertex3.x - Vertex2.x;
        delta_y_rc = Vertex3.y - Vertex2.y;
        norm_rc = (float)sqrt((double)delta_x_rc*delta_x_rc + delta_y_rc*delta_y_rc);
        if(norm_rc==0)
            continue;
    
        mult_cl_rc = norm_cl*norm_rc;
        _sin = (delta_y_rc* delta_x_cl - delta_x_rc* delta_y_cl)/mult_cl_rc;
        _cos = -(delta_x_cl*delta_x_rc + delta_y_cl*delta_y_rc)/mult_cl_rc;

        if((_sin > angl_eps) || (_sin > 0 && _cos > 0))
        {
            pSite = _cvSeqPush(SiteSeq, &Site);
            _cvInitVoronoiSite(pSite,pNode1,pNode2,pSite_prev);
            pSite_prev->next_site = pSite;
        }
        else if((_sin < -angl_eps) || (_sin < 0 && _cos > 0))
        {
            pSite = _cvSeqPush(SiteSeq, &Site);
            _cvInitVoronoiSite(pSite,pNode1,pNode1,pSite_prev);
            pReflexSite = pSite; 
            NReflexSite++;
            pSite_prev->next_site = pSite;

            pSite_prev = pSite;
            pSite = _cvSeqPush(SiteSeq, &Site);
            _cvInitVoronoiSite(pSite,pNode1,pNode2,pSite_prev);
            pSite_prev->next_site = pSite;
        }
        else
        {
            Vertex2 = Vertex3;
            delta_y_rc = delta_y_cl + delta_y_rc;
            delta_x_rc = delta_x_cl + delta_x_rc;
            pSite->node2 = pNode2;
        
            norm_rc = (float)sqrt((double)delta_y_rc*delta_y_rc + delta_x_rc*delta_x_rc);
        }
        Vertex2=Vertex3;
        delta_y_cl= delta_y_rc;
        delta_x_cl= delta_x_rc;
        norm_cl = norm_rc;
        pSite_prev = pSite;
        pNode1 = pNode2;
    }

    if(SiteTemp.next_site==NULL)
        return 0;

    if(ContourSeq->total - NReflexSite<2)
        return 0;

    if(SiteSeq->total<3)
        return 0;

    pSite->node2 = SiteTemp.next_site->node1;
    pSite->next_site = SiteTemp.next_site;
    SiteTemp.next_site->prev_site = pSite;

    i = 0;
    if(pReflexSite!=NULL)
        for(i=0; i<SiteSeq->total; i++)
        {
            if(pReflexSite->next_site->next_site->node1 !=
              pReflexSite->next_site->next_site->node2)
              break;
            else
                pReflexSite = pReflexSite->next_site->next_site;
        }
    pVoronoiDiagram->reflex_site = pReflexSite;
    return (i<SiteSeq->total);
}//end of _cvConstructExtSites

template<class T>
int _cvConstructIntSites(CvVoronoiDiagramInt* pVoronoiDiagram,
                                 CvSeq* CurrSiteSeq,
                                 CvSeq* CurrContourSeq, 
                                 pCvVoronoiSite &pTopSite,
                                 int orientation,
                                 T type)
{ 
    const double angl_eps = 0.03;
    float min_x = (float)999999999;
    CvSeq* SiteSeq = CurrSiteSeq;
    CvSeq* NodeSeq = pVoronoiDiagram->NodeSeq;
    //CvSeq* DirectionSeq = pVoronoiDiagram->DirectionSeq;
    CvPointFloat Vertex1,Vertex2,Vertex3;
    CvLeePoint<T> VertexT1,VertexT2,VertexT3;
    
    CvSeqReader ContourReader;
    CvVoronoiSiteInt Site = {NULL,NULL,NULL,NULL,NULL,NULL,NULL};
    CvVoronoiSiteInt SiteTemp = {NULL,NULL,NULL,NULL,NULL,NULL,NULL};
    CvVoronoiNodeInt Node;
    pCvVoronoiNode pNode1,pNode2;
    pCvVoronoiSite pSite = &SiteTemp,pSite_prev = &SiteTemp;
    pTopSite = NULL;
    int NReflexSite = 0;
    
    if(CurrContourSeq->total == 1)
    {
        cvStartReadSeq(CurrContourSeq, &ContourReader,0); 
        CV_READ_SEQ_ELEM(VertexT1,ContourReader); 
        Vertex1.x = (float)VertexT1.x;
        Vertex1.y = (float)VertexT1.y;

        _cvInitVoronoiNode(&Node, &Vertex1);
        pNode1 = _cvSeqPush(NodeSeq, &Node);
        pTopSite = pSite = _cvSeqPush(SiteSeq, &Site);
        _cvInitVoronoiSite(pSite,pNode1,pNode1,pSite);
        pSite->next_site = pSite;
        return 1;
    }

    float delta_x_rc, delta_x_cl, delta_y_rc, delta_y_cl;
    float norm_cl,norm_rc, mult_cl_rc;
    float _sin, _cos;
    int i;

    if(orientation == 1)
    {
        cvStartReadSeq(CurrContourSeq, &ContourReader,0);
        CV_READ_SEQ_ELEM(VertexT1,ContourReader);
        CV_READ_SEQ_ELEM(VertexT2,ContourReader);
    }
    else
    {
        cvStartReadSeq(CurrContourSeq, &ContourReader,1); 
        CV_REV_READ_SEQ_ELEM(VertexT1,ContourReader);
        CV_REV_READ_SEQ_ELEM(VertexT2,ContourReader);   
    }

    Vertex1.x = (float)VertexT1.x;
    Vertex1.y = (float)VertexT1.y;
    Vertex2.x = (float)VertexT2.x;
    Vertex2.y = (float)VertexT2.y;

    _cvInitVoronoiNode(&Node, &Vertex2);
    pNode1 = _cvSeqPush(NodeSeq, &Node);

    delta_x_cl = Vertex2.x - Vertex1.x;
    delta_y_cl = Vertex2.y - Vertex1.y;
    norm_cl = (float)sqrt((double)delta_x_cl*delta_x_cl + delta_y_cl*delta_y_cl);
    for( i = 0;i<CurrContourSeq->total;i++)
    {
        if(orientation == 1)
        {
            CV_READ_SEQ_ELEM(VertexT3,ContourReader);
        }
        else
        {
            CV_REV_READ_SEQ_ELEM(VertexT3,ContourReader);
        }
        Vertex3.x = (float)VertexT3.x;
        Vertex3.y = (float)VertexT3.y;
    
        _cvInitVoronoiNode(&Node, &Vertex3);
        pNode2 = _cvSeqPush(NodeSeq, &Node);

        delta_x_rc = Vertex3.x - Vertex2.x;
        delta_y_rc = Vertex3.y - Vertex2.y;
        norm_rc = (float)sqrt((double)delta_x_rc*delta_x_rc + delta_y_rc*delta_y_rc);
        if(norm_rc==0)
            continue;

        mult_cl_rc = norm_cl*norm_rc;
        _sin = (delta_y_rc* delta_x_cl - delta_x_rc* delta_y_cl)/mult_cl_rc;
        _cos = -(delta_x_cl*delta_x_rc + delta_y_cl*delta_y_rc)/mult_cl_rc;
        if((_sin > angl_eps) || (_sin > 0 && _cos > 0))
        {
            pSite = _cvSeqPush(SiteSeq, &Site);
            _cvInitVoronoiSite(pSite,pNode1,pNode2,pSite_prev);
            pSite_prev->next_site = pSite;
        }
        else if((_sin < -angl_eps) || (_sin < 0 && _cos > 0) || (_sin == 0 && CurrContourSeq->total == 2))
        {
            pSite = _cvSeqPush(SiteSeq, &Site);
            _cvInitVoronoiSite(pSite,pNode1,pNode1,pSite_prev);
            if(pNode1->node.x < min_x)
            {
                min_x = pNode1->node.x;
                pTopSite = pSite; 
            }
            NReflexSite++;
            pSite_prev->next_site = pSite;

            pSite_prev = pSite;
            pSite = _cvSeqPush(SiteSeq, &Site);
            _cvInitVoronoiSite(pSite,pNode1,pNode2,pSite_prev);
            pSite_prev->next_site = pSite;
        }
        else
        {
            Vertex2 = Vertex3;
            delta_y_rc = delta_y_cl + delta_y_rc;
            delta_x_rc = delta_x_cl + delta_x_rc;
            norm_rc = (float)sqrt((double)delta_y_rc*delta_y_rc + delta_x_rc*delta_x_rc);
            pSite->node2 = pNode2;
        }

        Vertex1=Vertex2;
        Vertex2=Vertex3;
        delta_y_cl= delta_y_rc;
        delta_x_cl= delta_x_rc;
        norm_cl = norm_rc;
        pSite_prev = pSite;
        pNode1 = pNode2;
    }

    if(SiteTemp.next_site==NULL)
        return 0;

    if(NReflexSite < 3 && CurrContourSeq->total > 2 || NReflexSite < 2)
        return 0;

    pSite->node2 = SiteTemp.next_site->node1;
    pSite->next_site = SiteTemp.next_site;
    SiteTemp.next_site->prev_site = pSite;

    return 1;
}//end of _cvConstructIntSites

static int _cvConstructExtChains(CvVoronoiDiagramInt* pVoronoiDiagram)
{
    CvSeq* SiteSeq = pVoronoiDiagram->SiteSeq;
    CvSeq* ChainSeq = pVoronoiDiagram->ChainSeq;

    CvVoronoiChainInt Chain;
    pCvVoronoiChain pChain,pChainFirst;
    pCvVoronoiSite pSite, pSite_prev, pSiteFirst,pReflexSite = pVoronoiDiagram->reflex_site;

    if(pReflexSite==NULL)
        pSite = pSiteFirst = (pCvVoronoiSite)cvGetSeqElem(SiteSeq, 0);
    else
    {
        while(pReflexSite->next_site->next_site->node1==
              pReflexSite->next_site->next_site->node2)
            pReflexSite = pReflexSite->next_site->next_site;

        pSite = pSiteFirst = pReflexSite->next_site;
    }
    
    Chain.last_site = pSite;
    _cvConstructEdges(pSite,pVoronoiDiagram);
    pSite_prev = pSite;
    pSite = pSite->prev_site;
    do
    {
        if(pSite->node1!=pSite->node2)
        {
            Chain.first_site = pSite_prev;
            pChain = _cvSeqPushFront(ChainSeq,&Chain);

            _cvConstructEdges(pSite,pVoronoiDiagram);
            Chain.last_site = pSite;
            Chain.next_chain = pChain;
        }
        else
        {
            pSite=pSite->prev_site;
            _cvConstructEdges(pSite,pVoronoiDiagram);
            _cvConstructEdges(pSite->next_site,pVoronoiDiagram);
        }
        pSite_prev = pSite;
        pSite = pSite->prev_site;
    }while(pSite!=pSiteFirst);

    Chain.first_site = pSite_prev;
    pChain = _cvSeqPushFront(ChainSeq,&Chain);
    pChainFirst = (pCvVoronoiChain)cvGetSeqElem(ChainSeq,ChainSeq->total - 1);
    pChainFirst->next_chain = pChain;
    if(ChainSeq->total < 3)
        return 0;
    else
        return 1;
}// end of _cvConstructExtChains

static void _cvConstructIntChains(CvVoronoiDiagramInt* pVoronoiDiagram,
                                   CvSeq* CurrChainSeq,
                                   CvSeq* CurrSiteSeq,
                                   pCvVoronoiSite pTopSite)
{
    CvSeq* ChainSeq = CurrChainSeq;

    if(CurrSiteSeq->total == 1)
        return;

    CvVoronoiChainInt Chain = {NULL,NULL,NULL};
    pCvVoronoiChain pChain,pChainFirst;;
    pCvVoronoiSite pSite, pSite_prev, pSiteFirst;
    pSite = pSiteFirst = pTopSite->next_site;

    Chain.last_site = pSite;
    _cvConstructEdges(pSite,pVoronoiDiagram);
    pSite_prev = pSite;
    pSite = pSite->prev_site;
    do
    {
        if(pSite->node1!=pSite->node2)
        {
            Chain.first_site = pSite_prev;
            pChain = _cvSeqPushFront(ChainSeq,&Chain);

            _cvConstructEdges(pSite,pVoronoiDiagram);
            Chain.last_site = pSite;
            Chain.next_chain = pChain;
        }
        else
        {
            pSite=pSite->prev_site;
            if(pSite != pSiteFirst)
                _cvConstructEdges(pSite,pVoronoiDiagram);
            _cvConstructEdges(pSite->next_site,pVoronoiDiagram);
        }
        pSite_prev = pSite;
        pSite = pSite->prev_site;
    }while(pSite!=pSiteFirst && pSite!= pSiteFirst->prev_site);

    if(pSite == pSiteFirst->prev_site && ChainSeq->total == 0)
        return;
    
    Chain.first_site = pSite_prev;
    if(pSite == pSiteFirst->prev_site)
    {
        pChainFirst = (pCvVoronoiChain)cvGetSeqElem(ChainSeq,ChainSeq->total - 1);
        pChainFirst->last_site = Chain.last_site;
        pChainFirst->next_chain = Chain.next_chain;
        return;
    }
    else
    {
        pChain = _cvSeqPushFront(ChainSeq,&Chain);
        pChainFirst = (pCvVoronoiChain)cvGetSeqElem(ChainSeq,ChainSeq->total - 1);
        pChainFirst->next_chain = pChain;
        return;
    }
}// end of _cvConstructIntChains

CV_INLINE void _cvConstructEdges(pCvVoronoiSite pSite,CvVoronoiDiagramInt* pVoronoiDiagram)
{
    CvSeq* EdgeSeq = pVoronoiDiagram->EdgeSeq;
    CvSeq* DirectionSeq = pVoronoiDiagram->DirectionSeq;
    CvVoronoiEdgeInt Edge = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
    pCvVoronoiEdge pEdge1,pEdge2;
    CvDirection EdgeDirection,SiteDirection;
    float site_lengh;

    Edge.site = pSite;
    if(pSite->node1!=pSite->node2)
    {
        SiteDirection.x = pSite->node2->node.x - pSite->node1->node.x;
        SiteDirection.y = pSite->node2->node.y - pSite->node1->node.y;
        site_lengh = (float)sqrt((double)SiteDirection.x*SiteDirection.x + SiteDirection.y * SiteDirection.y);
        SiteDirection.x /= site_lengh;
        SiteDirection.y /= site_lengh;
        EdgeDirection.x = -SiteDirection.y;
        EdgeDirection.y = SiteDirection.x;
        Edge.direction = _cvSeqPush(DirectionSeq,&EdgeDirection);
        pSite->direction = _cvSeqPush(DirectionSeq,&SiteDirection);

        pEdge1 = _cvSeqPush(EdgeSeq,&Edge);
        pEdge2 = _cvSeqPush(EdgeSeq,&Edge);
    }
    else
    {
        pCvVoronoiSite pSite_prev = pSite->prev_site;
        pCvVoronoiSite pSite_next = pSite->next_site;

        pEdge1 = _cvSeqPush(EdgeSeq,&Edge);
        pEdge2 = _cvSeqPush(EdgeSeq,&Edge);
    
        pEdge2->direction = pSite_next->edge1->direction;
        pEdge2->twin_edge = pSite_next->edge1;
        pSite_next->edge1->twin_edge = pEdge2;

        pEdge1->direction = pSite_prev->edge2->direction;
        pEdge1->twin_edge = pSite_prev->edge2;
        pSite_prev->edge2->twin_edge = pEdge1;
    }

        pEdge2->node1 = pSite->node2;
        pEdge1->node2 = pSite->node1;
        pSite->edge1 = pEdge1;
        pSite->edge2 = pEdge2;
        pEdge2->next_edge = pEdge1;
        pEdge1->prev_edge = pEdge2;
}// end of _cvConstructEdges

static int _cvConstructExtVD(CvVoronoiDiagramInt* pVoronoiDiagram)
{
    pCvVoronoiSite pSite_right = 0,pSite_left = 0;
    pCvVoronoiEdge pEdge_left,pEdge_right;
    pCvVoronoiChain pChain1, pChain2;

    pChain1 = (pCvVoronoiChain)cvGetSeqElem(pVoronoiDiagram->ChainSeq,0);
    do
    {
        pChain2 = pChain1->next_chain;
        if(pChain2->next_chain==pChain1)
        {
            pSite_right = pChain1->first_site;
            pSite_left = pChain2->last_site;
            pEdge_left = pSite_left->edge2->next_edge;
            pEdge_right = pSite_right->edge1->prev_edge;
            pEdge_left->prev_edge = NULL;
            pEdge_right->next_edge = NULL;
            pSite_right->edge1 = NULL;
            pSite_left->edge2 = NULL;
        }

        if(!_cvJoinChains(pChain1,pChain2,pVoronoiDiagram))
            return 0;

        pChain1->last_site = pChain2->last_site;
        pChain1->next_chain = pChain2->next_chain;
        pChain1 = pChain1->next_chain;
    }while(pChain1->next_chain != pChain1);

    pCvVoronoiNode pEndNode = pSite_left->node2;
    if(pSite_right->edge1==NULL)
        return 0;
    else
        pSite_right->edge1->node2 = pEndNode;

    if(pSite_left->edge2==NULL)
        return 0;
    else
        pSite_left->edge2->node1 = pEndNode;

    return 1;
}//end of _cvConstructExtVD

static int _cvJoinChains(pCvVoronoiChain pChain1, 
                          pCvVoronoiChain pChain2,
                          CvVoronoiDiagramInt* pVoronoiDiagram)
{
    const double dist_eps = 0.05;
    if(pChain1->first_site == NULL || pChain1->last_site == NULL || 
        pChain2->first_site == NULL || pChain2->last_site == NULL) 
        return 0;

    CvVoronoiEdgeInt EdgeNULL = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};

    CvSeq* NodeSeq = pVoronoiDiagram->NodeSeq;
    CvSeq* EdgeSeq = pVoronoiDiagram->EdgeSeq;
        
    pCvVoronoiSite pSite_left = pChain1->last_site;
    pCvVoronoiSite pSite_right = pChain2->first_site;
    
    pCvVoronoiEdge pEdge_left = pSite_left->edge2->next_edge;
    pCvVoronoiEdge pEdge_right = pSite_right->edge1->prev_edge;

    pCvVoronoiEdge pEdge_left_cur = pEdge_left;
    pCvVoronoiEdge pEdge_right_cur = pEdge_right;

    pCvVoronoiEdge pEdge_left_prev = NULL;
    pCvVoronoiEdge pEdge_right_next = NULL;

    pCvVoronoiNode pNode_siteleft = pChain1->first_site->node1;
    pCvVoronoiNode pNode_siteright = pChain2->last_site->node2;
    /*CvVoronoiSiteInt Site_left_chain = {pNode_siteleft,pNode_siteleft,NULL,NULL,NULL,NULL};
    CvVoronoiSiteInt Site_right_chain = {pNode_siteright,pNode_siteright,NULL,NULL,NULL,NULL};*/

    pCvVoronoiEdge pEdge1,pEdge2;
    CvPointFloat Point1 = {0,0}, Point2 = {0,0};
    
    float radius1,radius2,dist1,dist2;
    bool left = true,right = true;

    CvVoronoiNodeInt Node;
    pCvVoronoiNode pNode_begin = pSite_left->node2;

    pEdge1 = pSite_left->edge2;
    pEdge1->node2 = NULL;
    pEdge2 = pSite_right->edge1;
    pEdge2->node1 = NULL;
    
    for(;;)
    {
        
        if(left)
            pEdge1->node1 = pNode_begin;
        if(right)
            pEdge2->node2 = pNode_begin;

        pEdge_left = pEdge_left_cur;
        pEdge_right = pEdge_right_cur;

        if(left&&right)
        {
            _cvCalcEdge(pSite_left,pSite_right,pEdge1,pVoronoiDiagram);
            _cvMakeTwinEdge(pEdge2,pEdge1);
            _cvStickEdgeLeftBegin(pEdge1,pEdge_left_prev,pSite_left);
            _cvStickEdgeRightBegin(pEdge2,pEdge_right_next,pSite_right);
        }
        else if(!left)
        {
            _cvCalcEdge(pNode_siteleft,pSite_right,pEdge2,pVoronoiDiagram);
            _cvStickEdgeRightBegin(pEdge2,pEdge_right_next,pSite_right);
        }
        else if(!right)
        {
            _cvCalcEdge(pSite_left,pNode_siteright,pEdge1,pVoronoiDiagram);
            _cvStickEdgeLeftBegin(pEdge1,pEdge_left_prev,pSite_left);
        }

        dist1=dist2=-1;
        radius1 = -1; radius2 = -2;

        while(pEdge_left!=NULL)
        {
            if(pEdge_left->node2 == NULL)
            {
                pEdge_left_cur = pEdge_left = pEdge_left->next_edge;
                if(pEdge_left == NULL)
                    break;
            }
            
            if(left)
                dist1 = _cvCalcEdgeIntersection(pEdge1, pEdge_left, &Point1,radius1);
            else
                dist1 = _cvCalcEdgeIntersection(pEdge2, pEdge_left, &Point1,radius1);

            if(dist1>=0)
                break;

            pEdge_left = pEdge_left->next_edge;
        }
    
        while(pEdge_right!=NULL)
        {
            if(pEdge_right->node1 == NULL)
            {
                pEdge_right_cur = pEdge_right = pEdge_right->prev_edge;
                if(pEdge_right == NULL)
                    break;
            }
        
            if(left)
                dist2 = _cvCalcEdgeIntersection(pEdge1, pEdge_right, &Point2, radius2);
            else
                dist2 = _cvCalcEdgeIntersection(pEdge2, pEdge_right, &Point2, radius2);

            if(dist2>=0)
                break;
    
            pEdge_right = pEdge_right->prev_edge;
        }

        if(dist1<0&&dist2<0)
        {
            if(left)
            {
                pEdge_left = pSite_left->edge1;
                if(pEdge_left==NULL)
                    _cvStickEdgeLeftEnd(pEdge1,NULL,pSite_left);
                else
                {
                    while(pEdge_left->node1!=NULL
                        &&pEdge_left->node1==pEdge_left->prev_edge->node2)
                    {
                        pEdge_left = pEdge_left->prev_edge;
                        if(pEdge_left==NULL || pEdge_left->prev_edge == NULL)
                            return 0;
                    }
                    _cvStickEdgeLeftEnd(pEdge1,pEdge_left,pSite_left);
                }
            }
            if(right)
            {
                pEdge_right = pSite_right->edge2;
                if(pEdge_right==NULL)
                    _cvStickEdgeRightEnd(pEdge2,NULL,pSite_right);
                else
                {
                    while(pEdge_right->node2!=NULL
                        &&  pEdge_right->node2==pEdge_right->next_edge->node1)
                    {
                        pEdge_right = pEdge_right->next_edge;
                        if(pEdge_right==NULL || pEdge_right->next_edge == NULL )
                            return 0;
                    }
                    _cvStickEdgeRightEnd(pEdge2,pEdge_right,pSite_right);
                }
            }
            return 1;
        }

        if(fabs(dist1 - dist2)<dist_eps)
        {           
            pNode_begin = _cvSeqPush(NodeSeq,&Node);
            _cvInitVoronoiNode(pNode_begin, &Point2,radius2);
            
            pEdge1->node2 = pNode_begin;
            pEdge2->node1 = pNode_begin;

            _cvStickEdgeLeftEnd(pEdge1,pEdge_left,pSite_left);
            _cvTwinNULLLeft(pEdge_left_cur,pEdge_left);
            
            _cvStickEdgeRightEnd(pEdge2,pEdge_right,pSite_right);
            _cvTwinNULLRight(pEdge_right_cur,pEdge_right);

            if(pEdge_left->twin_edge!=NULL&&pEdge_right->twin_edge!=NULL)
            {
                pEdge_left_prev = pEdge_left->twin_edge;
                if(!pEdge_left_prev)
                    return 0;
                pEdge_left_cur = pEdge_left_prev->next_edge;
                pEdge_right_next = pEdge_right->twin_edge;
                if(!pEdge_right_next)
                    return 0;
                pEdge_right_cur = pEdge_right_next->prev_edge;
                pSite_right = pEdge_right_next->site;
                pEdge2 = _cvSeqPush(EdgeSeq, &EdgeNULL);
                pSite_left = pEdge_left_prev->site;
                pEdge1 = _cvSeqPush(EdgeSeq, &EdgeNULL);
                continue;
            }

            if(pEdge_left->twin_edge==NULL&&pEdge_right->twin_edge!=NULL)
            {
                pEdge_right_next = pEdge_right->twin_edge;
                if(!pEdge_right_next)
                    return 0;
                pEdge_right_cur = pEdge_right_next->prev_edge;
                pSite_right = pEdge_right_next->site;
                pEdge2 = _cvSeqPush(EdgeSeq, &EdgeNULL);
                pEdge_left_cur = NULL;
                left = false;
                continue;
            }

            if(pEdge_left->twin_edge!=NULL&&pEdge_right->twin_edge==NULL)
            {
                pEdge_left_prev = pEdge_left->twin_edge;
                if(!pEdge_left_prev)
                    return 0;
                pEdge_left_cur = pEdge_left_prev->next_edge;
                pSite_left = pEdge_left_prev->site;
                pEdge1 = _cvSeqPush(EdgeSeq, &EdgeNULL);
                pEdge_right_cur = NULL;
                right = false;
                continue;
            }
            if(pEdge_left->twin_edge==NULL&&pEdge_right->twin_edge==NULL)
                return 1;
        }

        if((dist1<dist2&&dist1>=0)||(dist1>=0&&dist2<0))
        {

            pNode_begin = _cvSeqPush(NodeSeq,&Node);
            _cvInitVoronoiNode(pNode_begin, &Point1,radius1);
            pEdge1->node2 = pNode_begin;
            _cvTwinNULLLeft(pEdge_left_cur,pEdge_left); 
            _cvStickEdgeLeftEnd(pEdge1,pEdge_left,pSite_left);
            if(right)
            {
                pEdge2->node1 = pNode_begin;
                pEdge_right_next = pEdge2;
                pEdge2 = _cvSeqPush(EdgeSeq, &EdgeNULL);
                if(pEdge_left->twin_edge!=NULL)
                {
                    pEdge_left_prev = pEdge_left->twin_edge;
                    if(!pEdge_left_prev)
                        return 0;
                    pEdge_left_cur = pEdge_left_prev->next_edge;
                    pSite_left = pEdge_left_prev->site;
                    pEdge1 = _cvSeqPush(EdgeSeq, &EdgeNULL);
                    continue;
                }
                else
                {
                    pEdge_left_cur = NULL;
                    left = false;
                    continue;
                }
            }
            else
            {
                if(pEdge_left->twin_edge!=NULL)
                {
                    pEdge_left_prev = pEdge_left->twin_edge;
                    if(!pEdge_left_prev)
                        return 0;
                    pEdge_left_cur = pEdge_left_prev->next_edge;
                    pSite_left = pEdge_left_prev->site;
                    pEdge1 = _cvSeqPush(EdgeSeq, &EdgeNULL);
                    continue;
                }
                else
                    return 1;

            }
        
        }

        if((dist1>dist2&&dist2>=0)||(dist1<0&&dist2>=0))
        {
            pNode_begin = _cvSeqPush(NodeSeq,&Node);
            _cvInitVoronoiNode(pNode_begin, &Point2,radius2);
            pEdge2->node1 = pNode_begin;
            _cvTwinNULLRight(pEdge_right_cur,pEdge_right);
            _cvStickEdgeRightEnd(pEdge2,pEdge_right,pSite_right);
            if(left)
            {
                pEdge1->node2 = pNode_begin;
                pEdge_left_prev = pEdge1;
                pEdge1 = _cvSeqPush(EdgeSeq, &EdgeNULL);
                if(pEdge_right->twin_edge!=NULL)
                {
                    pEdge_right_next = pEdge_right->twin_edge;
                    if(!pEdge_right_next)
                        return 0;
                    pEdge_right_cur = pEdge_right_next->prev_edge;
                    pSite_right = pEdge_right_next->site;
                    pEdge2 = _cvSeqPush(EdgeSeq, &EdgeNULL);
                    continue;
                }
                else
                {
                    pEdge_right_cur = NULL;
                    right = false;
                    continue;
                }
            }
            else
            {
                if(pEdge_right->twin_edge!=NULL)
                {
                    pEdge_right_next = pEdge_right->twin_edge;
                    if(!pEdge_right_next)
                        return 0;
                    pEdge_right_cur = pEdge_right_next->prev_edge;
                    pSite_right = pEdge_right_next->site;
                    pEdge2 = _cvSeqPush(EdgeSeq, &EdgeNULL);
                    continue;
                }
                else
                    return 1;
            }

        }
    }

}// end of _cvJoinChains

static void _cvFindNearestSite(CvVoronoiDiagramInt* pVoronoiDiagram)
{
    pCvVoronoiHole pCurrHole, pHole = pVoronoiDiagram->top_hole;
    pCvPointFloat pTopPoint,pPoint1,pPoint2;
    CvPointFloat Direction;
    pCvVoronoiSite pSite;
    CvVoronoiNodeInt Node;
    CvSeq* CurrSeq;
    float min_distance,distance;
    int i;
    for(;pHole != NULL; pHole = pHole->next_hole)
    {
        if(pHole->error)
            continue;
        pTopPoint = &pHole->site_top->node1->node;
        pCurrHole = NULL;
        CurrSeq = pVoronoiDiagram->SiteSeq;
        min_distance = (float)3e+34;
        while(pCurrHole != pHole)
        {
            if(pCurrHole && pCurrHole->error)
                continue;
            pSite = (pCvVoronoiSite)cvGetSeqElem(CurrSeq,0);
            if(CurrSeq->total == 1)
            {
                distance = _cvCalcDist(pTopPoint, pSite);
                if(distance < min_distance)
                {
                    min_distance = distance;
                    pHole->site_nearest = pSite;
                }
            }
            else
            {
                for(i = 0; i < CurrSeq->total;i++, pSite = pSite->next_site)
                {
                    if(pSite->node1 != pSite->node2)
                    {
                        pPoint1 = &pSite->node1->node;
                        pPoint2 = &pSite->node2->node;
                        
                        Direction.x = -pSite->direction->y;
                        Direction.y = pSite->direction->x;
                                        
                        if(
                                 (pTopPoint->x - pPoint2->x)*Direction.y - 
                                 (pTopPoint->y - pPoint2->y)*Direction.x > 0
                            ||
                                 (pTopPoint->x - pPoint1->x)*Direction.y - 
                                 (pTopPoint->y - pPoint1->y)*Direction.x < 0
                            ||
                                 (pTopPoint->x - pPoint1->x)*pSite->direction->y -  
                                 (pTopPoint->y - pPoint1->y)*pSite->direction->x > 0
                           )                       
                                continue;

                        distance = _cvCalcDist(pTopPoint, pSite);
                    }
                    else
                    {
                        pPoint1 = &pSite->node1->node;
                        if(
                                 (pTopPoint->x - pPoint1->x)*pSite->edge2->direction->y - 
                                 (pTopPoint->y - pPoint1->y)*pSite->edge2->direction->x > 0
                            ||
                                 (pTopPoint->x - pPoint1->x)*pSite->edge1->direction->y -  
                                 (pTopPoint->y - pPoint1->y)*pSite->edge1->direction->x < 0
                           )                       
                                continue;

                        distance = _cvCalcDist(pTopPoint, pSite);
                    }
                    
                    
                    if(distance < min_distance)
                    {
                        min_distance = distance;
                        pHole->site_nearest = pSite;
                    }
                }
            }
            
            if(pCurrHole == NULL)
                pCurrHole = pVoronoiDiagram->top_hole;
            else
                pCurrHole = pCurrHole->next_hole;
            
            CurrSeq = pCurrHole->SiteSeq;
        }
        pHole->x_coord = min_distance; 
        
        if(pHole->site_nearest->node1 == pHole->site_nearest->node2)
        {
            Direction.x = (pHole->site_nearest->node1->node.x - pHole->site_top->node1->node.x)/2;
            Direction.y = (pHole->site_nearest->node1->node.y - pHole->site_top->node1->node.y)/2;
        }
        else
        {
            
            Direction.x = pHole->site_nearest->direction->y * min_distance / 2;
            Direction.y = - pHole->site_nearest->direction->x * min_distance / 2;
        }

        Node.node.x = pHole->site_top->node1->node.x + Direction.x;
        Node.node.y = pHole->site_top->node1->node.y + Direction.y;
        pHole->node = _cvSeqPush(pVoronoiDiagram->NodeSeq, &Node);
    }
}//end of _cvFindNearestSite

static void _cvConstructIntVD(CvVoronoiDiagramInt* pVoronoiDiagram)
{
    pCvVoronoiChain pChain1, pChain2;
    pCvVoronoiHole pHole;
    int i;
    
    pHole = pVoronoiDiagram->top_hole;

    for(;pHole != NULL; pHole = pHole->next_hole)
    {
        if(pHole->ChainSeq->total == 0)
            continue;
        pChain1 = (pCvVoronoiChain)cvGetSeqElem(pHole->ChainSeq,0);
        for(i = pHole->ChainSeq->total; i > 0;i--)
        {
            pChain2 = pChain1->next_chain;
            if(!_cvJoinChains(pChain1,pChain2,pVoronoiDiagram))
            {
                pHole->error = true;
                break;
            }

            pChain1->last_site = pChain2->last_site;
            pChain1->next_chain = pChain2->next_chain;
            pChain1 = pChain1->next_chain;
        }
    }
}// end of _cvConstructIntVD

static int _cvFindOppositSiteCW(pCvVoronoiHole pHole, CvVoronoiDiagramInt* pVoronoiDiagram)
{
    pCvVoronoiSite pSite_left = pHole->site_nearest;
    pCvVoronoiSite pSite_right = pHole->site_top;
    pCvVoronoiNode pNode = pHole->node;

    CvDirection Direction = {-1,0};
    CvVoronoiEdgeInt Edge_right = {NULL,pSite_right->node1,pSite_right,NULL,NULL,NULL,NULL,&Direction};

    pCvVoronoiEdge pEdge_left = pSite_left->edge2->next_edge;
    pCvVoronoiEdge pEdge_right = &Edge_right;

    CvVoronoiEdgeInt Edge = {NULL,pNode,pSite_right,NULL,NULL,NULL,NULL,NULL};
    CvVoronoiEdgeInt Edge_cur = {NULL,NULL,NULL,NULL,NULL,NULL,NULL};
    pCvVoronoiEdge pEdge = &Edge;
    
    float radius1, radius2,dist1, dist2;
    CvPointFloat Point1 = {0,0}, Point2 = {0,0};

    for(;;)
    {
        pEdge->direction = NULL;
        pEdge->parabola = NULL;
        _cvCalcEdge(pSite_left,pSite_right,pEdge,pVoronoiDiagram);

        dist1=dist2=-1;
        radius1 = -1; radius2 = -2;
        while(pEdge_left!=NULL)
        {
            dist1 = _cvCalcEdgeIntersection(pEdge, pEdge_left, &Point1,radius1);
            if(dist1>=0)
                break;
            pEdge_left = pEdge_left->next_edge;
        }
    
        dist2 = _cvCalcEdgeIntersection(pEdge, pEdge_right, &Point2, radius2);
        if(dist2>=0 && dist1 >= dist2)
        {
            pHole->site_opposite = pSite_left;
            pNode->node = Point2;
            return 1;
        }

        if(dist1<0)
            return 0;

        Edge_cur = *pEdge_left->twin_edge;
        Edge_cur.node1 = pNode;
        pEdge_left = &Edge_cur;

        pSite_left = pEdge_left->site;
        pNode->node = Point1;
    }
}//end of _cvFindOppositSiteCW

static int _cvFindOppositSiteCCW(pCvVoronoiHole pHole,CvVoronoiDiagramInt* pVoronoiDiagram)
{
    pCvVoronoiSite pSite_right = pHole->site_nearest;
    pCvVoronoiSite pSite_left = pHole->site_top;
    pCvVoronoiNode pNode = pHole->node;

    CvDirection Direction = {-1,0};
    CvVoronoiEdgeInt Edge_left = {pSite_left->node1,NULL,pSite_left,NULL,NULL,NULL, NULL, &Direction};
    
    pCvVoronoiEdge pEdge_left = &Edge_left;
    pCvVoronoiEdge pEdge_right = pSite_right->edge1->prev_edge;

    CvVoronoiEdgeInt Edge = {NULL,pNode,pSite_left,NULL,NULL,NULL,NULL};
    CvVoronoiEdgeInt Edge_cur = {NULL,NULL,NULL,NULL,NULL,NULL,NULL};
    pCvVoronoiEdge pEdge = &Edge;
    
    double dist1, dist2;
    float radius1, radius2;
    CvPointFloat Point1 = {0,0}, Point2 = {0,0};

    for(;;)
    {
        pEdge->direction = NULL;
        pEdge->parabola = NULL;
        _cvCalcEdge(pSite_left,pSite_right,pEdge,pVoronoiDiagram);
    
        dist1=dist2=-1;
        radius1 = -1; radius2 = -2;
        while(pEdge_right!=NULL)
        {
            dist1 = _cvCalcEdgeIntersection(pEdge, pEdge_right, &Point1,radius2);
            if(dist1>=0)
                break;
            pEdge_right = pEdge_right->prev_edge;
        }
    
        dist2 = _cvCalcEdgeIntersection(pEdge, pEdge_left, &Point2, radius1);
        if(dist2>=0 && dist1 > dist2)
        {
            pHole->site_opposite = pSite_right;
            pNode->node = Point2;
            return 1;
        }

        if(dist1<0)
            return 0;

        Edge_cur = *pEdge_right->twin_edge;
        Edge_cur.node2 = pNode;
        pEdge_right = &Edge_cur;

        pSite_right = pEdge_right->site;
        pNode->node = Point1;
    }
}//end of _cvFindOppositSiteCCW

static int _cvMergeVD(pCvVoronoiHole pHole,CvVoronoiDiagramInt* pVoronoiDiagram)
{
    pCvVoronoiSite pSite_left_first = pHole->site_top;
    pCvVoronoiSite pSite_right_first = pHole->site_opposite;
    pCvVoronoiNode pNode_begin = pHole->node;
    if(pSite_left_first == NULL || pSite_right_first == NULL || pNode_begin == NULL)
        return 0;

    pCvVoronoiSite pSite_left = pSite_left_first;
    pCvVoronoiSite pSite_right = pSite_right_first;

    const double dist_eps = 0.05;
    CvVoronoiEdgeInt EdgeNULL = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};

    CvSeq* NodeSeq = pVoronoiDiagram->NodeSeq;
    CvSeq* EdgeSeq = pVoronoiDiagram->EdgeSeq;
        
    pCvVoronoiEdge pEdge_left = NULL;
    if(pSite_left->edge2 != NULL)
        pEdge_left = pSite_left->edge2->next_edge;

    pCvVoronoiEdge pEdge_right = pSite_right->edge1;
    pCvVoronoiEdge pEdge_left_cur = pEdge_left;
    pCvVoronoiEdge pEdge_right_cur = pEdge_right;

    pCvVoronoiEdge pEdge_left_prev = NULL;
    pCvVoronoiEdge pEdge_right_next = NULL;

    pCvVoronoiEdge pEdge1,pEdge2,pEdge1_first, pEdge2_first;
    CvPointFloat Point1 = {0,0}, Point2 = {0,0};
    
    float radius1,radius2,dist1,dist2;
    
    CvVoronoiNodeInt Node;

    pEdge1_first = pEdge1 = _cvSeqPush(EdgeSeq, &EdgeNULL);;
    pEdge2_first = pEdge2 = _cvSeqPush(EdgeSeq, &EdgeNULL);;
    pEdge1->site = pSite_left_first;
    pEdge2->site = pSite_right_first;

    do
    {
        pEdge1->node1 = pEdge2->node2 = pNode_begin;
        
        pEdge_left = pEdge_left_cur;
        pEdge_right = pEdge_right_cur->prev_edge;

        _cvCalcEdge(pSite_left,pSite_right,pEdge1,pVoronoiDiagram);
        _cvMakeTwinEdge(pEdge2,pEdge1);
        
        if(pEdge_left_prev != NULL)
            _cvStickEdgeLeftBegin(pEdge1,pEdge_left_prev,pSite_left);
        if(pEdge_right_next != NULL)
            _cvStickEdgeRightBegin(pEdge2,pEdge_right_next,pSite_right);
            
        dist1=dist2=-1;
        radius1 = -1; radius2 = -2;

//LEFT:
        while(pEdge_left!=NULL)
        {
            if(pEdge_left->node2 == NULL)
                pEdge_left_cur = pEdge_left = pEdge_left->next_edge;

            dist1 = _cvCalcEdgeIntersection(pEdge1, pEdge_left, &Point1,radius1);
            if(dist1>=0)
                goto RIGHT;
            pEdge_left = pEdge_left->next_edge;
        }

RIGHT:  
        while(pEdge_right!=NULL)
        {
            dist2 = _cvCalcEdgeIntersection(pEdge1, pEdge_right, &Point2,radius2);
            if(dist2>=0)
                goto RESULTHANDLING;
        
            pEdge_right = pEdge_right->prev_edge;
        }
        pEdge_right = pEdge_right_cur;
        dist2 = _cvCalcEdgeIntersection(pEdge1, pEdge_right, &Point2, radius2);

RESULTHANDLING:
        if(dist1<0&&dist2<0)
            return 0;
    
        if(fabs(dist1 - dist2)<dist_eps)
        {
            pNode_begin = _cvSeqPush(NodeSeq,&Node);
            _cvInitVoronoiNode(pNode_begin, &Point2,radius2);
        
            pEdge1->node2 = pNode_begin;
            pEdge2->node1 = pNode_begin;

            pEdge_right_cur = _cvDivideRightEdge(pEdge_right,pNode_begin,EdgeSeq);

            _cvStickEdgeLeftEnd(pEdge1,pEdge_left,pSite_left);
            _cvStickEdgeRightEnd(pEdge2,pEdge_right,pSite_right);
        
            pEdge_left_prev = pEdge_left->twin_edge;
            if(!pEdge_left_prev)
                return 0;
            pEdge_left_cur = pEdge_left_prev->next_edge;
            pSite_left = pEdge_left_prev->site;
            pEdge1 = _cvSeqPush(EdgeSeq, &EdgeNULL);
                
            pEdge_right_next = pEdge_right->twin_edge;
            if(!pEdge_right_next)
                return 0;
            pSite_right = pEdge_right_next->site;
            pEdge2 = _cvSeqPush(EdgeSeq, &EdgeNULL);

            continue;
        }

        if((dist1<dist2&&dist1>=0)||(dist1>=0&&dist2<0))
        {
            pNode_begin = _cvSeqPush(NodeSeq,&Node);
            _cvInitVoronoiNode(pNode_begin, &Point1,radius1);
            
            pEdge1->node2 = pNode_begin;
            _cvStickEdgeLeftEnd(pEdge1,pEdge_left,pSite_left);
        
            pEdge2->node1 = pNode_begin;
            pEdge_right_next = pEdge2;
            pEdge2 = _cvSeqPush(EdgeSeq, &EdgeNULL);
            
            pEdge_left_prev = pEdge_left->twin_edge;
            if(!pEdge_left_prev)
                return 0;
            pEdge_left_cur = pEdge_left_prev->next_edge;
            pSite_left = pEdge_left_prev->site;
            pEdge1 = _cvSeqPush(EdgeSeq, &EdgeNULL);

            continue;
        }

        if((dist1>dist2&&dist2>=0)||(dist1<0&&dist2>=0))
        {
            pNode_begin = _cvSeqPush(NodeSeq,&Node);
            _cvInitVoronoiNode(pNode_begin, &Point2,radius2);
    
            pEdge_right_cur = _cvDivideRightEdge(pEdge_right,pNode_begin,EdgeSeq);

            pEdge2->node1 = pNode_begin;
            _cvStickEdgeRightEnd(pEdge2,pEdge_right,pSite_right);
        
            pEdge1->node2 = pNode_begin;
            pEdge_left_prev = pEdge1;
            pEdge1 = _cvSeqPush(EdgeSeq, &EdgeNULL);
            
            pEdge_right_next = pEdge_right->twin_edge;
            if(!pEdge_right_next)
                return 0;
            pSite_right = pEdge_right_next->site;
            pEdge2 = _cvSeqPush(EdgeSeq, &EdgeNULL);

            continue;
        }

    }while(!(pSite_left == pSite_left_first && pSite_right == pSite_right_first));

        pEdge1_first->node1 = pNode_begin;
        pEdge2_first->node2 = pNode_begin; 
        _cvStickEdgeLeftBegin(pEdge1_first,pEdge_left_prev,pSite_left_first);
        _cvStickEdgeRightBegin(pEdge2_first,pEdge_right_next,pSite_right_first);

        if(pSite_left_first->edge2 == NULL)
            pSite_left_first->edge2 = pSite_left_first->edge1 = pEdge1_first;
        return 1;
}// end of _cvMergeVD


/*////////////////////////////////////////////////////////////////////////////////////////
//                               Computation of bisectors                               //
////////////////////////////////////////////////////////////////////////////////////////*/

void _cvCalcEdge(pCvVoronoiSite pSite_left, 
                 pCvVoronoiSite pSite_right,
                 pCvVoronoiEdge pEdge,
                 CvVoronoiDiagramInt* pVoronoiDiagram)
{
    if((pSite_left->node1!=pSite_left->node2)&&
        (pSite_right->node1!=pSite_right->node2))
        _cvCalcEdgeLL(pSite_left->direction,
                     pSite_right->direction,
                     pEdge,pVoronoiDiagram);

    else if((pSite_left->node1!=pSite_left->node2)&&
            (pSite_right->node1 == pSite_right->node2))
        _cvCalcEdgeLP(pSite_left,pSite_right->node1,pEdge,pVoronoiDiagram);

    else if((pSite_left->node1==pSite_left->node2)&&
            (pSite_right->node1!=pSite_right->node2))
        _cvCalcEdgePL(pSite_left->node1,pSite_right,pEdge,pVoronoiDiagram);

    else
        _cvCalcEdgePP(&(pSite_left->node1->node),
                     &(pSite_right->node1->node),
                     pEdge,pVoronoiDiagram);
}//end of _cvCalcEdge

void _cvCalcEdge(pCvVoronoiSite pSite,
                 pCvVoronoiNode pNode,
                 pCvVoronoiEdge pEdge,
                 CvVoronoiDiagramInt* pVoronoiDiagram)
{
    if(pSite->node1!=pSite->node2)
        _cvCalcEdgeLP(pSite, pNode, pEdge,pVoronoiDiagram);
    else
        _cvCalcEdgePP(&(pSite->node1->node),
                     &pNode->node,
                     pEdge,pVoronoiDiagram);
}//end of _cvCalcEdge

void _cvCalcEdge(pCvVoronoiNode pNode, 
                         pCvVoronoiSite pSite,
                         pCvVoronoiEdge pEdge,
                         CvVoronoiDiagramInt* pVoronoiDiagram)
{
    if(pSite->node1!=pSite->node2)
        _cvCalcEdgePL(pNode,pSite,pEdge,pVoronoiDiagram);
    else
        _cvCalcEdgePP(&pNode->node,&pSite->node1->node,pEdge,pVoronoiDiagram);
}//end of _cvCalcEdge

CV_INLINE
void _cvCalcEdgeLL(pCvDirection pDirection1,
                  pCvDirection pDirection2,
                  pCvVoronoiEdge pEdge,
                  CvVoronoiDiagramInt* pVoronoiDiagram)
{
    CvDirection Direction = {pDirection2->x - pDirection1->x, pDirection2->y - pDirection1->y};
    if((fabs(Direction.x)<LEE_CONST_ZERO)&&(fabs(Direction.y)<LEE_CONST_ZERO))
            Direction = *pDirection2;
    pEdge->direction = _cvSeqPush(pVoronoiDiagram->DirectionSeq,&Direction);;
}//end of _cvCalcEdgeLL

CV_INLINE
void _cvCalcEdgePP(pCvPointFloat pPoint1,
                  pCvPointFloat pPoint2,
                  pCvVoronoiEdge pEdge,
                  CvVoronoiDiagramInt* pVoronoiDiagram)
{
    CvDirection Direction = {pPoint1->y - pPoint2->y,pPoint2->x - pPoint1->x};
    pEdge->direction = _cvSeqPush(pVoronoiDiagram->DirectionSeq,&Direction);
}//end of _cvCalcEdgePP

CV_INLINE
void _cvCalcEdgePL(pCvVoronoiNode pFocus,
                  pCvVoronoiSite pDirectrice,
                  pCvVoronoiEdge pEdge,
                  CvVoronoiDiagramInt* pVoronoiDiagram)
{
    pCvPointFloat pPoint0 = &pFocus->node;
    pCvPointFloat pPoint1 = &pDirectrice->node1->node; 

    CvDirection Vector01 = {pPoint0->x - pPoint1->x,pPoint0->y - pPoint1->y};
    float half_h = (Vector01.y*pDirectrice->direction->x - Vector01.x*pDirectrice->direction->y)/2;
    CvDirection Normal = {-pDirectrice->direction->y,pDirectrice->direction->x};
    if(half_h < LEE_CONST_ZERO)
    {
        pEdge->direction = _cvSeqPush(pVoronoiDiagram->DirectionSeq,&Normal);
        return;
    }
    CvVoronoiParabolaInt Parabola;
    pCvVoronoiParabola  pParabola = _cvSeqPush(pVoronoiDiagram->ParabolaSeq,&Parabola);
    float* map = pParabola->map; 
        
    map[1] = Normal.x;
    map[4] = Normal.y;
    map[0] = Normal.y;
    map[3] = -Normal.x;
    map[2] = pPoint0->x - Normal.x*half_h;
    map[5] = pPoint0->y - Normal.y*half_h;

    pParabola->a = 1/(4*half_h);
    pParabola->focus = pFocus;
    pParabola->directrice = pDirectrice;
    pEdge->parabola = pParabola;
}//end of _cvCalcEdgePL

CV_INLINE
void _cvCalcEdgeLP(pCvVoronoiSite pDirectrice,
                  pCvVoronoiNode pFocus,
                  pCvVoronoiEdge pEdge,
                  CvVoronoiDiagramInt* pVoronoiDiagram)
{
    pCvPointFloat pPoint0 = &pFocus->node;
    pCvPointFloat pPoint1 = &pDirectrice->node1->node; 

    CvDirection Vector01 = {pPoint0->x - pPoint1->x,pPoint0->y - pPoint1->y};
    float half_h = (Vector01.y*pDirectrice->direction->x - Vector01.x*pDirectrice->direction->y)/2;
    CvDirection Normal = {-pDirectrice->direction->y,pDirectrice->direction->x};
    if(half_h < LEE_CONST_ZERO)
    {
        pEdge->direction = _cvSeqPush(pVoronoiDiagram->DirectionSeq,&Normal);
        return;
    }
    CvVoronoiParabolaInt Parabola;
    pCvVoronoiParabola  pParabola = _cvSeqPush(pVoronoiDiagram->ParabolaSeq,&Parabola);
    float* map = pParabola->map; 

    map[1] = Normal.x;
    map[4] = Normal.y;
    map[0] = -Normal.y;
    map[3] = Normal.x;
    map[2] = pPoint0->x - Normal.x*half_h;
    map[5] = pPoint0->y - Normal.y*half_h;
    
    pParabola->a = 1/(4*half_h);
    pParabola->focus = pFocus;
    pParabola->directrice = pDirectrice;
    pEdge->parabola = pParabola;
}//end of _cvCalcEdgeLP

/*////////////////////////////////////////////////////////////////////////////////////////
//                  Computation of intersections of bisectors                           //
////////////////////////////////////////////////////////////////////////////////////////*/

static 
float _cvCalcEdgeIntersection(pCvVoronoiEdge pEdge1,
                              pCvVoronoiEdge pEdge2,
                              CvPointFloat* pPoint,
                              float &Radius)
{
    if((pEdge1->parabola==NULL)&&(pEdge2->parabola==NULL))
        return _cvLine_LineIntersection(pEdge1,pEdge2,pPoint,Radius);
    if((pEdge1->parabola==NULL)&&(pEdge2->parabola!=NULL)) 
        return _cvLine_ParIntersection(pEdge1,pEdge2,pPoint,Radius);
    if((pEdge1->parabola!=NULL)&&(pEdge2->parabola==NULL))
        return _cvPar_LineIntersection(pEdge1,pEdge2,pPoint,Radius);
    if((pEdge1->parabola!=NULL)&&(pEdge2->parabola!=NULL))
        return _cvPar_ParIntersection(pEdge1,pEdge2,pPoint,Radius);
    return -1;
}//end of _cvCalcEdgeIntersection

static
float _cvLine_LineIntersection(pCvVoronoiEdge pEdge1,
                                pCvVoronoiEdge pEdge2,
                                pCvPointFloat  pPoint,
                                float &Radius)
{
    if(((pEdge1->node1 == pEdge2->node1 ||
        pEdge1->node1 == pEdge2->node2) &&
        pEdge1->node1 != NULL)||
       ((pEdge1->node2 == pEdge2->node1 ||
        pEdge1->node2 == pEdge2->node2) &&
        pEdge1->node2 != NULL))
        return -1;

    CvPointFloat Point1,Point3;
    float det;
    float k,m;
    float x21,x43,y43,y21,x31,y31;

    if(pEdge1->node1!=NULL)
    {
        Point1.x = pEdge1->node1->node.x;
        Point1.y = pEdge1->node1->node.y;
    }
    else
    {
        Point1.x = pEdge1->node2->node.x;
        Point1.y = pEdge1->node2->node.y;
    }
    x21 = pEdge1->direction->x;
    y21 = pEdge1->direction->y;

    if(pEdge2->node2==NULL)
    {
        Point3.x = pEdge2->node1->node.x;
        Point3.y = pEdge2->node1->node.y;
        x43 = pEdge2->direction->x;
        y43 = pEdge2->direction->y;
    
    }
    else if(pEdge2->node1==NULL)
    {
        Point3.x = pEdge2->node2->node.x;
        Point3.y = pEdge2->node2->node.y;
        x43 = pEdge2->direction->x;
        y43 = pEdge2->direction->y;
    }
    else
    {
        Point3.x = pEdge2->node1->node.x;
        Point3.y = pEdge2->node1->node.y;
        x43 = pEdge2->node2->node.x - Point3.x;
        y43 = pEdge2->node2->node.y - Point3.y;
    }

    x31 = Point3.x - Point1.x;
    y31 = Point3.y - Point1.y;

    det = y21*x43 - x21*y43;
    if(fabs(det) < LEE_CONST_ZERO)
        return -1;

    k = (x43*y31 - y43*x31)/det;
    m = (x21*y31 - y21*x31)/det;

    if(k<-LEE_CONST_ACCEPTABLE_ERROR||m<-LEE_CONST_ACCEPTABLE_ERROR)
        return -1;
    if(((pEdge1->node2!=NULL)&&(pEdge1->node1!=NULL))&&(k>1.f+LEE_CONST_ACCEPTABLE_ERROR))
        return -1;
    if(((pEdge2->node2!=NULL)&&(pEdge2->node1!=NULL))&&(m>1.f+LEE_CONST_ACCEPTABLE_ERROR))
        return -1;
    
    pPoint->x = (float)(k*x21) + Point1.x;
    pPoint->y = (float)(k*y21) + Point1.y; 
    
    Radius = _cvCalcDist(pPoint,pEdge1->site);
    return _cvPPDist(pPoint,&Point1);;
}//end of _cvLine_LineIntersection

static
float _cvLine_ParIntersection(pCvVoronoiEdge pEdge1,
                                pCvVoronoiEdge pEdge2,
                                pCvPointFloat  pPoint,
                                float &Radius)
{
    if(pEdge2->node1==NULL||pEdge2->node2==NULL)
        return _cvLine_OpenParIntersection(pEdge1,pEdge2,pPoint,Radius);
    else
        return _cvLine_CloseParIntersection(pEdge1,pEdge2,pPoint,Radius);
}//end of _cvLine_ParIntersection

static
float _cvLine_OpenParIntersection(pCvVoronoiEdge pEdge1,
                                pCvVoronoiEdge pEdge2,
                                pCvPointFloat  pPoint,
                                float &Radius)
{
    int IntersectionNumber = 1;
    if(((pEdge1->node1 == pEdge2->node1 ||
        pEdge1->node1 == pEdge2->node2) &&
        pEdge1->node1 != NULL)||
       ((pEdge1->node2 == pEdge2->node1 ||
        pEdge1->node2 == pEdge2->node2) &&
        pEdge1->node2 != NULL))
        IntersectionNumber = 2;
    
    pCvPointFloat pRayPoint1; 
    if(pEdge1->node1!=NULL)
        pRayPoint1 = &(pEdge1->node1->node);
    else
        pRayPoint1 = &(pEdge1->node2->node);

    pCvDirection pDirection = pEdge1->direction;
    float* Parabola = pEdge2->parabola->map;

    pCvPointFloat pParPoint1;
    if(pEdge2->node1==NULL)
        pParPoint1 = &(pEdge2->node2->node);
    else 
        pParPoint1 = &(pEdge2->node1->node);
    
    float InversParabola[6];
    _cvCalcOrtogInverse(InversParabola, Parabola);

    CvPointFloat  Point,ParPoint1_img,RayPoint1_img;
    CvDirection Direction_img;
    _cvCalcPointImage(&RayPoint1_img, pRayPoint1, InversParabola);
    _cvCalcVectorImage(&Direction_img,pDirection, InversParabola);

    float c2 = pEdge2->parabola->a*Direction_img.x;
    float c1 = -Direction_img.y;
    float c0 = Direction_img.y* RayPoint1_img.x - Direction_img.x*RayPoint1_img.y;
    float X[2];
    int N = _cvSolveEqu2thR(c2,c1,c0,X);
    if(N==0)
        return -1;

    _cvCalcPointImage(&ParPoint1_img, pParPoint1, InversParabola);
    int sign_x = SIGN(Direction_img.x);
    int sign_y = SIGN(Direction_img.y);
    if(N==1)
    {
        if(X[0]<ParPoint1_img.x - LEE_CONST_ACCEPTABLE_ERROR)
            return -1;
        float pr0 = (X[0]-RayPoint1_img.x)*sign_x + \
                    (pEdge2->parabola->a*X[0]*X[0]-RayPoint1_img.y)*sign_y;
        if(pr0 <= -LEE_CONST_ACCEPTABLE_ERROR)
            return -1;
    }
    else
    {
        if(X[1]<ParPoint1_img.x - LEE_CONST_ACCEPTABLE_ERROR)
            return -1;
        float pr0 = (X[0]-RayPoint1_img.x)*sign_x + \
                        (pEdge2->parabola->a*X[0]*X[0]-RayPoint1_img.y)*sign_y;
        float pr1 = (X[1]-RayPoint1_img.x)*sign_x + \
                        (pEdge2->parabola->a*X[1]*X[1]-RayPoint1_img.y)*sign_y;

        if(pr0 <= -LEE_CONST_ACCEPTABLE_ERROR &&pr1 <= -LEE_CONST_ACCEPTABLE_ERROR)
            return -1;

        if(pr0 >- LEE_CONST_ACCEPTABLE_ERROR && pr1 >- LEE_CONST_ACCEPTABLE_ERROR)
        {
            if(X[0] >= ParPoint1_img.x - LEE_CONST_ACCEPTABLE_ERROR)
            {
                if(pr0>pr1)
                    _cvSwap(X[0],X[1]);
            }
            else
            {
                N=1;
                X[0] = X[1];
            }
        }
        else if(pr0 >- LEE_CONST_ACCEPTABLE_ERROR)
        {
            N = 1;
            if(X[0] < ParPoint1_img.x - LEE_CONST_ACCEPTABLE_ERROR)
                return -1;
        }
        else if(pr1 >- LEE_CONST_ACCEPTABLE_ERROR)
        {
            N=1;
            X[0] = X[1];
        }
        else
            return -1;
    }

    Point.x = X[(N-1)*(IntersectionNumber - 1)];
    Point.y = pEdge2->parabola->a*Point.x*Point.x;

    Radius = Point.y + 1.f/(4*pEdge2->parabola->a);
    _cvCalcPointImage(pPoint,&Point,Parabola);
    float dist = _cvPPDist(pPoint, pRayPoint1);
    if(IntersectionNumber == 2 && dist < LEE_CONST_DIFF_POINTS)
        return -1;
    else
        return dist;
}// end of _cvLine_OpenParIntersection

static
float _cvLine_CloseParIntersection(pCvVoronoiEdge pEdge1,
                                pCvVoronoiEdge pEdge2,
                                pCvPointFloat  pPoint,
                                float &Radius)
{
    int IntersectionNumber = 1;
    if(((pEdge1->node1 == pEdge2->node1 ||
        pEdge1->node1 == pEdge2->node2) &&
        pEdge1->node1 != NULL)||
       ((pEdge1->node2 == pEdge2->node1 ||
        pEdge1->node2 == pEdge2->node2) &&
        pEdge1->node2 != NULL))
        IntersectionNumber = 2;

    pCvPointFloat pRayPoint1; 
    if(pEdge1->node1!=NULL)
        pRayPoint1 = &(pEdge1->node1->node);
    else
        pRayPoint1 = &(pEdge1->node2->node);

    pCvDirection pDirection = pEdge1->direction;
    float* Parabola = pEdge2->parabola->map;

    pCvPointFloat pParPoint1,pParPoint2;
    pParPoint2 = &(pEdge2->node1->node);
    pParPoint1 = &(pEdge2->node2->node);


    float InversParabola[6];
    _cvCalcOrtogInverse(InversParabola, Parabola);

    CvPointFloat  Point,ParPoint1_img,ParPoint2_img,RayPoint1_img;
    CvDirection Direction_img;
    _cvCalcPointImage(&RayPoint1_img, pRayPoint1, InversParabola);
    _cvCalcVectorImage(&Direction_img,pDirection, InversParabola);

    float c2 = pEdge2->parabola->a*Direction_img.x;
    float c1 = -Direction_img.y;
    float c0 = Direction_img.y* RayPoint1_img.x - Direction_img.x*RayPoint1_img.y;
    float X[2];
    int N = _cvSolveEqu2thR(c2,c1,c0,X);
    if(N==0)
        return -1;
    
    _cvCalcPointImage(&ParPoint1_img, pParPoint1, InversParabola);
    _cvCalcPointImage(&ParPoint2_img, pParPoint2, InversParabola);
    if(ParPoint1_img.x>ParPoint2_img.x)
        _cvSwap(ParPoint1_img,ParPoint2_img);
    int sign_x = SIGN(Direction_img.x);
    int sign_y = SIGN(Direction_img.y);
    if(N==1)
    {
        if((X[0]<ParPoint1_img.x - LEE_CONST_ACCEPTABLE_ERROR) ||
           (X[0]>ParPoint2_img.x + LEE_CONST_ACCEPTABLE_ERROR))
            return -1;
        float pr0 = (X[0]-RayPoint1_img.x)*sign_x + \
                    (pEdge2->parabola->a*X[0]*X[0]-RayPoint1_img.y)*sign_y;
        if(pr0 <= -LEE_CONST_ACCEPTABLE_ERROR)
            return -1;
    }
    else
    {
        if((X[1]<ParPoint1_img.x - LEE_CONST_ACCEPTABLE_ERROR) ||
           (X[0]>ParPoint2_img.x + LEE_CONST_ACCEPTABLE_ERROR))
            return -1;

        if((X[0]<ParPoint1_img.x - LEE_CONST_ACCEPTABLE_ERROR) &&
           (X[1]>ParPoint2_img.x + LEE_CONST_ACCEPTABLE_ERROR))
            return -1;

        float pr0 = (X[0]-RayPoint1_img.x)*sign_x + \
                    (pEdge2->parabola->a*X[0]*X[0]-RayPoint1_img.y)*sign_y;
        float pr1 = (X[1]-RayPoint1_img.x)*sign_x + \
                    (pEdge2->parabola->a*X[1]*X[1]-RayPoint1_img.y)*sign_y;

        if(pr0 <= -LEE_CONST_ACCEPTABLE_ERROR && pr1 <= -LEE_CONST_ACCEPTABLE_ERROR)
            return -1;

        if(pr0 > -LEE_CONST_ACCEPTABLE_ERROR && pr1 > -LEE_CONST_ACCEPTABLE_ERROR)
        {
            if(X[0] >= ParPoint1_img.x - LEE_CONST_ACCEPTABLE_ERROR)
            {
                if(X[1] <= ParPoint2_img.x + LEE_CONST_ACCEPTABLE_ERROR)
                {
                    if(pr0>pr1)
                        _cvSwap(X[0], X[1]);
                }
                else
                    N=1;
            }
            else
            {
                N=1;
                X[0] = X[1];
            }
        }
        else if(pr0 > -LEE_CONST_ACCEPTABLE_ERROR)
        {
            
            if(X[0] >= ParPoint1_img.x - LEE_CONST_ACCEPTABLE_ERROR)
                N=1;
            else
                return -1;
        }
        else if(pr1 > -LEE_CONST_ACCEPTABLE_ERROR)
        {
            if(X[1] <= ParPoint2_img.x + LEE_CONST_ACCEPTABLE_ERROR)
            {
                N=1;
                X[0] = X[1];
            }
            else
                return -1;
        }
        else
            return -1;
    }

    Point.x = X[(N-1)*(IntersectionNumber - 1)];
    Point.y = pEdge2->parabola->a*Point.x*Point.x;
    Radius = Point.y + 1.f/(4*pEdge2->parabola->a);
    _cvCalcPointImage(pPoint,&Point,Parabola);
    float dist = _cvPPDist(pPoint, pRayPoint1);
    if(IntersectionNumber == 2 && dist < LEE_CONST_DIFF_POINTS)
        return -1;
    else
        return dist;
}// end of _cvLine_CloseParIntersection

static
float _cvPar_LineIntersection(pCvVoronoiEdge pEdge1,
                                pCvVoronoiEdge pEdge2,
                                pCvPointFloat  pPoint,
                                float &Radius)
{
    if(pEdge2->node1==NULL||pEdge2->node2==NULL)
        return _cvPar_OpenLineIntersection(pEdge1,pEdge2,pPoint,Radius);
    else
        return _cvPar_CloseLineIntersection(pEdge1,pEdge2,pPoint,Radius);
}//end _cvPar_LineIntersection

static
float _cvPar_OpenLineIntersection(pCvVoronoiEdge pEdge1,
                                pCvVoronoiEdge pEdge2,
                                pCvPointFloat  pPoint,
                                float &Radius)
{
    int i, IntersectionNumber = 1;
    if(((pEdge1->node1 == pEdge2->node1 ||
        pEdge1->node1 == pEdge2->node2) &&
        pEdge1->node1 != NULL)||
       ((pEdge1->node2 == pEdge2->node1 ||
        pEdge1->node2 == pEdge2->node2) &&
        pEdge1->node2 != NULL))
        IntersectionNumber = 2;

    float* Parabola = pEdge1->parabola->map;
    pCvPointFloat pParPoint1;
    if(pEdge1->node1!=NULL)
        pParPoint1 = &(pEdge1->node1->node);
    else
        pParPoint1 = &(pEdge1->node2->node);

    pCvPointFloat pRayPoint1;
    if(pEdge2->node1==NULL)
        pRayPoint1 = &(pEdge2->node2->node);
    else 
        pRayPoint1 = &(pEdge2->node1->node);
    pCvDirection pDirection = pEdge2->direction;

            
    float InversParabola[6];
    _cvCalcOrtogInverse(InversParabola, Parabola);

    CvPointFloat  Point = {0,0},ParPoint1_img,RayPoint1_img;
    CvDirection Direction_img;
    _cvCalcVectorImage(&Direction_img,pDirection, InversParabola);
    _cvCalcPointImage(&RayPoint1_img, pRayPoint1, InversParabola);

    
    float q = RayPoint1_img.y - pEdge1->parabola->a*RayPoint1_img.x*RayPoint1_img.x;
    if(pEdge2->site->node1 == pEdge2->site->node2 && q < 0 ||
        pEdge2->site->node1 != pEdge2->site->node2 && q > 0)
        return -1;

    float c2 = pEdge1->parabola->a*Direction_img.x;
    float c1 = -Direction_img.y;
    float c0 = Direction_img.y* RayPoint1_img.x - Direction_img.x*RayPoint1_img.y;
    float X[2];
    int N = _cvSolveEqu2thR(c2,c1,c0,X);
    if(N==0)
        return -1;

    _cvCalcPointImage(&ParPoint1_img, pParPoint1, InversParabola);
    int sign_x = SIGN(Direction_img.x);
    int sign_y = SIGN(Direction_img.y);
    float pr;

    if(N==2 && IntersectionNumber == 2)
        _cvSwap(X[0], X[1]);

    for( i=0;i<N;i++)
    {
        if(X[i]<=ParPoint1_img.x - LEE_CONST_ACCEPTABLE_ERROR)
            continue;
        pr = (X[i]-RayPoint1_img.x)*sign_x + 
                        (pEdge1->parabola->a*X[i]*X[i]-RayPoint1_img.y)*sign_y;
        if(pr <= -LEE_CONST_ACCEPTABLE_ERROR)
            continue;
        else
        {
            Point.x = X[i];
            break;
        }
    }

    if(i==N)
        return -1;
        
    Point.y = pEdge1->parabola->a*Point.x*Point.x;
    Radius = Point.y + 1.f/(4*pEdge1->parabola->a);
    _cvCalcPointImage(pPoint,&Point,Parabola);
    float dist = Point.x - ParPoint1_img.x;
    if(IntersectionNumber == 2 && dist < LEE_CONST_DIFF_POINTS)
        return -1;
    else
        return dist;
}// end of _cvPar_OpenLineIntersection

static
float _cvPar_CloseLineIntersection(pCvVoronoiEdge pEdge1,
                                    pCvVoronoiEdge pEdge2,
                                    pCvPointFloat  pPoint,
                                    float &Radius)
{
    int i, IntersectionNumber = 1;
    if(((pEdge1->node1 == pEdge2->node1 ||
        pEdge1->node1 == pEdge2->node2) &&
        pEdge1->node1 != NULL)||
       ((pEdge1->node2 == pEdge2->node1 ||
        pEdge1->node2 == pEdge2->node2) &&
        pEdge1->node2 != NULL))
        IntersectionNumber = 2;

    float* Parabola = pEdge1->parabola->map;
    pCvPointFloat pParPoint1;
    if(pEdge1->node1!=NULL)
        pParPoint1 = &(pEdge1->node1->node);
    else
        pParPoint1 = &(pEdge1->node2->node);

    pCvPointFloat pRayPoint1,pRayPoint2;
    pRayPoint2 = &(pEdge2->node1->node);
    pRayPoint1 = &(pEdge2->node2->node);

    pCvDirection pDirection = pEdge2->direction;
    float InversParabola[6];
    _cvCalcOrtogInverse(InversParabola, Parabola);

    CvPointFloat  Point={0,0},ParPoint1_img,RayPoint1_img,RayPoint2_img;
    CvDirection Direction_img;
    _cvCalcPointImage(&RayPoint1_img, pRayPoint1, InversParabola);
    _cvCalcPointImage(&RayPoint2_img, pRayPoint2, InversParabola);
    
    float q;
    if(Radius == -1)
    {
         q = RayPoint1_img.y - pEdge1->parabola->a*RayPoint1_img.x*RayPoint1_img.x;
         if(pEdge2->site->node1 == pEdge2->site->node2 && q < 0 ||
            pEdge2->site->node1 != pEdge2->site->node2 && q > 0)
                return -1;
    }
    if(Radius == -2)
    {
         q = RayPoint2_img.y - pEdge1->parabola->a*RayPoint2_img.x*RayPoint2_img.x;
        if(pEdge2->site->node1 == pEdge2->site->node2 && q < 0 ||
            pEdge2->site->node1 != pEdge2->site->node2 && q > 0)
                return -1;
    }

    _cvCalcPointImage(&ParPoint1_img, pParPoint1, InversParabola);
    _cvCalcVectorImage(&Direction_img,pDirection, InversParabola);

    float c2 = pEdge1->parabola->a*Direction_img.x;
    float c1 = -Direction_img.y;
    float c0 = Direction_img.y* RayPoint1_img.x - Direction_img.x*RayPoint1_img.y;
    float X[2];
    int N = _cvSolveEqu2thR(c2,c1,c0,X);
    if(N==0)
        return -1;
    int sign_x = SIGN(RayPoint2_img.x - RayPoint1_img.x);
    int sign_y = SIGN(RayPoint2_img.y - RayPoint1_img.y);
    float pr_dir = (RayPoint2_img.x - RayPoint1_img.x)*sign_x + 
                   (RayPoint2_img.y - RayPoint1_img.y)*sign_y;
    float pr;

    if(N==2 && IntersectionNumber == 2)
        _cvSwap(X[0], X[1]);

    for( i =0;i<N;i++)
    {
        if(X[i] <= ParPoint1_img.x - LEE_CONST_ACCEPTABLE_ERROR)
            continue;
        pr = (X[i]-RayPoint1_img.x)*sign_x + \
             (pEdge1->parabola->a*X[i]*X[i]-RayPoint1_img.y)*sign_y;
        if(pr <= -LEE_CONST_ACCEPTABLE_ERROR || pr>=pr_dir + LEE_CONST_ACCEPTABLE_ERROR)
            continue;
        else
        {
            Point.x = X[i];
            break;
        }
    }

    if(i==N)
        return -1;

    Point.y = pEdge1->parabola->a*Point.x*Point.x;
    Radius = Point.y + 1.f/(4*pEdge1->parabola->a);
    _cvCalcPointImage(pPoint,&Point,Parabola);
    float dist = Point.x - ParPoint1_img.x;
    if(IntersectionNumber == 2 && dist < LEE_CONST_DIFF_POINTS)
        return -1;
    else
        return dist;
}// end of _cvPar_CloseLineIntersection

static
float _cvPar_ParIntersection(pCvVoronoiEdge pEdge1,
                            pCvVoronoiEdge pEdge2,
                            pCvPointFloat  pPoint,
                            float &Radius)
{
    if(pEdge2->node1==NULL||pEdge2->node2==NULL)
        return _cvPar_OpenParIntersection(pEdge1,pEdge2,pPoint,Radius);
    else
        return _cvPar_CloseParIntersection(pEdge1,pEdge2,pPoint,Radius);
}// end of _cvPar_ParIntersection

static
float _cvPar_OpenParIntersection(pCvVoronoiEdge pEdge1,
                            pCvVoronoiEdge pEdge2,
                            pCvPointFloat  pPoint,
                            float &Radius)
{
    int i, IntersectionNumber = 1;
    if(((pEdge1->node1 == pEdge2->node1 ||
        pEdge1->node1 == pEdge2->node2) &&
        pEdge1->node1 != NULL)||
       ((pEdge1->node2 == pEdge2->node1 ||
        pEdge1->node2 == pEdge2->node2) &&
        pEdge1->node2 != NULL))
        IntersectionNumber = 2;

    float* Parabola1 = pEdge1->parabola->map;
    pCvPointFloat pPar1Point1;
    if(pEdge1->node1!=NULL)
        pPar1Point1 = &(pEdge1->node1->node);
    else
        pPar1Point1 = &(pEdge1->node2->node);

    float* Parabola2 = pEdge2->parabola->map;
    pCvPointFloat pPar2Point1;
    if(pEdge2->node1!=NULL)
        pPar2Point1 = &(pEdge2->node1->node);
    else
        pPar2Point1 = &(pEdge2->node2->node);

    CvPointFloat Point;
    CvDirection Direction;
    if(pEdge1->parabola->directrice==pEdge2->parabola->directrice)  //common site is segment -> different focuses
    {
        pCvPointFloat pFocus1 = &(pEdge1->parabola->focus->node);
        pCvPointFloat pFocus2 = &(pEdge2->parabola->focus->node);
        
        Point.x = (pFocus1->x + pFocus2->x)/2;
        Point.y = (pFocus1->y + pFocus2->y)/2;
        Direction.x = pFocus1->y - pFocus2->y;
        Direction.y = pFocus2->x - pFocus1->x;
    }
    else//common site is focus -> different directrices
    {
        pCvVoronoiSite pDirectrice1 = pEdge1->parabola->directrice;
        pCvPointFloat pPoint1 = &(pDirectrice1->node1->node);
        pCvDirection pVector21 = pDirectrice1->direction;
    
        pCvVoronoiSite pDirectrice2 = pEdge2->parabola->directrice;
        pCvPointFloat pPoint3 = &(pDirectrice2->node1->node);
        pCvDirection pVector43 = pDirectrice2->direction;
        
        Direction.x = pVector43->x - pVector21->x;
        Direction.y = pVector43->y - pVector21->y;

        if((fabs(Direction.x) < LEE_CONST_ZERO) &&
           (fabs(Direction.y) < LEE_CONST_ZERO))
                Direction = *pVector43;
    
        float det = pVector21->y * pVector43->x - pVector21->x * pVector43->y;
        if(fabs(det) < LEE_CONST_ZERO)
        {
            Point.x = (pPoint1->x + pPoint3->x)/2;
            Point.y = (pPoint1->y + pPoint3->y)/2;
        }
        else
        {
            float d1 = pVector21->y*pPoint1->x - pVector21->x*pPoint1->y;
            float d2 = pVector43->y*pPoint3->x - pVector43->x*pPoint3->y;
            Point.x = (float)((pVector43->x*d1 - pVector21->x*d2)/det);
            Point.y = (float)((pVector43->y*d1 - pVector21->y*d2)/det);
        }
    }

    float InversParabola2[6];
    _cvCalcOrtogInverse(InversParabola2, Parabola2);
    
    CvPointFloat  Par2Point1_img,Point_img;
    CvDirection Direction_img;
    _cvCalcVectorImage(&Direction_img,&Direction, InversParabola2);
    _cvCalcPointImage(&Point_img, &Point, InversParabola2);
    
    float a1 = pEdge1->parabola->a;
    float a2 = pEdge2->parabola->a;
    float c2 = a2*Direction_img.x;
    float c1 = -Direction_img.y;
    float c0 = Direction_img.y* Point_img.x - Direction_img.x*Point_img.y;
    float X[2];
    int N = _cvSolveEqu2thR(c2,c1,c0,X);

    if(N==0)
        return -1;

    _cvCalcPointImage(&Par2Point1_img, pPar2Point1, InversParabola2);

    if(X[N-1]<Par2Point1_img.x)
        return -1;

    if(X[0]<Par2Point1_img.x)
    {
        X[0] = X[1];
        N=1;
    }

    float InversParabola1[6];
    CvPointFloat Par1Point1_img;
    _cvCalcOrtogInverse(InversParabola1, Parabola1);
    _cvCalcPointImage(&Par1Point1_img, pPar1Point1, InversParabola1);
    float InvPar1_Par2[6];
    _cvCalcComposition(InvPar1_Par2,InversParabola1,Parabola2);
    for(i=0;i<N;i++)
        X[i] = (InvPar1_Par2[1]*a2*X[i] + InvPar1_Par2[0])*X[i] +  InvPar1_Par2[2];

    if(N!=1)
    {
        if((X[0]>X[1] && IntersectionNumber == 1)||
            (X[0]<X[1] && IntersectionNumber == 2))
            _cvSwap(X[0], X[1]);
    }

    for(i = 0;i<N;i++)
    {
        Point.x = X[i];
        Point.y = a1*Point.x*Point.x;
        if(Point.x < Par1Point1_img.x - LEE_CONST_ACCEPTABLE_ERROR)
            continue;
        else
            break;
    }
    
    if(i==N)
        return -1;
        
    Radius = Point.y + 1.f/(4*pEdge1->parabola->a);
    _cvCalcPointImage(pPoint,&Point,Parabola1);
    float dist = Point.x - Par1Point1_img.x;
    if(IntersectionNumber == 2 && dist < LEE_CONST_DIFF_POINTS)
        return -1;
    else
        return dist;
}// end of _cvPar_OpenParIntersection

static
float _cvPar_CloseParIntersection(pCvVoronoiEdge pEdge1,
                                  pCvVoronoiEdge pEdge2,
                                  pCvPointFloat  pPoint,
                                  float &Radius)
{
    int i, IntersectionNumber = 1;
    if(((pEdge1->node1 == pEdge2->node1 ||
        pEdge1->node1 == pEdge2->node2) &&
        pEdge1->node1 != NULL)||
       ((pEdge1->node2 == pEdge2->node1 ||
        pEdge1->node2 == pEdge2->node2) &&
        pEdge1->node2 != NULL))
        IntersectionNumber = 2;

    float* Parabola1 = pEdge1->parabola->map;
    float* Parabola2 = pEdge2->parabola->map;
    pCvPointFloat pPar1Point1;
    if(pEdge1->node1!=NULL)
        pPar1Point1 = &(pEdge1->node1->node);
    else
        pPar1Point1 = &(pEdge1->node2->node);

    pCvPointFloat pPar2Point1 = &(pEdge2->node1->node);
    pCvPointFloat pPar2Point2 = &(pEdge2->node2->node);
    
    CvPointFloat Point;
    CvDirection Direction;
    if(pEdge1->parabola->directrice==pEdge2->parabola->directrice)  //common site is segment -> different focuses
    {
        pCvPointFloat pFocus1 = &(pEdge1->parabola->focus->node);
        pCvPointFloat pFocus2 = &(pEdge2->parabola->focus->node);
        
        Point.x = (pFocus1->x + pFocus2->x)/2;
        Point.y = (pFocus1->y + pFocus2->y)/2;
        Direction.x = pFocus1->y - pFocus2->y;
        Direction.y = pFocus2->x - pFocus1->x;
    }
    else//common site is focus -> different directrices
    {
        pCvVoronoiSite pDirectrice1 = pEdge1->parabola->directrice;
        pCvPointFloat pPoint1 = &(pDirectrice1->node1->node);
        pCvDirection pVector21 = pDirectrice1->direction;
    
        pCvVoronoiSite pDirectrice2 = pEdge2->parabola->directrice;
        pCvPointFloat pPoint3 = &(pDirectrice2->node1->node);
        pCvDirection pVector43 = pDirectrice2->direction;
        
        Direction.x = pVector43->x - pVector21->x;
        Direction.y = pVector43->y - pVector21->y;

        if((fabs(Direction.x) < LEE_CONST_ZERO) &&
           (fabs(Direction.y) < LEE_CONST_ZERO))
                Direction = *pVector43;
    
        float det = pVector21->y * pVector43->x - pVector21->x * pVector43->y;
        if(fabs(det) < LEE_CONST_ZERO)
        {
            Point.x = (pPoint1->x + pPoint3->x)/2;
            Point.y = (pPoint1->y + pPoint3->y)/2;
        }
        else
        {
            float d1 = pVector21->y*pPoint1->x - pVector21->x*pPoint1->y;
            float d2 = pVector43->y*pPoint3->x - pVector43->x*pPoint3->y;
            Point.x = (float)((pVector43->x*d1 - pVector21->x*d2)/det);
            Point.y = (float)((pVector43->y*d1 - pVector21->y*d2)/det);
        }
    }


    
    float InversParabola2[6];
    _cvCalcOrtogInverse(InversParabola2, Parabola2);
    
    CvPointFloat  Par2Point1_img,Par2Point2_img,Point_img;
    CvDirection Direction_img;
    _cvCalcVectorImage(&Direction_img,&Direction, InversParabola2);
    _cvCalcPointImage(&Point_img, &Point, InversParabola2);
    
    float a1 = pEdge1->parabola->a;
    float a2 = pEdge2->parabola->a;
    float c2 = a2*Direction_img.x;
    float c1 = -Direction_img.y;
    float c0 = Direction_img.y* Point_img.x - Direction_img.x*Point_img.y;
    float X[2];
    int N = _cvSolveEqu2thR(c2,c1,c0,X);

    if(N==0)
        return -1;

    _cvCalcPointImage(&Par2Point1_img, pPar2Point1, InversParabola2);
    _cvCalcPointImage(&Par2Point2_img, pPar2Point2, InversParabola2);
    if(Par2Point1_img.x>Par2Point2_img.x)
        _cvSwap(Par2Point1_img,Par2Point2_img);

    if(X[0]>Par2Point2_img.x||X[N-1]<Par2Point1_img.x)
        return -1;

    if(X[0]<Par2Point1_img.x)
    {
        if(X[1]<Par2Point2_img.x)
        {
            X[0] = X[1];
            N=1;
        }
        else
            return -1;
    }
    else if(X[N-1]>Par2Point2_img.x)
            N=1;
    
    float InversParabola1[6];
    CvPointFloat Par1Point1_img;
    _cvCalcOrtogInverse(InversParabola1, Parabola1);
    _cvCalcPointImage(&Par1Point1_img, pPar1Point1, InversParabola1);
    float InvPar1_Par2[6];
    _cvCalcComposition(InvPar1_Par2,InversParabola1,Parabola2);
    for(i=0;i<N;i++)
        X[i] = (InvPar1_Par2[1]*a2*X[i] + InvPar1_Par2[0])*X[i] +  InvPar1_Par2[2];

    if(N!=1)
    {
        if((X[0]>X[1] && IntersectionNumber == 1)||
            (X[0]<X[1] && IntersectionNumber == 2))
            _cvSwap(X[0], X[1]);
    }
            

    for(i = 0;i<N;i++)
    {
        Point.x = (float)X[i];
        Point.y = (float)a1*Point.x*Point.x;
        if(Point.x < Par1Point1_img.x - LEE_CONST_ACCEPTABLE_ERROR)
            continue;
        else
            break;
    }

    if(i==N)
        return -1;
        
    Radius = Point.y + 1.f/(4*a1);
    _cvCalcPointImage(pPoint,&Point,Parabola1);
    float dist = Point.x - Par1Point1_img.x;
    if(IntersectionNumber == 2 && dist < LEE_CONST_DIFF_POINTS)
        return -1;
    else
        return dist;
}// end of _cvPar_CloseParIntersection

/*////////////////////////////////////////////////////////////////////////////////////////
//                           Subsidiary functions                                       //
////////////////////////////////////////////////////////////////////////////////////////*/

CV_INLINE
void _cvMakeTwinEdge(pCvVoronoiEdge pEdge2,
                    pCvVoronoiEdge pEdge1)
{
    pEdge2->direction = pEdge1->direction;
    pEdge2->parabola = pEdge1->parabola;
    pEdge2->node1 = pEdge1->node2;
    pEdge2->twin_edge = pEdge1;
    pEdge1->twin_edge = pEdge2;
}//end of _cvMakeTwinEdge

CV_INLINE
void _cvStickEdgeLeftBegin(pCvVoronoiEdge pEdge, 
                          pCvVoronoiEdge pEdge_left_prev,
                          pCvVoronoiSite pSite_left)
{
    pEdge->prev_edge = pEdge_left_prev;
    pEdge->site = pSite_left;
    if(pEdge_left_prev == NULL)
        pSite_left->edge2 = pEdge;
    else
    {
        pEdge_left_prev->node2 = pEdge->node1;
        pEdge_left_prev->next_edge = pEdge;
    }
}//end of _cvStickEdgeLeftBegin

CV_INLINE
void _cvStickEdgeRightBegin(pCvVoronoiEdge pEdge, 
                          pCvVoronoiEdge pEdge_right_next,
                          pCvVoronoiSite pSite_right)
{
    pEdge->next_edge = pEdge_right_next;
    pEdge->site = pSite_right;
    if(pEdge_right_next == NULL)
        pSite_right->edge1 = pEdge;
    else
    {
        pEdge_right_next->node1 = pEdge->node2;
        pEdge_right_next->prev_edge = pEdge;
    }
}// end of _cvStickEdgeRightBegin

CV_INLINE
void _cvStickEdgeLeftEnd(pCvVoronoiEdge pEdge, 
                        pCvVoronoiEdge pEdge_left_next,
                        pCvVoronoiSite pSite_left)
{
    pEdge->next_edge = pEdge_left_next;
    if(pEdge_left_next == NULL)
        pSite_left->edge1 = pEdge;
    else
    {
        pEdge_left_next->node1 = pEdge->node2;
        pEdge_left_next->prev_edge = pEdge;
    }
}//end of _cvStickEdgeLeftEnd

CV_INLINE
void _cvStickEdgeRightEnd(pCvVoronoiEdge pEdge, 
                         pCvVoronoiEdge pEdge_right_prev,
                         pCvVoronoiSite pSite_right)
{
    pEdge->prev_edge = pEdge_right_prev;
    if(pEdge_right_prev == NULL)
        pSite_right->edge2 = pEdge;
    else
    {
        pEdge_right_prev->node2 = pEdge->node1;
        pEdge_right_prev->next_edge = pEdge;
    }
}//end of _cvStickEdgeRightEnd

template <class T> CV_INLINE
void _cvInitVoronoiNode(pCvVoronoiNode pNode,
                        T pPoint, 
                        float radius)
{
    pNode->node.x = (float)pPoint->x;
    pNode->node.y = (float)pPoint->y;
    pNode->radius = radius;
}//end of _cvInitVoronoiNode

CV_INLINE
void _cvInitVoronoiSite(pCvVoronoiSite pSite,
                       pCvVoronoiNode pNode1,
                       pCvVoronoiNode pNode2,
                       pCvVoronoiSite pPrev_site)
{
    pSite->node1 = pNode1;
    pSite->node2 = pNode2;
    pSite->prev_site = pPrev_site;
}//end of _cvInitVoronoiSite

template <class T> CV_INLINE 
T _cvSeqPush(CvSeq* Seq, T pElem)
{
    cvSeqPush(Seq, pElem);
    return (T)(Seq->ptr - Seq->elem_size);
//  return (T)cvGetSeqElem(Seq, Seq->total - 1,NULL);
}//end of _cvSeqPush

template <class T> CV_INLINE
T _cvSeqPushFront(CvSeq* Seq, T pElem)
{
    cvSeqPushFront(Seq,pElem);
    return (T)Seq->first->data;
//  return (T)cvGetSeqElem(Seq,0,NULL);
}//end of _cvSeqPushFront

CV_INLINE
void _cvTwinNULLLeft(pCvVoronoiEdge pEdge_left_cur,
                    pCvVoronoiEdge pEdge_left)
{
    while(pEdge_left_cur!=pEdge_left)
    {
        if(pEdge_left_cur->twin_edge!=NULL)
            pEdge_left_cur->twin_edge->twin_edge = NULL;
        pEdge_left_cur = pEdge_left_cur->next_edge;
    }
}//end of _cvTwinNULLLeft

CV_INLINE
void _cvTwinNULLRight(pCvVoronoiEdge pEdge_right_cur,
                     pCvVoronoiEdge pEdge_right)
{
    while(pEdge_right_cur!=pEdge_right)
    {
        if(pEdge_right_cur->twin_edge!=NULL)
            pEdge_right_cur->twin_edge->twin_edge = NULL;
        pEdge_right_cur = pEdge_right_cur->prev_edge;
    }
}//end of _cvTwinNULLRight

CV_INLINE
void _cvSeqPushInOrder(CvVoronoiDiagramInt* pVoronoiDiagram, pCvVoronoiHole pHole)
{
    pHole = _cvSeqPush(pVoronoiDiagram->HoleSeq, pHole);
    if(pVoronoiDiagram->HoleSeq->total == 1)
    {
        pVoronoiDiagram->top_hole = pHole;
        return;
    }

    pCvVoronoiHole pTopHole = pVoronoiDiagram->top_hole;
    pCvVoronoiHole pCurrHole;
    if(pTopHole->x_coord >= pHole->x_coord)
    {
        pHole->next_hole = pTopHole;
        pVoronoiDiagram->top_hole = pHole;
        return;
    }

    for(pCurrHole = pTopHole; \
        pCurrHole->next_hole != NULL; \
        pCurrHole = pCurrHole->next_hole)
        if(pCurrHole->next_hole->x_coord >= pHole->x_coord)
            break;
    pHole->next_hole = pCurrHole->next_hole;
    pCurrHole->next_hole = pHole;
}//end of _cvSeqPushInOrder

CV_INLINE
pCvVoronoiEdge _cvDivideRightEdge(pCvVoronoiEdge pEdge,pCvVoronoiNode pNode, CvSeq* EdgeSeq)
{
    CvVoronoiEdgeInt Edge1 = *pEdge;
    CvVoronoiEdgeInt Edge2 = *pEdge->twin_edge;
    pCvVoronoiEdge pEdge1, pEdge2;
    
    pEdge1 = _cvSeqPush(EdgeSeq, &Edge1);
    pEdge2 = _cvSeqPush(EdgeSeq, &Edge2);

    if(pEdge1->next_edge != NULL)
        pEdge1->next_edge->prev_edge = pEdge1;
    pEdge1->prev_edge = NULL;

    if(pEdge2->prev_edge != NULL)
        pEdge2->prev_edge->next_edge = pEdge2;
    pEdge2->next_edge = NULL;

    pEdge1->node1 = pEdge2->node2= pNode;
    pEdge1->twin_edge = pEdge2;
    pEdge2->twin_edge = pEdge1;
    return pEdge2;
}//end of _cvDivideRightEdge

CV_INLINE
pCvVoronoiEdge _cvDivideLeftEdge(pCvVoronoiEdge pEdge,pCvVoronoiNode pNode, CvSeq* EdgeSeq)
{
    CvVoronoiEdgeInt Edge1 = *pEdge;
    CvVoronoiEdgeInt Edge2 = *pEdge->twin_edge;
    pCvVoronoiEdge pEdge1, pEdge2;
    
    pEdge1 = _cvSeqPush(EdgeSeq, &Edge1);
    pEdge2 = _cvSeqPush(EdgeSeq, &Edge2);

    if(pEdge2->next_edge != NULL)
        pEdge2->next_edge->prev_edge = pEdge2;
    pEdge2->prev_edge = NULL;

    if(pEdge1->prev_edge != NULL)
        pEdge1->prev_edge->next_edge = pEdge1;
    pEdge1->next_edge = NULL;
    
    pEdge1->node2 = pEdge2->node1= pNode;
    pEdge1->twin_edge = pEdge2;
    pEdge2->twin_edge = pEdge1;
    return pEdge2;
}//end of _cvDivideLeftEdge

template<class T> CV_INLINE
T _cvWriteSeqElem(T pElem, CvSeqWriter &writer)
{
    if( writer.ptr >= writer.block_max )        
          cvCreateSeqBlock( &writer); 

    T ptr = (T)writer.ptr;
    memcpy(writer.ptr, pElem, sizeof(*pElem));      
    writer.ptr += sizeof(*pElem);   
    return ptr;
}//end of _cvWriteSeqElem

/*////////////////////////////////////////////////////////////////////////////////////////
//                           Mathematical functions                                     //
////////////////////////////////////////////////////////////////////////////////////////*/

template<class T> CV_INLINE
void _cvCalcPointImage(pCvPointFloat pImgPoint,pCvPointFloat pPoint,T* A)
{
    pImgPoint->x = (float)(A[0]*pPoint->x + A[1]*pPoint->y + A[2]);
    pImgPoint->y = (float)(A[3]*pPoint->x + A[4]*pPoint->y + A[5]);
}//end of _cvCalcPointImage

template <class T> CV_INLINE
void _cvSwap(T &x, T &y)
{
    T z; z=x; x=y; y=z;
}//end of _cvSwap

template <class T> CV_INLINE
int _cvCalcOrtogInverse(T* B, T* A)
{
    int sign_det = SIGN(A[0]*A[4] - A[1]*A[3]);

    if(sign_det)
    {
        B[0] =  A[4]*sign_det;
        B[1] = -A[1]*sign_det;
        B[3] = -A[3]*sign_det;
        B[4] =  A[0]*sign_det;
        B[2] = - (B[0]*A[2]+B[1]*A[5]);
        B[5] = - (B[3]*A[2]+B[4]*A[5]);
        return 1;
    }
    else
        return 0;
}//end of _cvCalcOrtogInverse

template<class T> CV_INLINE 
void _cvCalcVectorImage(pCvDirection pImgVector,pCvDirection pVector,T* A)
{
    pImgVector->x = A[0]*pVector->x + A[1]*pVector->y;
    pImgVector->y = A[3]*pVector->x + A[4]*pVector->y;
}//end of _cvCalcVectorImage

template <class T> CV_INLINE
void _cvCalcComposition(T* Result,T* A,T* B)
{
    Result[0] = A[0]*B[0] + A[1]*B[3];
    Result[1] = A[0]*B[1] + A[1]*B[4];
    Result[3] = A[3]*B[0] + A[4]*B[3];
    Result[4] = A[3]*B[1] + A[4]*B[4];
    Result[2] = A[0]*B[2] + A[1]*B[5] + A[2];
    Result[5] = A[3]*B[2] + A[4]*B[5] + A[5];
}//end of _cvCalcComposition

CV_INLINE
float _cvCalcDist(pCvPointFloat pPoint, pCvVoronoiSite pSite)
{
    if(pSite->node1==pSite->node2)
        return _cvPPDist(pPoint,&(pSite->node1->node));
    else
        return _cvPLDist(pPoint,&(pSite->node1->node),pSite->direction);
}//end of _cvCalcComposition

CV_INLINE
float _cvPPDist(pCvPointFloat pPoint1,pCvPointFloat pPoint2)
{
    float delta_x = pPoint1->x - pPoint2->x;
    float delta_y = pPoint1->y - pPoint2->y;
    return (float)sqrt((double)delta_x*delta_x + delta_y*delta_y);
}//end of _cvPPDist


CV_INLINE
float _cvPLDist(pCvPointFloat pPoint,pCvPointFloat pPoint1,pCvDirection pDirection)
{
    return (float)fabs(pDirection->x*(pPoint->y - pPoint1->y) -
                     pDirection->y*(pPoint->x - pPoint1->x));
}//end of _cvPLDist

template <class T>
int _cvSolveEqu2thR(T c2, T c1, T c0, T* X)
{
    const T eps = (T)1e-6;
    if(fabs(c2)<eps)
        return _cvSolveEqu1th(c1,c0,X);

    T Discr = c1*c1 - c2*c0*4;
    if(Discr<-eps)
        return 0;
    Discr = (T)sqrt((double)fabs(Discr));

    if(fabs(Discr)<eps)
    {
        X[0] = -c1/(c2*2);
        if(fabs(X[0])<eps)
            X[0]=0;
        return 1;
    }
    else
    {
        if(c1 >=0)
        {
            if(c2>0)
            {
                X[0] = (-c1 - Discr)/(2*c2);
                X[1] = -2*c0/(c1+Discr);
                return 2;
            }
            else
            {
                X[1] = (-c1 - Discr)/(2*c2);
                X[0] = -2*c0/(c1+Discr);
                return 2;
            }
        }
        else
        {
            if(c2>0)
            {
                X[1] = (-c1 + Discr)/(2*c2);
                X[0] = -2*c0/(c1-Discr);
                return 2;
            }
            else
            {
                X[0] = (-c1 + Discr)/(2*c2);
                X[1] = -2*c0/(c1-Discr);
                return 2;
            }
        }
    }
}//end of _cvSolveEqu2thR   

template <class T> CV_INLINE
int _cvSolveEqu1th(T c1, T c0, T* X)
{
    const T eps = (T)1e-6;
    if(fabs(c1)<eps)
        return 0;

    X[0] = -c0/c1;
    return 1;
}//end of _cvSolveEqu1th
