// 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 <cstring>
#include <glib.h>
#include <2geom/affine.h>
#include "Shape.h"
#include "livarot/sweep-event-queue.h"
#include "livarot/sweep-tree-list.h"
#include "livarot/sweep-tree.h"

//int   doDebug=0;

/*
 * El Intersector.
 * algorithm: 1) benley ottman to get intersections of all the polygon's edges
 *            2) rounding of the points of the polygon, Hooby's algorithm
 *            3) DFS with clockwise choice of the edge to compute the windings
 *            4) choose edges according to winding numbers and fill rule
 * some additional nastyness: step 2 needs a seed winding number for the upper-left point of each 
 * connex subgraph of the graph. computing these brutally is O(n^3): baaaad. so during the sweeping in 1)
 * we keep for each point the edge of the resulting graph (not the original) that lies just on its left; 
 * when the time comes for the point to get its winding number computed, that edge must have been treated,
 * because its upper end lies above the aforementioned point, meaning we know the winding number of the point.
 * only, there is a catch: since we're sweeping the polygon, the edge we want to link the point to has not yet been
 * added (that would be too easy...). so the points are put on a linked list on the original shape's edge, and the list
 * is flushed when the edge is added.
 * rounding: to do the rounding, we need to find which edges cross the surrounding of the rounded points (at 
 * each sweepline position). grunt method tries all combination of "rounded points in the sweepline"x"edges crossing 
 * the sweepline". That's bad (and that's what polyboolean does, if i am not mistaken). so for each point 
 * rounded in a given sweepline, keep immediate left and right edges at the time the point is treated.
 * when edges/points crossing are searched, walk the edge list (in the  sweepline at the end of the batch) starting 
 * from the rounded points' left and right from that time. may sound strange, but it works because edges that
 * end or start in the batch have at least one end in the batch.
 * all these are the cause of the numerous linked lists of points and edges maintained in the sweeping
 */

void
Shape::ResetSweep ()
{
  MakePointData (true);
  MakeEdgeData (true);
  MakeSweepSrcData (true);
}

void
Shape::CleanupSweep ()
{
  MakePointData (false);
  MakeEdgeData (false);
  MakeSweepSrcData (false);
}

void
Shape::ForceToPolygon ()
{
  type = shape_polygon;
}

int
Shape::Reoriente (Shape * a)
{
  Reset (0, 0);
  if (a->numberOfPoints() <= 1 || a->numberOfEdges() <= 1)
    return 0;
  if (directedEulerian(a) == false)
    return shape_input_err;

  _pts = a->_pts;
  if (numberOfPoints() > maxPt)
    {
      maxPt = numberOfPoints();
      if (_has_points_data) {
        pData.resize(maxPt);
        _point_data_initialised = false;
        _bbox_up_to_date = false;
        }
    }

  _aretes = a->_aretes;
  if (numberOfEdges() > maxAr)
    {
      maxAr = numberOfEdges();
      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);
    }

  MakePointData (true);
  MakeEdgeData (true);
  MakeSweepDestData (true);

  initialisePointData();

  for (int i = 0; i < numberOfPoints(); i++) {
      _pts[i].x = pData[i].rx;
      _pts[i].oldDegree = getPoint(i).totalDegree();
  }
  
  for (int i = 0; i < a->numberOfEdges(); i++)
    {
      eData[i].rdx = pData[getEdge(i).en].rx - pData[getEdge(i).st].rx;
      eData[i].weight = 1;
      _aretes[i].dx = eData[i].rdx;
    }

  SortPointsRounded ();

  _need_edges_sorting = true;
  GetWindings (this, nullptr, bool_op_union, true);

//      Plot(341,56,8,400,400,true,true,false,true);
  for (int i = 0; i < numberOfEdges(); i++)
    {
      swdData[i].leW %= 2;
      swdData[i].riW %= 2;
      if (swdData[i].leW < 0)
	swdData[i].leW = -swdData[i].leW;
      if (swdData[i].riW < 0)
	swdData[i].riW = -swdData[i].riW;
      if (swdData[i].leW > 0 && swdData[i].riW <= 0)
	{
	  eData[i].weight = 1;
	}
      else if (swdData[i].leW <= 0 && swdData[i].riW > 0)
	{
	  Inverse (i);
	  eData[i].weight = 1;
	}
      else
	{
	  eData[i].weight = 0;
	  SubEdge (i);
	  i--;
	}
    }

  MakePointData (false);
  MakeEdgeData (false);
  MakeSweepDestData (false);

  if (directedEulerian(this) == false)
    {
//              printf( "pas euclidian2");
      _pts.clear();
      _aretes.clear();
      return shape_euler_err;
    }

  type = shape_polygon;
  return 0;
}

int
Shape::ConvertToShape (Shape * a, FillRule directed, bool invert)
{
    Reset (0, 0);

    if (a->numberOfPoints() <= 1 || a->numberOfEdges() <= 1) {
	return 0;
    }
    
    if ( directed != fill_justDont && directedEulerian(a) == false ) {
  			g_warning ("Shape error in ConvertToShape: directedEulerian(a) == false\n");
				return shape_input_err;
    }
  
    a->ResetSweep();

    if (sTree == nullptr) {
	sTree = new SweepTreeList(a->numberOfEdges());
    }
    if (sEvts == nullptr) {
	sEvts = new SweepEventQueue(a->numberOfEdges());
    }
  
    MakePointData(true);
    MakeEdgeData(true);
    MakeSweepSrcData(true);
    MakeSweepDestData(true);
    MakeBackData(a->_has_back_data);

    a->initialisePointData();
    a->initialiseEdgeData();

    a->SortPointsRounded();

    chgts.clear();

    double lastChange = a->pData[0].rx[1] - 1.0;
    int lastChgtPt = 0;
    int edgeHead = -1;
    Shape *shapeHead = nullptr;

    clearIncidenceData();
    
    int curAPt = 0;

    while (curAPt < a->numberOfPoints() || sEvts->size() > 0) {
	Geom::Point ptX;
      double ptL, ptR;
      SweepTree *intersL = nullptr;
      SweepTree *intersR = nullptr;
      int nPt = -1;
      Shape *ptSh = nullptr;
      bool isIntersection = false;
      if (sEvts->peek(intersL, intersR, ptX, ptL, ptR))
	{
	  if (a->pData[curAPt].pending > 0
	      || (a->pData[curAPt].rx[1] > ptX[1]
		  || (a->pData[curAPt].rx[1] == ptX[1]
		      && a->pData[curAPt].rx[0] > ptX[0])))
	    {
	      /* FIXME: could just be pop? */
	      sEvts->extract(intersL, intersR, ptX, ptL, ptR);
	      isIntersection = true;
	    }
	  else
	    {
	      nPt = curAPt++;
	      ptSh = a;
	      ptX = ptSh->pData[nPt].rx;
	      isIntersection = false;
	    }
	}
      else
	{
	  nPt = curAPt++;
	  ptSh = a;
	  ptX = ptSh->pData[nPt].rx;
	  isIntersection = false;
	}

      if (isIntersection == false)
	{
	  if (ptSh->getPoint(nPt).dI == 0 && ptSh->getPoint(nPt).dO == 0)
	    continue;
	}

      Geom::Point rPtX;
      rPtX[0]= Round (ptX[0]);
      rPtX[1]= Round (ptX[1]);
      int lastPointNo = AddPoint (rPtX);
      pData[lastPointNo].rx = rPtX;

      if (rPtX[1] > lastChange)
	{
	  int lastI = AssemblePoints (lastChgtPt, lastPointNo);

	  Shape *curSh = shapeHead;
	  int curBo = edgeHead;
	  while (curSh)
	    {
	      curSh->swsData[curBo].leftRnd =
		pData[curSh->swsData[curBo].leftRnd].newInd;
	      curSh->swsData[curBo].rightRnd =
		pData[curSh->swsData[curBo].rightRnd].newInd;

	      Shape *neSh = curSh->swsData[curBo].nextSh;
	      curBo = curSh->swsData[curBo].nextBo;
	      curSh = neSh;
	    }

	  for (auto & chgt : chgts)
	    {
	      chgt.ptNo = pData[chgt.ptNo].newInd;
	      if (chgt.type == 0)
		{
		  if (chgt.src->getEdge(chgt.bord).st <
		      chgt.src->getEdge(chgt.bord).en)
		    {
		      chgt.src->swsData[chgt.bord].stPt =
			chgt.ptNo;
		    }
		  else
		    {
		      chgt.src->swsData[chgt.bord].enPt =
			chgt.ptNo;
		    }
		}
	      else if (chgt.type == 1)
		{
		  if (chgt.src->getEdge(chgt.bord).st >
		      chgt.src->getEdge(chgt.bord).en)
		    {
		      chgt.src->swsData[chgt.bord].stPt =
			chgt.ptNo;
		    }
		  else
		    {
		      chgt.src->swsData[chgt.bord].enPt =
			chgt.ptNo;
		    }
		}
	    }

	  CheckAdjacencies (lastI, lastChgtPt, shapeHead, edgeHead);

	  CheckEdges (lastI, lastChgtPt, a, nullptr, bool_op_union);

	  for (int i = lastChgtPt; i < lastI; i++) {
	    if (pData[i].askForWindingS) {
		    Shape *windS = pData[i].askForWindingS;
		    int windB = pData[i].askForWindingB;
		    pData[i].nextLinkedPoint = windS->swsData[windB].firstLinkedPoint;
		    windS->swsData[windB].firstLinkedPoint = i;
		  }
	   }

    if (lastI < lastPointNo) {
          _pts[lastI] = getPoint(lastPointNo);
	   pData[lastI] = pData[lastPointNo];
	  }
	  lastPointNo = lastI;
	  _pts.resize(lastI + 1);

	  lastChgtPt = lastPointNo;
	  lastChange = rPtX[1];
	  chgts.clear();
	  edgeHead = -1;
	  shapeHead = nullptr;
	}


      if (isIntersection)
	{
//                      printf("(%i %i [%i %i]) ",intersL->bord,intersR->bord,intersL->startPoint,intersR->startPoint);
	  intersL->RemoveEvent (*sEvts, LEFT);
	  intersR->RemoveEvent (*sEvts, RIGHT);

	  AddChgt (lastPointNo, lastChgtPt, shapeHead, edgeHead, INTERSECTION,
		   intersL->src, intersL->bord, intersR->src, intersR->bord);

	  intersL->SwapWithRight (*sTree, *sEvts);

	  TesteIntersection (intersL, LEFT, false);
	  TesteIntersection (intersR, RIGHT, false);
	}
      else
	{
	  int cb;

	  int nbUp = 0, nbDn = 0;
	  int upNo = -1, dnNo = -1;
	  cb = ptSh->getPoint(nPt).incidentEdge[FIRST];
	  while (cb >= 0 && cb < ptSh->numberOfEdges())
	    {
	      if ((ptSh->getEdge(cb).st < ptSh->getEdge(cb).en
		   && nPt == ptSh->getEdge(cb).en)
		  || (ptSh->getEdge(cb).st > ptSh->getEdge(cb).en
		      && nPt == ptSh->getEdge(cb).st))
		{
		  upNo = cb;
		  nbUp++;
		}
	      if ((ptSh->getEdge(cb).st > ptSh->getEdge(cb).en
		   && nPt == ptSh->getEdge(cb).en)
		  || (ptSh->getEdge(cb).st < ptSh->getEdge(cb).en
		      && nPt == ptSh->getEdge(cb).st))
		{
		  dnNo = cb;
		  nbDn++;
		}
	      cb = ptSh->NextAt (nPt, cb);
	    }

	  if (nbDn <= 0)
	    {
	      upNo = -1;
	    }
	  if (upNo >= 0 && (SweepTree *) ptSh->swsData[upNo].misc == nullptr)
	    {
	      upNo = -1;
	    }

	  bool doWinding = true;

	  if (nbUp > 0)
	    {
	      cb = ptSh->getPoint(nPt).incidentEdge[FIRST];
	      while (cb >= 0 && cb < ptSh->numberOfEdges())
		{
		  if ((ptSh->getEdge(cb).st < ptSh->getEdge(cb).en
		       && nPt == ptSh->getEdge(cb).en)
		      || (ptSh->getEdge(cb).st > ptSh->getEdge(cb).en
			  && nPt == ptSh->getEdge(cb).st))
		    {
		      if (cb != upNo)
			{
			  SweepTree *node =
			    (SweepTree *) ptSh->swsData[cb].misc;
			  if (node == nullptr)
			    {
			    }
			  else
			    {
			      AddChgt (lastPointNo, lastChgtPt, shapeHead,
				       edgeHead, EDGE_REMOVED, node->src, node->bord,
				       nullptr, -1);
			      ptSh->swsData[cb].misc = nullptr;

			      int onLeftB = -1, onRightB = -1;
			      Shape *onLeftS = nullptr;
			      Shape *onRightS = nullptr;
			      if (node->elem[LEFT])
				{
				  onLeftB =
				    (static_cast <
				     SweepTree * >(node->elem[LEFT]))->bord;
				  onLeftS =
				    (static_cast <
				     SweepTree * >(node->elem[LEFT]))->src;
				}
			      if (node->elem[RIGHT])
				{
				  onRightB =
				    (static_cast <
				     SweepTree * >(node->elem[RIGHT]))->bord;
				  onRightS =
				    (static_cast <
				     SweepTree * >(node->elem[RIGHT]))->src;
				}

			      node->Remove (*sTree, *sEvts, true);
			      if (onLeftS && onRightS)
				{
				  SweepTree *onLeft =
				    (SweepTree *) onLeftS->swsData[onLeftB].
				    misc;
				  if (onLeftS == ptSh
				      && (onLeftS->getEdge(onLeftB).en == nPt
					  || onLeftS->getEdge(onLeftB).st ==
					  nPt))
				    {
				    }
				  else
				    {
				      if (onRightS == ptSh
					  && (onRightS->getEdge(onRightB).en ==
					      nPt
					      || onRightS->getEdge(onRightB).
					      st == nPt))
					{
					}
				      else
					{
					  TesteIntersection (onLeft, RIGHT, false);
					}
				    }
				}
			    }
			}
		    }
		  cb = ptSh->NextAt (nPt, cb);
		}
	    }

	  // traitement du "upNo devient dnNo"
	  SweepTree *insertionNode = nullptr;
	  if (dnNo >= 0)
	    {
	      if (upNo >= 0)
		{
		  SweepTree *node = (SweepTree *) ptSh->swsData[upNo].misc;

		  AddChgt (lastPointNo, lastChgtPt, shapeHead, edgeHead, EDGE_REMOVED,
			   node->src, node->bord, nullptr, -1);

		  ptSh->swsData[upNo].misc = nullptr;

		  node->RemoveEvents (*sEvts);
		  node->ConvertTo (ptSh, dnNo, 1, lastPointNo);
		  ptSh->swsData[dnNo].misc = node;
		  TesteIntersection (node, RIGHT, false);
		  TesteIntersection (node, LEFT, false);
		  insertionNode = node;

		  ptSh->swsData[dnNo].curPoint = lastPointNo;
		  AddChgt (lastPointNo, lastChgtPt, shapeHead, edgeHead, EDGE_INSERTED,
			   node->src, node->bord, nullptr, -1);
		}
	      else
		{
		  SweepTree *node = sTree->add(ptSh, dnNo, 1, lastPointNo, this);
		  ptSh->swsData[dnNo].misc = node;
		  node->Insert (*sTree, *sEvts, this, lastPointNo, true);
		  if (doWinding)
		    {
		      SweepTree *myLeft =
			static_cast < SweepTree * >(node->elem[LEFT]);
		      if (myLeft)
			{
			  pData[lastPointNo].askForWindingS = myLeft->src;
			  pData[lastPointNo].askForWindingB = myLeft->bord;
			}
		      else
			{
			  pData[lastPointNo].askForWindingB = -1;
			}
		      doWinding = false;
		    }
		  TesteIntersection (node, RIGHT, false);
		  TesteIntersection (node, LEFT, false);
		  insertionNode = node;

		  ptSh->swsData[dnNo].curPoint = lastPointNo;
		  AddChgt (lastPointNo, lastChgtPt, shapeHead, edgeHead, EDGE_INSERTED,
			   node->src, node->bord, nullptr, -1);
		}
	    }

	  if (nbDn > 1)
	    {			// si nbDn == 1 , alors dnNo a deja ete traite
	      cb = ptSh->getPoint(nPt).incidentEdge[FIRST];
	      while (cb >= 0 && cb < ptSh->numberOfEdges())
		{
		  if ((ptSh->getEdge(cb).st > ptSh->getEdge(cb).en
		       && nPt == ptSh->getEdge(cb).en)
		      || (ptSh->getEdge(cb).st < ptSh->getEdge(cb).en
			  && nPt == ptSh->getEdge(cb).st))
		    {
		      if (cb != dnNo)
			{
			  SweepTree *node = sTree->add(ptSh, cb, 1, lastPointNo, this);
			  ptSh->swsData[cb].misc = node;
			  node->InsertAt (*sTree, *sEvts, this, insertionNode,
					  nPt, true);
			  if (doWinding)
			    {
			      SweepTree *myLeft =
				static_cast < SweepTree * >(node->elem[LEFT]);
			      if (myLeft)
				{
				  pData[lastPointNo].askForWindingS =
				    myLeft->src;
				  pData[lastPointNo].askForWindingB =
				    myLeft->bord;
				}
			      else
				{
				  pData[lastPointNo].askForWindingB = -1;
				}
			      doWinding = false;
			    }
			  TesteIntersection (node, RIGHT, false);
			  TesteIntersection (node, LEFT, false);

			  ptSh->swsData[cb].curPoint = lastPointNo;
			  AddChgt (lastPointNo, lastChgtPt, shapeHead,
				   edgeHead, EDGE_INSERTED, node->src, node->bord, nullptr,
				   -1);
			}
		    }
		  cb = ptSh->NextAt (nPt, cb);
		}
	    }
	}
    }
  {
    int lastI = AssemblePoints (lastChgtPt, numberOfPoints());


    Shape *curSh = shapeHead;
    int curBo = edgeHead;
    while (curSh)
      {
	curSh->swsData[curBo].leftRnd =
	  pData[curSh->swsData[curBo].leftRnd].newInd;
	curSh->swsData[curBo].rightRnd =
	  pData[curSh->swsData[curBo].rightRnd].newInd;

	Shape *neSh = curSh->swsData[curBo].nextSh;
	curBo = curSh->swsData[curBo].nextBo;
	curSh = neSh;
      }

    for (auto & chgt : chgts)
      {
	chgt.ptNo = pData[chgt.ptNo].newInd;
	if (chgt.type == 0)
	  {
	    if (chgt.src->getEdge(chgt.bord).st <
		chgt.src->getEdge(chgt.bord).en)
	      {
		chgt.src->swsData[chgt.bord].stPt = chgt.ptNo;
	      }
	    else
	      {
		chgt.src->swsData[chgt.bord].enPt = chgt.ptNo;
	      }
	  }
	else if (chgt.type == 1)
	  {
	    if (chgt.src->getEdge(chgt.bord).st >
		chgt.src->getEdge(chgt.bord).en)
	      {
		chgt.src->swsData[chgt.bord].stPt = chgt.ptNo;
	      }
	    else
	      {
		chgt.src->swsData[chgt.bord].enPt = chgt.ptNo;
	      }
	  }
      }

    CheckAdjacencies (lastI, lastChgtPt, shapeHead, edgeHead);

    CheckEdges (lastI, lastChgtPt, a, nullptr, bool_op_union);

    for (int i = lastChgtPt; i < lastI; i++)
      {
	if (pData[i].askForWindingS)
	  {
	    Shape *windS = pData[i].askForWindingS;
	    int windB = pData[i].askForWindingB;
	    pData[i].nextLinkedPoint = windS->swsData[windB].firstLinkedPoint;
	    windS->swsData[windB].firstLinkedPoint = i;
	  }
      }

    _pts.resize(lastI);

    edgeHead = -1;
    shapeHead = nullptr;
  }

    chgts.clear();

//  Plot (98.0, 112.0, 8.0, 400.0, 400.0, true, true, true, true);
//      Plot(200.0,200.0,2.0,400.0,400.0,true,true,true,true);

  //      AssemblePoints(a);

//      GetAdjacencies(a);

//      MakeAretes(a);
    clearIncidenceData();

  AssembleAretes (directed);

//  Plot (98.0, 112.0, 8.0, 400.0, 400.0, true, true, true, true);

  for (int i = 0; i < numberOfPoints(); i++)
    {
      _pts[i].oldDegree = getPoint(i).totalDegree();
    }
//      Validate();

  _need_edges_sorting = true;
  if ( directed == fill_justDont ) {
    SortEdges();
  } else {
    GetWindings (a);
  }
//  Plot (98.0, 112.0, 8.0, 400.0, 400.0, true, true, true, true);
//   if ( doDebug ) {
//   a->CalcBBox();
//     a->Plot(a->leftX,a->topY,32.0,0.0,0.0,true,true,true,true,"orig.svg");
//     Plot(a->leftX,a->topY,32.0,0.0,0.0,true,true,true,true,"winded.svg");
//   }
  if (directed == fill_positive)
  {
    if (invert)
    {
      for (int i = 0; i < numberOfEdges(); i++)
	    {
	      if (swdData[i].leW < 0 && swdData[i].riW >= 0)
        {
          eData[i].weight = 1;
        }
	      else if (swdData[i].leW >= 0 && swdData[i].riW < 0)
        {
          Inverse (i);
          eData[i].weight = 1;
        }
	      else
        {
          eData[i].weight = 0;
          SubEdge (i);
          i--;
        }
	    }
    }
    else
    {
      for (int i = 0; i < numberOfEdges(); i++)
	    {
	      if (swdData[i].leW > 0 && swdData[i].riW <= 0)
        {
          eData[i].weight = 1;
        }
	      else if (swdData[i].leW <= 0 && swdData[i].riW > 0)
        {
          Inverse (i);
          eData[i].weight = 1;
        }
	      else
        {
           eData[i].weight = 0;
          SubEdge (i);
          i--;
        }
	    }
    }
  }
  else if (directed == fill_nonZero)
  {
    if (invert)
    {
      for (int i = 0; i < numberOfEdges(); i++)
	    {
	      if (swdData[i].leW < 0 && swdData[i].riW == 0)
        {
          eData[i].weight = 1;
        }
	      else if (swdData[i].leW > 0 && swdData[i].riW == 0)
        {
          eData[i].weight = 1;
        }
	      else if (swdData[i].leW == 0 && swdData[i].riW < 0)
        {
          Inverse (i);
          eData[i].weight = 1;
        }
	      else if (swdData[i].leW == 0 && swdData[i].riW > 0)
        {
          Inverse (i);
          eData[i].weight = 1;
        }
	      else
        {
          eData[i].weight = 0;
          SubEdge (i);
          i--;
        }
	    }
    }
    else
    {
      for (int i = 0; i < numberOfEdges(); i++)
	    {
	      if (swdData[i].leW > 0 && swdData[i].riW == 0)
        {
          eData[i].weight = 1;
        }
	      else if (swdData[i].leW < 0 && swdData[i].riW == 0)
        {
          eData[i].weight = 1;
        }
	      else if (swdData[i].leW == 0 && swdData[i].riW > 0)
        {
          Inverse (i);
          eData[i].weight = 1;
        }
	      else if (swdData[i].leW == 0 && swdData[i].riW < 0)
        {
          Inverse (i);
          eData[i].weight = 1;
        }
	      else
        {
          eData[i].weight = 0;
          SubEdge (i);
          i--;
        }
	    }
    }
  }
  else if (directed == fill_oddEven)
  {
    for (int i = 0; i < numberOfEdges(); i++)
    {
      swdData[i].leW %= 2;
      swdData[i].riW %= 2;
      if (swdData[i].leW < 0)
        swdData[i].leW = -swdData[i].leW;
      if (swdData[i].riW < 0)
        swdData[i].riW = -swdData[i].riW;
      if (swdData[i].leW > 0 && swdData[i].riW <= 0)
	    {
	      eData[i].weight = 1;
	    }
      else if (swdData[i].leW <= 0 && swdData[i].riW > 0)
	    {
	      Inverse (i);
	      eData[i].weight = 1;
	    }
      else
	    {
	      eData[i].weight = 0;
	      SubEdge (i);
	      i--;
	    }
    }
  } else if ( directed == fill_justDont ) {
    for (int i=0;i<numberOfEdges();i++) {
      if ( getEdge(i).st < 0 || getEdge(i).en < 0 ) {
        SubEdge(i);
        i--;
      } else {
	      eData[i].weight = 0;
      }
    }
  }
  
//      Plot(200.0,200.0,2.0,400.0,400.0,true,true,true,true);

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

  MakePointData (false);
  MakeEdgeData (false);
  MakeSweepSrcData (false);
  MakeSweepDestData (false);
  a->CleanupSweep ();
  type = shape_polygon;
  return 0;
}

// technically it's just a ConvertToShape() on 2 polygons at the same time, and different rules
// for choosing the edges according to their winding numbers.
// probably one of the biggest function i ever wrote.
int
Shape::Booleen (Shape * a, Shape * b, BooleanOp mod,int cutPathID)
{
  if (a == b || a == nullptr || b == nullptr)
    return shape_input_err;
  Reset (0, 0);
  if (a->numberOfPoints() <= 1 || a->numberOfEdges() <= 1)
    return 0;
  if (b->numberOfPoints() <= 1 || b->numberOfEdges() <= 1)
    return 0;
  if ( mod == bool_op_cut ) {
  } else if ( mod == bool_op_slice ) {
  } else {
    if (a->type != shape_polygon)
      return shape_input_err;
    if (b->type != shape_polygon)
      return shape_input_err;
  }

  a->ResetSweep ();
  b->ResetSweep ();

  if (sTree == nullptr) {
      sTree = new SweepTreeList(a->numberOfEdges() + b->numberOfEdges());
  }
  if (sEvts == nullptr) {
      sEvts = new SweepEventQueue(a->numberOfEdges() + b->numberOfEdges());
  }
  
  MakePointData (true);
  MakeEdgeData (true);
  MakeSweepSrcData (true);
  MakeSweepDestData (true);
  if (a->hasBackData () && b->hasBackData ())
    {
      MakeBackData (true);
    }
  else
    {
      MakeBackData (false);
    }

  a->initialisePointData();
  b->initialisePointData();

  a->initialiseEdgeData();
  b->initialiseEdgeData();

  a->SortPointsRounded ();
  b->SortPointsRounded ();

  chgts.clear();

  double lastChange =
    (a->pData[0].rx[1] <
     b->pData[0].rx[1]) ? a->pData[0].rx[1] - 1.0 : b->pData[0].rx[1] - 1.0;
  int lastChgtPt = 0;
  int edgeHead = -1;
  Shape *shapeHead = nullptr;

  clearIncidenceData();

  int curAPt = 0;
  int curBPt = 0;

  while (curAPt < a->numberOfPoints() || curBPt < b->numberOfPoints() || sEvts->size() > 0)
    {
/*		for (int i=0;i<sEvts.nbEvt;i++) {
			printf("%f %f %i %i\n",sEvts.events[i].posx,sEvts.events[i].posy,sEvts.events[i].leftSweep->bord,sEvts.events[i].rightSweep->bord); // localizing ok
		}
		//		cout << endl;
		if ( sTree.racine ) {
			SweepTree*  ct=static_cast <SweepTree*> (sTree.racine->Leftmost());
			while ( ct ) {
				printf("%i %i [%i\n",ct->bord,ct->startPoint,(ct->src==a)?1:0);
				ct=static_cast <SweepTree*> (ct->elem[RIGHT]);
			}
		}
		printf("\n");*/

    Geom::Point ptX;
      double ptL, ptR;
      SweepTree *intersL = nullptr;
      SweepTree *intersR = nullptr;
      int nPt = -1;
      Shape *ptSh = nullptr;
      bool isIntersection = false;

      if (sEvts->peek(intersL, intersR, ptX, ptL, ptR))
	{
	  if (curAPt < a->numberOfPoints())
	    {
	      if (curBPt < b->numberOfPoints())
		{
		  if (a->pData[curAPt].rx[1] < b->pData[curBPt].rx[1]
		      || (a->pData[curAPt].rx[1] == b->pData[curBPt].rx[1]
			  && a->pData[curAPt].rx[0] < b->pData[curBPt].rx[0]))
		    {
		      if (a->pData[curAPt].pending > 0
			  || (a->pData[curAPt].rx[1] > ptX[1]
			      || (a->pData[curAPt].rx[1] == ptX[1]
				  && a->pData[curAPt].rx[0] > ptX[0])))
			{
			  /* FIXME: could be pop? */
			  sEvts->extract(intersL, intersR, ptX, ptL, ptR);
			  isIntersection = true;
			}
		      else
			{
			  nPt = curAPt++;
			  ptSh = a;
			  ptX = ptSh->pData[nPt].rx;
			  isIntersection = false;
			}
		    }
		  else
		    {
		      if (b->pData[curBPt].pending > 0
			  || (b->pData[curBPt].rx[1] > ptX[1]
			      || (b->pData[curBPt].rx[1] == ptX[1]
				  && b->pData[curBPt].rx[0] > ptX[0])))
			{
			  /* FIXME: could be pop? */
			  sEvts->extract(intersL, intersR, ptX, ptL, ptR);
			  isIntersection = true;
			}
		      else
			{
			  nPt = curBPt++;
			  ptSh = b;
			  ptX = ptSh->pData[nPt].rx;
			  isIntersection = false;
			}
		    }
		}
	      else
		{
		  if (a->pData[curAPt].pending > 0
		      || (a->pData[curAPt].rx[1] > ptX[1]
			  || (a->pData[curAPt].rx[1] == ptX[1]
			      && a->pData[curAPt].rx[0] > ptX[0])))
		    {
		      /* FIXME: could be pop? */
		      sEvts->extract(intersL, intersR, ptX, ptL, ptR);
		      isIntersection = true;
		    }
		  else
		    {
		      nPt = curAPt++;
		      ptSh = a;
		      ptX = ptSh->pData[nPt].rx;
		      isIntersection = false;
		    }
		}
	    }
	  else
	    {
	      if (b->pData[curBPt].pending > 0
		  || (b->pData[curBPt].rx[1] > ptX[1]
		      || (b->pData[curBPt].rx[1] == ptX[1]
			  && b->pData[curBPt].rx[0] > ptX[0])))
		{
		  /* FIXME: could be pop? */
		  sEvts->extract(intersL, intersR, ptX,  ptL, ptR);
		  isIntersection = true;
		}
	      else
		{
		  nPt = curBPt++;
		  ptSh = b;
		  ptX = ptSh->pData[nPt].rx;
		  isIntersection = false;
		}
	    }
	}
      else
	{
	  if (curAPt < a->numberOfPoints())
	    {
	      if (curBPt < b->numberOfPoints())
		{
		  if (a->pData[curAPt].rx[1] < b->pData[curBPt].rx[1]
		      || (a->pData[curAPt].rx[1] == b->pData[curBPt].rx[1]
			  && a->pData[curAPt].rx[0] < b->pData[curBPt].rx[0]))
		    {
		      nPt = curAPt++;
		      ptSh = a;
		    }
		  else
		    {
		      nPt = curBPt++;
		      ptSh = b;
		    }
		}
	      else
		{
		  nPt = curAPt++;
		  ptSh = a;
		}
	    }
	  else
	    {
	      nPt = curBPt++;
	      ptSh = b;
	    }
	  ptX = ptSh->pData[nPt].rx;
	  isIntersection = false;
	}

      if (isIntersection == false)
	{
	  if (ptSh->getPoint(nPt).dI == 0 && ptSh->getPoint(nPt).dO == 0)
	    continue;
	}

      Geom::Point rPtX;
      rPtX[0]= Round (ptX[0]);
      rPtX[1]= Round (ptX[1]);
      int lastPointNo = AddPoint (rPtX);
      pData[lastPointNo].rx = rPtX;

      if (rPtX[1] > lastChange)
	{
	  int lastI = AssemblePoints (lastChgtPt, lastPointNo);


	  Shape *curSh = shapeHead;
	  int curBo = edgeHead;
	  while (curSh)
	    {
	      curSh->swsData[curBo].leftRnd =
		pData[curSh->swsData[curBo].leftRnd].newInd;
	      curSh->swsData[curBo].rightRnd =
		pData[curSh->swsData[curBo].rightRnd].newInd;

	      Shape *neSh = curSh->swsData[curBo].nextSh;
	      curBo = curSh->swsData[curBo].nextBo;
	      curSh = neSh;
	    }

	  for (auto & chgt : chgts)
	    {
	      chgt.ptNo = pData[chgt.ptNo].newInd;
	      if (chgt.type == 0)
		{
		  if (chgt.src->getEdge(chgt.bord).st <
		      chgt.src->getEdge(chgt.bord).en)
		    {
		      chgt.src->swsData[chgt.bord].stPt =
			chgt.ptNo;
		    }
		  else
		    {
		      chgt.src->swsData[chgt.bord].enPt =
			chgt.ptNo;
		    }
		}
	      else if (chgt.type == 1)
		{
		  if (chgt.src->getEdge(chgt.bord).st >
		      chgt.src->getEdge(chgt.bord).en)
		    {
		      chgt.src->swsData[chgt.bord].stPt =
			chgt.ptNo;
		    }
		  else
		    {
		      chgt.src->swsData[chgt.bord].enPt =
			chgt.ptNo;
		    }
		}
	    }

	  CheckAdjacencies (lastI, lastChgtPt, shapeHead, edgeHead);

	  CheckEdges (lastI, lastChgtPt, a, b, mod);

	  for (int i = lastChgtPt; i < lastI; i++)
	    {
	      if (pData[i].askForWindingS)
		{
		  Shape *windS = pData[i].askForWindingS;
		  int windB = pData[i].askForWindingB;
		  pData[i].nextLinkedPoint =
		    windS->swsData[windB].firstLinkedPoint;
		  windS->swsData[windB].firstLinkedPoint = i;
		}
	    }

    if (lastI < lastPointNo)
	    {
	      _pts[lastI] = getPoint(lastPointNo);
	      pData[lastI] = pData[lastPointNo];
	    }
	  lastPointNo = lastI;
	  _pts.resize(lastI + 1);

	  lastChgtPt = lastPointNo;
	  lastChange = rPtX[1];
	  chgts.clear();
	  edgeHead = -1;
	  shapeHead = nullptr;
	}


      if (isIntersection)
	{
	  // les 2 events de part et d'autre de l'intersection
	  // (celui de l'intersection a deja ete depile)
	  intersL->RemoveEvent (*sEvts, LEFT);
	  intersR->RemoveEvent (*sEvts, RIGHT);

	  AddChgt (lastPointNo, lastChgtPt, shapeHead, edgeHead, INTERSECTION,
		   intersL->src, intersL->bord, intersR->src, intersR->bord);

	  intersL->SwapWithRight (*sTree, *sEvts);

	  TesteIntersection (intersL, LEFT, true);
	  TesteIntersection (intersR, RIGHT, true);
	}
      else
	{
	  int cb;

	  int nbUp = 0, nbDn = 0;
	  int upNo = -1, dnNo = -1;
	  cb = ptSh->getPoint(nPt).incidentEdge[FIRST];
	  while (cb >= 0 && cb < ptSh->numberOfEdges())
	    {
	      if ((ptSh->getEdge(cb).st < ptSh->getEdge(cb).en
		   && nPt == ptSh->getEdge(cb).en)
		  || (ptSh->getEdge(cb).st > ptSh->getEdge(cb).en
		      && nPt == ptSh->getEdge(cb).st))
		{
		  upNo = cb;
		  nbUp++;
		}
	      if ((ptSh->getEdge(cb).st > ptSh->getEdge(cb).en
		   && nPt == ptSh->getEdge(cb).en)
		  || (ptSh->getEdge(cb).st < ptSh->getEdge(cb).en
		      && nPt == ptSh->getEdge(cb).st))
		{
		  dnNo = cb;
		  nbDn++;
		}
	      cb = ptSh->NextAt (nPt, cb);
	    }

	  if (nbDn <= 0)
	    {
	      upNo = -1;
	    }
	  if (upNo >= 0 && (SweepTree *) ptSh->swsData[upNo].misc == nullptr)
	    {
	      upNo = -1;
	    }

//                      upNo=-1;

	  bool doWinding = true;

	  if (nbUp > 0)
	    {
	      cb = ptSh->getPoint(nPt).incidentEdge[FIRST];
	      while (cb >= 0 && cb < ptSh->numberOfEdges())
		{
		  if ((ptSh->getEdge(cb).st < ptSh->getEdge(cb).en
		       && nPt == ptSh->getEdge(cb).en)
		      || (ptSh->getEdge(cb).st > ptSh->getEdge(cb).en
			  && nPt == ptSh->getEdge(cb).st))
		    {
		      if (cb != upNo)
			{
			  SweepTree *node =
			    (SweepTree *) ptSh->swsData[cb].misc;
			  if (node == nullptr)
			    {
			    }
			  else
			    {
			      AddChgt (lastPointNo, lastChgtPt, shapeHead,
				       edgeHead, EDGE_REMOVED, node->src, node->bord,
				       nullptr, -1);
			      ptSh->swsData[cb].misc = nullptr;

			      int onLeftB = -1, onRightB = -1;
			      Shape *onLeftS = nullptr;
			      Shape *onRightS = nullptr;
			      if (node->elem[LEFT])
				{
				  onLeftB =
				    (static_cast <
				     SweepTree * >(node->elem[LEFT]))->bord;
				  onLeftS =
				    (static_cast <
				     SweepTree * >(node->elem[LEFT]))->src;
				}
			      if (node->elem[RIGHT])
				{
				  onRightB =
				    (static_cast <
				     SweepTree * >(node->elem[RIGHT]))->bord;
				  onRightS =
				    (static_cast <
				     SweepTree * >(node->elem[RIGHT]))->src;
				}

			      node->Remove (*sTree, *sEvts, true);
			      if (onLeftS && onRightS)
				{
				  SweepTree *onLeft =
				    (SweepTree *) onLeftS->swsData[onLeftB].
				    misc;
//                                                                      SweepTree* onRight=(SweepTree*)onRightS->swsData[onRightB].misc;
				  if (onLeftS == ptSh
				      && (onLeftS->getEdge(onLeftB).en == nPt
					  || onLeftS->getEdge(onLeftB).st ==
					  nPt))
				    {
				    }
				  else
				    {
				      if (onRightS == ptSh
					  && (onRightS->getEdge(onRightB).en ==
					      nPt
					      || onRightS->getEdge(onRightB).
					      st == nPt))
					{
					}
				      else
					{
					  TesteIntersection (onLeft, RIGHT, true);
					}
				    }
				}
			    }
			}
		    }
		  cb = ptSh->NextAt (nPt, cb);
		}
	    }

	  // traitement du "upNo devient dnNo"
	  SweepTree *insertionNode = nullptr;
	  if (dnNo >= 0)
	    {
	      if (upNo >= 0)
		{
		  SweepTree *node = (SweepTree *) ptSh->swsData[upNo].misc;

		  AddChgt (lastPointNo, lastChgtPt, shapeHead, edgeHead, EDGE_REMOVED,
			   node->src, node->bord, nullptr, -1);

		  ptSh->swsData[upNo].misc = nullptr;

		  node->RemoveEvents (*sEvts);
		  node->ConvertTo (ptSh, dnNo, 1, lastPointNo);
		  ptSh->swsData[dnNo].misc = node;
		  TesteIntersection (node, RIGHT, true);
		  TesteIntersection (node, LEFT, true);
		  insertionNode = node;

		  ptSh->swsData[dnNo].curPoint = lastPointNo;

		  AddChgt (lastPointNo, lastChgtPt, shapeHead, edgeHead, EDGE_INSERTED,
			   node->src, node->bord, nullptr, -1);
		}
	      else
		{
		  SweepTree *node = sTree->add(ptSh, dnNo, 1, lastPointNo, this);
		  ptSh->swsData[dnNo].misc = node;
		  node->Insert (*sTree, *sEvts, this, lastPointNo, true);

		  if (doWinding)
		    {
		      SweepTree *myLeft =
			static_cast < SweepTree * >(node->elem[LEFT]);
		      if (myLeft)
			{
			  pData[lastPointNo].askForWindingS = myLeft->src;
			  pData[lastPointNo].askForWindingB = myLeft->bord;
			}
		      else
			{
			  pData[lastPointNo].askForWindingB = -1;
			}
		      doWinding = false;
		    }

		  TesteIntersection (node, RIGHT, true);
		  TesteIntersection (node, LEFT, true);
		  insertionNode = node;

		  ptSh->swsData[dnNo].curPoint = lastPointNo;

		  AddChgt (lastPointNo, lastChgtPt, shapeHead, edgeHead, EDGE_INSERTED,
			   node->src, node->bord, nullptr, -1);
		}
	    }

	  if (nbDn > 1)
	    {			// si nbDn == 1 , alors dnNo a deja ete traite
	      cb = ptSh->getPoint(nPt).incidentEdge[FIRST];
	      while (cb >= 0 && cb < ptSh->numberOfEdges())
		{
		  if ((ptSh->getEdge(cb).st > ptSh->getEdge(cb).en
		       && nPt == ptSh->getEdge(cb).en)
		      || (ptSh->getEdge(cb).st < ptSh->getEdge(cb).en
			  && nPt == ptSh->getEdge(cb).st))
		    {
		      if (cb != dnNo)
			{
			  SweepTree *node = sTree->add(ptSh, cb, 1, lastPointNo, this);
			  ptSh->swsData[cb].misc = node;
//                                                      node->Insert(sTree,*sEvts,this,lastPointNo,true);
			  node->InsertAt (*sTree, *sEvts, this, insertionNode,
					  nPt, true);

			  if (doWinding)
			    {
			      SweepTree *myLeft =
				static_cast < SweepTree * >(node->elem[LEFT]);
			      if (myLeft)
				{
				  pData[lastPointNo].askForWindingS =
				    myLeft->src;
				  pData[lastPointNo].askForWindingB =
				    myLeft->bord;
				}
			      else
				{
				  pData[lastPointNo].askForWindingB = -1;
				}
			      doWinding = false;
			    }

			  TesteIntersection (node, RIGHT, true);
			  TesteIntersection (node, LEFT, true);

			  ptSh->swsData[cb].curPoint = lastPointNo;

			  AddChgt (lastPointNo, lastChgtPt, shapeHead,
				   edgeHead, EDGE_INSERTED, node->src, node->bord, nullptr,
				   -1);
			}
		    }
		  cb = ptSh->NextAt (nPt, cb);
		}
	    }
	}
    }
  {
    int lastI = AssemblePoints (lastChgtPt, numberOfPoints());


    Shape *curSh = shapeHead;
    int curBo = edgeHead;
    while (curSh)
      {
	curSh->swsData[curBo].leftRnd =
	  pData[curSh->swsData[curBo].leftRnd].newInd;
	curSh->swsData[curBo].rightRnd =
	  pData[curSh->swsData[curBo].rightRnd].newInd;

	Shape *neSh = curSh->swsData[curBo].nextSh;
	curBo = curSh->swsData[curBo].nextBo;
	curSh = neSh;
      }

    /* FIXME: this kind of code seems to appear frequently */
    for (auto & chgt : chgts)
      {
	chgt.ptNo = pData[chgt.ptNo].newInd;
	if (chgt.type == 0)
	  {
	    if (chgt.src->getEdge(chgt.bord).st <
		chgt.src->getEdge(chgt.bord).en)
	      {
		chgt.src->swsData[chgt.bord].stPt = chgt.ptNo;
	      }
	    else
	      {
		chgt.src->swsData[chgt.bord].enPt = chgt.ptNo;
	      }
	  }
	else if (chgt.type == 1)
	  {
	    if (chgt.src->getEdge(chgt.bord).st >
		chgt.src->getEdge(chgt.bord).en)
	      {
		chgt.src->swsData[chgt.bord].stPt = chgt.ptNo;
	      }
	    else
	      {
		chgt.src->swsData[chgt.bord].enPt = chgt.ptNo;
	      }
	  }
      }

    CheckAdjacencies (lastI, lastChgtPt, shapeHead, edgeHead);

    CheckEdges (lastI, lastChgtPt, a, b, mod);

    for (int i = lastChgtPt; i < lastI; i++)
      {
	if (pData[i].askForWindingS)
	  {
	    Shape *windS = pData[i].askForWindingS;
	    int windB = pData[i].askForWindingB;
	    pData[i].nextLinkedPoint = windS->swsData[windB].firstLinkedPoint;
	    windS->swsData[windB].firstLinkedPoint = i;
	  }
      }

    _pts.resize(lastI);

    edgeHead = -1;
    shapeHead = nullptr;
  }

  chgts.clear();
  clearIncidenceData();

//      Plot(190,70,6,400,400,true,false,true,true);

  if ( mod == bool_op_cut ) {
    AssembleAretes (fill_justDont);
    // dupliquer les aretes de la coupure
    int i=numberOfEdges()-1;
    for (;i>=0;i--) {
      if ( ebData[i].pathID == cutPathID ) {
        // on duplique
        int nEd=AddEdge(getEdge(i).en,getEdge(i).st);
        ebData[nEd].pathID=cutPathID;
        ebData[nEd].pieceID=ebData[i].pieceID;
        ebData[nEd].tSt=ebData[i].tEn;
        ebData[nEd].tEn=ebData[i].tSt;
        eData[nEd].weight=eData[i].weight;
        // lui donner les firstlinkedpoitn si besoin
        if ( getEdge(i).en >= getEdge(i).st ) {
          int cp = swsData[i].firstLinkedPoint;
          while (cp >= 0) {
            pData[cp].askForWindingB = nEd;
            cp = pData[cp].nextLinkedPoint;
          }
          swsData[nEd].firstLinkedPoint = swsData[i].firstLinkedPoint;
          swsData[i].firstLinkedPoint=-1;
        }
      }
    }
  } else if ( mod == bool_op_slice ) {
  } else {
    AssembleAretes ();
  }
  
  for (int i = 0; i < numberOfPoints(); i++)
    {
      _pts[i].oldDegree = getPoint(i).totalDegree();
    }

  _need_edges_sorting = true;
  if ( mod == bool_op_slice ) {
  } else {
    GetWindings (a, b, mod, false);
  }
//      Plot(190,70,6,400,400,true,true,true,true);

  if (mod == bool_op_symdiff)
  {
    for (int i = 0; i < numberOfEdges(); i++)
    {
      if (swdData[i].leW < 0)
        swdData[i].leW = -swdData[i].leW;
      if (swdData[i].riW < 0)
        swdData[i].riW = -swdData[i].riW;
      
      if (swdData[i].leW > 0 && swdData[i].riW <= 0)
	    {
	      eData[i].weight = 1;
	    }
      else if (swdData[i].leW <= 0 && swdData[i].riW > 0)
	    {
	      Inverse (i);
	      eData[i].weight = 1;
	    }
      else
	    {
	      eData[i].weight = 0;
	      SubEdge (i);
	      i--;
	    }
    }
  }
  else if (mod == bool_op_union || mod == bool_op_diff)
  {
    for (int i = 0; i < numberOfEdges(); i++)
    {
      if (swdData[i].leW > 0 && swdData[i].riW <= 0)
	    {
	      eData[i].weight = 1;
	    }
      else if (swdData[i].leW <= 0 && swdData[i].riW > 0)
	    {
	      Inverse (i);
	      eData[i].weight = 1;
	    }
      else
	    {
	      eData[i].weight = 0;
	      SubEdge (i);
	      i--;
	    }
    }
  }
  else if (mod == bool_op_inters)
  {
    for (int i = 0; i < numberOfEdges(); i++)
    {
      if (swdData[i].leW > 1 && swdData[i].riW <= 1)
	    {
	      eData[i].weight = 1;
	    }
      else if (swdData[i].leW <= 1 && swdData[i].riW > 1)
	    {
	      Inverse (i);
	      eData[i].weight = 1;
	    }
      else
	    {
	      eData[i].weight = 0;
	      SubEdge (i);
	      i--;
	    }
    }
  } else if ( mod == bool_op_cut ) {
    // inverser les aretes de la coupe au besoin
    for (int i=0;i<numberOfEdges();i++) {
      if ( getEdge(i).st < 0 || getEdge(i).en < 0 ) {
        if ( i < numberOfEdges()-1 ) {
          // decaler les askForWinding
          int cp = swsData[numberOfEdges()-1].firstLinkedPoint;
          while (cp >= 0) {
            pData[cp].askForWindingB = i;
            cp = pData[cp].nextLinkedPoint;
          }
        }
        SwapEdges(i,numberOfEdges()-1);
        SubEdge(numberOfEdges()-1);
//        SubEdge(i);
        i--;
      } else if ( ebData[i].pathID == cutPathID ) {
        swdData[i].leW=swdData[i].leW%2;
        swdData[i].riW=swdData[i].riW%2;
        if ( swdData[i].leW < swdData[i].riW ) {
          Inverse(i);
        }
      }
    }
  } else if ( mod == bool_op_slice ) {
    // supprimer les aretes de la coupe
    int i=numberOfEdges()-1;
    for (;i>=0;i--) {
      if ( ebData[i].pathID == cutPathID || getEdge(i).st < 0 || getEdge(i).en < 0 ) {
        SubEdge(i);
      }
    }
  }
  else
  {
    for (int i = 0; i < numberOfEdges(); i++)
    {
      if (swdData[i].leW > 0 && swdData[i].riW <= 0)
	    {
	      eData[i].weight = 1;
	    }
      else if (swdData[i].leW <= 0 && swdData[i].riW > 0)
	    {
	      Inverse (i);
	      eData[i].weight = 1;
	    }
      else
	    {
	      eData[i].weight = 0;
	      SubEdge (i);
	      i--;
	    }
    }
  }
  
  delete sTree;
  sTree = nullptr;
  delete sEvts;
  sEvts = nullptr;
  
  if ( mod == bool_op_cut ) {
    // on garde le askForWinding
  } else {
    MakePointData (false);
  }
  MakeEdgeData (false);
  MakeSweepSrcData (false);
  MakeSweepDestData (false);
  a->CleanupSweep ();
  b->CleanupSweep ();

  if (directedEulerian(this) == false)
    {
//              printf( "pas euclidian2");
      _pts.clear();
      _aretes.clear();
      return shape_euler_err;
    }
  type = shape_polygon;
  return 0;
}

// frontend to the TesteIntersection() below
void Shape::TesteIntersection(SweepTree *t, Side s, bool onlyDiff)
{
    SweepTree *tt = static_cast<SweepTree*>(t->elem[s]);
    if (tt == nullptr) {
	return;
    }

    SweepTree *a = (s == LEFT) ? tt : t;
    SweepTree *b = (s == LEFT) ? t : tt;

    Geom::Point atx;
    double atl;
    double atr;
    if (TesteIntersection(a, b, atx, atl, atr, onlyDiff)) {
	sEvts->add(a, b, atx, atl, atr);
    }
}

// a crucial piece of code: computing intersections between segments
bool
Shape::TesteIntersection (SweepTree * iL, SweepTree * iR, Geom::Point &atx, double &atL, double &atR, bool onlyDiff)
{
  int lSt = iL->src->getEdge(iL->bord).st, lEn = iL->src->getEdge(iL->bord).en;
  int rSt = iR->src->getEdge(iR->bord).st, rEn = iR->src->getEdge(iR->bord).en;
  Geom::Point ldir, rdir;
  ldir = iL->src->eData[iL->bord].rdx;
  rdir = iR->src->eData[iR->bord].rdx;
  // first, a round of checks to quickly dismiss edge which obviously dont intersect,
  // such as having disjoint bounding boxes
  if (lSt < lEn)
    {
    }
  else
    {
      int swap = lSt;
      lSt = lEn;
      lEn = swap;
      ldir = -ldir;
    }
  if (rSt < rEn)
    {
    }
  else
    {
      int swap = rSt;
      rSt = rEn;
      rEn = swap;
      rdir = -rdir;
    }

  if (iL->src->pData[lSt].rx[0] < iL->src->pData[lEn].rx[0])
    {
      if (iR->src->pData[rSt].rx[0] < iR->src->pData[rEn].rx[0])
	{
	  if (iL->src->pData[lSt].rx[0] > iR->src->pData[rEn].rx[0])
	    return false;
	  if (iL->src->pData[lEn].rx[0] < iR->src->pData[rSt].rx[0])
	    return false;
	}
      else
	{
	  if (iL->src->pData[lSt].rx[0] > iR->src->pData[rSt].rx[0])
	    return false;
	  if (iL->src->pData[lEn].rx[0] < iR->src->pData[rEn].rx[0])
	    return false;
	}
    }
  else
    {
      if (iR->src->pData[rSt].rx[0] < iR->src->pData[rEn].rx[0])
	{
	  if (iL->src->pData[lEn].rx[0] > iR->src->pData[rEn].rx[0])
	    return false;
	  if (iL->src->pData[lSt].rx[0] < iR->src->pData[rSt].rx[0])
	    return false;
	}
      else
	{
	  if (iL->src->pData[lEn].rx[0] > iR->src->pData[rSt].rx[0])
	    return false;
	  if (iL->src->pData[lSt].rx[0] < iR->src->pData[rEn].rx[0])
	    return false;
	}
    }

  double ang = cross (ldir, rdir);
//      ang*=iL->src->eData[iL->bord].isqlength;
//      ang*=iR->src->eData[iR->bord].isqlength;
  if (ang <= 0) return false;		// edges in opposite directions:  <-left  ... right ->
                                // they can't intersect

  // d'abord tester les bords qui partent d'un meme point
  if (iL->src == iR->src && lSt == rSt)
    {
      if (iL->src == iR->src && lEn == rEn)
	return false;		// c'est juste un doublon
      atx = iL->src->pData[lSt].rx;
      atR = atL = -1;
      return true;		// l'ordre est mauvais
    }
  if (iL->src == iR->src && lEn == rEn)
    return false;		// rien a faire=ils vont terminer au meme endroit

  // tester si on est dans une intersection multiple

  if (onlyDiff && iL->src == iR->src)
    return false;

  // on reprend les vrais points
  lSt = iL->src->getEdge(iL->bord).st;
  lEn = iL->src->getEdge(iL->bord).en;
  rSt = iR->src->getEdge(iR->bord).st;
  rEn = iR->src->getEdge(iR->bord).en;

  // compute intersection (if there is one)
  // Boissonat anr Preparata said in one paper that double precision floats were sufficient for get single precision
  // coordinates for the intersection, if the endpoints are single precision. i hope they're right...
  {
    Geom::Point sDiff, eDiff;
    double slDot, elDot;
    double srDot, erDot;
    sDiff = iL->src->pData[lSt].rx - iR->src->pData[rSt].rx;
    eDiff = iL->src->pData[lEn].rx - iR->src->pData[rSt].rx;
    srDot = cross(rdir, sDiff);
    erDot = cross(rdir, eDiff);
    sDiff = iR->src->pData[rSt].rx - iL->src->pData[lSt].rx;
    eDiff = iR->src->pData[rEn].rx - iL->src->pData[lSt].rx;
    slDot = cross(ldir, sDiff);
    elDot = cross(ldir, eDiff);

    if ((srDot >= 0 && erDot >= 0) || (srDot <= 0 && erDot <= 0))
      {
	if (srDot == 0)
	  {
	    if (lSt < lEn)
	      {
		atx = iL->src->pData[lSt].rx;
		atL = 0;
		atR = slDot / (slDot - elDot);
		return true;
	      }
	    else
	      {
		return false;
	      }
	  }
	else if (erDot == 0)
	  {
	    if (lSt > lEn)
	      {
		atx = iL->src->pData[lEn].rx;
		atL = 1;
		atR = slDot / (slDot - elDot);
		return true;
	      }
	    else
	      {
		return false;
	      }
	  }
	if (srDot > 0 && erDot > 0)
	  {
	    if (rEn < rSt)
	      {
		if (srDot < erDot)
		  {
		    if (lSt < lEn)
		      {
			atx = iL->src->pData[lSt].rx;
			atL = 0;
			atR = slDot / (slDot - elDot);
			return true;
		      }
		  }
		else
		  {
		    if (lEn < lSt)
		      {
			atx = iL->src->pData[lEn].rx;
			atL = 1;
			atR = slDot / (slDot - elDot);
			return true;
		      }
		  }
	      }
	  }
	if (srDot < 0 && erDot < 0)
	  {
	    if (rEn > rSt)
	      {
		if (srDot > erDot)
		  {
		    if (lSt < lEn)
		      {
			atx = iL->src->pData[lSt].rx;
			atL = 0;
			atR = slDot / (slDot - elDot);
			return true;
		      }
		  }
		else
		  {
		    if (lEn < lSt)
		      {
			atx = iL->src->pData[lEn].rx;
			atL = 1;
			atR = slDot / (slDot - elDot);
			return true;
		      }
		  }
	      }
	  }
	return false;
      }
    if ((slDot >= 0 && elDot >= 0) || (slDot <= 0 && elDot <= 0))
      {
	if (slDot == 0)
	  {
	    if (rSt < rEn)
	      {
		atx = iR->src->pData[rSt].rx;
		atR = 0;
		atL = srDot / (srDot - erDot);
		return true;
	      }
	    else
	      {
		return false;
	      }
	  }
	else if (elDot == 0)
	  {
	    if (rSt > rEn)
	      {
		atx = iR->src->pData[rEn].rx;
		atR = 1;
		atL = srDot / (srDot - erDot);
		return true;
	      }
	    else
	      {
		return false;
	      }
	  }
	if (slDot > 0 && elDot > 0)
	  {
	    if (lEn > lSt)
	      {
		if (slDot < elDot)
		  {
		    if (rSt < rEn)
		      {
			atx = iR->src->pData[rSt].rx;
			atR = 0;
			atL = srDot / (srDot - erDot);
			return true;
		      }
		  }
		else
		  {
		    if (rEn < rSt)
		      {
			atx = iR->src->pData[rEn].rx;
			atR = 1;
			atL = srDot / (srDot - erDot);
			return true;
		      }
		  }
	      }
	  }
	if (slDot < 0 && elDot < 0)
	  {
	    if (lEn < lSt)
	      {
		if (slDot > elDot)
		  {
		    if (rSt < rEn)
		      {
			atx = iR->src->pData[rSt].rx;
			atR = 0;
			atL = srDot / (srDot - erDot);
			return true;
		      }
		  }
		else
		  {
		    if (rEn < rSt)
		      {
			atx = iR->src->pData[rEn].rx;
			atR = 1;
			atL = srDot / (srDot - erDot);
			return true;
		      }
		  }
	      }
	  }
	return false;
      }

/*		double  slb=slDot-elDot,srb=srDot-erDot;
		if ( slb < 0 ) slb=-slb;
		if ( srb < 0 ) srb=-srb;*/
    if (iL->src->eData[iL->bord].siEd > iR->src->eData[iR->bord].siEd)
      {
	atx =
	  (slDot * iR->src->pData[rEn].rx -
	   elDot * iR->src->pData[rSt].rx) / (slDot - elDot);
      }
    else
      {
	atx =
	  (srDot * iL->src->pData[lEn].rx -
	   erDot * iL->src->pData[lSt].rx) / (srDot - erDot);
      }
    atL = srDot / (srDot - erDot);
    atR = slDot / (slDot - elDot);
    return true;
  }

  return true;
}

int
Shape::PushIncidence (Shape * a, int cb, int pt, double theta)
{
  if (theta < 0 || theta > 1)
    return -1;

  if (nbInc >= maxInc)
    {
      maxInc = 2 * nbInc + 1;
      iData =
	(incidenceData *) g_realloc(iData, maxInc * sizeof (incidenceData));
    }
  int n = nbInc++;
  iData[n].nextInc = a->swsData[cb].firstLinkedPoint;
  iData[n].pt = pt;
  iData[n].theta = theta;
  a->swsData[cb].firstLinkedPoint = n;
  return n;
}

int
Shape::CreateIncidence (Shape * a, int no, int nPt)
{
  Geom::Point adir, diff;
  adir = a->eData[no].rdx;
  diff = getPoint(nPt).x - a->pData[a->getEdge(no).st].rx;
  double t = dot (diff, adir);
  t *= a->eData[no].ilength;
  return PushIncidence (a, no, nPt, t);
}

int
Shape::Winding (int nPt) const 
{
  int askTo = pData[nPt].askForWindingB;
  if (askTo < 0 || askTo >= numberOfEdges())
    return 0;
  if (getEdge(askTo).st < getEdge(askTo).en)
    {
      return swdData[askTo].leW;
    }
  else
    {
      return swdData[askTo].riW;
    }
  return 0;
}

int
Shape::Winding (const Geom::Point px) const 
{
  int lr = 0, ll = 0, rr = 0;

  for (int i = 0; i < numberOfEdges(); i++)
    {
      Geom::Point adir, diff, ast, aen;
      adir = eData[i].rdx;

      ast = pData[getEdge(i).st].rx;
      aen = pData[getEdge(i).en].rx;

      int nWeight = eData[i].weight;

      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;
	}

      diff = px - ast;
      double 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;
}

// merging duplicate points and edges
int
Shape::AssemblePoints (int st, int en)
{
  if (en > st) {
   for (int i = st; i < en; i++) pData[i].oldInd = i;
//              SortPoints(st,en-1);
    SortPointsByOldInd (st, en - 1); // SortPointsByOldInd() is required here, because of the edges we have
                                       // associated with the point for later computation of winding numbers.
                                       // specifically, we need the first point we treated, it's the only one with a valid
                                       // associated edge (man, that was a nice bug).
     for (int i = st; i < en; i++) pData[pData[i].oldInd].newInd = i;

     int lastI = st;
     for (int i = st; i < en; i++) {
	      pData[i].pending = lastI++;
	      if (i > st && getPoint(i - 1).x[0] == getPoint(i).x[0] && getPoint(i - 1).x[1] == getPoint(i).x[1]) {
	        pData[i].pending = pData[i - 1].pending;
	        if (pData[pData[i].pending].askForWindingS == nullptr) {
		        pData[pData[i].pending].askForWindingS = pData[i].askForWindingS;
		        pData[pData[i].pending].askForWindingB = pData[i].askForWindingB;
		      } else {
		        if (pData[pData[i].pending].askForWindingS == pData[i].askForWindingS
		      && pData[pData[i].pending].askForWindingB == pData[i].askForWindingB) {
		      // meme bord, c bon
		        } else {
		      // meme point, mais pas le meme bord: ouille!
		      // il faut prendre le bord le plus a gauche
		      // en pratique, n'arrive que si 2 maxima sont dans la meme case -> le mauvais choix prend une arete incidente 
		      // au bon choix
//                                              printf("doh");
		        }
		      }
	        lastI--;
	      } else {
	        if (i > pData[i].pending) {
		        _pts[pData[i].pending].x = getPoint(i).x;
		        pData[pData[i].pending].rx = getPoint(i).x;
		        pData[pData[i].pending].askForWindingS = pData[i].askForWindingS;
		        pData[pData[i].pending].askForWindingB = pData[i].askForWindingB;
		      }
	      }
	    }
      for (int i = st; i < en; i++) pData[i].newInd = pData[pData[i].newInd].pending;
      return lastI;
  }
  return en;
}

void
Shape::AssemblePoints (Shape * a)
{
  if (hasPoints())
    {
      int lastI = AssemblePoints (0, numberOfPoints());

      for (int i = 0; i < a->numberOfEdges(); i++)
	{
	  a->swsData[i].stPt = pData[a->swsData[i].stPt].newInd;
	  a->swsData[i].enPt = pData[a->swsData[i].enPt].newInd;
	}
      for (int i = 0; i < nbInc; i++)
	iData[i].pt = pData[iData[i].pt].newInd;

      _pts.resize(lastI);
    }
}
void
Shape::AssembleAretes (FillRule directed)
{
  if ( directed == fill_justDont && _has_back_data == false ) {
    directed=fill_nonZero;
  }
  
  for (int i = 0; i < numberOfPoints(); i++) {
    if (getPoint(i).totalDegree() == 2) {
      int cb, cc;
      cb = getPoint(i).incidentEdge[FIRST];
      cc = getPoint(i).incidentEdge[LAST];
      bool  doublon=false;
      if ((getEdge(cb).st == getEdge(cc).st && getEdge(cb).en == getEdge(cc).en)
          || (getEdge(cb).st == getEdge(cc).en && getEdge(cb).en == getEdge(cc).en)) doublon=true;
      if ( directed == fill_justDont ) {
        if ( doublon ) {
          if ( ebData[cb].pathID > ebData[cc].pathID ) {
            cc = getPoint(i).incidentEdge[FIRST]; // on swappe pour enlever cc
            cb = getPoint(i).incidentEdge[LAST];
          } else if ( ebData[cb].pathID == ebData[cc].pathID ) {
            if ( ebData[cb].pieceID > ebData[cc].pieceID ) {
              cc = getPoint(i).incidentEdge[FIRST]; // on swappe pour enlever cc
              cb = getPoint(i).incidentEdge[LAST];
            } else if ( ebData[cb].pieceID == ebData[cc].pieceID ) { 
              if ( ebData[cb].tSt > ebData[cc].tSt ) {
                cc = getPoint(i).incidentEdge[FIRST]; // on swappe pour enlever cc
                cb = getPoint(i).incidentEdge[LAST];
              }
            }
          }
        }
        if ( doublon ) eData[cc].weight = 0;
      } else {
      }
      if ( doublon ) {
        if (getEdge(cb).st == getEdge(cc).st) {
          eData[cb].weight += eData[cc].weight;
        } else {
          eData[cb].weight -= eData[cc].weight;
        }
 	      eData[cc].weight = 0;
        
	      if (swsData[cc].firstLinkedPoint >= 0) {
          int cp = swsData[cc].firstLinkedPoint;
          while (cp >= 0) {
            pData[cp].askForWindingB = cb;
            cp = pData[cp].nextLinkedPoint;
          }
          if (swsData[cb].firstLinkedPoint < 0) {
            swsData[cb].firstLinkedPoint = swsData[cc].firstLinkedPoint;
          } else {
            int ncp = swsData[cb].firstLinkedPoint;
            while (pData[ncp].nextLinkedPoint >= 0) {
              ncp = pData[ncp].nextLinkedPoint;
            }
            pData[ncp].nextLinkedPoint = swsData[cc].firstLinkedPoint;
          }
        }
        
	      DisconnectStart (cc);
	      DisconnectEnd (cc);
	      if (numberOfEdges() > 1) {
          int cp = swsData[numberOfEdges() - 1].firstLinkedPoint;
          while (cp >= 0) {
            pData[cp].askForWindingB = cc;
            cp = pData[cp].nextLinkedPoint;
          }
        }
	      SwapEdges (cc, numberOfEdges() - 1);
	      if (cb == numberOfEdges() - 1) {
          cb = cc;
        }
	      _aretes.pop_back();
	    }
    } else {
      int cb;
      cb = getPoint(i).incidentEdge[FIRST];
      while (cb >= 0 && cb < numberOfEdges()) {
	      int other = Other (i, cb);
	      int cc;
	      cc = getPoint(i).incidentEdge[FIRST];
	      while (cc >= 0 && cc < numberOfEdges()) {
          int ncc = NextAt (i, cc);
          bool  doublon=false;
          if (cc != cb && Other (i, cc) == other ) doublon=true;
          if ( directed == fill_justDont ) {
            if ( doublon ) {
              if ( ebData[cb].pathID > ebData[cc].pathID ) {
                doublon=false;
              } else if ( ebData[cb].pathID == ebData[cc].pathID ) {
                if ( ebData[cb].pieceID > ebData[cc].pieceID ) {
                  doublon=false;
                } else if ( ebData[cb].pieceID == ebData[cc].pieceID ) { 
                  if ( ebData[cb].tSt > ebData[cc].tSt ) {
                    doublon=false;
                  }
                }
              }
            }
            if ( doublon ) eData[cc].weight = 0;
          } else {
          }
          if ( doublon ) {
//            if (cc != cb && Other (i, cc) == other) {
            // doublon
            if (getEdge(cb).st == getEdge(cc).st) {
              eData[cb].weight += eData[cc].weight;
            } else {
              eData[cb].weight -= eData[cc].weight;
            }
            eData[cc].weight = 0;
            
            if (swsData[cc].firstLinkedPoint >= 0) {
              int cp = swsData[cc].firstLinkedPoint;
              while (cp >= 0) {
                pData[cp].askForWindingB = cb;
                cp = pData[cp].nextLinkedPoint;
              }
              if (swsData[cb].firstLinkedPoint < 0) {
                swsData[cb].firstLinkedPoint = swsData[cc].firstLinkedPoint;
              } else {
                int ncp = swsData[cb].firstLinkedPoint;
                while (pData[ncp].nextLinkedPoint >= 0) {
                  ncp = pData[ncp].nextLinkedPoint;
                }
                pData[ncp].nextLinkedPoint = swsData[cc].firstLinkedPoint;
              }
            }
            
            DisconnectStart (cc);
            DisconnectEnd (cc);
            if (numberOfEdges() > 1) {
              int cp = swsData[numberOfEdges() - 1].firstLinkedPoint;
              while (cp >= 0) {
                pData[cp].askForWindingB = cc;
                cp = pData[cp].nextLinkedPoint;
              }
            }
            SwapEdges (cc, numberOfEdges() - 1);
            if (cb == numberOfEdges() - 1) {
              cb = cc;
            }
            if (ncc == numberOfEdges() - 1) {
              ncc = cc;
            }
	    _aretes.pop_back();
          }
          cc = ncc;
        }
	      cb = NextAt (i, cb);
	    }
    }
  }
  
  if ( directed == fill_justDont ) {
    for (int i = 0; i < numberOfEdges(); i++)  {
      if (eData[i].weight == 0) {
//        SubEdge(i);
 //       i--;
      } else {
        if (eData[i].weight < 0) Inverse (i);
      }
    }
  } else {
    for (int i = 0; i < numberOfEdges(); i++)  {
      if (eData[i].weight == 0) {
        //                      SubEdge(i);
        //                      i--;
      } else {
        if (eData[i].weight < 0) Inverse (i);
      }
    }
  }
}
void
Shape::GetWindings (Shape * /*a*/, Shape * /*b*/, BooleanOp /*mod*/, bool brutal)
{
  // preparation du parcours
  for (int i = 0; i < numberOfEdges(); i++)
    {
      swdData[i].misc = nullptr;
      swdData[i].precParc = swdData[i].suivParc = -1;
    }

  // chainage
  SortEdges ();

  int searchInd = 0;

  int lastPtUsed = 0;
  do
    {
      int startBord = -1;
      int outsideW = 0;
      {
	int fi = 0;
	for (fi = lastPtUsed; fi < numberOfPoints(); fi++)
	  {
	    if (getPoint(fi).incidentEdge[FIRST] >= 0 && swdData[getPoint(fi).incidentEdge[FIRST]].misc == nullptr)
	      break;
	  }
	lastPtUsed = fi + 1;
	if (fi < numberOfPoints())
	  {
	    int bestB = getPoint(fi).incidentEdge[FIRST];
	    if (bestB >= 0)
	      {
		startBord = bestB;
		if (fi == 0)
		  {
		    outsideW = 0;
		  }
		else
		  {
		    if (brutal)
		      {
			outsideW = Winding (getPoint(fi).x);
		      }
		    else
		      {
			outsideW = Winding (fi);
		      }
		  }
    if ( getPoint(fi).totalDegree() == 1 ) {
      if ( fi == getEdge(startBord).en ) {
        if ( eData[startBord].weight == 0 ) {
          // on se contente d'inverser
          Inverse(startBord);
        } else {
          // on passe le askForWinding (sinon ca va rester startBord)
          pData[getEdge(startBord).st].askForWindingB=pData[getEdge(startBord).en].askForWindingB;
        }
      }
    }
		if (getEdge(startBord).en == fi)
		  outsideW += eData[startBord].weight;
	      }
	  }
      }
      if (startBord >= 0)
	{
	  // parcours en profondeur pour mettre les leF et riF a leurs valeurs
	  swdData[startBord].misc = (void *) 1;
	  swdData[startBord].leW = outsideW;
	  swdData[startBord].riW = outsideW - eData[startBord].weight;
//    if ( doDebug ) printf("part de %d\n",startBord);
	  int curBord = startBord;
	  bool curDir = true;
	  swdData[curBord].precParc = -1;
	  swdData[curBord].suivParc = -1;
	  do
	    {
	      int cPt;
	      if (curDir)
		cPt = getEdge(curBord).en;
	      else
		cPt = getEdge(curBord).st;
	      int nb = curBord;
//        if ( doDebug ) printf("de curBord= %d avec leF= %d et riF= %d  -> ",curBord,swdData[curBord].leW,swdData[curBord].riW);
	      do
		{
		  int nnb = -1;
		  if (getEdge(nb).en == cPt)
		    {
		      outsideW = swdData[nb].riW;
		      nnb = CyclePrevAt (cPt, nb);
		    }
		  else
		    {
		      outsideW = swdData[nb].leW;
		      nnb = CyclePrevAt (cPt, nb);
		    }
		  if (nnb == nb)
		    {
		      // cul-de-sac
		      nb = -1;
		      break;
		    }
		  nb = nnb;
		}
	      while (nb >= 0 && nb != curBord && swdData[nb].misc != nullptr);
	      if (nb < 0 || nb == curBord)
		{
		  // retour en arriere
		  int oPt;
		  if (curDir)
		    oPt = getEdge(curBord).st;
		  else
		    oPt = getEdge(curBord).en;
		  curBord = swdData[curBord].precParc;
//    if ( doDebug ) printf("retour vers %d\n",curBord);
		  if (curBord < 0)
		    break;
		  if (oPt == getEdge(curBord).en)
		    curDir = true;
		  else
		    curDir = false;
		}
	      else
		{
		  swdData[nb].misc = (void *) 1;
		  swdData[nb].ind = searchInd++;
		  if (cPt == getEdge(nb).st)
		    {
		      swdData[nb].riW = outsideW;
		      swdData[nb].leW = outsideW + eData[nb].weight;
		    }
		  else
		    {
		      swdData[nb].leW = outsideW;
		      swdData[nb].riW = outsideW - eData[nb].weight;
		    }
		  swdData[nb].precParc = curBord;
		  swdData[curBord].suivParc = nb;
		  curBord = nb;
//		  if ( doDebug ) printf("suite %d\n",curBord);
		  if (cPt == getEdge(nb).en)
		    curDir = false;
		  else
		    curDir = true;
		}
	    }
	  while (true /*swdData[curBord].precParc >= 0 */ );
	  // fin du cas non-oriente
	}
    }
  while (lastPtUsed < numberOfPoints());
//      fflush(stdout);
}

bool
Shape::TesteIntersection (Shape * ils, Shape * irs, int ilb, int irb,
                          Geom::Point &atx, double &atL, double &atR,
			  bool /*onlyDiff*/)
{
  int lSt = ils->getEdge(ilb).st, lEn = ils->getEdge(ilb).en;
  int rSt = irs->getEdge(irb).st, rEn = irs->getEdge(irb).en;
  if (lSt == rSt || lSt == rEn)
    {
      return false;
    }
  if (lEn == rSt || lEn == rEn)
    {
      return false;
    }

  Geom::Point ldir, rdir;
  ldir = ils->eData[ilb].rdx;
  rdir = irs->eData[irb].rdx;

  double il = ils->pData[lSt].rx[0], it = ils->pData[lSt].rx[1], ir =
    ils->pData[lEn].rx[0], ib = ils->pData[lEn].rx[1];
  if (il > ir)
    {
      double swf = il;
      il = ir;
      ir = swf;
    }
  if (it > ib)
    {
      double swf = it;
      it = ib;
      ib = swf;
    }
  double jl = irs->pData[rSt].rx[0], jt = irs->pData[rSt].rx[1], jr =
    irs->pData[rEn].rx[0], jb = irs->pData[rEn].rx[1];
  if (jl > jr)
    {
      double swf = jl;
      jl = jr;
      jr = swf;
    }
  if (jt > jb)
    {
      double swf = jt;
      jt = jb;
      jb = swf;
    }

  if (il > jr || it > jb || ir < jl || ib < jt)
    return false;

  // pre-test
  {
    Geom::Point sDiff, eDiff;
    double slDot, elDot;
    double srDot, erDot;
    sDiff = ils->pData[lSt].rx - irs->pData[rSt].rx;
    eDiff = ils->pData[lEn].rx - irs->pData[rSt].rx;
    srDot = cross(rdir, sDiff);
    erDot = cross(rdir, eDiff);
    if ((srDot >= 0 && erDot >= 0) || (srDot <= 0 && erDot <= 0))
      return false;

    sDiff = irs->pData[rSt].rx - ils->pData[lSt].rx;
    eDiff = irs->pData[rEn].rx - ils->pData[lSt].rx;
    slDot = cross(ldir, sDiff);
    elDot = cross(ldir, eDiff);
    if ((slDot >= 0 && elDot >= 0) || (slDot <= 0 && elDot <= 0))
      return false;

    double slb = slDot - elDot, srb = srDot - erDot;
    if (slb < 0)
      slb = -slb;
    if (srb < 0)
      srb = -srb;
    if (slb > srb)
      {
	atx =
	  (slDot * irs->pData[rEn].rx - elDot * irs->pData[rSt].rx) / (slDot -
								       elDot);
      }
    else
      {
	atx =
	  (srDot * ils->pData[lEn].rx - erDot * ils->pData[lSt].rx) / (srDot -
								       erDot);
      }
    atL = srDot / (srDot - erDot);
    atR = slDot / (slDot - elDot);
    return true;
  }

  // a mettre en double precision pour des resultats exacts
  Geom::Point usvs;
  usvs = irs->pData[rSt].rx - ils->pData[lSt].rx;

  // pas sur de l'ordre des coefs de m
  Geom::Affine m(ldir[0], ldir[1],
	       rdir[0], rdir[1],
	       0, 0);
  double det = m.det();

  double tdet = det * ils->eData[ilb].isqlength * irs->eData[irb].isqlength;

  if (tdet > -0.0001 && tdet < 0.0001)
    {				// ces couillons de vecteurs sont colineaires
      Geom::Point sDiff, eDiff;
      double sDot, eDot;
      sDiff = ils->pData[lSt].rx - irs->pData[rSt].rx;
      eDiff = ils->pData[lEn].rx - irs->pData[rSt].rx;
      sDot = cross(rdir, sDiff);
      eDot = cross(rdir, eDiff);

      atx =
	(sDot * irs->pData[lEn].rx - eDot * irs->pData[lSt].rx) / (sDot -
								   eDot);
      atL = sDot / (sDot - eDot);

      sDiff = irs->pData[rSt].rx - ils->pData[lSt].rx;
       eDiff = irs->pData[rEn].rx - ils->pData[lSt].rx;
      sDot = cross(ldir, sDiff);
      eDot = cross(ldir, eDiff);

      atR = sDot / (sDot - eDot);

      return true;
    }

  // plus de colinearite ni d'extremites en commun
  m[1] = -m[1];
  m[2] = -m[2];
  {
    double swap = m[0];
    m[0] = m[3];
    m[3] = swap;
  }

  atL = (m[0]* usvs[0] + m[1] * usvs[1]) / det;
  atR = -(m[2] * usvs[0] + m[3] * usvs[1]) / det;
  atx = ils->pData[lSt].rx + atL * ldir;


  return true;
}

bool
Shape::TesteAdjacency (Shape * a, int no, const Geom::Point atx, int nPt,
		       bool push)
{
  if (nPt == a->swsData[no].stPt || nPt == a->swsData[no].enPt)
    return false;

  Geom::Point adir, diff, ast, aen, diff1, diff2, diff3, diff4;

  ast = a->pData[a->getEdge(no).st].rx;
  aen = a->pData[a->getEdge(no).en].rx;

  adir = a->eData[no].rdx;

  double sle = a->eData[no].length;
  double ile = a->eData[no].ilength;

  diff = atx - ast;
 
  double e = IHalfRound(cross(adir, diff) * a->eData[no].isqlength);
  if (-3 < e && e < 3)
    {
      double rad = HalfRound (0.501); // when using single precision, 0.505 is better (0.5 would be the correct value, 
                                      // but it produces lots of bugs)
      diff1[0] = diff[0] - rad;
      diff1[1] = diff[1] - rad;
      diff2[0] = diff[0] + rad;
      diff2[1] = diff[1] - rad;
      diff3[0] = diff[0] + rad;
      diff3[1] = diff[1] + rad;
      diff4[0] = diff[0] - rad;
      diff4[1] = diff[1] + rad;
      double di1, di2;
      bool adjacent = false;
      di1 = cross(adir, diff1);
      di2 = cross(adir, diff3);
      if ((di1 < 0 && di2 > 0) || (di1 > 0 && di2 < 0))
	{
	  adjacent = true;
	}
      else
	{
	  di1 = cross(adir, diff2);
	  di2 = cross(adir, diff4);
	  if ((di1 < 0 && di2 > 0) || (di1 > 0 && di2 < 0))
	    {
	      adjacent = true;
	    }
	}
      if (adjacent)
	{
	  double t = dot (diff, adir);
	  if (t > 0 && t < sle)
	    {
	      if (push)
		{
		  t *= ile;
		  PushIncidence (a, no, nPt, t);
		}
	      return true;
	    }
	}
    }
  return false;
}

void
Shape::CheckAdjacencies (int lastPointNo, int lastChgtPt, Shape * /*shapeHead*/,
			 int /*edgeHead*/)
{
  for (auto & chgt : chgts)
    {
      int chLeN = chgt.ptNo;
      int chRiN = chgt.ptNo;
      if (chgt.src)
	{
	  Shape *lS = chgt.src;
	  int lB = chgt.bord;
	  int lftN = lS->swsData[lB].leftRnd;
	  int rgtN = lS->swsData[lB].rightRnd;
	  if (lftN < chLeN)
	    chLeN = lftN;
	  if (rgtN > chRiN)
	    chRiN = rgtN;
//                      for (int n=lftN;n<=rgtN;n++) CreateIncidence(lS,lB,n);
	  for (int n = lftN - 1; n >= lastChgtPt; n--)
	    {
	      if (TesteAdjacency (lS, lB, getPoint(n).x, n, false) ==
		  false)
                break;
              lS->swsData[lB].leftRnd = n;
	    }
	  for (int n = rgtN + 1; n < lastPointNo; n++)
	    {
	      if (TesteAdjacency (lS, lB, getPoint(n).x, n, false) ==
		  false)
		break;
	      lS->swsData[lB].rightRnd = n;
	    }
	}
      if (chgt.osrc)
	{
	  Shape *rS = chgt.osrc;
	  int rB = chgt.obord;
	  int lftN = rS->swsData[rB].leftRnd;
	  int rgtN = rS->swsData[rB].rightRnd;
	  if (lftN < chLeN)
	    chLeN = lftN;
	  if (rgtN > chRiN)
	    chRiN = rgtN;
//                      for (int n=lftN;n<=rgtN;n++) CreateIncidence(rS,rB,n);
	  for (int n = lftN - 1; n >= lastChgtPt; n--)
	    {
	      if (TesteAdjacency (rS, rB, getPoint(n).x, n, false) ==
		  false)
		break;
              rS->swsData[rB].leftRnd = n;
	    }
	  for (int n = rgtN + 1; n < lastPointNo; n++)
	    {
	      if (TesteAdjacency (rS, rB, getPoint(n).x, n, false) ==
		  false)
		break;
	      rS->swsData[rB].rightRnd = n;
	    }
	}
      if (chgt.lSrc)
	{
	  if (chgt.lSrc->swsData[chgt.lBrd].leftRnd < lastChgtPt)
	    {
	      Shape *nSrc = chgt.lSrc;
	      int nBrd = chgt.lBrd /*,nNo=chgts[cCh].ptNo */ ;
	      bool hit;

	      do
		{
		  hit = false;
		  for (int n = chRiN; n >= chLeN; n--)
		    {
		      if (TesteAdjacency
			  (nSrc, nBrd, getPoint(n).x, n, false))
			{
			  if (nSrc->swsData[nBrd].leftRnd < lastChgtPt)
			    {
			      nSrc->swsData[nBrd].leftRnd = n;
			      nSrc->swsData[nBrd].rightRnd = n;
			    }
			  else
			    {
			      if (n < nSrc->swsData[nBrd].leftRnd)
				nSrc->swsData[nBrd].leftRnd = n;
			      if (n > nSrc->swsData[nBrd].rightRnd)
				nSrc->swsData[nBrd].rightRnd = n;
			    }
			  hit = true;
			}
		    }
		  for (int n = chLeN - 1; n >= lastChgtPt; n--)
		    {
		      if (TesteAdjacency
			  (nSrc, nBrd, getPoint(n).x, n, false) == false)
			break;
		      if (nSrc->swsData[nBrd].leftRnd < lastChgtPt)
			{
			  nSrc->swsData[nBrd].leftRnd = n;
			  nSrc->swsData[nBrd].rightRnd = n;
			}
		      else
			{
			  if (n < nSrc->swsData[nBrd].leftRnd)
			    nSrc->swsData[nBrd].leftRnd = n;
			  if (n > nSrc->swsData[nBrd].rightRnd)
			    nSrc->swsData[nBrd].rightRnd = n;
			}
		      hit = true;
		    }
		  if (hit)
		    {
		      SweepTree *node =
			static_cast < SweepTree * >(nSrc->swsData[nBrd].misc);
		      if (node == nullptr)
			break;
		      node = static_cast < SweepTree * >(node->elem[LEFT]);
		      if (node == nullptr)
			break;
		      nSrc = node->src;
		      nBrd = node->bord;
		      if (nSrc->swsData[nBrd].leftRnd >= lastChgtPt)
			break;
		    }
		}
	      while (hit);

	    }
	}
      if (chgt.rSrc)
	{
	  if (chgt.rSrc->swsData[chgt.rBrd].leftRnd < lastChgtPt)
	    {
	      Shape *nSrc = chgt.rSrc;
	      int nBrd = chgt.rBrd /*,nNo=chgts[cCh].ptNo */ ;
	      bool hit;
	      do
		{
		  hit = false;
		  for (int n = chLeN; n <= chRiN; n++)
		    {
		      if (TesteAdjacency
			  (nSrc, nBrd, getPoint(n).x, n, false))
			{
			  if (nSrc->swsData[nBrd].leftRnd < lastChgtPt)
			    {
			      nSrc->swsData[nBrd].leftRnd = n;
			      nSrc->swsData[nBrd].rightRnd = n;
			    }
			  else
			    {
			      if (n < nSrc->swsData[nBrd].leftRnd)
				nSrc->swsData[nBrd].leftRnd = n;
			      if (n > nSrc->swsData[nBrd].rightRnd)
				nSrc->swsData[nBrd].rightRnd = n;
			    }
			  hit = true;
			}
		    }
		  for (int n = chRiN + 1; n < lastPointNo; n++)
		    {
		      if (TesteAdjacency
			  (nSrc, nBrd, getPoint(n).x, n, false) == false)
			break;
		      if (nSrc->swsData[nBrd].leftRnd < lastChgtPt)
			{
			  nSrc->swsData[nBrd].leftRnd = n;
			  nSrc->swsData[nBrd].rightRnd = n;
			}
		      else
			{
			  if (n < nSrc->swsData[nBrd].leftRnd)
			    nSrc->swsData[nBrd].leftRnd = n;
			  if (n > nSrc->swsData[nBrd].rightRnd)
			    nSrc->swsData[nBrd].rightRnd = n;
			}
		      hit = true;
		    }
		  if (hit)
		    {
		      SweepTree *node =
			static_cast < SweepTree * >(nSrc->swsData[nBrd].misc);
		      if (node == nullptr)
			break;
		      node = static_cast < SweepTree * >(node->elem[RIGHT]);
		      if (node == nullptr)
			break;
		      nSrc = node->src;
		      nBrd = node->bord;
		      if (nSrc->swsData[nBrd].leftRnd >= lastChgtPt)
			break;
		    }
		}
	      while (hit);
	    }
	}
    }
}


void Shape::AddChgt(int lastPointNo, int lastChgtPt, Shape * &shapeHead,
		    int &edgeHead, sTreeChangeType type, Shape * lS, int lB, Shape * rS,
		    int rB)
{
    sTreeChange c;
    c.ptNo = lastPointNo;
    c.type = type;
    c.src = lS;
    c.bord = lB;
    c.osrc = rS;
    c.obord = rB;
    chgts.push_back(c);
    const int nCh = chgts.size() - 1;

    /* FIXME: this looks like a cut and paste job */

    if (lS) {
	SweepTree *lE = static_cast < SweepTree * >(lS->swsData[lB].misc);
	if (lE && lE->elem[LEFT]) {
	    SweepTree *llE = static_cast < SweepTree * >(lE->elem[LEFT]);
	    chgts[nCh].lSrc = llE->src;
	    chgts[nCh].lBrd = llE->bord;
	} else {
	    chgts[nCh].lSrc = nullptr;
	    chgts[nCh].lBrd = -1;
	}

	if (lS->swsData[lB].leftRnd < lastChgtPt) {
	    lS->swsData[lB].leftRnd = lastPointNo;
	    lS->swsData[lB].nextSh = shapeHead;
	    lS->swsData[lB].nextBo = edgeHead;
	    edgeHead = lB;
	    shapeHead = lS;
	} else {
	    int old = lS->swsData[lB].leftRnd;
	    if (getPoint(old).x[0] > getPoint(lastPointNo).x[0]) {
		lS->swsData[lB].leftRnd = lastPointNo;
	    }
	}
	if (lS->swsData[lB].rightRnd < lastChgtPt) {
	    lS->swsData[lB].rightRnd = lastPointNo;
	} else {
	    int old = lS->swsData[lB].rightRnd;
	    if (getPoint(old).x[0] < getPoint(lastPointNo).x[0])
		lS->swsData[lB].rightRnd = lastPointNo;
	}
    }

    if (rS) {
	SweepTree *rE = static_cast < SweepTree * >(rS->swsData[rB].misc);
	if (rE->elem[RIGHT]) {
	    SweepTree *rrE = static_cast < SweepTree * >(rE->elem[RIGHT]);
	    chgts[nCh].rSrc = rrE->src;
	    chgts[nCh].rBrd = rrE->bord;
	} else {
	    chgts[nCh].rSrc = nullptr;
	    chgts[nCh].rBrd = -1;
	}
	
	if (rS->swsData[rB].leftRnd < lastChgtPt) {
	    rS->swsData[rB].leftRnd = lastPointNo;
	    rS->swsData[rB].nextSh = shapeHead;
	    rS->swsData[rB].nextBo = edgeHead;
	    edgeHead = rB;
	    shapeHead = rS;
	} else {
	    int old = rS->swsData[rB].leftRnd;
	    if (getPoint(old).x[0] > getPoint(lastPointNo).x[0]) {
		rS->swsData[rB].leftRnd = lastPointNo;
	    }
	}
	if (rS->swsData[rB].rightRnd < lastChgtPt) {
	    rS->swsData[rB].rightRnd = lastPointNo;
	} else {
	    int old = rS->swsData[rB].rightRnd;
	    if (getPoint(old).x[0] < getPoint(lastPointNo).x[0])
		rS->swsData[rB].rightRnd = lastPointNo;
	}
    } else {
	SweepTree *lE = static_cast < SweepTree * >(lS->swsData[lB].misc);
	if (lE && lE->elem[RIGHT]) {
	    SweepTree *rlE = static_cast < SweepTree * >(lE->elem[RIGHT]);
	    chgts[nCh].rSrc = rlE->src;
	    chgts[nCh].rBrd = rlE->bord;
	} else {
	    chgts[nCh].rSrc = nullptr;
	    chgts[nCh].rBrd = -1;
	}
    }
}

// is this a debug function?  It's calling localized "printf" ...
void
Shape::Validate ()
{
  for (int i = 0; i < numberOfPoints(); i++)
    {
      pData[i].rx = getPoint(i).x;
    }
  for (int i = 0; i < numberOfEdges(); i++)
    {
      eData[i].rdx = getEdge(i).dx;
    }
  for (int i = 0; i < numberOfEdges(); i++)
    {
      for (int j = i + 1; j < numberOfEdges(); j++)
	{
        Geom::Point atx;
        double   atL, atR;
	  if (TesteIntersection (this, this, i, j, atx, atL, atR, false))
	    {
	      printf ("%i %i  %f %f di=%f %f  dj=%f %f\n", i, j, atx[0],atx[1],getEdge(i).dx[0],getEdge(i).dx[1],getEdge(j).dx[0],getEdge(j).dx[1]);
	    }
	}
    }
  fflush (stdout);
}

void
Shape::CheckEdges (int lastPointNo, int lastChgtPt, Shape * a, Shape * b,
		   BooleanOp mod)
{

  for (auto & chgt : chgts)
    {
      if (chgt.type == 0)
	{
	  Shape *lS = chgt.src;
	  int lB = chgt.bord;
	  lS->swsData[lB].curPoint = chgt.ptNo;
	}
    }
  for (auto & chgt : chgts)
    {
//              int   chLeN=chgts[cCh].ptNo;
//              int   chRiN=chgts[cCh].ptNo;
      if (chgt.src)
	{
	  Shape *lS = chgt.src;
	  int lB = chgt.bord;
	  Avance (lastPointNo, lastChgtPt, lS, lB, a, b, mod);
	}
      if (chgt.osrc)
	{
	  Shape *rS = chgt.osrc;
	  int rB = chgt.obord;
	  Avance (lastPointNo, lastChgtPt, rS, rB, a, b, mod);
	}
      if (chgt.lSrc)
	{
	  Shape *nSrc = chgt.lSrc;
	  int nBrd = chgt.lBrd;
	  while (nSrc->swsData[nBrd].leftRnd >=
		 lastChgtPt /*&& nSrc->swsData[nBrd].doneTo < lastChgtPt */ )
	    {
	      Avance (lastPointNo, lastChgtPt, nSrc, nBrd, a, b, mod);

	      SweepTree *node =
		static_cast < SweepTree * >(nSrc->swsData[nBrd].misc);
	      if (node == nullptr)
		break;
	      node = static_cast < SweepTree * >(node->elem[LEFT]);
	      if (node == nullptr)
		break;
	      nSrc = node->src;
	      nBrd = node->bord;
	    }
	}
      if (chgt.rSrc)
	{
	  Shape *nSrc = chgt.rSrc;
	  int nBrd = chgt.rBrd;
	  while (nSrc->swsData[nBrd].rightRnd >=
		 lastChgtPt /*&& nSrc->swsData[nBrd].doneTo < lastChgtPt */ )
	    {
	      Avance (lastPointNo, lastChgtPt, nSrc, nBrd, a, b, mod);

	      SweepTree *node =
		static_cast < SweepTree * >(nSrc->swsData[nBrd].misc);
	      if (node == nullptr)
		break;
	      node = static_cast < SweepTree * >(node->elem[RIGHT]);
	      if (node == nullptr)
		break;
	      nSrc = node->src;
	      nBrd = node->bord;
	    }
	}
    }
}

void
Shape::Avance (int lastPointNo, int lastChgtPt, Shape * lS, int lB, Shape * /*a*/,
	       Shape * b, BooleanOp mod)
{
  double dd = HalfRound (1);
  bool avoidDiag = false;
//      if ( lastChgtPt > 0 && pts[lastChgtPt-1].y+dd == pts[lastChgtPt].y ) avoidDiag=true;

  bool direct = true;
  if (lS == b && (mod == bool_op_diff || mod == bool_op_symdiff))
    direct = false;
  int lftN = lS->swsData[lB].leftRnd;
  int rgtN = lS->swsData[lB].rightRnd;
  if (lS->swsData[lB].doneTo < lastChgtPt)
    {
      int lp = lS->swsData[lB].curPoint;
      if (lp >= 0 && getPoint(lp).x[1] + dd == getPoint(lastChgtPt).x[1])
	avoidDiag = true;
      if (lS->eData[lB].rdx[1] == 0)
	{
	  // tjs de gauche a droite et pas de diagonale
	  if (lS->eData[lB].rdx[0] >= 0)
	    {
	      for (int p = lftN; p <= rgtN; p++)
		{
		  DoEdgeTo (lS, lB, p, direct, true);
		  lp = p;
		}
	    }
	  else
	    {
	      for (int p = lftN; p <= rgtN; p++)
		{
		  DoEdgeTo (lS, lB, p, direct, false);
		  lp = p;
		}
	    }
	}
      else if (lS->eData[lB].rdx[1] > 0)
	{
	  if (lS->eData[lB].rdx[0] >= 0)
	    {

	      for (int p = lftN; p <= rgtN; p++)
		{
		  if (avoidDiag && p == lftN && getPoint(lftN).x[0] == getPoint(lp).x[0] + dd)
		    {
		      if (lftN > 0 && lftN - 1 >= lastChgtPt
			  && getPoint(lftN - 1).x[0] == getPoint(lp).x[0])
			{
			  DoEdgeTo (lS, lB, lftN - 1, direct, true);
			  DoEdgeTo (lS, lB, lftN, direct, true);
			}
		      else
			{
			  DoEdgeTo (lS, lB, lftN, direct, true);
			}
		    }
		  else
		    {
		      DoEdgeTo (lS, lB, p, direct, true);
		    }
		  lp = p;
		}
	    }
	  else
	    {

	      for (int p = rgtN; p >= lftN; p--)
		{
		  if (avoidDiag && p == rgtN && getPoint(rgtN).x[0] == getPoint(lp).x[0] - dd)
		    {
		      if (rgtN < numberOfPoints() && rgtN + 1 < lastPointNo
			  && getPoint(rgtN + 1).x[0] == getPoint(lp).x[0])
			{
			  DoEdgeTo (lS, lB, rgtN + 1, direct, true);
			  DoEdgeTo (lS, lB, rgtN, direct, true);
			}
		      else
			{
			  DoEdgeTo (lS, lB, rgtN, direct, true);
			}
		    }
		  else
		    {
		      DoEdgeTo (lS, lB, p, direct, true);
		    }
		  lp = p;
		}
	    }
	}
      else
	{
	  if (lS->eData[lB].rdx[0] >= 0)
	    {

	      for (int p = rgtN; p >= lftN; p--)
		{
		  if (avoidDiag && p == rgtN && getPoint(rgtN).x[0] == getPoint(lp).x[0] - dd)
		    {
		      if (rgtN < numberOfPoints() && rgtN + 1 < lastPointNo
			  && getPoint(rgtN + 1).x[0] == getPoint(lp).x[0])
			{
			  DoEdgeTo (lS, lB, rgtN + 1, direct, false);
			  DoEdgeTo (lS, lB, rgtN, direct, false);
			}
		      else
			{
			  DoEdgeTo (lS, lB, rgtN, direct, false);
			}
		    }
		  else
		    {
		      DoEdgeTo (lS, lB, p, direct, false);
		    }
		  lp = p;
		}
	    }
	  else
	    {

	      for (int p = lftN; p <= rgtN; p++)
		{
		  if (avoidDiag && p == lftN && getPoint(lftN).x[0] == getPoint(lp).x[0] + dd)
		    {
		      if (lftN > 0 && lftN - 1 >= lastChgtPt
			  && getPoint(lftN - 1).x[0] == getPoint(lp).x[0])
			{
			  DoEdgeTo (lS, lB, lftN - 1, direct, false);
			  DoEdgeTo (lS, lB, lftN, direct, false);
			}
		      else
			{
			  DoEdgeTo (lS, lB, lftN, direct, false);
			}
		    }
		  else
		    {
		      DoEdgeTo (lS, lB, p, direct, false);
		    }
		  lp = p;
		}
	    }
	}
      lS->swsData[lB].curPoint = lp;
    }
  lS->swsData[lB].doneTo = lastPointNo - 1;
}

void
Shape::DoEdgeTo (Shape * iS, int iB, int iTo, bool direct, bool sens)
{
  int lp = iS->swsData[iB].curPoint;
  int ne = -1;
  if (sens)
    {
      if (direct)
	ne = AddEdge (lp, iTo);
      else
	ne = AddEdge (iTo, lp);
    }
  else
    {
      if (direct)
	ne = AddEdge (iTo, lp);
      else
	ne = AddEdge (lp, iTo);
    }
  if (ne >= 0 && _has_back_data)
    {
      ebData[ne].pathID = iS->ebData[iB].pathID;
      ebData[ne].pieceID = iS->ebData[iB].pieceID;
      if (iS->eData[iB].length < 0.00001)
	{
	  ebData[ne].tSt = ebData[ne].tEn = iS->ebData[iB].tSt;
	}
      else
	{
	  double bdl = iS->eData[iB].ilength;
    Geom::Point bpx = iS->pData[iS->getEdge(iB).st].rx;
	  Geom::Point bdx = iS->eData[iB].rdx;
	  Geom::Point psx = getPoint(getEdge(ne).st).x;
	  Geom::Point pex = getPoint(getEdge(ne).en).x;
        Geom::Point psbx=psx-bpx;
        Geom::Point pebx=pex-bpx;
	  double pst = dot(psbx,bdx) * bdl;
	  double pet = dot(pebx,bdx) * bdl;
	  pst = iS->ebData[iB].tSt * (1 - pst) + iS->ebData[iB].tEn * pst;
	  pet = iS->ebData[iB].tSt * (1 - pet) + iS->ebData[iB].tEn * pet;
	  ebData[ne].tEn = pet;
	  ebData[ne].tSt = pst;
	}
    }
  iS->swsData[iB].curPoint = iTo;
  if (ne >= 0)
    {
      int cp = iS->swsData[iB].firstLinkedPoint;
      swsData[ne].firstLinkedPoint = iS->swsData[iB].firstLinkedPoint;
      while (cp >= 0)
	{
	  pData[cp].askForWindingB = ne;
	  cp = pData[cp].nextLinkedPoint;
	}
      iS->swsData[iB].firstLinkedPoint = -1;
    }
}
