diff options
Diffstat (limited to '')
-rw-r--r-- | src/livarot/ShapeMisc.cpp | 1461 |
1 files changed, 1461 insertions, 0 deletions
diff --git a/src/livarot/ShapeMisc.cpp b/src/livarot/ShapeMisc.cpp new file mode 100644 index 0000000..07e0fc9 --- /dev/null +++ b/src/livarot/ShapeMisc.cpp @@ -0,0 +1,1461 @@ +// 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 "livarot/Shape.h" +#include "livarot/Path.h" +#include "livarot/path-description.h" +#include <glib.h> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <2geom/point.h> +#include <2geom/affine.h> + +/* + * polygon offset and polyline to path reassembling (when using back data) + */ + +// until i find something better +#define MiscNormalize(v) {\ + double _l=sqrt(dot(v,v)); \ + if ( _l < 0.0000001 ) { \ + v[0]=v[1]=0; \ + } else { \ + v/=_l; \ + }\ +} + +// extracting the contour of an uncrossed polygon: a mere depth first search +// more precisely that's extracting an eulerian path from a graph, but here we want to split +// the polygon into contours and avoid holes. so we take a "next counter-clockwise edge first" approach +// (make a checkboard and extract its contours to see the difference) +void +Shape::ConvertToForme (Path * dest) +{ + if (numberOfPoints() <= 1 || numberOfEdges() <= 1) + return; + + // prepare + dest->Reset (); + + MakePointData (true); + MakeEdgeData (true); + MakeSweepDestData (true); + + for (int i = 0; i < numberOfPoints(); i++) + { + pData[i].rx[0] = Round (getPoint(i).x[0]); + pData[i].rx[1] = Round (getPoint(i).x[1]); + } + for (int i = 0; i < numberOfEdges(); i++) + { + eData[i].rdx = pData[getEdge(i).en].rx - pData[getEdge(i).st].rx; + } + + // sort edge clockwise, with the closest after midnight being first in the doubly-linked list + // that's vital to the algorithm... + SortEdges (); + + // depth-first search implies: we make a stack of edges traversed. + // precParc: previous in the stack + // suivParc: next in the stack + for (int i = 0; i < numberOfEdges(); i++) + { + swdData[i].misc = nullptr; + swdData[i].precParc = swdData[i].suivParc = -1; + } + + int searchInd = 0; + + int lastPtUsed = 0; + do + { + // first get a starting point, and a starting edge + // -> take the upper left point, and take its first edge + // points traversed have swdData[].misc != 0, so it's easy + int startBord = -1; + { + 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]; + while (bestB >= 0 && getEdge(bestB).st != fi) + bestB = NextAt (fi, bestB); + if (bestB >= 0) + { + startBord = bestB; + dest->MoveTo (getPoint(getEdge(startBord).en).x); + } + } + } + // and walk the graph, doing contours when needed + if (startBord >= 0) + { + // parcours en profondeur pour mettre les leF et riF a leurs valeurs + swdData[startBord].misc = (void *) 1; + // printf("part de %d\n",startBord); + int curBord = startBord; + bool back = false; + swdData[curBord].precParc = -1; + swdData[curBord].suivParc = -1; + do + { + int cPt = getEdge(curBord).en; + int nb = curBord; + // printf("de curBord= %d au point %i -> ",curBord,cPt); + // get next edge + do + { + int nnb = CycleNextAt (cPt, nb); + if (nnb == nb) + { + // cul-de-sac + nb = -1; + break; + } + nb = nnb; + if (nb < 0 || nb == curBord) + break; + } + while (swdData[nb].misc != nullptr || getEdge(nb).st != cPt); + + if (nb < 0 || nb == curBord) + { + // no next edge: end of this contour, we get back + if (back == false) + dest->Close (); + back = true; + // retour en arriere + curBord = swdData[curBord].precParc; + // printf("retour vers %d\n",curBord); + if (curBord < 0) + break; + } + else + { + // new edge, maybe for a new contour + if (back) + { + // we were backtracking, so if we have a new edge, that means we're creating a new contour + dest->MoveTo (getPoint(cPt).x); + back = false; + } + swdData[nb].misc = (void *) 1; + swdData[nb].ind = searchInd++; + swdData[nb].precParc = curBord; + swdData[curBord].suivParc = nb; + curBord = nb; + // printf("suite %d\n",curBord); + { + // add that edge + dest->LineTo (getPoint(getEdge(nb).en).x); + } + } + } + while (true /*swdData[curBord].precParc >= 0 */ ); + // fin du cas non-oriente + } + } + while (lastPtUsed < numberOfPoints()); + + MakePointData (false); + MakeEdgeData (false); + MakeSweepDestData (false); +} + +// same as before, but each time we have a contour, try to reassemble the segments on it to make chunks of +// the original(s) path(s) +// originals are in the orig array, whose size is nbP +void +Shape::ConvertToForme (Path * dest, int nbP, Path * *orig, bool splitWhenForced) +{ + if (numberOfPoints() <= 1 || numberOfEdges() <= 1) + return; +// if (Eulerian (true) == false) +// return; + + if (_has_back_data == false) + { + ConvertToForme (dest); + return; + } + + dest->Reset (); + + MakePointData (true); + MakeEdgeData (true); + MakeSweepDestData (true); + + for (int i = 0; i < numberOfPoints(); i++) + { + pData[i].rx[0] = Round (getPoint(i).x[0]); + pData[i].rx[1] = Round (getPoint(i).x[1]); + } + for (int i = 0; i < numberOfEdges(); i++) + { + eData[i].rdx = pData[getEdge(i).en].rx - pData[getEdge(i).st].rx; + } + + SortEdges (); + + for (int i = 0; i < numberOfEdges(); i++) + { + swdData[i].misc = nullptr; + swdData[i].precParc = swdData[i].suivParc = -1; + } + + int searchInd = 0; + + int lastPtUsed = 0; + do + { + int startBord = -1; + { + 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]; + while (bestB >= 0 && getEdge(bestB).st != fi) + bestB = NextAt (fi, bestB); + if (bestB >= 0) + { + startBord = bestB; + } + } + } + if (startBord >= 0) + { + // parcours en profondeur pour mettre les leF et riF a leurs valeurs + swdData[startBord].misc = (void *) 1; + //printf("part de %d\n",startBord); + int curBord = startBord; + bool back = false; + swdData[curBord].precParc = -1; + swdData[curBord].suivParc = -1; + int curStartPt=getEdge(curBord).st; + do + { + int cPt = getEdge(curBord).en; + int nb = curBord; + //printf("de curBord= %d au point %i -> ",curBord,cPt); + do + { + int nnb = CycleNextAt (cPt, nb); + if (nnb == nb) + { + // cul-de-sac + nb = -1; + break; + } + nb = nnb; + if (nb < 0 || nb == curBord) + break; + } + while (swdData[nb].misc != nullptr || getEdge(nb).st != cPt); + + if (nb < 0 || nb == curBord) + { + if (back == false) + { + if (curBord == startBord || curBord < 0) + { + // probleme -> on vire le moveto + // dest->descr_nb--; + } + else + { + swdData[curBord].suivParc = -1; + AddContour (dest, nbP, orig, startBord, curBord,splitWhenForced); + } + // dest->Close(); + } + back = true; + // retour en arriere + curBord = swdData[curBord].precParc; + //printf("retour vers %d\n",curBord); + if (curBord < 0) + break; + } + else + { + if (back) + { + back = false; + startBord = nb; + curStartPt=getEdge(nb).st; + } else { + if ( getEdge(curBord).en == curStartPt ) { + //printf("contour %i ",curStartPt); + swdData[curBord].suivParc = -1; + AddContour (dest, nbP, orig, startBord, curBord,splitWhenForced); + startBord=nb; + } + } + swdData[nb].misc = (void *) 1; + swdData[nb].ind = searchInd++; + swdData[nb].precParc = curBord; + swdData[curBord].suivParc = nb; + curBord = nb; + //printf("suite %d\n",curBord); + } + } + while (true /*swdData[curBord].precParc >= 0 */ ); + // fin du cas non-oriente + } + } + while (lastPtUsed < numberOfPoints()); + + MakePointData (false); + MakeEdgeData (false); + MakeSweepDestData (false); +} +void +Shape::ConvertToFormeNested (Path * dest, int nbP, Path * *orig, int /*wildPath*/,int &nbNest,int *&nesting,int *&contStart,bool splitWhenForced) +{ + nesting=nullptr; + contStart=nullptr; + nbNest=0; + + if (numberOfPoints() <= 1 || numberOfEdges() <= 1) + return; + // if (Eulerian (true) == false) + // return; + + if (_has_back_data == false) + { + ConvertToForme (dest); + return; + } + + dest->Reset (); + +// MakePointData (true); + MakeEdgeData (true); + MakeSweepDestData (true); + + for (int i = 0; i < numberOfPoints(); i++) + { + pData[i].rx[0] = Round (getPoint(i).x[0]); + pData[i].rx[1] = Round (getPoint(i).x[1]); + } + for (int i = 0; i < numberOfEdges(); i++) + { + eData[i].rdx = pData[getEdge(i).en].rx - pData[getEdge(i).st].rx; + } + + SortEdges (); + + for (int i = 0; i < numberOfEdges(); i++) + { + swdData[i].misc = nullptr; + swdData[i].precParc = swdData[i].suivParc = -1; + } + + int searchInd = 0; + + int lastPtUsed = 0; + int parentContour=-1; + do + { + int childEdge = -1; + bool foundChild = false; + int startBord = -1; + { + int fi = 0; + for (fi = lastPtUsed; fi < numberOfPoints(); fi++) + { + if (getPoint(fi).incidentEdge[FIRST] >= 0 && swdData[getPoint(fi).incidentEdge[FIRST]].misc == nullptr) + break; + } + { + if (pData.size()<= fi || fi == numberOfPoints()) { + parentContour=-1; + } else { + int askTo = pData[fi].askForWindingB; + if (askTo < 0 || askTo >= numberOfEdges() ) { + parentContour=-1; + } else { + if (getEdge(askTo).prevS >= 0) { + parentContour = GPOINTER_TO_INT(swdData[askTo].misc); + parentContour-=1; // pour compenser le decalage + } + childEdge = getPoint(fi).incidentEdge[FIRST]; + } + } + } + lastPtUsed = fi + 1; + if (fi < numberOfPoints()) + { + int bestB = getPoint(fi).incidentEdge[FIRST]; + while (bestB >= 0 && getEdge(bestB).st != fi) + bestB = NextAt (fi, bestB); + if (bestB >= 0) + { + startBord = bestB; + } + } + } + if (startBord >= 0) + { + // parcours en profondeur pour mettre les leF et riF a leurs valeurs + swdData[startBord].misc = (void *)(intptr_t)(1 + nbNest); + if (startBord == childEdge) { + foundChild = true; + } + //printf("part de %d\n",startBord); + int curBord = startBord; + bool back = false; + swdData[curBord].precParc = -1; + swdData[curBord].suivParc = -1; + int curStartPt=getEdge(curBord).st; + do + { + int cPt = getEdge(curBord).en; + int nb = curBord; + //printf("de curBord= %d au point %i -> ",curBord,cPt); + do + { + int nnb = CycleNextAt (cPt, nb); + if (nnb == nb) + { + // cul-de-sac + nb = -1; + break; + } + nb = nnb; + if (nb < 0 || nb == curBord) + break; + } + while (swdData[nb].misc != nullptr || getEdge(nb).st != cPt); + + if (nb < 0 || nb == curBord) + { + if (back == false) + { + if (curBord == startBord || curBord < 0) + { + // probleme -> on vire le moveto + // dest->descr_nb--; + } + else + { +// bool escapePath=false; +// int tb=curBord; +// while ( tb >= 0 && tb < numberOfEdges() ) { +// if ( ebData[tb].pathID == wildPath ) { +// escapePath=true; +// break; +// } +// tb=swdData[tb].precParc; +// } + nesting=(int*)g_realloc(nesting,(nbNest+1)*sizeof(int)); + contStart=(int*)g_realloc(contStart,(nbNest+1)*sizeof(int)); + contStart[nbNest]=dest->descr_cmd.size(); + if (foundChild) { + nesting[nbNest++]=parentContour; + foundChild = false; + } else { + nesting[nbNest++]=-1; // contient des bouts de coupure -> a part + } + swdData[curBord].suivParc = -1; + AddContour (dest, nbP, orig, startBord, curBord,splitWhenForced); + } + // dest->Close(); + } + back = true; + // retour en arriere + curBord = swdData[curBord].precParc; + //printf("retour vers %d\n",curBord); + if (curBord < 0) + break; + } + else + { + if (back) + { + back = false; + startBord = nb; + curStartPt=getEdge(nb).st; + } else { + if ( getEdge(curBord).en == curStartPt ) { + //printf("contour %i ",curStartPt); + +// bool escapePath=false; +// int tb=curBord; +// while ( tb >= 0 && tb < numberOfEdges() ) { +// if ( ebData[tb].pathID == wildPath ) { +// escapePath=true; +// break; +// } +// tb=swdData[tb].precParc; +// } + nesting=(int*)g_realloc(nesting,(nbNest+1)*sizeof(int)); + contStart=(int*)g_realloc(contStart,(nbNest+1)*sizeof(int)); + contStart[nbNest]=dest->descr_cmd.size(); + if (foundChild) { + nesting[nbNest++]=parentContour; + foundChild = false; + } else { + nesting[nbNest++]=-1; // contient des bouts de coupure -> a part + } + swdData[curBord].suivParc = -1; + AddContour (dest, nbP, orig, startBord, curBord,splitWhenForced); + startBord=nb; + } + } + swdData[nb].misc = (void *)(intptr_t)(1 + nbNest); + swdData[nb].ind = searchInd++; + swdData[nb].precParc = curBord; + swdData[curBord].suivParc = nb; + curBord = nb; + if (nb == childEdge) { + foundChild = true; + } + //printf("suite %d\n",curBord); + } + } + while (true /*swdData[curBord].precParc >= 0 */ ); + // fin du cas non-oriente + } + } + while (lastPtUsed < numberOfPoints()); + + MakePointData (false); + MakeEdgeData (false); + MakeSweepDestData (false); +} + + +int +Shape::MakeTweak (int mode, Shape *a, double power, JoinType join, double miter, bool do_profile, Geom::Point c, Geom::Point vector, double radius, Geom::Affine *i2doc) +{ + Reset (0, 0); + MakeBackData(a->_has_back_data); + + bool done_something = false; + + if (power == 0) + { + _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); + if (_has_back_data) + ebData.resize(maxAr); + } + return 0; + } + if (a->numberOfPoints() <= 1 || a->numberOfEdges() <= 1 || a->type != shape_polygon) + return shape_input_err; + + a->SortEdges (); + + a->MakeSweepDestData (true); + a->MakeSweepSrcData (true); + + for (int i = 0; i < a->numberOfEdges(); i++) + { + int stB = -1, enB = -1; + if (power <= 0 || mode == tweak_mode_push || mode == tweak_mode_repel || mode == tweak_mode_roughen) { + stB = a->CyclePrevAt (a->getEdge(i).st, i); + enB = a->CycleNextAt (a->getEdge(i).en, i); + } else { + stB = a->CycleNextAt (a->getEdge(i).st, i); + enB = a->CyclePrevAt (a->getEdge(i).en, i); + } + + Geom::Point stD = a->getEdge(stB).dx; + Geom::Point seD = a->getEdge(i).dx; + Geom::Point enD = a->getEdge(enB).dx; + + double stL = sqrt (dot(stD,stD)); + double seL = sqrt (dot(seD,seD)); + //double enL = sqrt (dot(enD,enD)); + MiscNormalize (stD); + MiscNormalize (enD); + MiscNormalize (seD); + + Geom::Point ptP; + int stNo, enNo; + ptP = a->getPoint(a->getEdge(i).st).x; + + Geom::Point to_center = ptP * (*i2doc) - c; + Geom::Point to_center_normalized = (1/Geom::L2(to_center)) * to_center; + + double this_power; + if (do_profile && i2doc) { + double alpha = 1; + double x; + if (mode == tweak_mode_repel) { + x = (Geom::L2(to_center)/radius); + } else { + x = (Geom::L2(ptP * (*i2doc) - c)/radius); + } + if (x > 1) { + this_power = 0; + } else if (x <= 0) { + if (mode == tweak_mode_repel) { + this_power = 0; + } else { + this_power = power; + } + } else { + this_power = power * (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5); + } + } else { + if (mode == tweak_mode_repel) { + this_power = 0; + } else { + this_power = power; + } + } + + if (this_power != 0) + done_something = true; + + double scaler = 1 / (*i2doc).descrim(); + + Geom::Point this_vec(0,0); + if (mode == tweak_mode_push) { + Geom::Affine tovec (*i2doc); + tovec[4] = tovec[5] = 0; + tovec = tovec.inverse(); + this_vec = this_power * (vector * tovec) ; + } else if (mode == tweak_mode_repel) { + this_vec = this_power * scaler * to_center_normalized; + } else if (mode == tweak_mode_roughen) { + double angle = g_random_double_range(0, 2*M_PI); + this_vec = g_random_double_range(0, 1) * this_power * scaler * Geom::Point(sin(angle), cos(angle)); + } + + int usePathID=-1; + int usePieceID=0; + double useT=0.0; + if ( a->_has_back_data ) { + if ( a->ebData[i].pathID >= 0 && a->ebData[stB].pathID == a->ebData[i].pathID && a->ebData[stB].pieceID == a->ebData[i].pieceID + && a->ebData[stB].tEn == a->ebData[i].tSt ) { + usePathID=a->ebData[i].pathID; + usePieceID=a->ebData[i].pieceID; + useT=a->ebData[i].tSt; + } else { + usePathID=a->ebData[i].pathID; + usePieceID=0; + useT=0; + } + } + + if (mode == tweak_mode_push || mode == tweak_mode_repel || mode == tweak_mode_roughen) { + Path::DoLeftJoin (this, 0, join, ptP+this_vec, stD+this_vec, seD+this_vec, miter, stL, seL, + stNo, enNo,usePathID,usePieceID,useT); + a->swsData[i].stPt = enNo; + a->swsData[stB].enPt = stNo; + } else { + if (power > 0) { + Path::DoRightJoin (this, this_power * scaler, join, ptP, stD, seD, miter, stL, seL, + stNo, enNo,usePathID,usePieceID,useT); + a->swsData[i].stPt = enNo; + a->swsData[stB].enPt = stNo; + } else { + Path::DoLeftJoin (this, -this_power * scaler, join, ptP, stD, seD, miter, stL, seL, + stNo, enNo,usePathID,usePieceID,useT); + a->swsData[i].stPt = enNo; + a->swsData[stB].enPt = stNo; + } + } + } + + if (power < 0 || mode == tweak_mode_push || mode == tweak_mode_repel || mode == tweak_mode_roughen) + { + for (int i = 0; i < numberOfEdges(); i++) + Inverse (i); + } + + if ( _has_back_data ) { + for (int i = 0; i < a->numberOfEdges(); i++) + { + int nEd=AddEdge (a->swsData[i].stPt, a->swsData[i].enPt); + ebData[nEd]=a->ebData[i]; + } + } else { + for (int i = 0; i < a->numberOfEdges(); i++) + { + AddEdge (a->swsData[i].stPt, a->swsData[i].enPt); + } + } + + a->MakeSweepSrcData (false); + a->MakeSweepDestData (false); + + return (done_something? 0 : shape_nothing_to_do); +} + + +// offsets +// take each edge, offset it, and make joins with previous at edge start and next at edge end (previous and +// next being with respect to the clockwise order) +// you gotta be very careful with the join, as anything but the right one will fuck everything up +// see PathStroke.cpp for the "right" joins +int +Shape::MakeOffset (Shape * a, double dec, JoinType join, double miter, bool do_profile, double cx, double cy, double radius, Geom::Affine *i2doc) +{ + Reset (0, 0); + MakeBackData(a->_has_back_data); + + bool done_something = false; + + if (dec == 0) + { + _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); + if (_has_back_data) + ebData.resize(maxAr); + } + return 0; + } + if (a->numberOfPoints() <= 1 || a->numberOfEdges() <= 1 || a->type != shape_polygon) + return shape_input_err; + + a->SortEdges (); + + a->MakeSweepDestData (true); + a->MakeSweepSrcData (true); + + for (int i = 0; i < a->numberOfEdges(); i++) + { + // int stP=a->swsData[i].stPt/*,enP=a->swsData[i].enPt*/; + int stB = -1, enB = -1; + if (dec > 0) + { + stB = a->CycleNextAt (a->getEdge(i).st, i); + enB = a->CyclePrevAt (a->getEdge(i).en, i); + } + else + { + stB = a->CyclePrevAt (a->getEdge(i).st, i); + enB = a->CycleNextAt (a->getEdge(i).en, i); + } + + Geom::Point stD = a->getEdge(stB).dx; + Geom::Point seD = a->getEdge(i).dx; + Geom::Point enD = a->getEdge(enB).dx; + + double stL = sqrt (dot(stD,stD)); + double seL = sqrt (dot(seD,seD)); + //double enL = sqrt (dot(enD,enD)); + MiscNormalize (stD); + MiscNormalize (enD); + MiscNormalize (seD); + + Geom::Point ptP; + int stNo, enNo; + ptP = a->getPoint(a->getEdge(i).st).x; + + double this_dec; + if (do_profile && i2doc) { + double alpha = 1; + double x = (Geom::L2(ptP * (*i2doc) - Geom::Point(cx,cy))/radius); + if (x > 1) { + this_dec = 0; + } else if (x <= 0) { + this_dec = dec; + } else { + this_dec = dec * (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5); + } + } else { + this_dec = dec; + } + + if (this_dec != 0) + done_something = true; + + int usePathID=-1; + int usePieceID=0; + double useT=0.0; + if ( a->_has_back_data ) { + if ( a->ebData[i].pathID >= 0 && a->ebData[stB].pathID == a->ebData[i].pathID && a->ebData[stB].pieceID == a->ebData[i].pieceID + && a->ebData[stB].tEn == a->ebData[i].tSt ) { + usePathID=a->ebData[i].pathID; + usePieceID=a->ebData[i].pieceID; + useT=a->ebData[i].tSt; + } else { + usePathID=a->ebData[i].pathID; + usePieceID=0; + useT=0; + } + } + if (dec > 0) + { + Path::DoRightJoin (this, this_dec, join, ptP, stD, seD, miter, stL, seL, + stNo, enNo,usePathID,usePieceID,useT); + a->swsData[i].stPt = enNo; + a->swsData[stB].enPt = stNo; + } + else + { + Path::DoLeftJoin (this, -this_dec, join, ptP, stD, seD, miter, stL, seL, + stNo, enNo,usePathID,usePieceID,useT); + a->swsData[i].stPt = enNo; + a->swsData[stB].enPt = stNo; + } + } + + if (dec < 0) + { + for (int i = 0; i < numberOfEdges(); i++) + Inverse (i); + } + + if ( _has_back_data ) { + for (int i = 0; i < a->numberOfEdges(); i++) + { + int nEd=AddEdge (a->swsData[i].stPt, a->swsData[i].enPt); + ebData[nEd]=a->ebData[i]; + } + } else { + for (int i = 0; i < a->numberOfEdges(); i++) + { + AddEdge (a->swsData[i].stPt, a->swsData[i].enPt); + } + } + + a->MakeSweepSrcData (false); + a->MakeSweepDestData (false); + + return (done_something? 0 : shape_nothing_to_do); +} + + + +// we found a contour, now reassemble the edges on it, instead of dumping them in the Path "dest" as a +// polyline. since it was a DFS, the precParc and suivParc make a nice doubly-linked list of the edges in +// the contour. the first and last edges of the contour are startBord and curBord +void +Shape::AddContour (Path * dest, int nbP, Path * *orig, int startBord, int curBord, bool splitWhenForced) +{ + int bord = startBord; + + { + dest->MoveTo (getPoint(getEdge(bord).st).x); + } + + while (bord >= 0) + { + int nPiece = ebData[bord].pieceID; + int nPath = ebData[bord].pathID; + + if (nPath < 0 || nPath >= nbP || orig[nPath] == nullptr) + { + // segment batard + dest->LineTo (getPoint(getEdge(bord).en).x); + bord = swdData[bord].suivParc; + } + else + { + Path *from = orig[nPath]; + if (nPiece < 0 || nPiece >= int(from->descr_cmd.size())) + { + // segment batard + dest->LineTo (getPoint(getEdge(bord).en).x); + bord = swdData[bord].suivParc; + } + else + { + int nType = from->descr_cmd[nPiece]->getType(); + if (nType == descr_close || nType == descr_moveto + || nType == descr_forced) + { + // devrait pas arriver + dest->LineTo (getPoint(getEdge(bord).en).x); + bord = swdData[bord].suivParc; + } + else if (nType == descr_lineto) + { + bord = ReFormeLineTo (bord, curBord, dest, from); + } + else if (nType == descr_arcto) + { + bord = ReFormeArcTo (bord, curBord, dest, from); + } + else if (nType == descr_cubicto) + { + bord = ReFormeCubicTo (bord, curBord, dest, from); + } + else if (nType == descr_bezierto) + { + PathDescrBezierTo* nBData = + dynamic_cast<PathDescrBezierTo *>(from->descr_cmd[nPiece]); + + if (nBData->nb == 0) + { + bord = ReFormeLineTo (bord, curBord, dest, from); + } + else + { + bord = ReFormeBezierTo (bord, curBord, dest, from); + } + } + else if (nType == descr_interm_bezier) + { + bord = ReFormeBezierTo (bord, curBord, dest, from); + } + else + { + // devrait pas arriver non plus + dest->LineTo (getPoint(getEdge(bord).en).x); + bord = swdData[bord].suivParc; + } + if (bord >= 0 && getPoint(getEdge(bord).st).totalDegree() > 2 ) { + dest->ForcePoint (); + } else if ( bord >= 0 && getPoint(getEdge(bord).st).oldDegree > 2 && getPoint(getEdge(bord).st).totalDegree() == 2) { + if ( splitWhenForced ) { + // pour les coupures + dest->ForcePoint (); + } else { + if ( _has_back_data ) { + int prevEdge=getPoint(getEdge(bord).st).incidentEdge[FIRST]; + int nextEdge=getPoint(getEdge(bord).st).incidentEdge[LAST]; + if ( getEdge(prevEdge).en != getEdge(bord).st ) { + int swai=prevEdge;prevEdge=nextEdge;nextEdge=swai; + } + if ( ebData[prevEdge].pieceID == ebData[nextEdge].pieceID && ebData[prevEdge].pathID == ebData[nextEdge].pathID ) { + if ( fabs(ebData[prevEdge].tEn-ebData[nextEdge].tSt) < 0.05 ) { + } else { + dest->ForcePoint (); + } + } else { + dest->ForcePoint (); + } + } else { + dest->ForcePoint (); + } + } + } + } + } + } + dest->Close (); +} + +int +Shape::ReFormeLineTo (int bord, int /*curBord*/, Path * dest, Path * /*orig*/) +{ + int nPiece = ebData[bord].pieceID; + int nPath = ebData[bord].pathID; + double /*ts=ebData[bord].tSt, */ te = ebData[bord].tEn; + Geom::Point nx = getPoint(getEdge(bord).en).x; + bord = swdData[bord].suivParc; + while (bord >= 0) + { + if (getPoint(getEdge(bord).st).totalDegree() > 2 + || getPoint(getEdge(bord).st).oldDegree > 2) + { + break; + } + if (ebData[bord].pieceID == nPiece && ebData[bord].pathID == nPath) + { + if (fabs (te - ebData[bord].tSt) > 0.0001) + break; + nx = getPoint(getEdge(bord).en).x; + te = ebData[bord].tEn; + } + else + { + break; + } + bord = swdData[bord].suivParc; + } + { + dest->LineTo (nx); + } + return bord; +} + +int +Shape::ReFormeArcTo (int bord, int /*curBord*/, Path * dest, Path * from) +{ + int nPiece = ebData[bord].pieceID; + int nPath = ebData[bord].pathID; + double ts = ebData[bord].tSt, te = ebData[bord].tEn; + // double px=pts[getEdge(bord).st].x,py=pts[getEdge(bord).st].y; + Geom::Point nx = getPoint(getEdge(bord).en).x; + bord = swdData[bord].suivParc; + while (bord >= 0) + { + if (getPoint(getEdge(bord).st).totalDegree() > 2 + || getPoint(getEdge(bord).st).oldDegree > 2) + { + break; + } + if (ebData[bord].pieceID == nPiece && ebData[bord].pathID == nPath) + { + if (fabs (te - ebData[bord].tSt) > 0.0001) + { + break; + } + nx = getPoint(getEdge(bord).en).x; + te = ebData[bord].tEn; + } + else + { + break; + } + bord = swdData[bord].suivParc; + } + double sang, eang; + PathDescrArcTo* nData = dynamic_cast<PathDescrArcTo *>(from->descr_cmd[nPiece]); + bool nLarge = nData->large; + bool nClockwise = nData->clockwise; + Path::ArcAngles (from->PrevPoint (nPiece - 1), nData->p,nData->rx,nData->ry,nData->angle*M_PI/180.0, nLarge, nClockwise, sang, eang); + if (nClockwise) + { + if (sang < eang) + sang += 2 * M_PI; + } + else + { + if (sang > eang) + sang -= 2 * M_PI; + } + double delta = eang - sang; + double ndelta = delta * (te - ts); + if (ts > te) + nClockwise = !nClockwise; + if (ndelta < 0) + ndelta = -ndelta; + if (ndelta > M_PI) + nLarge = true; + else + nLarge = false; + /* if ( delta < 0 ) delta=-delta; + if ( ndelta < 0 ) ndelta=-ndelta; + if ( ( delta < M_PI && ndelta < M_PI ) || ( delta >= M_PI && ndelta >= M_PI ) ) { + if ( ts < te ) { + } else { + nClockwise=!(nClockwise); + } + } else { + // nLarge=!(nLarge); + nLarge=false; // c'est un sous-segment -> l'arc ne peut que etre plus petit + if ( ts < te ) { + } else { + nClockwise=!(nClockwise); + } + }*/ + { + PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(from->descr_cmd[nPiece]); + dest->ArcTo (nx, nData->rx,nData->ry,nData->angle, nLarge, nClockwise); + } + return bord; +} + +int +Shape::ReFormeCubicTo (int bord, int /*curBord*/, Path * dest, Path * from) +{ + int nPiece = ebData[bord].pieceID; + int nPath = ebData[bord].pathID; + double ts = ebData[bord].tSt, te = ebData[bord].tEn; + Geom::Point nx = getPoint(getEdge(bord).en).x; + bord = swdData[bord].suivParc; + while (bord >= 0) + { + if (getPoint(getEdge(bord).st).totalDegree() > 2 + || getPoint(getEdge(bord).st).oldDegree > 2) + { + break; + } + if (ebData[bord].pieceID == nPiece && ebData[bord].pathID == nPath) + { + if (fabs (te - ebData[bord].tSt) > 0.0001) + { + break; + } + nx = getPoint(getEdge(bord).en).x; + te = ebData[bord].tEn; + } + else + { + break; + } + bord = swdData[bord].suivParc; + } + Geom::Point prevx = from->PrevPoint (nPiece - 1); + + Geom::Point sDx, eDx; + { + PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(from->descr_cmd[nPiece]); + Path::CubicTangent (ts, sDx, prevx,nData->start,nData->p,nData->end); + Path::CubicTangent (te, eDx, prevx,nData->start,nData->p,nData->end); + } + sDx *= (te - ts); + eDx *= (te - ts); + { + dest->CubicTo (nx,sDx,eDx); + } + return bord; +} + +int +Shape::ReFormeBezierTo (int bord, int /*curBord*/, Path * dest, Path * from) +{ + int nPiece = ebData[bord].pieceID; + int nPath = ebData[bord].pathID; + double ts = ebData[bord].tSt, te = ebData[bord].tEn; + int ps = nPiece, pe = nPiece; + Geom::Point px = getPoint(getEdge(bord).st).x; + Geom::Point nx = getPoint(getEdge(bord).en).x; + int inBezier = -1, nbInterm = -1; + int typ; + typ = from->descr_cmd[nPiece]->getType(); + PathDescrBezierTo *nBData = nullptr; + if (typ == descr_bezierto) + { + nBData = dynamic_cast<PathDescrBezierTo *>(from->descr_cmd[nPiece]); + inBezier = nPiece; + nbInterm = nBData->nb; + } + else + { + int n = nPiece - 1; + while (n > 0) + { + typ = from->descr_cmd[n]->getType(); + if (typ == descr_bezierto) + { + inBezier = n; + nBData = dynamic_cast<PathDescrBezierTo *>(from->descr_cmd[n]); + nbInterm = nBData->nb; + break; + } + n--; + } + if (inBezier < 0) + { + bord = swdData[bord].suivParc; + dest->LineTo (nx); + return bord; + } + } + bord = swdData[bord].suivParc; + while (bord >= 0) + { + if (getPoint(getEdge(bord).st).totalDegree() > 2 + || getPoint(getEdge(bord).st).oldDegree > 2) + { + break; + } + if (ebData[bord].pathID == nPath) + { + if (ebData[bord].pieceID < inBezier + || ebData[bord].pieceID >= inBezier + nbInterm) + break; + if (ebData[bord].pieceID == pe + && fabs (te - ebData[bord].tSt) > 0.0001) + break; + if (ebData[bord].pieceID != pe + && (ebData[bord].tSt > 0.0001 && ebData[bord].tSt < 0.9999)) + break; + if (ebData[bord].pieceID != pe && (te > 0.0001 && te < 0.9999)) + break; + nx = getPoint(getEdge(bord).en).x; + te = ebData[bord].tEn; + pe = ebData[bord].pieceID; + } + else + { + break; + } + bord = swdData[bord].suivParc; + } + + g_return_val_if_fail(nBData != nullptr, 0); + + if (pe == ps) + { + ReFormeBezierChunk (px, nx, dest, inBezier, nbInterm, from, ps, + ts, te); + } + else if (ps < pe) + { + if (ts < 0.0001) + { + if (te > 0.9999) + { + dest->BezierTo (nx); + for (int i = ps; i <= pe; i++) + { + PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[i+1]); + dest->IntermBezierTo (nData->p); + } + dest->EndBezierTo (); + } + else + { + Geom::Point tx; + { + PathDescrIntermBezierTo* psData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[pe]); + PathDescrIntermBezierTo* pnData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[pe+1]); + tx = (pnData->p + psData->p) / 2; + } + dest->BezierTo (tx); + for (int i = ps; i < pe; i++) + { + PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[i+1]); + dest->IntermBezierTo (nData->p); + } + dest->EndBezierTo (); + ReFormeBezierChunk (tx, nx, dest, inBezier, nbInterm, + from, pe, 0.0, te); + } + } + else + { + if (te > 0.9999) + { + Geom::Point tx; + { + PathDescrIntermBezierTo* psData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[ps+1]); + PathDescrIntermBezierTo* pnData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[ps+2]); + tx = (psData->p + pnData->p) / 2; + } + ReFormeBezierChunk (px, tx, dest, inBezier, nbInterm, + from, ps, ts, 1.0); + dest->BezierTo (nx); + for (int i = ps + 1; i <= pe; i++) + { + PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[i+1]); + dest->IntermBezierTo (nData->p); + } + dest->EndBezierTo (); + } + else + { + Geom::Point tx; + { + PathDescrIntermBezierTo* psData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[ps+1]); + PathDescrIntermBezierTo* pnData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[ps+2]); + tx = (pnData->p + psData->p) / 2; + } + ReFormeBezierChunk (px, tx, dest, inBezier, nbInterm, + from, ps, ts, 1.0); + { + PathDescrIntermBezierTo* psData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[pe]); + PathDescrIntermBezierTo* pnData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[pe+1]); + tx = (pnData->p + psData->p) / 2; + } + dest->BezierTo (tx); + for (int i = ps + 1; i <= pe; i++) + { + PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[i+1]); + dest->IntermBezierTo (nData->p); + } + dest->EndBezierTo (); + ReFormeBezierChunk (tx, nx, dest, inBezier, nbInterm, + from, pe, 0.0, te); + } + } + } + else + { + if (ts > 0.9999) + { + if (te < 0.0001) + { + dest->BezierTo (nx); + for (int i = ps; i >= pe; i--) + { + PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[i+1]); + dest->IntermBezierTo (nData->p); + } + dest->EndBezierTo (); + } + else + { + Geom::Point tx; + { + PathDescrIntermBezierTo* psData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[pe+1]); + PathDescrIntermBezierTo* pnData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[pe+2]); + tx = (pnData->p + psData->p) / 2; + } + dest->BezierTo (tx); + for (int i = ps; i > pe; i--) + { + PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[i+1]); + dest->IntermBezierTo (nData->p); + } + dest->EndBezierTo (); + ReFormeBezierChunk (tx, nx, dest, inBezier, nbInterm, + from, pe, 1.0, te); + } + } + else + { + if (te < 0.0001) + { + Geom::Point tx; + { + PathDescrIntermBezierTo* psData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[ps]); + PathDescrIntermBezierTo* pnData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[ps+1]); + tx = (pnData->p + psData->p) / 2; + } + ReFormeBezierChunk (px, tx, dest, inBezier, nbInterm, + from, ps, ts, 0.0); + dest->BezierTo (nx); + for (int i = ps + 1; i >= pe; i--) + { + PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[i]); + dest->IntermBezierTo (nData->p); + } + dest->EndBezierTo (); + } + else + { + Geom::Point tx; + { + PathDescrIntermBezierTo* psData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[ps]); + PathDescrIntermBezierTo* pnData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[ps+1]); + tx = (pnData->p + psData->p) / 2; + } + ReFormeBezierChunk (px, tx, dest, inBezier, nbInterm, + from, ps, ts, 0.0); + { + PathDescrIntermBezierTo* psData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[pe+1]); + PathDescrIntermBezierTo* pnData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[pe+2]); + tx = (pnData->p + psData->p) / 2; + } + dest->BezierTo (tx); + for (int i = ps + 1; i > pe; i--) + { + PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[i]); + dest->IntermBezierTo (nData->p); + } + dest->EndBezierTo (); + ReFormeBezierChunk (tx, nx, dest, inBezier, nbInterm, + from, pe, 1.0, te); + } + } + } + return bord; +} + +void +Shape::ReFormeBezierChunk (Geom::Point px, Geom::Point nx, + Path * dest, int inBezier, int nbInterm, + Path * from, int p, double ts, double te) +{ + PathDescrBezierTo* nBData = dynamic_cast<PathDescrBezierTo*>(from->descr_cmd[inBezier]); + Geom::Point bstx = from->PrevPoint (inBezier - 1); + Geom::Point benx = nBData->p; + + Geom::Point mx; + if (p == inBezier) + { + // premier bout + if (nbInterm <= 1) + { + // seul bout de la spline + PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[inBezier+1]); + mx = nData->p; + } + else + { + // premier bout d'une spline qui en contient plusieurs + PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[inBezier+1]); + mx = nData->p; + nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[inBezier+2]); + benx = (nData->p + mx) / 2; + } + } + else if (p == inBezier + nbInterm - 1) + { + // dernier bout + // si nbInterm == 1, le cas a deja ete traite + // donc dernier bout d'une spline qui en contient plusieurs + PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[inBezier+nbInterm]); + mx = nData->p; + nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[inBezier+nbInterm-1]); + bstx = (nData->p + mx) / 2; + } + else + { + // la spline contient forcément plusieurs bouts, et ce n'est ni le premier ni le dernier + PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[p+1]); + mx = nData->p; + nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[p]); + bstx = (nData->p + mx) / 2; + nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[p+2]); + benx = (nData->p + mx) / 2; + } + Geom::Point cx; + { + Path::QuadraticPoint ((ts + te) / 2, cx, bstx, mx, benx); + } + cx = 2 * cx - (px + nx) / 2; + { + dest->BezierTo (nx); + dest->IntermBezierTo (cx); + dest->EndBezierTo (); + } +} + +#undef MiscNormalize |