// SPDX-License-Identifier: GPL-2.0-or-later
/** @file
 * TODO: insert short description here
 *//*
 * Authors:
 * see git history
 * Fred
 *
 * Copyright (C) 2018 Authors
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#include <cstdio>
#include <cstdlib>
#include <glib.h>
#include "Shape.h"
#include "livarot/sweep-event-queue.h"
#include "livarot/sweep-tree-list.h"

/*
 * Shape instances handling.
 * never (i repeat: never) modify edges and points links; use Connect() and Disconnect() instead
 * the graph is stored as a set of points and edges, with edges in a doubly-linked list for each point.
 */

Shape::Shape()
  : nbQRas(0),
    firstQRas(-1),
    lastQRas(-1),
    qrsData(nullptr),
    nbInc(0),
    maxInc(0),
    iData(nullptr),
    sTree(nullptr),
    sEvts(nullptr),
    _need_points_sorting(false),
    _need_edges_sorting(false),
    _has_points_data(false),
    _point_data_initialised(false),
    _has_edges_data(false),
    _has_sweep_src_data(false),
    _has_sweep_dest_data(false),
    _has_raster_data(false),
    _has_quick_raster_data(false),
    _has_back_data(false),
    _has_voronoi_data(false),
    _bbox_up_to_date(false)
{
  leftX = topY = rightX = bottomY = 0;
  maxPt = 0;
  maxAr = 0;

  type = shape_polygon;
}
Shape::~Shape ()
{
  maxPt = 0;
  maxAr = 0;
  free(qrsData);
}

void Shape::Affiche()
{
  printf("sh=%p nbPt=%i nbAr=%i\n", this, static_cast<int>(_pts.size()), static_cast<int>(_aretes.size())); // localizing ok
  for (unsigned int i=0; i<_pts.size(); i++) {
    printf("pt %u : x=(%f %f) dI=%i dO=%i\n",i, _pts[i].x[0], _pts[i].x[1], _pts[i].dI, _pts[i].dO); // localizing ok
  }
  for (unsigned int i=0; i<_aretes.size(); i++) {
    printf("ar %u : dx=(%f %f) st=%i en=%i\n",i, _aretes[i].dx[0], _aretes[i].dx[1], _aretes[i].st, _aretes[i].en); // localizing ok
  }
}

/**
 * Allocates space for point cache or clears the cache
 \param  nVal   Allocate a cache (true) or clear it (false)
 */
void
Shape::MakePointData (bool nVal)
{
  if (nVal)
    {
      if (_has_points_data == false)
        {
          _has_points_data = true;
          _point_data_initialised = false;
          _bbox_up_to_date = false;
          pData.resize(maxPt);
        }
    }
    /* no need to clean point data - keep it cached*/
}

void
Shape::MakeEdgeData (bool nVal)
{
  if (nVal)
    {
      if (_has_edges_data == false)
        {
          _has_edges_data = true;
          eData.resize(maxAr);
        }
    }
  else
    {
      if (_has_edges_data)
        {
          _has_edges_data = false;
          eData.clear();
        }
    }
}

void
Shape::MakeRasterData (bool nVal)
{
  if (nVal)
    {
      if (_has_raster_data == false)
        {
          _has_raster_data = true;
          swrData.resize(maxAr);
        }
    }
  else
    {
      if (_has_raster_data)
        {
          _has_raster_data = false;
          swrData.clear();
        }
    }
}
void
Shape::MakeQuickRasterData (bool nVal)
{
  if (nVal)
    {
      if (_has_quick_raster_data == false)
        {
          _has_quick_raster_data = true;
          quick_raster_data* new_qrsData = static_cast<quick_raster_data*>(realloc(qrsData, maxAr * sizeof(quick_raster_data)));
          if (!new_qrsData) {
              g_error("Not enough memory available for reallocating Shape::qrsData");
          } else {
              qrsData = new_qrsData;
          }
        }
    }
  else
    {
      if (_has_quick_raster_data)
        {
          _has_quick_raster_data = false;
        }
    }
}
void
Shape::MakeSweepSrcData (bool nVal)
{
  if (nVal)
    {
      if (_has_sweep_src_data == false)
        {
          _has_sweep_src_data = true;
          swsData.resize(maxAr);
        }
    }
  else
    {
      if (_has_sweep_src_data)
        {
          _has_sweep_src_data = false;
          swsData.clear();
        }
    }
}
void
Shape::MakeSweepDestData (bool nVal)
{
  if (nVal)
    {
      if (_has_sweep_dest_data == false)
        {
          _has_sweep_dest_data = true;
          swdData.resize(maxAr);
        }
    }
  else
    {
      if (_has_sweep_dest_data)
        {
          _has_sweep_dest_data = false;
          swdData.clear();
        }
    }
}
void
Shape::MakeBackData (bool nVal)
{
  if (nVal)
    {
      if (_has_back_data == false)
        {
          _has_back_data = true;
          ebData.resize(maxAr);
        }
    }
  else
    {
      if (_has_back_data)
        {
          _has_back_data = false;
          ebData.clear();
        }
    }
}
void
Shape::MakeVoronoiData (bool nVal)
{
  if (nVal)
    {
      if (_has_voronoi_data == false)
        {
          _has_voronoi_data = true;
          vorpData.resize(maxPt);
          voreData.resize(maxAr);
        }
    }
  else
    {
      if (_has_voronoi_data)
        {
          _has_voronoi_data = false;
          vorpData.clear();
          voreData.clear();
        }
    }
}


/**
 *  Copy point and edge data from `who' into this object, discarding
 *  any cached data that we have.
 */
void
Shape::Copy (Shape * who)
{
  if (who == nullptr)
    {
      Reset (0, 0);
      return;
    }
  MakePointData (false);
  MakeEdgeData (false);
  MakeSweepSrcData (false);
  MakeSweepDestData (false);
  MakeRasterData (false);
  MakeQuickRasterData (false);
  MakeBackData (false);

  delete sTree;
  sTree = nullptr;
  delete sEvts;
  sEvts = nullptr;

  Reset (who->numberOfPoints(), who->numberOfEdges());
  type = who->type;
  _need_points_sorting = who->_need_points_sorting;
  _need_edges_sorting = who->_need_edges_sorting;
  _has_points_data = false;
  _point_data_initialised = false;
  _has_edges_data = false;
  _has_sweep_src_data = false;
  _has_sweep_dest_data = false;
  _has_raster_data = false;
  _has_quick_raster_data = false;
  _has_back_data = false;
  _has_voronoi_data = false;
  _bbox_up_to_date = false;

  _pts = who->_pts;
  _aretes = who->_aretes;
}

/**
 *  Clear points and edges and prepare internal data using new size.
 */
void
Shape::Reset (int pointCount, int edgeCount)
{
  _pts.clear();
  _aretes.clear();
  
  type = shape_polygon;
  if (pointCount > maxPt)
    {
      maxPt = pointCount;
      if (_has_points_data)
        pData.resize(maxPt);
      if (_has_voronoi_data)
        vorpData.resize(maxPt);
    }
  if (edgeCount > maxAr)
    {
      maxAr = edgeCount;
      if (_has_edges_data)
        eData.resize(maxAr);
      if (_has_sweep_dest_data)
        swdData.resize(maxAr);
      if (_has_sweep_src_data)
        swsData.resize(maxAr);
      if (_has_back_data)
        ebData.resize(maxAr);
      if (_has_voronoi_data)
        voreData.resize(maxAr);
    }
  _need_points_sorting = false;
  _need_edges_sorting = false;
  _point_data_initialised = false;
  _bbox_up_to_date = false;
}

int
Shape::AddPoint (const Geom::Point x)
{
  if (numberOfPoints() >= maxPt)
    {
      maxPt = 2 * numberOfPoints() + 1;
      if (_has_points_data)
        pData.resize(maxPt);
      if (_has_voronoi_data)
        vorpData.resize(maxPt);
    }

  dg_point p;
  p.x = x;
  p.dI = p.dO = 0;
  p.incidentEdge[FIRST] = p.incidentEdge[LAST] = -1;
  p.oldDegree = -1;
  _pts.push_back(p);
  int const n = _pts.size() - 1;

  if (_has_points_data)
    {
      pData[n].pending = 0;
      pData[n].edgeOnLeft = -1;
      pData[n].nextLinkedPoint = -1;
      pData[n].askForWindingS = nullptr;
      pData[n].askForWindingB = -1;
      pData[n].rx[0] = Round(p.x[0]);
      pData[n].rx[1] = Round(p.x[1]);
    }
  if (_has_voronoi_data)
    {
      vorpData[n].value = 0.0;
      vorpData[n].winding = -2;
    }
  _need_points_sorting = true;

  return n;
}

void
Shape::SubPoint (int p)
{
  if (p < 0 || p >= numberOfPoints())
    return;
  _need_points_sorting = true;
  int cb;
  cb = getPoint(p).incidentEdge[FIRST];
  while (cb >= 0 && cb < numberOfEdges())
    {
      if (getEdge(cb).st == p)
        {
          int ncb = getEdge(cb).nextS;
          _aretes[cb].nextS = _aretes[cb].prevS = -1;
          _aretes[cb].st = -1;
          cb = ncb;
        }
      else if (getEdge(cb).en == p)
        {
          int ncb = getEdge(cb).nextE;
          _aretes[cb].nextE = _aretes[cb].prevE = -1;
          _aretes[cb].en = -1;
          cb = ncb;
        }
      else
        {
          break;
        }
    }
  _pts[p].incidentEdge[FIRST] = _pts[p].incidentEdge[LAST] = -1;
  if (p < numberOfPoints() - 1)
    SwapPoints (p, numberOfPoints() - 1);
  _pts.pop_back();
}

void
Shape::SwapPoints (int a, int b)
{
  if (a == b)
    return;
  if (getPoint(a).totalDegree() == 2 && getPoint(b).totalDegree() == 2)
    {
      int cb = getPoint(a).incidentEdge[FIRST];
      if (getEdge(cb).st == a)
        {
          _aretes[cb].st = numberOfPoints();
        }
      else if (getEdge(cb).en == a)
        {
          _aretes[cb].en = numberOfPoints();
        }
      cb = getPoint(a).incidentEdge[LAST];
      if (getEdge(cb).st == a)
        {
          _aretes[cb].st = numberOfPoints();
        }
      else if (getEdge(cb).en == a)
        {
          _aretes[cb].en = numberOfPoints();
        }

      cb = getPoint(b).incidentEdge[FIRST];
      if (getEdge(cb).st == b)
        {
          _aretes[cb].st = a;
        }
      else if (getEdge(cb).en == b)
        {
          _aretes[cb].en = a;
        }
      cb = getPoint(b).incidentEdge[LAST];
      if (getEdge(cb).st == b)
        {
          _aretes[cb].st = a;
        }
      else if (getEdge(cb).en == b)
        {
          _aretes[cb].en = a;
        }

      cb = getPoint(a).incidentEdge[FIRST];
      if (getEdge(cb).st == numberOfPoints())
        {
          _aretes[cb].st = b;
        }
      else if (getEdge(cb).en == numberOfPoints())
        {
          _aretes[cb].en = b;
        }
      cb = getPoint(a).incidentEdge[LAST];
      if (getEdge(cb).st == numberOfPoints())
        {
          _aretes[cb].st = b;
        }
      else if (getEdge(cb).en == numberOfPoints())
        {
          _aretes[cb].en = b;
        }

    }
  else
    {
      int cb;
      cb = getPoint(a).incidentEdge[FIRST];
      while (cb >= 0)
        {
          int ncb = NextAt (a, cb);
          if (getEdge(cb).st == a)
            {
              _aretes[cb].st = numberOfPoints();
            }
          else if (getEdge(cb).en == a)
            {
              _aretes[cb].en = numberOfPoints();
            }
          cb = ncb;
        }
      cb = getPoint(b).incidentEdge[FIRST];
      while (cb >= 0)
        {
          int ncb = NextAt (b, cb);
          if (getEdge(cb).st == b)
            {
              _aretes[cb].st = a;
            }
          else if (getEdge(cb).en == b)
            {
              _aretes[cb].en = a;
            }
          cb = ncb;
        }
      cb = getPoint(a).incidentEdge[FIRST];
      while (cb >= 0)
        {
          int ncb = NextAt (numberOfPoints(), cb);
          if (getEdge(cb).st == numberOfPoints())
            {
              _aretes[cb].st = b;
            }
          else if (getEdge(cb).en == numberOfPoints())
            {
              _aretes[cb].en = b;
            }
          cb = ncb;
        }
    }
  {
    dg_point swap = getPoint(a);
    _pts[a] = getPoint(b);
    _pts[b] = swap;
  }
  if (_has_points_data)
    {
      point_data swad = pData[a];
      pData[a] = pData[b];
      pData[b] = swad;
      //              pData[pData[a].oldInd].newInd=a;
      //              pData[pData[b].oldInd].newInd=b;
    }
  if (_has_voronoi_data)
    {
      voronoi_point swav = vorpData[a];
      vorpData[a] = vorpData[b];
      vorpData[b] = swav;
    }
}
void
Shape::SwapPoints (int a, int b, int c)
{
  if (a == b || b == c || a == c)
    return;
  SwapPoints (a, b);
  SwapPoints (b, c);
}

void
Shape::SortPoints ()
{
  if (_need_points_sorting && hasPoints())
    SortPoints (0, numberOfPoints() - 1);
  _need_points_sorting = false;
}

void
Shape::SortPointsRounded ()
{
  if (hasPoints())
    SortPointsRounded (0, numberOfPoints() - 1);
}

void
Shape::SortPoints (int s, int e)
{
  if (s >= e)
    return;
  if (e == s + 1)
    {
      if (getPoint(s).x[1] > getPoint(e).x[1]
          || (getPoint(s).x[1] == getPoint(e).x[1] && getPoint(s).x[0] > getPoint(e).x[0]))
        SwapPoints (s, e);
      return;
    }

  int ppos = (s + e) / 2;
  int plast = ppos;
  double pvalx = getPoint(ppos).x[0];
  double pvaly = getPoint(ppos).x[1];

  int le = s, ri = e;
  while (le < ppos || ri > plast)
    {
      if (le < ppos)
        {
          do
            {
              int test = 0;
              if (getPoint(le).x[1] > pvaly)
                {
                  test = 1;
                }
              else if (getPoint(le).x[1] == pvaly)
                {
                  if (getPoint(le).x[0] > pvalx)
                    {
                      test = 1;
                    }
                  else if (getPoint(le).x[0] == pvalx)
                    {
                      test = 0;
                    }
                  else
                    {
                      test = -1;
                    }
                }
              else
                {
                  test = -1;
                }
              if (test == 0)
                {
                  // on colle les valeurs egales au pivot ensemble
                  if (le < ppos - 1)
                    {
                      SwapPoints (le, ppos - 1, ppos);
                      ppos--;
                      continue;	// sans changer le
                    }
                  else if (le == ppos - 1)
                    {
                      ppos--;
                      break;
                    }
                  else
                    {
                      // oupsie
                      break;
                    }
                }
              if (test > 0)
                {
                  break;
                }
              le++;
            }
          while (le < ppos);
        }
      if (ri > plast)
        {
          do
            {
              int test = 0;
              if (getPoint(ri).x[1] > pvaly)
                {
                  test = 1;
                }
              else if (getPoint(ri).x[1] == pvaly)
                {
                  if (getPoint(ri).x[0] > pvalx)
                    {
                      test = 1;
                    }
                  else if (getPoint(ri).x[0] == pvalx)
                    {
                      test = 0;
                    }
                  else
                    {
                      test = -1;
                    }
                }
              else
                {
                  test = -1;
                }
              if (test == 0)
                {
                  // on colle les valeurs egales au pivot ensemble
                  if (ri > plast + 1)
                    {
                      SwapPoints (ri, plast + 1, plast);
                      plast++;
                      continue;	// sans changer ri
                    }
                  else if (ri == plast + 1)
                    {
                      plast++;
                      break;
                    }
                  else
                    {
                      // oupsie
                      break;
                    }
                }
              if (test < 0)
                {
                  break;
                }
              ri--;
            }
          while (ri > plast);
        }
      if (le < ppos)
        {
          if (ri > plast)
            {
              SwapPoints (le, ri);
              le++;
              ri--;
            }
          else
            {
              if (le < ppos - 1)
                {
                  SwapPoints (ppos - 1, plast, le);
                  ppos--;
                  plast--;
                }
              else if (le == ppos - 1)
                {
                  SwapPoints (plast, le);
                  ppos--;
                  plast--;
                }
            }
        }
      else
        {
          if (ri > plast + 1)
            {
              SwapPoints (plast + 1, ppos, ri);
              ppos++;
              plast++;
            }
          else if (ri == plast + 1)
            {
              SwapPoints (ppos, ri);
              ppos++;
              plast++;
            }
          else
            {
              break;
            }
        }
    }
  SortPoints (s, ppos - 1);
  SortPoints (plast + 1, e);
}

void
Shape::SortPointsByOldInd (int s, int e)
{
  if (s >= e)
    return;
  if (e == s + 1)
    {
      if (getPoint(s).x[1] > getPoint(e).x[1] || (getPoint(s).x[1] == getPoint(e).x[1] && getPoint(s).x[0] > getPoint(e).x[0])
          || (getPoint(s).x[1] == getPoint(e).x[1] && getPoint(s).x[0] == getPoint(e).x[0]
              && pData[s].oldInd > pData[e].oldInd))
        SwapPoints (s, e);
      return;
    }

  int ppos = (s + e) / 2;
  int plast = ppos;
  double pvalx = getPoint(ppos).x[0];
  double pvaly = getPoint(ppos).x[1];
  int pvali = pData[ppos].oldInd;

  int le = s, ri = e;
  while (le < ppos || ri > plast)
    {
      if (le < ppos)
        {
          do
            {
              int test = 0;
              if (getPoint(le).x[1] > pvaly)
                {
                  test = 1;
                }
              else if (getPoint(le).x[1] == pvaly)
                {
                  if (getPoint(le).x[0] > pvalx)
                    {
                      test = 1;
                    }
                  else if (getPoint(le).x[0] == pvalx)
                    {
                      if (pData[le].oldInd > pvali)
                        {
                          test = 1;
                        }
                      else if (pData[le].oldInd == pvali)
                        {
                          test = 0;
                        }
                      else
                        {
                          test = -1;
                        }
                    }
                  else
                    {
                      test = -1;
                    }
                }
              else
                {
                  test = -1;
                }
              if (test == 0)
                {
                  // on colle les valeurs egales au pivot ensemble
                  if (le < ppos - 1)
                    {
                      SwapPoints (le, ppos - 1, ppos);
                      ppos--;
                      continue;	// sans changer le
                    }
                  else if (le == ppos - 1)
                    {
                      ppos--;
                      break;
                    }
                  else
                    {
                      // oupsie
                      break;
                    }
                }
              if (test > 0)
                {
                  break;
                }
              le++;
            }
          while (le < ppos);
        }
      if (ri > plast)
        {
          do
            {
              int test = 0;
              if (getPoint(ri).x[1] > pvaly)
                {
                  test = 1;
                }
              else if (getPoint(ri).x[1] == pvaly)
                {
                  if (getPoint(ri).x[0] > pvalx)
                    {
                      test = 1;
                    }
                  else if (getPoint(ri).x[0] == pvalx)
                    {
                      if (pData[ri].oldInd > pvali)
                        {
                          test = 1;
                        }
                      else if (pData[ri].oldInd == pvali)
                        {
                          test = 0;
                        }
                      else
                        {
                          test = -1;
                        }
                    }
                  else
                    {
                      test = -1;
                    }
                }
              else
                {
                  test = -1;
                }
              if (test == 0)
                {
                  // on colle les valeurs egales au pivot ensemble
                  if (ri > plast + 1)
                    {
                      SwapPoints (ri, plast + 1, plast);
                      plast++;
                      continue;	// sans changer ri
                    }
                  else if (ri == plast + 1)
                    {
                      plast++;
                      break;
                    }
                  else
                    {
                      // oupsie
                      break;
                    }
                }
              if (test < 0)
                {
                  break;
                }
              ri--;
            }
          while (ri > plast);
        }
      if (le < ppos)
        {
          if (ri > plast)
            {
              SwapPoints (le, ri);
              le++;
              ri--;
            }
          else
            {
              if (le < ppos - 1)
                {
                  SwapPoints (ppos - 1, plast, le);
                  ppos--;
                  plast--;
                }
              else if (le == ppos - 1)
                {
                  SwapPoints (plast, le);
                  ppos--;
                  plast--;
                }
            }
        }
      else
        {
          if (ri > plast + 1)
            {
              SwapPoints (plast + 1, ppos, ri);
              ppos++;
              plast++;
            }
          else if (ri == plast + 1)
            {
              SwapPoints (ppos, ri);
              ppos++;
              plast++;
            }
          else
            {
              break;
            }
        }
    }
  SortPointsByOldInd (s, ppos - 1);
  SortPointsByOldInd (plast + 1, e);
}

void
Shape::SortPointsRounded (int s, int e)
{
  if (s >= e)
    return;
  if (e == s + 1)
    {
      if (pData[s].rx[1] > pData[e].rx[1]
          || (pData[s].rx[1] == pData[e].rx[1] && pData[s].rx[0] > pData[e].rx[0]))
        SwapPoints (s, e);
      return;
    }

  int ppos = (s + e) / 2;
  int plast = ppos;
  double pvalx = pData[ppos].rx[0];
  double pvaly = pData[ppos].rx[1];

  int le = s, ri = e;
  while (le < ppos || ri > plast)
    {
      if (le < ppos)
        {
          do
            {
              int test = 0;
              if (pData[le].rx[1] > pvaly)
                {
                  test = 1;
                }
              else if (pData[le].rx[1] == pvaly)
                {
                  if (pData[le].rx[0] > pvalx)
                    {
                      test = 1;
                    }
                  else if (pData[le].rx[0] == pvalx)
                    {
                      test = 0;
                    }
                  else
                    {
                      test = -1;
                    }
                }
              else
                {
                  test = -1;
                }
              if (test == 0)
                {
                  // on colle les valeurs egales au pivot ensemble
                  if (le < ppos - 1)
                    {
                      SwapPoints (le, ppos - 1, ppos);
                      ppos--;
                      continue;	// sans changer le
                    }
                  else if (le == ppos - 1)
                    {
                      ppos--;
                      break;
                    }
                  else
                    {
                      // oupsie
                      break;
                    }
                }
              if (test > 0)
                {
                  break;
                }
              le++;
            }
          while (le < ppos);
        }
      if (ri > plast)
        {
          do
            {
              int test = 0;
              if (pData[ri].rx[1] > pvaly)
                {
                  test = 1;
                }
              else if (pData[ri].rx[1] == pvaly)
                {
                  if (pData[ri].rx[0] > pvalx)
                    {
                      test = 1;
                    }
                  else if (pData[ri].rx[0] == pvalx)
                    {
                      test = 0;
                    }
                  else
                    {
                      test = -1;
                    }
                }
              else
                {
                  test = -1;
                }
              if (test == 0)
                {
                  // on colle les valeurs egales au pivot ensemble
                  if (ri > plast + 1)
                    {
                      SwapPoints (ri, plast + 1, plast);
                      plast++;
                      continue;	// sans changer ri
                    }
                  else if (ri == plast + 1)
                    {
                      plast++;
                      break;
                    }
                  else
                    {
                      // oupsie
                      break;
                    }
                }
              if (test < 0)
                {
                  break;
                }
              ri--;
            }
          while (ri > plast);
        }
      if (le < ppos)
        {
          if (ri > plast)
            {
              SwapPoints (le, ri);
              le++;
              ri--;
            }
          else
            {
              if (le < ppos - 1)
                {
                  SwapPoints (ppos - 1, plast, le);
                  ppos--;
                  plast--;
                }
              else if (le == ppos - 1)
                {
                  SwapPoints (plast, le);
                  ppos--;
                  plast--;
                }
            }
        }
      else
        {
          if (ri > plast + 1)
            {
              SwapPoints (plast + 1, ppos, ri);
              ppos++;
              plast++;
            }
          else if (ri == plast + 1)
            {
              SwapPoints (ppos, ri);
              ppos++;
              plast++;
            }
          else
            {
              break;
            }
        }
    }
  SortPointsRounded (s, ppos - 1);
  SortPointsRounded (plast + 1, e);
}

/*
 *
 */
int
Shape::AddEdge (int st, int en)
{
  if (st == en)
    return -1;
  if (st < 0 || en < 0)
    return -1;
  type = shape_graph;
  if (numberOfEdges() >= maxAr)
    {
      maxAr = 2 * numberOfEdges() + 1;
      if (_has_edges_data)
        eData.resize(maxAr);
      if (_has_sweep_src_data)
        swsData.resize(maxAr);
      if (_has_sweep_dest_data)
        swdData.resize(maxAr);
      if (_has_raster_data)
        swrData.resize(maxAr);
      if (_has_back_data)
        ebData.resize(maxAr);
      if (_has_voronoi_data)
        voreData.resize(maxAr);
    }

  dg_arete a;
  a.dx = Geom::Point(0, 0);
  a.st = a.en = -1;
  a.prevS = a.nextS = -1;
  a.prevE = a.nextE = -1;
  if (st >= 0 && en >= 0) {
    a.dx = getPoint(en).x - getPoint(st).x;
  }

  _aretes.push_back(a);
  int const n = numberOfEdges() - 1;
  
  ConnectStart (st, n);
  ConnectEnd (en, n);
  if (_has_edges_data)
    {
      eData[n].weight = 1;
      eData[n].rdx = getEdge(n).dx;
    }
  if (_has_sweep_src_data)
    {
      swsData[n].misc = nullptr;
      swsData[n].firstLinkedPoint = -1;
    }
  if (_has_back_data)
    {
      ebData[n].pathID = -1;
      ebData[n].pieceID = -1;
      ebData[n].tSt = ebData[n].tEn = 0;
    }
  if (_has_voronoi_data)
    {
      voreData[n].leF = -1;
      voreData[n].riF = -1;
    }
  _need_edges_sorting = true;
  return n;
}

int
Shape::AddEdge (int st, int en, int leF, int riF)
{
  if (st == en)
    return -1;
  if (st < 0 || en < 0)
    return -1;
  {
    int cb = getPoint(st).incidentEdge[FIRST];
    while (cb >= 0)
      {
        if (getEdge(cb).st == st && getEdge(cb).en == en)
          return -1;		// doublon
        if (getEdge(cb).st == en && getEdge(cb).en == st)
          return -1;		// doublon
        cb = NextAt (st, cb);
      }
  }
  type = shape_graph;
  if (numberOfEdges() >= maxAr)
    {
      maxAr = 2 * numberOfEdges() + 1;
      if (_has_edges_data)
        eData.resize(maxAr);
      if (_has_sweep_src_data)
        swsData.resize(maxAr);
      if (_has_sweep_dest_data)
        swdData.resize(maxAr);
      if (_has_raster_data)
        swrData.resize(maxAr);
      if (_has_back_data)
        ebData.resize(maxAr);
      if (_has_voronoi_data)
        voreData.resize(maxAr);
    }

  dg_arete a;
  a.dx = Geom::Point(0, 0);
  a.st = a.en = -1;
  a.prevS = a.nextS = -1;
  a.prevE = a.nextE = -1;
  if (st >= 0 && en >= 0) {
    a.dx = getPoint(en).x - getPoint(st).x;
  }
  
  _aretes.push_back(a);
  int const n = numberOfEdges() - 1;

  ConnectStart (st, n);
  ConnectEnd (en, n);
  if (_has_edges_data)
    {
      eData[n].weight = 1;
      eData[n].rdx = getEdge(n).dx;
    }
  if (_has_sweep_src_data)
    {
      swsData[n].misc = nullptr;
      swsData[n].firstLinkedPoint = -1;
    }
  if (_has_back_data)
    {
      ebData[n].pathID = -1;
      ebData[n].pieceID = -1;
      ebData[n].tSt = ebData[n].tEn = 0;
    }
  if (_has_voronoi_data)
    {
      voreData[n].leF = leF;
      voreData[n].riF = riF;
    }
  _need_edges_sorting = true;
  return n;
}

void
Shape::SubEdge (int e)
{
  if (e < 0 || e >= numberOfEdges())
    return;
  type = shape_graph;
  DisconnectStart (e);
  DisconnectEnd (e);
  if (e < numberOfEdges() - 1)
    SwapEdges (e, numberOfEdges() - 1);
  _aretes.pop_back();
  _need_edges_sorting = true;
}

void
Shape::SwapEdges (int a, int b)
{
  if (a == b)
    return;
  if (getEdge(a).prevS >= 0 && getEdge(a).prevS != b)
    {
      if (getEdge(getEdge(a).prevS).st == getEdge(a).st)
        {
          _aretes[getEdge(a).prevS].nextS = b;
        }
      else if (getEdge(getEdge(a).prevS).en == getEdge(a).st)
        {
          _aretes[getEdge(a).prevS].nextE = b;
        }
    }
  if (getEdge(a).nextS >= 0 && getEdge(a).nextS != b)
    {
      if (getEdge(getEdge(a).nextS).st == getEdge(a).st)
        {
          _aretes[getEdge(a).nextS].prevS = b;
        }
      else if (getEdge(getEdge(a).nextS).en == getEdge(a).st)
        {
          _aretes[getEdge(a).nextS].prevE = b;
        }
    }
  if (getEdge(a).prevE >= 0 && getEdge(a).prevE != b)
    {
      if (getEdge(getEdge(a).prevE).st == getEdge(a).en)
        {
          _aretes[getEdge(a).prevE].nextS = b;
        }
      else if (getEdge(getEdge(a).prevE).en == getEdge(a).en)
        {
          _aretes[getEdge(a).prevE].nextE = b;
        }
    }
  if (getEdge(a).nextE >= 0 && getEdge(a).nextE != b)
    {
      if (getEdge(getEdge(a).nextE).st == getEdge(a).en)
        {
          _aretes[getEdge(a).nextE].prevS = b;
        }
      else if (getEdge(getEdge(a).nextE).en == getEdge(a).en)
        {
          _aretes[getEdge(a).nextE].prevE = b;
        }
    }
  if (getEdge(a).st >= 0)
    {
      if (getPoint(getEdge(a).st).incidentEdge[FIRST] == a)
        _pts[getEdge(a).st].incidentEdge[FIRST] = numberOfEdges();
      if (getPoint(getEdge(a).st).incidentEdge[LAST] == a)
        _pts[getEdge(a).st].incidentEdge[LAST] = numberOfEdges();
    }
  if (getEdge(a).en >= 0)
    {
      if (getPoint(getEdge(a).en).incidentEdge[FIRST] == a)
        _pts[getEdge(a).en].incidentEdge[FIRST] = numberOfEdges();
      if (getPoint(getEdge(a).en).incidentEdge[LAST] == a)
        _pts[getEdge(a).en].incidentEdge[LAST] = numberOfEdges();
    }


  if (getEdge(b).prevS >= 0 && getEdge(b).prevS != a)
    {
      if (getEdge(getEdge(b).prevS).st == getEdge(b).st)
        {
          _aretes[getEdge(b).prevS].nextS = a;
        }
      else if (getEdge(getEdge(b).prevS).en == getEdge(b).st)
        {
          _aretes[getEdge(b).prevS].nextE = a;
        }
    }
  if (getEdge(b).nextS >= 0 && getEdge(b).nextS != a)
    {
      if (getEdge(getEdge(b).nextS).st == getEdge(b).st)
        {
          _aretes[getEdge(b).nextS].prevS = a;
        }
      else if (getEdge(getEdge(b).nextS).en == getEdge(b).st)
        {
          _aretes[getEdge(b).nextS].prevE = a;
        }
    }
  if (getEdge(b).prevE >= 0 && getEdge(b).prevE != a)
    {
      if (getEdge(getEdge(b).prevE).st == getEdge(b).en)
        {
          _aretes[getEdge(b).prevE].nextS = a;
        }
      else if (getEdge(getEdge(b).prevE).en == getEdge(b).en)
        {
          _aretes[getEdge(b).prevE].nextE = a;
        }
    }
  if (getEdge(b).nextE >= 0 && getEdge(b).nextE != a)
    {
      if (getEdge(getEdge(b).nextE).st == getEdge(b).en)
        {
          _aretes[getEdge(b).nextE].prevS = a;
        }
      else if (getEdge(getEdge(b).nextE).en == getEdge(b).en)
        {
          _aretes[getEdge(b).nextE].prevE = a;
        }
    }

  
  for (int i = 0; i < 2; i++) {
    int p = getEdge(b).st;
    if (p >= 0) {
      if (getPoint(p).incidentEdge[i] == b) {
        _pts[p].incidentEdge[i] = a;
      }
    }

    p = getEdge(b).en;
    if (p >= 0) {
      if (getPoint(p).incidentEdge[i] == b) {
        _pts[p].incidentEdge[i] = a;
      }
    }

    p = getEdge(a).st;
    if (p >= 0) {
      if (getPoint(p).incidentEdge[i] == numberOfEdges()) {
        _pts[p].incidentEdge[i] = b;
      }
    }

    p = getEdge(a).en;
    if (p >= 0) {
      if (getPoint(p).incidentEdge[i] == numberOfEdges()) {
        _pts[p].incidentEdge[i] = b;
      }
    }

  }
    
  if (getEdge(a).prevS == b)
    _aretes[a].prevS = a;
  if (getEdge(a).prevE == b)
    _aretes[a].prevE = a;
  if (getEdge(a).nextS == b)
    _aretes[a].nextS = a;
  if (getEdge(a).nextE == b)
    _aretes[a].nextE = a;
  if (getEdge(b).prevS == a)
    _aretes[a].prevS = b;
  if (getEdge(b).prevE == a)
    _aretes[a].prevE = b;
  if (getEdge(b).nextS == a)
    _aretes[a].nextS = b;
  if (getEdge(b).nextE == a)
    _aretes[a].nextE = b;

  dg_arete swap = getEdge(a);
  _aretes[a] = getEdge(b);
  _aretes[b] = swap;
  if (_has_edges_data)
    {
      edge_data swae = eData[a];
      eData[a] = eData[b];
      eData[b] = swae;
    }
  if (_has_sweep_src_data)
    {
      sweep_src_data swae = swsData[a];
      swsData[a] = swsData[b];
      swsData[b] = swae;
    }
  if (_has_sweep_dest_data)
    {
      sweep_dest_data swae = swdData[a];
      swdData[a] = swdData[b];
      swdData[b] = swae;
    }
  if (_has_raster_data)
    {
      raster_data swae = swrData[a];
      swrData[a] = swrData[b];
      swrData[b] = swae;
    }
  if (_has_back_data)
    {
      back_data swae = ebData[a];
      ebData[a] = ebData[b];
      ebData[b] = swae;
    }
  if (_has_voronoi_data)
    {
      voronoi_edge swav = voreData[a];
      voreData[a] = voreData[b];
      voreData[b] = swav;
    }
}
void
Shape::SwapEdges (int a, int b, int c)
{
  if (a == b || b == c || a == c)
    return;
  SwapEdges (a, b);
  SwapEdges (b, c);
}

void
Shape::SortEdges ()
{
  if (_need_edges_sorting == false) {
    return;
  }
  _need_edges_sorting = false;

  // allocate the edge_list array as it's needed by SortEdgesList
  edge_list *list = (edge_list *) g_malloc(numberOfEdges() * sizeof (edge_list));
  // for each point
  for (int p = 0; p < numberOfPoints(); p++)
    {
      int const d = getPoint(p).totalDegree();
      // if degree is greater than 1
      if (d > 1)
        {
          int cb;
          // get the first edge
          cb = getPoint(p).incidentEdge[FIRST];
          int nb = 0;
          // for all the edges connected at this point, form the list
          while (cb >= 0)
            {
              int n = nb++;
              list[n].no = cb;
              if (getEdge(cb).st == p)
                {
                  list[n].x = getEdge(cb).dx;
                  list[n].starting = true;
                }
              else
                {
                  list[n].x = -getEdge(cb).dx;
                  list[n].starting = false;
                }
              cb = NextAt (p, cb);
            }
          // sort the list
          SortEdgesList (list, 0, nb - 1);
          // recreate the linked list with the sorted edges
          _pts[p].incidentEdge[FIRST] = list[0].no;
          _pts[p].incidentEdge[LAST] = list[nb - 1].no;
          for (int i = 0; i < nb; i++)
            {
              if (list[i].starting)
                {
                  if (i > 0)
                    {
                      _aretes[list[i].no].prevS = list[i - 1].no;
                    }
                  else
                    {
                      _aretes[list[i].no].prevS = -1;
                    }
                  if (i < nb - 1)
                    {
                      _aretes[list[i].no].nextS = list[i + 1].no;
                    }
                  else
                    {
                      _aretes[list[i].no].nextS = -1;
                    }
                }
              else
                {
                  if (i > 0)
                    {
                      _aretes[list[i].no].prevE = list[i - 1].no;
                    }
                  else
                    {
                      _aretes[list[i].no].prevE = -1;
                    }
                  if (i < nb - 1)
                    {
                      _aretes[list[i].no].nextE = list[i + 1].no;
                    }
                  else
                    {
                      _aretes[list[i].no].nextE = -1;
                    }
                }
            }
        }
    }
  g_free(list);
}

int
Shape::CmpToVert (Geom::Point ax, Geom::Point bx,bool as,bool bs)
{
  // these tst variables store the sign of the x and y coordinates of each of the vectors
  // + is +1, - is -1 and 0 is 0
  int tstAX = 0;
  int tstAY = 0;
  int tstBX = 0;
  int tstBY = 0;
  if (ax[0] > 0)
    tstAX = 1;
  if (ax[0] < 0)
    tstAX = -1;
  if (ax[1] > 0)
    tstAY = 1;
  if (ax[1] < 0)
    tstAY = -1;
  if (bx[0] > 0)
    tstBX = 1;
  if (bx[0] < 0)
    tstBX = -1;
  if (bx[1] > 0)
    tstBY = 1;
  if (bx[1] < 0)
    tstBY = -1;

  // calcuate quadrant for both a and b edge vectors
  // for quadrant numbers, see the figure in header file documentation of this
  // function
  int quadA = 0, quadB = 0;
  if (tstAX < 0)
    {
      if (tstAY < 0)
        {
          quadA = 7;
        }
      else if (tstAY == 0)
        {
          quadA = 6;
        }
      else if (tstAY > 0)
        {
          quadA = 5;
        }
    }
  else if (tstAX == 0)
    {
      if (tstAY < 0)
        {
          quadA = 0;
        }
      else if (tstAY == 0)
        {
          quadA = -1;
        }
      else if (tstAY > 0)
        {
          quadA = 4;
        }
    }
  else if (tstAX > 0)
    {
      if (tstAY < 0)
        {
          quadA = 1;
        }
      else if (tstAY == 0)
        {
          quadA = 2;
        }
      else if (tstAY > 0)
        {
          quadA = 3;
        }
    }
  if (tstBX < 0)
    {
      if (tstBY < 0)
        {
          quadB = 7;
        }
      else if (tstBY == 0)
        {
          quadB = 6;
        }
      else if (tstBY > 0)
        {
          quadB = 5;
        }
    }
  else if (tstBX == 0)
    {
      if (tstBY < 0)
        {
          quadB = 0;
        }
      else if (tstBY == 0)
        {
          quadB = -1;
        }
      else if (tstBY > 0)
        {
          quadB = 4;
        }
    }
  else if (tstBX > 0)
    {
      if (tstBY < 0)
        {
          quadB = 1;
        }
      else if (tstBY == 0)
        {
          quadB = 2;
        }
      else if (tstBY > 0)
        {
          quadB = 3;
        }
    }


  // B should have a smaller quadrant number, if the opposite is true, we need to swap
  if (quadA < quadB)
    return 1;
  if (quadA > quadB)
    return -1;

  // if the quadrants are same, we need to do more maths to figure out their relative orientation

  Geom::Point av, bv;
  av = ax;
  bv = bx;

  // take cross from av to bv
  // cross product in SVG coordinates is different visually. With your right hand, let index finger point to vector
  // av, and let middle finger point to vector bv, if thumb is out of page, cross is negative, if into the page, it's
  // positive
  double si = cross(av, bv);
  // if the angle that bv makes from -y axis is smaller than the one av makes, our arrangement is good and needs no swap
  // this ideal orientation will give a negative cross product (thumb out of page)
  int tstSi = 0;
  if (si > 0.000001) tstSi = 1; // if positive cross product, swap needed
  if (si < -0.000001) tstSi = -1; // if negative cross product, we are good

  // if the edges are kinda on top of each other
  // I have no idea if there is a reason behind these two rules below
  if ( tstSi == 0 ) {
    if ( as && !bs ) return -1; // if b ends at point and a starts, we are good
    if ( !as && bs ) return 1; // if b starts at point and a ends, swap
  }

  // if we stil can't decide whether a swap is needed or not (both edges on top of each other and both starting
  // from the point) or ending at the point, we return 0
  return tstSi;
}

void
Shape::SortEdgesList (edge_list * list, int s, int e)
{
  if (s >= e)
    return;
  if (e == s + 1) {
    int cmpval=CmpToVert (list[e].x, list[s].x,list[e].starting,list[s].starting);
    if ( cmpval > 0 )  { // priorite aux sortants
      edge_list swap = list[s];
      list[s] = list[e];
      list[e] = swap;
    }
    return;
 }

  int ppos = (s + e) / 2;
  int plast = ppos;
  Geom::Point pvalx = list[ppos].x;
  bool      pvals = list[ppos].starting;
  
  int le = s, ri = e;
  while (le < ppos || ri > plast)
    {
      if (le < ppos)
        {
          do
            {
        int test = CmpToVert (pvalx, list[le].x,pvals,list[le].starting);
              if (test == 0)
                {
                  // on colle les valeurs egales au pivot ensemble
                  if (le < ppos - 1)
                    {
                      edge_list swap = list[le];
                      list[le] = list[ppos - 1];
                      list[ppos - 1] = list[ppos];
                      list[ppos] = swap;
                      ppos--;
                      continue;	// sans changer le
                    }
                  else if (le == ppos - 1)
                    {
                      ppos--;
                      break;
                    }
                  else
                    {
                      // oupsie
                      break;
                    }
                }
              if (test > 0)
                {
                  break;
                }
              le++;
            }
          while (le < ppos);
        }
      if (ri > plast)
        {
          do
            {
        int test = CmpToVert (pvalx, list[ri].x,pvals,list[ri].starting);
              if (test == 0)
                {
                  // on colle les valeurs egales au pivot ensemble
                  if (ri > plast + 1)
                    {
                      edge_list swap = list[ri];
                      list[ri] = list[plast + 1];
                      list[plast + 1] = list[plast];
                      list[plast] = swap;
                      plast++;
                      continue;	// sans changer ri
                    }
                  else if (ri == plast + 1)
                    {
                      plast++;
                      break;
                    }
                  else
                    {
                      // oupsie
                      break;
                    }
                }
              if (test < 0)
                {
                  break;
                }
              ri--;
            }
          while (ri > plast);
        }

      if (le < ppos)
        {
          if (ri > plast)
            {
              edge_list swap = list[le];
              list[le] = list[ri];
              list[ri] = swap;
              le++;
              ri--;
            }
          else if (le < ppos - 1)
            {
              edge_list swap = list[ppos - 1];
              list[ppos - 1] = list[plast];
              list[plast] = list[le];
              list[le] = swap;
              ppos--;
              plast--;
            }
          else if (le == ppos - 1)
            {
              edge_list swap = list[plast];
              list[plast] = list[le];
              list[le] = swap;
              ppos--;
              plast--;
            }
          else
            {
              break;
            }
        }
      else
        {
          if (ri > plast + 1)
            {
              edge_list swap = list[plast + 1];
              list[plast + 1] = list[ppos];
              list[ppos] = list[ri];
              list[ri] = swap;
              ppos++;
              plast++;
            }
          else if (ri == plast + 1)
            {
              edge_list swap = list[ppos];
              list[ppos] = list[ri];
              list[ri] = swap;
              ppos++;
              plast++;
            }
          else
            {
              break;
            }
        }
    }
  SortEdgesList (list, s, ppos - 1);
  SortEdgesList (list, plast + 1, e);

}



/*
 *
 */
void
Shape::ConnectStart (int p, int b)
{
  if (getEdge(b).st >= 0)
    DisconnectStart (b);
  
  _aretes[b].st = p;
  _pts[p].dO++;
  _aretes[b].nextS = -1;
  _aretes[b].prevS = getPoint(p).incidentEdge[LAST];
  if (getPoint(p).incidentEdge[LAST] >= 0)
    {
      if (getEdge(getPoint(p).incidentEdge[LAST]).st == p)
        {
          _aretes[getPoint(p).incidentEdge[LAST]].nextS = b;
        }
      else if (getEdge(getPoint(p).incidentEdge[LAST]).en == p)
        {
          _aretes[getPoint(p).incidentEdge[LAST]].nextE = b;
        }
    }
  _pts[p].incidentEdge[LAST] = b;
  if (getPoint(p).incidentEdge[FIRST] < 0)
    _pts[p].incidentEdge[FIRST] = b;
}

void
Shape::ConnectEnd (int p, int b)
{
  if (getEdge(b).en >= 0)
    DisconnectEnd (b);
  _aretes[b].en = p;
  _pts[p].dI++;
  _aretes[b].nextE = -1;
  _aretes[b].prevE = getPoint(p).incidentEdge[LAST];
  if (getPoint(p).incidentEdge[LAST] >= 0)
    {
      if (getEdge(getPoint(p).incidentEdge[LAST]).st == p)
        {
          _aretes[getPoint(p).incidentEdge[LAST]].nextS = b;
        }
      else if (getEdge(getPoint(p).incidentEdge[LAST]).en == p)
        {
          _aretes[getPoint(p).incidentEdge[LAST]].nextE = b;
        }
    }
  _pts[p].incidentEdge[LAST] = b;
  if (getPoint(p).incidentEdge[FIRST] < 0)
    _pts[p].incidentEdge[FIRST] = b;
}

void
Shape::DisconnectStart (int b)
{
  if (getEdge(b).st < 0)
    return;
  _pts[getEdge(b).st].dO--;
  if (getEdge(b).prevS >= 0)
    {
      if (getEdge(getEdge(b).prevS).st == getEdge(b).st)
        {
          _aretes[getEdge(b).prevS].nextS = getEdge(b).nextS;
        }
      else if (getEdge(getEdge(b).prevS).en == getEdge(b).st)
        {
          _aretes[getEdge(b).prevS].nextE = getEdge(b).nextS;
        }
    }
  if (getEdge(b).nextS >= 0)
    {
      if (getEdge(getEdge(b).nextS).st == getEdge(b).st)
        {
          _aretes[getEdge(b).nextS].prevS = getEdge(b).prevS;
        }
      else if (getEdge(getEdge(b).nextS).en == getEdge(b).st)
        {
          _aretes[getEdge(b).nextS].prevE = getEdge(b).prevS;
        }
    }
  if (getPoint(getEdge(b).st).incidentEdge[FIRST] == b)
    _pts[getEdge(b).st].incidentEdge[FIRST] = getEdge(b).nextS;
  if (getPoint(getEdge(b).st).incidentEdge[LAST] == b)
    _pts[getEdge(b).st].incidentEdge[LAST] = getEdge(b).prevS;
  _aretes[b].st = -1;
}

void
Shape::DisconnectEnd (int b)
{
  if (getEdge(b).en < 0)
    return;
  _pts[getEdge(b).en].dI--;
  if (getEdge(b).prevE >= 0)
    {
      if (getEdge(getEdge(b).prevE).st == getEdge(b).en)
        {
          _aretes[getEdge(b).prevE].nextS = getEdge(b).nextE;
        }
      else if (getEdge(getEdge(b).prevE).en == getEdge(b).en)
        {
          _aretes[getEdge(b).prevE].nextE = getEdge(b).nextE;
        }
    }
  if (getEdge(b).nextE >= 0)
    {
      if (getEdge(getEdge(b).nextE).st == getEdge(b).en)
        {
          _aretes[getEdge(b).nextE].prevS = getEdge(b).prevE;
        }
      else if (getEdge(getEdge(b).nextE).en == getEdge(b).en)
        {
          _aretes[getEdge(b).nextE].prevE = getEdge(b).prevE;
        }
    }
  if (getPoint(getEdge(b).en).incidentEdge[FIRST] == b)
    _pts[getEdge(b).en].incidentEdge[FIRST] = getEdge(b).nextE;
  if (getPoint(getEdge(b).en).incidentEdge[LAST] == b)
    _pts[getEdge(b).en].incidentEdge[LAST] = getEdge(b).prevE;
  _aretes[b].en = -1;
}


void
Shape::Inverse (int b)
{
  int swap;
  swap = getEdge(b).st;
  _aretes[b].st = getEdge(b).en;
  _aretes[b].en = swap;
  swap = getEdge(b).prevE;
  _aretes[b].prevE = getEdge(b).prevS;
  _aretes[b].prevS = swap;
  swap = getEdge(b).nextE;
  _aretes[b].nextE = getEdge(b).nextS;
  _aretes[b].nextS = swap;
  _aretes[b].dx = -getEdge(b).dx;
  if (getEdge(b).st >= 0)
    {
      _pts[getEdge(b).st].dO++;
      _pts[getEdge(b).st].dI--;
    }
  if (getEdge(b).en >= 0)
    {
      _pts[getEdge(b).en].dO--;
      _pts[getEdge(b).en].dI++;
    }
  if (_has_edges_data)
    eData[b].weight = -eData[b].weight;
  if (_has_sweep_dest_data)
    {
      int swap = swdData[b].leW;
      swdData[b].leW = swdData[b].riW;
      swdData[b].riW = swap;
    }
  if (_has_back_data)
    {
      double swat = ebData[b].tSt;
      ebData[b].tSt = ebData[b].tEn;
      ebData[b].tEn = swat;
    }
  if (_has_voronoi_data)
    {
      int swai = voreData[b].leF;
      voreData[b].leF = voreData[b].riF;
      voreData[b].riF = swai;
    }
}
void
Shape::CalcBBox (bool strict_degree)
{
  if (_bbox_up_to_date)
    return;
  if (hasPoints() == false)
  {
    leftX = rightX = topY = bottomY = 0;
    _bbox_up_to_date = true;
    return;
  }
  leftX = rightX = getPoint(0).x[0];
  topY = bottomY = getPoint(0).x[1];
  bool not_set=true;
  for (int i = 0; i < numberOfPoints(); i++)
  {
    if ( strict_degree == false || getPoint(i).dI > 0 || getPoint(i).dO > 0 ) {
      if ( not_set ) {
        leftX = rightX = getPoint(i).x[0];
        topY = bottomY = getPoint(i).x[1];
        not_set=false;
      } else {
        if (  getPoint(i).x[0] < leftX) leftX = getPoint(i).x[0];
        if (  getPoint(i).x[0] > rightX) rightX = getPoint(i).x[0];
        if (  getPoint(i).x[1] < topY) topY = getPoint(i).x[1];
        if (  getPoint(i).x[1] > bottomY) bottomY = getPoint(i).x[1];
      }
    }
  }

  _bbox_up_to_date = true;
}

// winding of a point with respect to the Shape
// 0= outside
// 1= inside (or -1, that usually the same)
// other=depends on your fill rule
// if the polygon is uncrossed, it's all the same, usually
int
Shape::PtWinding (const Geom::Point px) const 
{
  int lr = 0, ll = 0, rr = 0;
  
  for (int i = 0; i < numberOfEdges(); i++)
  {
    Geom::Point const adir = getEdge(i).dx;

    Geom::Point const ast = getPoint(getEdge(i).st).x;
    Geom::Point const aen = getPoint(getEdge(i).en).x;
    
    //int const nWeight = eData[i].weight;
    int const nWeight = 1;

    if (ast[0] < aen[0]) {
      if (ast[0] > px[0]) continue;
      if (aen[0] < px[0]) continue;
    } else {
      if (ast[0] < px[0]) continue;
      if (aen[0] > px[0]) continue;
    }
    if (ast[0] == px[0]) {
      if (ast[1] >= px[1]) continue;
      if (aen[0] == px[0]) continue;
      if (aen[0] < px[0]) ll += nWeight;  else rr -= nWeight;
      continue;
    }
    if (aen[0] == px[0]) {
      if (aen[1] >= px[1]) continue;
      if (ast[0] == px[0]) continue;
      if (ast[0] < px[0]) ll -= nWeight; else rr += nWeight;
      continue;
    }
    
    if (ast[1] < aen[1]) {
      if (ast[1] >= px[1])  continue;
    } else {
      if (aen[1] >= px[1]) continue;
    }

    Geom::Point const diff = px - ast;
    double const cote = cross(adir, diff);
    if (cote == 0) continue;
    if (cote < 0) {
      if (ast[0] > px[0]) lr += nWeight;
    } else {
      if (ast[0] < px[0]) lr -= nWeight;
    }
  }
  return lr + (ll + rr) / 2;
}


void Shape::initialisePointData()
{
    if (_point_data_initialised)
        return;
    int const N = numberOfPoints();
  
    for (int i = 0; i < N; i++) {
        pData[i].pending = 0;
        pData[i].edgeOnLeft = -1;
        pData[i].nextLinkedPoint = -1;
        pData[i].rx[0] = Round(getPoint(i).x[0]);
        pData[i].rx[1] = Round(getPoint(i).x[1]);
    }

    _point_data_initialised = true;
}

void Shape::initialiseEdgeData()
{
    int const N = numberOfEdges();

    for (int i = 0; i < N; i++) {
        eData[i].rdx = pData[getEdge(i).en].rx - pData[getEdge(i).st].rx;
        eData[i].length = dot(eData[i].rdx, eData[i].rdx);
        eData[i].ilength = 1 / eData[i].length;
        eData[i].sqlength = sqrt(eData[i].length);
        eData[i].isqlength = 1 / eData[i].sqlength;
        eData[i].siEd = eData[i].rdx[1] * eData[i].isqlength;
        eData[i].coEd = eData[i].rdx[0] * eData[i].isqlength;
        
        if (eData[i].siEd < 0) {
            eData[i].siEd = -eData[i].siEd;
            eData[i].coEd = -eData[i].coEd;
        }

        swsData[i].misc = nullptr;
        swsData[i].firstLinkedPoint = -1;
        swsData[i].stPt = swsData[i].enPt = -1;
        swsData[i].leftRnd = swsData[i].rightRnd = -1;
        swsData[i].nextSh = nullptr;
        swsData[i].nextBo = -1;
        swsData[i].curPoint = -1;
        swsData[i].doneTo = -1;
  }
}


void Shape::clearIncidenceData()
{
    g_free(iData);
    iData = nullptr;
    nbInc = maxInc = 0;
}



/**
 *    A directed graph is Eulerian iff every vertex has equal indegree and outdegree.
 *    http://mathworld.wolfram.com/EulerianGraph.html
 *
 *    \param s Directed shape.
 *    \return true if s is Eulerian.
 */

bool directedEulerian(Shape const *s)
{
    for (int i = 0; i < s->numberOfPoints(); i++) {
        if (s->getPoint(i).dI != s->getPoint(i).dO) {
            return false;
        }
    }

    return true;
}



/**
 *    \param s Shape.
 *    \param p Point.
 *    \return Minimum distance from p to any of the points or edges of s.
 */

double distance(Shape const *s, Geom::Point const &p)
{
    if ( s->hasPoints() == false) {
        return 0.0;
    }

    /* Find the minimum distance from p to one of the points on s.
    ** Computing the dot product of the difference vector gives
    ** us the distance squared; we can leave the square root
    ** until the end.
    */
    double bdot = Geom::dot(p - s->getPoint(0).x, p - s->getPoint(0).x);

    for (int i = 0; i < s->numberOfPoints(); i++) {
        Geom::Point const offset( p - s->getPoint(i).x );
        double ndot = Geom::dot(offset, offset);
        if ( ndot < bdot ) {
            bdot = ndot;
        }
    }

    for (int i = 0; i < s->numberOfEdges(); i++) {
        if ( s->getEdge(i).st >= 0 && s->getEdge(i).en >= 0 ) {
            /* The edge has start and end points */
            Geom::Point const st(s->getPoint(s->getEdge(i).st).x); // edge start
            Geom::Point const en(s->getPoint(s->getEdge(i).en).x); // edge end

            Geom::Point const d(p - st);       // vector between p and edge start
            Geom::Point const e(en - st);      // vector of the edge
            double const el = Geom::dot(e, e); // edge length

            /* Update bdot if appropriate */
            if ( el > 0.001 ) {
                double const npr = Geom::dot(d, e);
                if ( npr > 0 && npr < el ) {
                    double const nl = fabs( Geom::cross(d, e) );
                    double ndot = nl * nl / el;
                    if ( ndot < bdot ) {
                        bdot = ndot;
                    }
                }
            }
        }
    }
    
    return sqrt(bdot);
}



/**
 *    Returns true iff the L2 distance from \a thePt to this shape is <= \a max_l2.
 *    Distance = the min of distance to its points and distance to its edges.
 *    Points without edges are considered, which is maybe unwanted...
 *
 *    This is largely similar to distance().
 *
 *    \param s Shape.
 *    \param p Point.
 *    \param max_l2 L2 distance.
 */

bool distanceLessThanOrEqual(Shape const *s, Geom::Point const &p, double const max_l2)
{
    if ( s->hasPoints() == false ) {
        return false;
    }
    
    /* TODO: Consider using bbox to return early, perhaps conditional on nbPt or nbAr. */
    
    /* TODO: Efficiency: In one test case (scribbling with the freehand tool to create a small number of long
    ** path elements), changing from a Distance method to a DistanceLE method reduced this
    ** function's CPU time from about 21% of total inkscape CPU time to 14-15% of total inkscape
    ** CPU time, due to allowing early termination.  I don't know how much the L1 test helps, it
    ** may well be a case of premature optimization.  Consider testing dot(offset, offset)
    ** instead.
    */
  
    double const max_l1 = max_l2 * M_SQRT2;
    for (int i = 0; i < s->numberOfPoints(); i++) {
        Geom::Point const offset( p - s->getPoint(i).x );
        double const l1 = Geom::L1(offset);
        if ( (l1 <= max_l2) || ((l1 <= max_l1) && (Geom::L2(offset) <= max_l2)) ) {
            return true;
        }
    }
    
    for (int i = 0; i < s->numberOfEdges(); i++) {
        if ( s->getEdge(i).st >= 0 && s->getEdge(i).en >= 0 ) {
            Geom::Point const st(s->getPoint(s->getEdge(i).st).x);
            Geom::Point const en(s->getPoint(s->getEdge(i).en).x);
            Geom::Point const d(p - st);
            Geom::Point const e(en - st);
            double const el = Geom::L2(e);
            if ( el > 0.001 ) {
                Geom::Point const e_unit(e / el);
                double const npr = Geom::dot(d, e_unit);
                if ( npr > 0 && npr < el ) {
                    double const nl = fabs(Geom::cross(d, e_unit));
                    if ( nl <= max_l2 ) {
                        return true;
                    }
                }
            }
        }
    }
    
    return false;
}

//};

