/*
 * vim: ts=4 sw=4 et tw=0 wm=0
 *
 * libavoid - Fast, Incremental, Object-avoiding Line Router
 *
 * Copyright (C) 2004-2009  Monash University
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * See the file LICENSE.LGPL distributed with the library.
 *
 * Licensees holding a valid commercial license may use this file in
 * accordance with the commercial license agreement provided with the 
 * library.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
 *
 * Author(s):   Michael Wybrow <mjwybrow@users.sourceforge.net>
*/


#include <cstring>
#include <cfloat>
#include <cmath>
#include <cstdlib>

#include "libavoid/graph.h"
#include "libavoid/connector.h"
#include "libavoid/makepath.h"
#include "libavoid/visibility.h"
#include "libavoid/debug.h"
#include "libavoid/router.h"
#include "libavoid/assertions.h"


namespace Avoid {

    
ConnEnd::ConnEnd(const Point& point) 
    : _point(point),
      _directions(ConnDirAll),
      _shapeRef(NULL)
{
}


ConnEnd::ConnEnd(const Point& point, const ConnDirFlags visDirs) 
    : _point(point),
      _directions(visDirs),
      _shapeRef(NULL)
{
}

ConnEnd::ConnEnd(ShapeRef *shapeRef, const double x_pos, const double y_pos,
        const double insideOffset, const ConnDirFlags visDirs) 
    : _directions(visDirs),
      _shapeRef(shapeRef),
      _xPosition(x_pos),
      _yPosition(y_pos),
      _insideOffset(insideOffset)
{
}

const Point ConnEnd::point(void) const
{
    if (_shapeRef)
    {
        const Polygon& poly = _shapeRef->polygon();

        double x_min = DBL_MAX;
        double x_max = -DBL_MAX;
        double y_min = DBL_MAX;
        double y_max = -DBL_MAX;
        for (size_t i = 0; i < poly.size(); ++i)
        {
            x_min = std::min(x_min, poly.ps[i].x);
            x_max = std::max(x_max, poly.ps[i].x);
            y_min = std::min(y_min, poly.ps[i].y);
            y_max = std::max(y_max, poly.ps[i].y);
        }

        Point point;

        // We want to place connection points on the edges of shapes, 
        // or possibly slightly inside them (if _insideOfset is set).

        point.vn = kUnassignedVertexNumber;
        if (_xPosition == ATTACH_POS_LEFT)
        {
            point.x = x_min + _insideOffset;
            point.vn = 6;
        }
        else if (_xPosition == ATTACH_POS_RIGHT)
        {
            point.x = x_max - _insideOffset;
            point.vn = 4;
        }
        else
        {
            point.x = x_min + (_xPosition * (x_max - x_min));
        }

        if (_yPosition == ATTACH_POS_TOP)
        {
            point.y = y_max - _insideOffset;
            point.vn = 5;
        }
        else if (_yPosition == ATTACH_POS_BOTTOM)
        {
            point.y = y_min + _insideOffset;
            point.vn = 7;
        }
        else
        {
            point.y = y_min + (_yPosition * (y_max - y_min));
            point.vn = kUnassignedVertexNumber;
        }

        return point;
    }
    else
    {
        return _point;
    }
}


ConnDirFlags ConnEnd::directions(void) const
{
    if (_shapeRef)
    {
        ConnDirFlags visDir = _directions;
        if (_directions == ConnDirNone)
        {
            // None is set, use the defaults:
            if (_xPosition == ATTACH_POS_LEFT)
            {
                visDir = ConnDirLeft;
            }
            else if (_xPosition == ATTACH_POS_RIGHT)
            {
                visDir = ConnDirRight;
            }
            if (_yPosition == ATTACH_POS_TOP)
            {
                visDir = ConnDirDown;
            }
            else if (_yPosition == ATTACH_POS_BOTTOM)
            {
                visDir = ConnDirUp;
            }

            if (visDir == ConnDirNone)
            {
                visDir = ConnDirAll;
            }
        }
        return visDir;
    }
    else
    {
        return _directions;
    }
}


ConnRef::ConnRef(Router *router, const unsigned int id)
    : _router(router),
      _type(router->validConnType()),
      _srcId(0),
      _dstId(0),
      _needs_reroute_flag(true),
      _false_path(false),
      _needs_repaint(false),
      _active(false),
      _route_dist(0),
      _srcVert(NULL),
      _dstVert(NULL),
      _startVert(NULL),
      _initialised(false),
      _callback(NULL),
      _connector(NULL),
      _hateCrossings(false)
{
    _id = router->assignId(id);

    // TODO: Store endpoints and details.
    _route.clear();
}


ConnRef::ConnRef(Router *router, const ConnEnd& src, const ConnEnd& dst,
        const unsigned int id)
    : _router(router),
      _type(router->validConnType()),
      _srcId(0),
      _dstId(0),
      _needs_reroute_flag(true),
      _false_path(false),
      _needs_repaint(false),
      _active(false),
      _route_dist(0),
      _srcVert(NULL),
      _dstVert(NULL),
      _initialised(false),
      _callback(NULL),
      _connector(NULL),
      _hateCrossings(false)
{
    _id = router->assignId(id);
    _route.clear();

    bool isShape = false;
    _srcVert = new VertInf(_router, VertID(_id, isShape, 1), src.point());
    _srcVert->visDirections = src.directions();
    _dstVert = new VertInf(_router, VertID(_id, isShape, 2), dst.point());
    _dstVert->visDirections = dst.directions();
    makeActive();
    _initialised = true;
    
    setEndpoints(src, dst);
}


ConnRef::~ConnRef()
{
    _router->removeQueuedConnectorActions(this);
    removeFromGraph();

    freeRoutes();

    if (_srcVert)
    {
        _router->vertices.removeVertex(_srcVert);
        delete _srcVert;
        _srcVert = NULL;
    }

    if (_dstVert)
    {
        _router->vertices.removeVertex(_dstVert);
        delete _dstVert;
        _dstVert = NULL;
    }

    makeInactive();
}


ConnType ConnRef::routingType(void) const
{
    return _type;
}


void ConnRef::setRoutingType(ConnType type)
{
    type = _router->validConnType(type);
    if (_type != type)
    {
        _type = type;

        makePathInvalid();

        _router->modifyConnector(this);
    }
}


void ConnRef::common_updateEndPoint(const unsigned int type, const ConnEnd& connEnd)
{
    const Point& point = connEnd.point();
    //db_printf("common_updateEndPoint(%d,(pid=%d,vn=%d,(%f,%f)))\n",
    //      type,point.id,point.vn,point.x,point.y);
    COLA_ASSERT((type == (unsigned int) VertID::src) ||
                (type == (unsigned int) VertID::tar));
    
    if (!_initialised)
    {
        makeActive();
        _initialised = true;
    }
    
    VertInf *altered = NULL;
    // VertInf *partner = NULL;
    bool isShape = false;

    if (type == (unsigned int) VertID::src)
    {
        if (_srcVert)
        {
            _srcVert->Reset(VertID(_id, isShape, type), point);
        }
        else
        {
            _srcVert = new VertInf(_router, VertID(_id, isShape, type), point);
        }
        _srcVert->visDirections = connEnd.directions();
        
        altered = _srcVert;
        // partner = _dstVert;
    }
    else // if (type == (unsigned int) VertID::tar)
    {
        if (_dstVert)
        {
            _dstVert->Reset(VertID(_id, isShape, type), point);
        }
        else
        {
            _dstVert = new VertInf(_router, VertID(_id, isShape, type), point);
        }
        _dstVert->visDirections = connEnd.directions();
        
        altered = _dstVert;
        // partner = _srcVert;
    }
    
    // XXX: Seems to be faster to just remove the edges and recreate
    bool isConn = true;
    altered->removeFromGraph(isConn);

    makePathInvalid();
    _router->setStaticGraphInvalidated(true);
}


void ConnRef::setEndpoints(const ConnEnd& srcPoint, const ConnEnd& dstPoint)
{
    _router->modifyConnector(this, VertID::src, srcPoint);
    _router->modifyConnector(this, VertID::tar, dstPoint);
}


void ConnRef::setEndpoint(const unsigned int type, const ConnEnd& connEnd)
{
    _router->modifyConnector(this, type, connEnd);
}


void ConnRef::setSourceEndpoint(const ConnEnd& srcPoint)
{
    _router->modifyConnector(this, VertID::src, srcPoint);
}


void ConnRef::setDestEndpoint(const ConnEnd& dstPoint)
{
    _router->modifyConnector(this, VertID::tar, dstPoint);
}


void ConnRef::updateEndPoint(const unsigned int type, const ConnEnd& connEnd)
{
    common_updateEndPoint(type, connEnd);

    if (_router->_polyLineRouting)
    {
        bool knownNew = true;
        bool genContains = true;
        if (type == (unsigned int) VertID::src)
        {
            vertexVisibility(_srcVert, _dstVert, knownNew, genContains);
        }
        else
        {
            vertexVisibility(_dstVert, _srcVert, knownNew, genContains);
        }
    }
}


bool ConnRef::setEndpoint(const unsigned int type, const VertID& pointID,
        Point *pointSuggestion)
{
    VertInf *vInf = _router->vertices.getVertexByID(pointID);
    if (vInf == NULL)
    {
        return false;
    }
    Point& point = vInf->point;
    if (pointSuggestion)
    {
        if (euclideanDist(point, *pointSuggestion) > 0.5)
        {
            return false;
        }
    }

    common_updateEndPoint(type, point);

    // Give this visibility just to the point it is over.
    EdgeInf *edge = new EdgeInf(
            (type == VertID::src) ? _srcVert : _dstVert, vInf);
    // XXX: We should be able to set this to zero, but can't due to 
    //      assumptions elsewhere in the code.
    edge->setDist(0.001);

    _router->processTransaction();
    return true;
}


void ConnRef::setEndPointId(const unsigned int type, const unsigned int id)
{
    if (type == (unsigned int) VertID::src)
    {
        _srcId = id;
    }
    else  // if (type == (unsigned int) VertID::dst)
    {
        _dstId = id;
    }
}


unsigned int ConnRef::getSrcShapeId(void)
{
    return _srcId;
}


unsigned int ConnRef::getDstShapeId(void)
{
    return _dstId;
}


void ConnRef::makeActive(void)
{
    COLA_ASSERT(!_active);
    
    // Add to connRefs list.
    _pos = _router->connRefs.insert(_router->connRefs.begin(), this);
    _active = true;
}


void ConnRef::makeInactive(void)
{
    if (_active) {
        // Remove from connRefs list.
        _router->connRefs.erase(_pos);
        _active = false;
    }
}


void ConnRef::freeRoutes(void)
{
    _route.clear();
    _display_route.clear();
}
    

const PolyLine& ConnRef::route(void) const
{
    return _route;
}


PolyLine& ConnRef::routeRef(void)
{
    return _route;
}


void ConnRef::set_route(const PolyLine& route)
{
    if (&_display_route == &route)
    {
        db_printf("Error:\tTrying to update libavoid route with itself.\n");
        return;
    }
    _display_route.ps = route.ps;

    //_display_route.clear();
}


Polygon& ConnRef::displayRoute(void)
{
    if (_display_route.empty())
    {
        // No displayRoute is set.  Simplify the current route to get it.
        _display_route = _route.simplify();
    }
    return _display_route;
}


void ConnRef::calcRouteDist(void)
{
    double (*dist)(const Point& a, const Point& b) = 
            (_type == ConnType_PolyLine) ? euclideanDist : manhattanDist;

    _route_dist = 0;
    for (size_t i = 1; i < _route.size(); ++i)
    {
        _route_dist += dist(_route.at(i), _route.at(i - 1));
    }
}


bool ConnRef::needsRepaint(void) const
{
    return _needs_repaint;
}


unsigned int ConnRef::id(void) const
{
    return _id;
}


VertInf *ConnRef::src(void)
{
    return _srcVert;
}

    
VertInf *ConnRef::dst(void)
{
    return _dstVert;
}


VertInf *ConnRef::start(void)
{
    return _startVert;
}


bool ConnRef::isInitialised(void)
{
    return _initialised;
}


void ConnRef::unInitialise(void)
{
    _router->vertices.removeVertex(_srcVert);
    _router->vertices.removeVertex(_dstVert);
    makeInactive();
    _initialised = false;
}


void ConnRef::removeFromGraph(void)
{
    if (_srcVert) {
        _srcVert->removeFromGraph();
    }
    if (_dstVert) {
        _dstVert->removeFromGraph();
    }
}


void ConnRef::setCallback(void (*cb)(void *), void *ptr)
{
    _callback = cb;
    _connector = ptr;
}


void ConnRef::performCallback(void)
{
    if (_callback) 
    {
        _callback(_connector);
    }
}


void ConnRef::makePathInvalid(void)
{
    _needs_reroute_flag = true;
}


Router *ConnRef::router(void) const
{
    return _router;
}


bool ConnRef::generatePath(Point /*p0*/, Point /*p1*/)
{
    // XXX Code to determine when connectors really need to be rerouted
    //     does not yet work for orthogonal connectors.
    if (_type != ConnType_Orthogonal)
    {
        if (!_false_path && !_needs_reroute_flag) 
        {
            // This connector is up to date.
            return false;
        }
    }

    bool result = generatePath();

    return result;
}


// Validates a bend point on a path to check it does not form a zigzag corner.
// a, b, c are consecutive points on the path.  d and e are b's neighbours,
// forming the shape corner d-b-e.
//
bool validateBendPoint(VertInf *aInf, VertInf *bInf, VertInf *cInf)
{
    bool bendOkay = true;

    if ((aInf == NULL) || (cInf == NULL))
    {
        // Not a bendpoint, i.e., the end of the connector, so don't test.
        return bendOkay;
    }

    COLA_ASSERT(bInf != NULL);
    VertInf *dInf = bInf->shPrev;
    VertInf *eInf = bInf->shNext;
    COLA_ASSERT(dInf != NULL);
    COLA_ASSERT(eInf != NULL);

    Point& a = aInf->point;
    Point& b = bInf->point;
    Point& c = cInf->point;
    Point& d = dInf->point;
    Point& e = eInf->point;

    if ((a == b) || (b == c))
    {
        return bendOkay;
    }

#ifdef PATHDEBUG
    db_printf("a=(%g, %g)\n", a.x, a.y);
    db_printf("b=(%g, %g)\n", b.x, b.y);
    db_printf("c=(%g, %g)\n", c.x, c.y);
    db_printf("d=(%g, %g)\n", d.x, d.y);
    db_printf("e=(%g, %g)\n", e.x, e.y);
#endif
    // Check angle:
    int abc = vecDir(a, b, c);
#ifdef PATHDEBUG
    db_printf("(abc == %d) ", abc);
#endif
   
    if (abc == 0)
    {
        // The three consecutive point on the path are in a line.
        // Thus, there should always be an equally short path that
        // skips this bend point.
        bendOkay = false;
    }
    else // (abc != 0)
    {
        COLA_ASSERT(vecDir(d, b, e) > 0);
        int abe = vecDir(a, b, e);
        int abd = vecDir(a, b, d);
        int bce = vecDir(b, c, e);
        int bcd = vecDir(b, c, d);
#ifdef PATHDEBUG
        db_printf("&& (abe == %d) && (abd == %d) &&\n(bce == %d) && (bcd == %d)",
                abe, abd, bce, bcd);
#endif

        bendOkay = false;
        if (abe > 0)
        {
            if ((abc > 0) && (abd >= 0) && (bce >= 0))
            {
                bendOkay = true;
            }
        }
        else if (abd < 0)
        {
            if ((abc < 0) && (abe <= 0) && (bcd <= 0))
            {
                bendOkay = true;
            }
        }
    }
#ifdef PATHDEBUG
    db_printf("\n");
#endif
    return bendOkay;
}


bool ConnRef::generatePath(void)
{
    if (!_false_path && !_needs_reroute_flag) 
    {
        // This connector is up to date.
        return false;
    }

    if (!_dstVert || !_srcVert)
    {
        // Connector is not fully initialised..
        return false;
    }
    
    //COLA_ASSERT(_srcVert->point != _dstVert->point);

    _false_path = false;
    _needs_reroute_flag = false;

    VertInf *tar = _dstVert;
    _startVert = _srcVert;

    bool *flag = &(_needs_reroute_flag);
   
    size_t existingPathStart = 0;
    const PolyLine& currRoute = route();
    if (_router->RubberBandRouting)
    {
        COLA_ASSERT(_router->IgnoreRegions == true);

#ifdef PATHDEBUG
        db_printf("\n");
        _srcVert->id.db_print();
        db_printf(": %g, %g\n", _srcVert->point.x, _srcVert->point.y);
        tar->id.db_print();
        db_printf(": %g, %g\n", tar->point.x, tar->point.y);
        for (size_t i = 0; i < currRoute.ps.size(); ++i)
        {
            db_printf("%g, %g  ", currRoute.ps[i].x, currRoute.ps[i].y);
        }
        db_printf("\n");
#endif
        if (currRoute.size() > 2)
        {
            if (_srcVert->point == currRoute.ps[0])
            {
                existingPathStart = currRoute.size() - 2;
                COLA_ASSERT(existingPathStart != 0);
                const Point& pnt = currRoute.at(existingPathStart);
                bool isShape = true;
                VertID vID(pnt.id, isShape, pnt.vn);

                _startVert = _router->vertices.getVertexByID(vID);
            }
        }
    }
    //db_printf("GO\n");
    //db_printf("src: %X strt: %X dst: %x\n", (int) _srcVert, (int) _startVert, (int) _dstVert);
    bool found = false;
    while (!found)
    {
        makePath(this, flag);
        for (VertInf *i = tar; i != NULL; i = i->pathNext)
        {
            if (i == _srcVert)
            {
                found = true;
                break;
            }
        }
        if (!found)
        {
            if (existingPathStart == 0)
            {
                break;
            }
#ifdef PATHDEBUG
            db_printf("BACK\n");
#endif
            existingPathStart--;
            const Point& pnt = currRoute.at(existingPathStart);
            bool isShape = (existingPathStart > 0);
            VertID vID(pnt.id, isShape, pnt.vn);

            _startVert = _router->vertices.getVertexByID(vID);
            COLA_ASSERT(_startVert);
        }
        else if (_router->RubberBandRouting)
        {
            // found.
            bool unwind = false;

#ifdef PATHDEBUG
            db_printf("\n\n\nSTART:\n\n");
#endif
            VertInf *prior = NULL;
            for (VertInf *curr = tar; curr != _startVert->pathNext; 
                    curr = curr->pathNext)
            {
                if (!validateBendPoint(curr->pathNext, curr, prior))
                {
                    unwind = true;
                    break;
                }
                prior = curr;
            }
            if (unwind)
            {
#ifdef PATHDEBUG
                db_printf("BACK II\n");
#endif
                if (existingPathStart == 0)
                {
                    break;
                }
                existingPathStart--;
                const Point& pnt = currRoute.at(existingPathStart);
                bool isShape = (existingPathStart > 0);
                VertID vID(pnt.id, isShape, pnt.vn);

                _startVert = _router->vertices.getVertexByID(vID);
                COLA_ASSERT(_startVert);

                found = false;
            }
        }
    }

    
    bool result = true;
    
    int pathlen = 1;
    for (VertInf *i = tar; i != _srcVert; i = i->pathNext)
    {
        pathlen++;
        if (i == NULL)
        {
            db_printf("Warning: Path not found...\n");
            pathlen = 2;
            tar->pathNext = _srcVert;
            if ((_type == ConnType_PolyLine) && _router->InvisibilityGrph)
            {
                // TODO:  Could we know this edge already?
                EdgeInf *edge = EdgeInf::existingEdge(_srcVert, tar);
                COLA_ASSERT(edge != NULL);
                edge->addCycleBlocker();
            }
            break;
        }
        // Check we don't have an apparent infinite connector path.
        COLA_ASSERT(pathlen < 200);
    }
    std::vector<Point> path(pathlen);

    int j = pathlen - 1;
    for (VertInf *i = tar; i != _srcVert; i = i->pathNext)
    {
        if (_router->InvisibilityGrph && (_type == ConnType_PolyLine))
        {
            // TODO: Again, we could know this edge without searching.
            EdgeInf *edge = EdgeInf::existingEdge(i, i->pathNext);
            COLA_ASSERT(edge != NULL);
            edge->addConn(flag);
        }
        else
        {
            _false_path = true;
        }
        path[j] = i->point;
        if (i->id.isShape)
        {
            path[j].id = i->id.objID;
            path[j].vn = i->id.vn;
        }
        else
        {
            path[j].id = _id;
            path[j].vn = kUnassignedVertexNumber;
        }
        j--;

        if (i->pathNext && (i->pathNext->point == i->point))
        {
            if (i->pathNext->id.isShape && i->id.isShape)
            {
                // Check for consecutive points on opposite 
                // corners of two touching shapes.
                COLA_ASSERT(abs(i->pathNext->id.objID - i->id.objID) != 2);
            }
        }
    }
    path[0] = _srcVert->point;
    // Use topbit to differentiate between start and end point of connector.
    // They need unique IDs for nudging.
    unsigned int topbit = ((unsigned int) 1) << 31;
    path[0].id = _id | topbit; 
    path[0].vn = kUnassignedVertexNumber;

    // Would clear visibility for endpoints here if required.

    freeRoutes();
    PolyLine& output_route = _route;
    output_route.ps = path;
 
#ifdef PATHDEBUG
    db_printf("Output route:\n");
    for (size_t i = 0; i < output_route.ps.size(); ++i)
    {
        db_printf("[%d,%d] %g, %g   ", output_route.ps[i].id, 
                output_route.ps[i].vn, output_route.ps[i].x, 
                output_route.ps[i].y);
    }
    db_printf("\n\n");
#endif

    return result;
}


void ConnRef::setHateCrossings(bool value)
{
    _hateCrossings = value;
}


bool ConnRef::doesHateCrossings(void)
{
    return _hateCrossings;
}


PtOrder::~PtOrder()
{
    // Free the PointRep lists.
    for (int dim = 0; dim < 2; ++dim)
    {
        PointRepList::iterator curr = connList[dim].begin();
        while (curr != connList[dim].end())
        {
            PointRep *doomed = *curr;
            curr = connList[dim].erase(curr);
            delete doomed;
        }
    }
}

bool PointRep::follow_inner(PointRep *target)
{
    if (this == target)
    {
        return true;
    }
    else
    {
        for (PointRepSet::iterator curr = inner_set.begin(); 
                curr != inner_set.end(); ++curr)
        {
            if ((*curr)->follow_inner(target))
            {
                return true;
            }
        }
    }
    return false;
}


int PtOrder::positionFor(const ConnRef *conn, const size_t dim) const
{
    int position = 0;
    for (PointRepList::const_iterator curr = connList[dim].begin(); 
            curr != connList[dim].end(); ++curr)
    {
        if ((*curr)->conn == conn)
        {
            return position;
        }
        ++position;
    }
    // Not found.
    return -1;
}


bool PtOrder::addPoints(const int dim, PtConnPtrPair innerArg, 
        PtConnPtrPair outerArg, bool swapped)
{
    PtConnPtrPair inner = (swapped) ? outerArg : innerArg;
    PtConnPtrPair outer = (swapped) ? innerArg : outerArg;
    COLA_ASSERT(inner != outer);

    //printf("addPoints(%d, [%g, %g]-%X, [%g, %g]-%X)\n", dim,
    //        inner->x, inner->y, (int) inner, outer->x, outer->y, (int) outer);

    PointRep *innerPtr = NULL;
    PointRep *outerPtr = NULL;
    for (PointRepList::iterator curr = connList[dim].begin(); 
            curr != connList[dim].end(); ++curr)
    {
        if ((*curr)->point == inner.first)
        {
            innerPtr = *curr;
        }
        if ((*curr)->point == outer.first)
        {
            outerPtr = *curr;
        }
    }
    
    if (innerPtr == NULL)
    {
        innerPtr = new PointRep(inner.first, inner.second);
        connList[dim].push_back(innerPtr);
    }
    
    if (outerPtr == NULL)
    {
        outerPtr = new PointRep(outer.first, outer.second);
        connList[dim].push_back(outerPtr);
    }
    // TODO COLA_ASSERT(innerPtr->inner_set.find(outerPtr) == innerPtr->inner_set.end());
    bool cycle = innerPtr->follow_inner(outerPtr);
    if (cycle)
    {
        // Must reverse to avoid a cycle.
        innerPtr->inner_set.insert(outerPtr);
    }
    else
    {
        outerPtr->inner_set.insert(innerPtr);
    }
    return cycle;
}


// Assuming that addPoints has been called for each pair of points in the 
// shared path at that corner, then the contents of inner_set can be used 
// to determine the correct ordering.
static bool pointRepLessThan(PointRep *r1, PointRep *r2)
{
    size_t r1less = r1->inner_set.size();
    size_t r2less = r2->inner_set.size();
    //COLA_ASSERT(r1less != r2less);
    
    return (r1less > r2less);
}


void PtOrder::sort(const int dim)
{
    connList[dim].sort(pointRepLessThan);
}


// Returns a vertex number representing a point on the line between 
// two shape corners, represented by p0 and p1.
//
static int midVertexNumber(const Point& p0, const Point& p1, const Point& c)
{
    if (c.vn != kUnassignedVertexNumber)
    {
        // The split point is a shape corner, so doesn't need its 
        // vertex number adjusting.
        return c.vn;
    }
    if ((p0.vn >= 4) && (p0.vn < kUnassignedVertexNumber))
    {
        // The point next to this has the correct nudging direction,
        // so use that.
        return p0.vn;
    }
    if ((p1.vn >= 4) && (p1.vn < kUnassignedVertexNumber))
    {
        // The point next to this has the correct nudging direction,
        // so use that.
        return p1.vn;
    }
    if ((p0.vn < 4) && (p1.vn < 4))
    {
        if (p0.vn != p1.vn)
        {
            return p0.vn;
        }
        // Splitting between two ordinary shape corners.
        int vn_mid = std::min(p0.vn, p1.vn);
        if ((std::max(p0.vn, p1.vn) == 3) && (vn_mid == 0))
        {
            vn_mid = 3; // Next vn is effectively 4.
        }
        return vn_mid + 4;
    }
    COLA_ASSERT((p0.x == p1.x) || (p0.y == p1.y));
    if (p0.vn != kUnassignedVertexNumber)
    {
        if (p0.x == p1.x)
        {
            if ((p0.vn == 2) || (p0.vn == 3))
            {
                return 6;
            }
            return 4;
        }
        else
        {
            if ((p0.vn == 0) || (p0.vn == 3))
            {
                return 7;
            }
            return 5;
        }
    }
    else if (p1.vn != kUnassignedVertexNumber)
    {
        if (p0.x == p1.x)
        {
            if ((p1.vn == 2) || (p1.vn == 3))
            {
                return 6;
            }
            return 4;
        }
        else
        {
            if ((p1.vn == 0) || (p1.vn == 3))
            {
                return 7;
            }
            return 5;
        }
    }

    // Shouldn't both be new (kUnassignedVertexNumber) points.
    db_printf("midVertexNumber(): p0.vn and p1.vn both = "
            "kUnassignedVertexNumber\n");
    db_printf("p0.vn %d p1.vn %d\n", p0.vn, p1.vn);
    return kUnassignedVertexNumber;
}


// Break up overlapping parallel segments that are not the same edge in 
// the visibility graph, i.e., where one segment is a subsegment of another.
void splitBranchingSegments(Avoid::Polygon& poly, bool polyIsConn,
        Avoid::Polygon& conn, const double tolerance)
{
    for (std::vector<Avoid::Point>::iterator i = conn.ps.begin(); 
            i != conn.ps.end(); ++i)
    {
        if (i == conn.ps.begin())
        {
            // Skip the first point.
            // There are points-1 segments in a connector.
            continue;
        }

        for (std::vector<Avoid::Point>::iterator j = poly.ps.begin(); 
                j != poly.ps.end(); )
        {
            if (polyIsConn && (j == poly.ps.begin()))
            {
                // Skip the first point.
                // There are points-1 segments in a connector.
                ++j;
                continue;
            }
            Point& c0 = *(i - 1);
            Point& c1 = *i;

            Point& p0 = (j == poly.ps.begin()) ? poly.ps.back() : *(j - 1);
            Point& p1 = *j;

            // Check the first point of the first segment.
            if (((i - 1) == conn.ps.begin()) && 
                    pointOnLine(p0, p1, c0, tolerance))
            {
                //db_printf("add to poly %g %g\n", c0.x, c0.y);
                
                c0.vn = midVertexNumber(p0, p1, c0);
                j = poly.ps.insert(j, c0);
                if (j != poly.ps.begin())
                {
                    --j;
                }
                continue;
            }
            // And the second point of every segment.
            if (pointOnLine(p0, p1, c1, tolerance))
            {
                //db_printf("add to poly %g %g\n", c1.x, c1.y);
                
                c1.vn = midVertexNumber(p0, p1, c1);
                j = poly.ps.insert(j, c1);
                if (j != poly.ps.begin())
                {
                    --j;
                }
                continue;
            }

            // Check the first point of the first segment.
            if (polyIsConn && ((j - 1) == poly.ps.begin()) && 
                        pointOnLine(c0, c1, p0, tolerance))
            {
                //db_printf("add to conn %g %g\n", p0.x, p0.y);

                p0.vn = midVertexNumber(c0, c1, p0);
                i = conn.ps.insert(i, p0);
                continue;
            }
            // And the second point of every segment.
            if (pointOnLine(c0, c1, p1, tolerance))
            {
                //db_printf("add to conn %g %g\n", p1.x, p1.y);

                p1.vn = midVertexNumber(c0, c1, p1);
                i = conn.ps.insert(i, p1);
            }
            ++j;
        }
    }
}


static int segDir(const Point& p1, const Point& p2)
{
    int result = 1;
    if (p1.x == p2.x)
    {
        if (p2.y > p1.y)
        {
            result = -1;
        }
    }
    else if (p1.y == p2.y)
    {
        if (p2.x < p1.x)
        {
            result = -1;
        }
    }
    return result;
}


// Works out if the segment conn[cIndex-1]--conn[cIndex] really crosses poly.
// This does not not count non-crossing shared paths as crossings.
// poly can be either a connector (polyIsConn = true) or a cluster
// boundary (polyIsConn = false).
//
CrossingsInfoPair countRealCrossings(Avoid::Polygon& poly, 
        bool polyIsConn, Avoid::Polygon& conn, size_t cIndex, 
        bool checkForBranchingSegments, const bool finalSegment, 
        PointSet *crossingPoints, PtOrderMap *pointOrders, 
        ConnRef *polyConnRef, ConnRef *connConnRef)
{
    unsigned int crossingFlags = CROSSING_NONE;
    if (checkForBranchingSegments)
    {
        size_t conn_pn = conn.size();
        // XXX When doing the pointOnLine test we allow the points to be 
        // slightly non-collinear.  This addresses a problem with clustered
        // routing where connectors could otherwise route cheaply through
        // shape corners that were not quite on the cluster boundary, but
        // reported to be on there by the line segment intersection code,
        // which I suspect is not numerically accurate enough.  This occured
        // for points that only differed by about 10^-12 in the y-dimension.
        double tolerance = (!polyIsConn) ? 0.00001 : 0.0;
        splitBranchingSegments(poly, polyIsConn, conn, tolerance);
        // cIndex is going to be the last, so take into account added points.
        cIndex += (conn.size() - conn_pn);
    }
    COLA_ASSERT(cIndex >= 1);
    COLA_ASSERT(cIndex < conn.size());

    bool polyIsOrthogonal = (polyConnRef && 
            (polyConnRef->routingType() == ConnType_Orthogonal));
    bool connIsOrthogonal = (connConnRef &&
            (connConnRef->routingType() == ConnType_Orthogonal));

    size_t poly_size = poly.size();
    int crossingCount = 0;
    std::vector<Avoid::Point *> c_path;
    std::vector<Avoid::Point *> p_path;

    Avoid::Point& a1 = conn.ps[cIndex - 1];
    Avoid::Point& a2 = conn.ps[cIndex];
    //db_printf("a1: %g %g\n", a1.x, a1.y);
    //db_printf("a2: %g %g\n", a2.x, a2.y);

    for (size_t j = ((polyIsConn) ? 1 : 0); j < poly_size; ++j)
    {
        Avoid::Point& b1 = poly.ps[(j - 1 + poly_size) % poly_size];
        Avoid::Point& b2 = poly.ps[j];
        //db_printf("b1: %g %g\n", b1.x, b1.y);
        //db_printf("b2: %g %g\n", b2.x, b2.y);

        p_path.clear();
        c_path.clear();
        bool converging = false;

        const bool a1_eq_b1 = (a1 == b1);
        const bool a2_eq_b1 = (a2 == b1);
        const bool a2_eq_b2 = (a2 == b2);
        const bool a1_eq_b2 = (a1 == b2);

        if ( (a1_eq_b1 && a2_eq_b2) ||
             (a2_eq_b1 && a1_eq_b2) )
        {
            if (finalSegment)
            {
                converging = true;
            }
            else
            {
                // Route along same segment: no penalty.  We detect
                // crossovers when we see the segments diverge.
                continue;
            }
        }
        else if (a2_eq_b1 || a2_eq_b2 || a1_eq_b2)
        {
            // Each crossing that is at a vertex in the 
            // visibility graph gets noticed four times.
            // We ignore three of these cases.
            // This also catches the case of a shared path,
            // but this is one that terminates at a common
            // endpoint, so we don't care about it.
            continue;
        }
    
        if (a1_eq_b1 || converging)
        {
            if (!converging)
            {
                if (polyIsConn && (j == 1))
                {
                    // Can't be the end of a shared path or crossing path 
                    // since the common point is the first point of the 
                    // connector path.  This is not a shared path at all.
                    continue;
                }

                Avoid::Point& b0 = poly.ps[(j - 2 + poly_size) % poly_size];
                // The segments share an endpoint -- a1==b1.
                if (a2 == b0)
                {
                    // a2 is not a split, continue.
                    continue;
                }
            }
            
            // If here and not converging, then we know that a2 != b2
            // And a2 and its pair in b are a split.
            COLA_ASSERT(converging || !a2_eq_b2);

            bool shared_path = false;
            
            // Initial values here don't matter. They are only used after 
            // being set to sensible values, but we set them to stop a MSVC
            // warning.
            bool p_dir_back;
            int p_dir = 0;
            int trace_c = 0;
            int trace_p = 0;
            
            if (converging)
            {
                // Determine direction we have to look through
                // the points of connector b.
                p_dir_back = a2_eq_b2 ? true : false;
                p_dir = p_dir_back ? -1 : 1;
                trace_c = (int) cIndex;
                trace_p = (int) j;
                if (!p_dir_back)
                {
                    if (finalSegment)
                    {
                        trace_p--;
                    }
                    else
                    {   
                        trace_c--;
                    }
                }

                shared_path = true;
            }
            else if (cIndex >= 2)
            {
                Avoid::Point& b0 = poly.ps[(j - 2 + poly_size) % poly_size];
                Avoid::Point& a0 = conn.ps[cIndex - 2];
            
                //db_printf("a0: %g %g\n", a0.x, a0.y);
                //db_printf("b0: %g %g\n", b0.x, b0.y);

                if ((a0 == b2) || (a0 == b0))
                {
                    // Determine direction we have to look through
                    // the points of connector b.
                    p_dir_back = (a0 == b0) ? true : false;
                    p_dir = p_dir_back ? -1 : 1;
                    trace_c = (int) cIndex;
                    trace_p = (int) (p_dir_back ? j : j - 2);
                    
                    shared_path = true;
                }
            }    

            if (shared_path)
            {
                crossingFlags |= CROSSING_SHARES_PATH;
                // Shouldn't be here if p_dir is still equal to zero.
                COLA_ASSERT(p_dir != 0);

                // Build the shared path, including the diverging points at
                // each end if the connector does not end at a common point.
                while ( (trace_c >= 0) && (!polyIsConn || 
                            ((trace_p >= 0) && (trace_p < (int) poly_size))) )
                {
                    // If poly is a cluster boundary, then it is a closed 
                    // poly-line and so it wraps arounds.
                    size_t index_p = (size_t)
                            ((trace_p + (2 * poly_size)) % poly_size);
                    size_t index_c = (size_t) trace_c;
                    c_path.push_back(&conn.ps[index_c]);
                    p_path.push_back(&poly.ps[index_p]);
                    if ((c_path.size() > 1) && 
                            (conn.ps[index_c] != poly.ps[index_p]))
                    {
                        // Points don't match, so break out of loop.
                        break;
                    }
                    trace_c--;
                    trace_p += p_dir;
                }

                // Are there diverging points at the ends of the shared path.
                bool front_same = (*(c_path.front()) == *(p_path.front()));
                bool back_same  = (*(c_path.back())  == *(p_path.back()));

                size_t size = c_path.size();
                
                // Check to see if these share a fixed segment.
                if (polyIsOrthogonal && connIsOrthogonal)
                {
                    size_t startPt = (front_same) ? 0 : 1;
                    if (c_path[startPt]->x == c_path[startPt + 1]->x)
                    {
                        // Vertical
                        double xPos = c_path[startPt]->x;
                        // See if this is inline with either the start
                        // or end point of both connectors.
                        if ( ((xPos == poly.ps[0].x) || 
                                (xPos == poly.ps[poly_size - 1].x)) &&
                             ((xPos == conn.ps[0].x) || 
                                (xPos == conn.ps[cIndex].x)) )
                        {
                            crossingFlags |= CROSSING_SHARES_FIXED_SEGMENT;
                        }
                    }
                    else
                    {
                        // Horizontal
                        double yPos = c_path[startPt]->y;
                        // See if this is inline with either the start
                        // or end point of both connectors.
                        if ( ((yPos == poly.ps[0].y) || 
                                (yPos == poly.ps[poly_size - 1].y)) &&
                             ((yPos == conn.ps[0].y) || 
                                (yPos == conn.ps[cIndex].y)) )
                        {
                            crossingFlags |= CROSSING_SHARES_FIXED_SEGMENT;
                        }
                    }
                }

                int prevTurnDir = -1;
                int startCornerSide = 1;
                int endCornerSide = 1;
                bool reversed = false;
                if (!front_same)
                {
                    // If there is a divergence at the beginning, 
                    // then order the shared path based on this.
                    prevTurnDir = vecDir(*c_path[0], *c_path[1], *c_path[2]);
                    startCornerSide = Avoid::cornerSide(*c_path[0], *c_path[1], 
                            *c_path[2], *p_path[0]) 
                        * segDir(*c_path[1], *c_path[2]);
                    reversed = (startCornerSide != -prevTurnDir);
                }
                if (!back_same)
                {
                    // If there is a divergence at the end of the path, 
                    // then order the shared path based on this.
                    prevTurnDir = vecDir(*c_path[size - 3],
                            *c_path[size - 2], *c_path[size - 1]);
                    endCornerSide = Avoid::cornerSide(*c_path[size - 3], 
                            *c_path[size - 2], *c_path[size - 1], 
                            *p_path[size - 1])
                        * segDir(*c_path[size - 3], *c_path[size - 2]);
                    reversed = (endCornerSide != -prevTurnDir);
                }
                else
                {
                    endCornerSide = startCornerSide;
                }
                if (front_same)
                {
                    startCornerSide = endCornerSide;
                }
                
                if (front_same || back_same)
                {
                    crossingFlags |= CROSSING_SHARES_PATH_AT_END;
                }
                else if (polyIsOrthogonal && connIsOrthogonal)
                {
                    int cStartDir = vecDir(*c_path[0], *c_path[1], *c_path[2]);
                    int pStartDir = vecDir(*p_path[0], *p_path[1], *p_path[2]);
                    if ((cStartDir != 0) && (cStartDir == -pStartDir))
                    {
                        // The start segments diverge at 180 degrees to each 
                        // other.  So order based on not introducing overlap
                        // of the diverging segments when these are nudged
                        // apart.
                        startCornerSide = -cStartDir * 
                                segDir(*c_path[1], *c_path[2]);
                    }
                    else 
                    {
                        int cEndDir = vecDir(*c_path[size - 3], 
                                *c_path[size - 2], *c_path[size - 1]);
                        int pEndDir = vecDir(*p_path[size - 3], 
                                *p_path[size - 2], *p_path[size - 1]);
                        if ((cEndDir != 0) && (cEndDir == -pEndDir))
                        {
                            // The end segments diverge at 180 degrees to 
                            // each other.  So order based on not introducing 
                            // overlap of the diverging segments when these 
                            // are nudged apart.
                            startCornerSide = -cEndDir * segDir(
                                    *c_path[size - 3], *c_path[size - 2]);
                        }
                    }
                }

#if 0
                prevTurnDir = 0;
                if (pointOrders)
                {
                    // Return the ordering for the shared path.
                    COLA_ASSERT(c_path.size() > 0 || back_same);
                    size_t adj_size = (c_path.size() - ((back_same) ? 0 : 1));
                    for (size_t i = (front_same) ? 0 : 1; i < adj_size; ++i)
                    {
                        Avoid::Point& an = *(c_path[i]);
                        Avoid::Point& bn = *(p_path[i]);
                        int currTurnDir = ((i > 0) && (i < (adj_size - 1))) ?  
                                vecDir(*c_path[i - 1], an,
                                       *c_path[i + 1]) : 0;
                        VertID vID(an.id, true, an.vn);
                        if ( (currTurnDir == (-1 * prevTurnDir)) &&
                                (currTurnDir != 0) && (prevTurnDir != 0) )
                        {
                            // The connector turns the opposite way around 
                            // this shape as the previous bend on the path,
                            // so reverse the order so that the inner path
                            // become the outer path and vice versa.
                            reversed = !reversed;
                        }
                        bool orderSwapped = (*pointOrders)[an].addPoints(
                                &bn, &an, reversed);
                        if (orderSwapped)
                        {
                            // Reverse the order for later points.
                            reversed = !reversed;
                        }
                        prevTurnDir = currTurnDir;
                    }
                }
#endif
                prevTurnDir = 0;
                if (pointOrders)
                {
                    reversed = false;
                    size_t startPt = (front_same) ? 0 : 1;
                    
                    // Orthogonal should always have at least one segment.
                    COLA_ASSERT(c_path.size() > (startPt + 1));
                    
                    if (startCornerSide > 0)
                    {
                        reversed = !reversed;
                    }

                    int prevDir = 0;
                    // Return the ordering for the shared path.
                    COLA_ASSERT(c_path.size() > 0 || back_same);
                    size_t adj_size = (c_path.size() - ((back_same) ? 0 : 1));
                    for (size_t i = (front_same) ? 0 : 1; i < adj_size; ++i)
                    {
                        Avoid::Point& an = *(c_path[i]);
                        Avoid::Point& bn = *(p_path[i]);
                        COLA_ASSERT(an == bn);

                        int thisDir = prevDir;
                        if ((i > 0) && (*(c_path[i - 1]) == *(p_path[i - 1])))
                        {
                            thisDir = segDir(*c_path[i - 1], *c_path[i]);
                        }

                        if (thisDir != prevDir)
                        {
                            reversed = !reversed;
                        }
                        prevDir = thisDir;

                        if (i > startPt)
                        {
                            Avoid::Point& ap = *(c_path[i - 1]);
                            Avoid::Point& bp = *(p_path[i - 1]);
                            int orientation = (ap.x == an.x) ? 0 : 1;
                            //printf("prevOri %d\n", prevOrientation);
                            //printf("1: %X, %X\n", (int) &(bn), (int) &(an));
                            bool orderSwapped = (*pointOrders)[an].addPoints(
                                    orientation, 
                                    std::make_pair(&bn, polyConnRef), 
                                    std::make_pair(&an, connConnRef), 
                                    reversed);
                            if (orderSwapped)
                            {
                                // Reverse the order for later points.
                                reversed = !reversed;
                            }
                            COLA_ASSERT(ap == bp);
                            //printf("2: %X, %X\n", (int) &bp, (int) &ap);
                            orderSwapped = (*pointOrders)[ap].addPoints(
                                    orientation, 
                                    std::make_pair(&bp, polyConnRef), 
                                    std::make_pair(&ap, connConnRef), 
                                    reversed);
                            COLA_ASSERT(!orderSwapped);
                        }
                    }
                }
#if 0
                    int ymod = -1;
                    if ((id.vn == 1) || (id.vn == 2))
                    {
                        // bottom.
                        ymod = +1;
                    }
                    
                    int xmod = -1;
                    if ((id.vn == 0) || (id.vn == 1))
                    {
                        // right.
                        xmod = +1;
                    }
                    if(id.vn > 3)
                    {
                        xmod = ymod = 0;
                        if (id.vn == 4)
                        {
                            // right.
                            xmod = +1;
                        }
                        else if (id.vn == 5)
                        {
                            // bottom.
                            ymod = +1;
                        }
                        else if (id.vn == 6)
                        {
                            // left.
                            xmod = -1;
                        }
                        else if (id.vn == 7)
                        {
                            // top.
                            ymod = -1;
                        }
                    }
#endif
 
                if (endCornerSide != startCornerSide)
                {
                    // Mark that the shared path crosses.
                    //db_printf("shared path crosses.\n");
                    crossingCount += 1;
                    if (crossingPoints)
                    {
                        crossingPoints->insert(*c_path[1]);
                    }
                }
                crossingFlags |= CROSSING_TOUCHES;
            }
            else if (cIndex >= 2)
            {
                // The connectors cross or touch at this point.
                //db_printf("Cross or touch at point... \n");
                
                // Crossing shouldn't be at an endpoint.
                COLA_ASSERT(cIndex >= 2);
                COLA_ASSERT(polyIsConn && (j >= 2));

                Avoid::Point& b0 = poly.ps[(j - 2 + poly_size) % poly_size];
                Avoid::Point& a0 = conn.ps[cIndex - 2];
            
                int side1 = Avoid::cornerSide(a0, a1, a2, b0);
                int side2 = Avoid::cornerSide(a0, a1, a2, b2);
                if (side1 != side2)
                {
                    // The connectors cross at this point.
                    //db_printf("cross.\n");
                    crossingCount += 1;
                    if (crossingPoints)
                    {
                        crossingPoints->insert(a1);
                    }
                }

                crossingFlags |= CROSSING_TOUCHES;
                if (pointOrders)
                {
                    if (polyIsOrthogonal && connIsOrthogonal)
                    {
                        // Orthogonal case:
                        // Just order based on which comes from the left and
                        // top in each dimension because this can only be two
                        // L-shaped segments touching at the bend.
                        bool reversedX = ((a0.x < a1.x) || (a2.x < a1.x));
                        bool reversedY = ((a0.y < a1.y) || (a2.y < a1.y));
                        // XXX: Why do we need to invert the reversed values 
                        //      here?  Are they wrong for orthogonal points
                        //      in the other places?
                        (*pointOrders)[b1].addPoints(0, 
                                std::make_pair(&b1, polyConnRef), 
                                std::make_pair(&a1, connConnRef), 
                                !reversedX);
                        (*pointOrders)[b1].addPoints(1, 
                                std::make_pair(&b1, polyConnRef), 
                                std::make_pair(&a1, connConnRef),
                                !reversedY);
                    }
                    else
                    { /// \todo FIXME: this whole branch was not doing anything
                    /*
                        int turnDirA = vecDir(a0, a1, a2); 
                        int turnDirB = vecDir(b0, b1, b2); 
                        bool reversed = (side1 != -turnDirA); 
                        if (side1 != side2) 
                        { 
                            // Interesting case where a connector routes round 
                            // the edge of a shape and intersects a connector 
                            // which is connected to a port on the edge of the 
                            // shape. 
                            if (turnDirA == 0) 
                            { 
                                // We'll make B the outer by preference,  
                                // because the points of A are collinear. 
                                reversed = false; 
                            } 
                            else if (turnDirB == 0) 
                            { 
                                reversed = true; 
                            } 
                            // TODO COLA_ASSERT((turnDirB != 0) || 
                            //          (turnDirA != 0)); 
                        }
                        VertID vID(b1.id, true, b1.vn);
                        //(*pointOrders)[b1].addPoints(&b1, &a1, reversed);
                    */
                    }
                }
            }
        }
        else
        {
            if ( polyIsOrthogonal && connIsOrthogonal)
            {
                // All crossings in orthogonal connectors will be at a
                // vertex in the visibility graph, so we need not bother
                // doing normal line intersection.
                continue;
            }

            // No endpoint is shared between these two line segments,
            // so just calculate normal segment intersection.

            Point cPt;
            int intersectResult = Avoid::segmentIntersectPoint(
                    a1, a2, b1, b2, &(cPt.x), &(cPt.y));

            if (intersectResult == Avoid::DO_INTERSECT)
            {
                if (!polyIsConn && 
                        ((a1 == cPt) || (a2 == cPt) || (b1 == cPt) || (b2 == cPt)))
                {
                    // XXX: This shouldn't actually happen, because these
                    //      points should be added as bends to each line by
                    //      splitBranchingSegments().  Thus, lets ignore them.
                    COLA_ASSERT(a1 != cPt);
                    COLA_ASSERT(a2 != cPt);
                    COLA_ASSERT(b1 != cPt);
                    COLA_ASSERT(b2 != cPt);
                    continue;
                }                
                //db_printf("crossing lines:\n");
                //db_printf("cPt: %g %g\n", cPt.x, cPt.y);
                crossingCount += 1;
                if (crossingPoints)
                {
                    crossingPoints->insert(cPt);
                }
            }
        }
    }
    //db_printf("crossingcount %d\n", crossingCount);
    return std::make_pair(crossingCount, crossingFlags);
}


//============================================================================

}


