summaryrefslogtreecommitdiffstats
path: root/src/livarot
diff options
context:
space:
mode:
Diffstat (limited to 'src/livarot')
-rw-r--r--src/livarot/AVL.cpp969
-rw-r--r--src/livarot/AVL.h104
-rw-r--r--src/livarot/AlphaLigne.cpp308
-rw-r--r--src/livarot/AlphaLigne.h87
-rw-r--r--src/livarot/BitLigne.cpp180
-rw-r--r--src/livarot/BitLigne.h64
-rw-r--r--src/livarot/CMakeLists.txt44
-rw-r--r--src/livarot/Livarot.h43
-rw-r--r--src/livarot/LivarotDefs.h158
-rw-r--r--src/livarot/Path.cpp939
-rw-r--r--src/livarot/Path.h416
-rw-r--r--src/livarot/PathConversion.cpp1579
-rw-r--r--src/livarot/PathCutting.cpp1534
-rw-r--r--src/livarot/PathOutline.cpp1526
-rw-r--r--src/livarot/PathSimplify.cpp1404
-rw-r--r--src/livarot/PathStroke.cpp763
-rw-r--r--src/livarot/README17
-rw-r--r--src/livarot/Shape.cpp2317
-rw-r--r--src/livarot/Shape.h577
-rw-r--r--src/livarot/ShapeDraw.cpp114
-rw-r--r--src/livarot/ShapeMisc.cpp1457
-rw-r--r--src/livarot/ShapeRaster.cpp2014
-rw-r--r--src/livarot/ShapeSweep.cpp3319
-rw-r--r--src/livarot/float-line.cpp916
-rw-r--r--src/livarot/float-line.h145
-rw-r--r--src/livarot/int-line.cpp1071
-rw-r--r--src/livarot/int-line.h119
-rw-r--r--src/livarot/path-description.cpp180
-rw-r--r--src/livarot/path-description.h185
-rw-r--r--src/livarot/sweep-event-queue.h60
-rw-r--r--src/livarot/sweep-event.cpp284
-rw-r--r--src/livarot/sweep-event.h54
-rw-r--r--src/livarot/sweep-tree-list.cpp56
-rw-r--r--src/livarot/sweep-tree-list.h49
-rw-r--r--src/livarot/sweep-tree.cpp567
-rw-r--r--src/livarot/sweep-tree.h91
36 files changed, 23710 insertions, 0 deletions
diff --git a/src/livarot/AVL.cpp b/src/livarot/AVL.cpp
new file mode 100644
index 0000000..9bf6eb4
--- /dev/null
+++ b/src/livarot/AVL.cpp
@@ -0,0 +1,969 @@
+// 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 "AVL.h"
+
+/*
+ * the algorithm explanation for this code comes from purists.org, which seems to have disappeared since
+ * it's a classic AVL tree rebalancing, nothing fancy
+ */
+
+AVLTree::AVLTree()
+{
+ MakeNew();
+}
+
+AVLTree::~AVLTree()
+{
+ MakeDelete();
+}
+
+void AVLTree::MakeNew()
+{
+ for (int i = 0; i < 2; i++)
+ {
+ elem[i] = nullptr;
+ child[i] = nullptr;
+ }
+
+ parent = nullptr;
+ balance = 0;
+}
+
+void AVLTree::MakeDelete()
+{
+ for (int i = 0; i < 2; i++) {
+ if (elem[i]) {
+ elem[i]->elem[1 - i] = elem[1 - i];
+ }
+ elem[i] = nullptr;
+ }
+}
+
+AVLTree *AVLTree::Leftmost()
+{
+ return leafFromParent(nullptr, LEFT);
+}
+
+AVLTree *AVLTree::leaf(AVLTree *from, Side s)
+{
+ if (from == child[1 - s]) {
+ if (child[s]) {
+ return child[s]->leafFromParent(this, s);
+ }
+ else if (parent) {
+ return parent->leaf(this, s);
+ }
+ }
+ else if (from == child[s]) {
+ if (parent) {
+ return parent->leaf(this, s);
+ }
+ }
+
+ return nullptr;
+}
+
+AVLTree *AVLTree::leafFromParent(AVLTree */*from*/, Side s)
+{
+ if (child[s]) {
+ return child[s]->leafFromParent(this, s);
+ }
+
+ return this;
+}
+
+int
+AVLTree::RestoreBalances (AVLTree * from, AVLTree * &racine)
+{
+ if (from == nullptr)
+ {
+ if (parent)
+ return parent->RestoreBalances (this, racine);
+ }
+ else
+ {
+ if (balance == 0)
+ {
+ if (from == child[LEFT])
+ balance = 1;
+ if (from == child[RIGHT])
+ balance = -1;
+ if (parent)
+ return parent->RestoreBalances (this, racine);
+ return avl_no_err;
+ }
+ else if (balance > 0)
+ {
+ if (from == child[RIGHT])
+ {
+ balance = 0;
+ return avl_no_err;
+ }
+ if (child[LEFT] == nullptr)
+ {
+// cout << "mierda\n";
+ return avl_bal_err;
+ }
+ AVLTree *a = this;
+ AVLTree *b = child[LEFT];
+ AVLTree *e = child[RIGHT];
+ AVLTree *c = child[LEFT]->child[LEFT];
+ AVLTree *d = child[LEFT]->child[RIGHT];
+ if (child[LEFT]->balance > 0)
+ {
+ AVLTree *r = parent;
+
+ a->parent = b;
+ b->child[RIGHT] = a;
+ a->child[RIGHT] = e;
+ if (e)
+ e->parent = a;
+ a->child[LEFT] = d;
+ if (d)
+ d->parent = a;
+ b->child[LEFT] = c;
+ if (c)
+ c->parent = b;
+ b->parent = r;
+ if (r)
+ {
+ if (r->child[LEFT] == a)
+ r->child[LEFT] = b;
+ if (r->child[RIGHT] == a)
+ r->child[RIGHT] = b;
+ }
+ if (racine == a)
+ racine = b;
+
+ a->balance = 0;
+ b->balance = 0;
+ return avl_no_err;
+ }
+ else
+ {
+ if (child[LEFT]->child[RIGHT] == nullptr)
+ {
+ // cout << "mierda\n";
+ return avl_bal_err;
+ }
+ AVLTree *f = child[LEFT]->child[RIGHT]->child[LEFT];
+ AVLTree *g = child[LEFT]->child[RIGHT]->child[RIGHT];
+ AVLTree *r = parent;
+
+ a->parent = d;
+ d->child[RIGHT] = a;
+ b->parent = d;
+ d->child[LEFT] = b;
+ a->child[LEFT] = g;
+ if (g)
+ g->parent = a;
+ a->child[RIGHT] = e;
+ if (e)
+ e->parent = a;
+ b->child[LEFT] = c;
+ if (c)
+ c->parent = b;
+ b->child[RIGHT] = f;
+ if (f)
+ f->parent = b;
+
+ d->parent = r;
+ if (r)
+ {
+ if (r->child[LEFT] == a)
+ r->child[LEFT] = d;
+ if (r->child[RIGHT] == a)
+ r->child[RIGHT] = d;
+ }
+ if (racine == a)
+ racine = d;
+
+ int old_bal = d->balance;
+ d->balance = 0;
+ if (old_bal == 0)
+ {
+ a->balance = 0;
+ b->balance = 0;
+ }
+ else if (old_bal > 0)
+ {
+ a->balance = -1;
+ b->balance = 0;
+ }
+ else if (old_bal < 0)
+ {
+ a->balance = 0;
+ b->balance = 1;
+ }
+ return avl_no_err;
+ }
+ }
+ else if (balance < 0)
+ {
+ if (from == child[LEFT])
+ {
+ balance = 0;
+ return avl_no_err;
+ }
+ if (child[RIGHT] == nullptr)
+ {
+// cout << "mierda\n";
+ return avl_bal_err;
+ }
+ AVLTree *a = this;
+ AVLTree *b = child[RIGHT];
+ AVLTree *e = child[LEFT];
+ AVLTree *c = child[RIGHT]->child[RIGHT];
+ AVLTree *d = child[RIGHT]->child[LEFT];
+ AVLTree *r = parent;
+ if (child[RIGHT]->balance < 0)
+ {
+
+ a->parent = b;
+ b->child[LEFT] = a;
+ a->child[LEFT] = e;
+ if (e)
+ e->parent = a;
+ a->child[RIGHT] = d;
+ if (d)
+ d->parent = a;
+ b->child[RIGHT] = c;
+ if (c)
+ c->parent = b;
+ b->parent = r;
+ if (r)
+ {
+ if (r->child[LEFT] == a)
+ r->child[LEFT] = b;
+ if (r->child[RIGHT] == a)
+ r->child[RIGHT] = b;
+ }
+ if (racine == a)
+ racine = b;
+ a->balance = 0;
+ b->balance = 0;
+ return avl_no_err;
+ }
+ else
+ {
+ if (child[RIGHT]->child[LEFT] == nullptr)
+ {
+// cout << "mierda\n";
+ return avl_bal_err;
+ }
+ AVLTree *f = child[RIGHT]->child[LEFT]->child[RIGHT];
+ AVLTree *g = child[RIGHT]->child[LEFT]->child[LEFT];
+
+ a->parent = d;
+ d->child[LEFT] = a;
+ b->parent = d;
+ d->child[RIGHT] = b;
+ a->child[RIGHT] = g;
+ if (g)
+ g->parent = a;
+ a->child[LEFT] = e;
+ if (e)
+ e->parent = a;
+ b->child[RIGHT] = c;
+ if (c)
+ c->parent = b;
+ b->child[LEFT] = f;
+ if (f)
+ f->parent = b;
+
+ d->parent = r;
+ if (r)
+ {
+ if (r->child[LEFT] == a)
+ r->child[LEFT] = d;
+ if (r->child[RIGHT] == a)
+ r->child[RIGHT] = d;
+ }
+ if (racine == a)
+ racine = d;
+ int old_bal = d->balance;
+ d->balance = 0;
+ if (old_bal == 0)
+ {
+ a->balance = 0;
+ b->balance = 0;
+ }
+ else if (old_bal > 0)
+ {
+ a->balance = 0;
+ b->balance = -1;
+ }
+ else if (old_bal < 0)
+ {
+ a->balance = 1;
+ b->balance = 0;
+ }
+ return avl_no_err;
+ }
+ }
+ }
+ return avl_no_err;
+}
+
+int
+AVLTree::RestoreBalances (int diff, AVLTree * &racine)
+{
+ if (balance > 0)
+ {
+ if (diff < 0)
+ {
+ balance = 0;
+ if (parent)
+ {
+ if (this == parent->child[RIGHT])
+ return parent->RestoreBalances (1, racine);
+ if (this == parent->child[LEFT])
+ return parent->RestoreBalances (-1, racine);
+ }
+ return avl_no_err;
+ }
+ else if (diff == 0)
+ {
+ }
+ else if (diff > 0)
+ {
+ if (child[LEFT] == nullptr)
+ {
+// cout << "un probleme\n";
+ return avl_bal_err;
+ }
+ AVLTree *r = parent;
+ AVLTree *a = this;
+ AVLTree *b = child[RIGHT];
+ AVLTree *e = child[LEFT];
+ AVLTree *f = e->child[RIGHT];
+ AVLTree *g = e->child[LEFT];
+ if (e->balance > 0)
+ {
+ e->child[RIGHT] = a;
+ e->child[LEFT] = g;
+ a->child[RIGHT] = b;
+ a->child[LEFT] = f;
+ if (a)
+ a->parent = e;
+ if (g)
+ g->parent = e;
+ if (b)
+ b->parent = a;
+ if (f)
+ f->parent = a;
+ e->parent = r;
+ if (r)
+ {
+ if (r->child[LEFT] == a)
+ r->child[LEFT] = e;
+ if (r->child[RIGHT] == a)
+ r->child[RIGHT] = e;
+ }
+ if (racine == this)
+ racine = e;
+ e->balance = 0;
+ a->balance = 0;
+ if (r)
+ {
+ if (e == r->child[RIGHT])
+ return r->RestoreBalances (1, racine);
+ if (e == r->child[LEFT])
+ return r->RestoreBalances (-1, racine);
+ }
+ return avl_no_err;
+ }
+ else if (e->balance == 0)
+ {
+ e->child[RIGHT] = a;
+ e->child[LEFT] = g;
+ a->child[RIGHT] = b;
+ a->child[LEFT] = f;
+ if (a)
+ a->parent = e;
+ if (g)
+ g->parent = e;
+ if (b)
+ b->parent = a;
+ if (f)
+ f->parent = a;
+ e->parent = r;
+ if (r)
+ {
+ if (r->child[LEFT] == a)
+ r->child[LEFT] = e;
+ if (r->child[RIGHT] == a)
+ r->child[RIGHT] = e;
+ }
+ if (racine == this)
+ racine = e;
+ e->balance = -1;
+ a->balance = 1;
+ return avl_no_err;
+ }
+ else if (e->balance < 0)
+ {
+ if (child[LEFT]->child[RIGHT] == nullptr)
+ {
+// cout << "un probleme\n";
+ return avl_bal_err;
+ }
+ AVLTree *i = child[LEFT]->child[RIGHT]->child[RIGHT];
+ AVLTree *j = child[LEFT]->child[RIGHT]->child[LEFT];
+
+ f->child[RIGHT] = a;
+ f->child[LEFT] = e;
+ a->child[RIGHT] = b;
+ a->child[LEFT] = i;
+ e->child[RIGHT] = j;
+ e->child[LEFT] = g;
+ if (b)
+ b->parent = a;
+ if (i)
+ i->parent = a;
+ if (g)
+ g->parent = e;
+ if (j)
+ j->parent = e;
+ if (a)
+ a->parent = f;
+ if (e)
+ e->parent = f;
+ f->parent = r;
+ if (r)
+ {
+ if (r->child[LEFT] == a)
+ r->child[LEFT] = f;
+ if (r->child[RIGHT] == a)
+ r->child[RIGHT] = f;
+ }
+ if (racine == this)
+ racine = f;
+ int oBal = f->balance;
+ f->balance = 0;
+ if (oBal > 0)
+ {
+ a->balance = -1;
+ e->balance = 0;
+ }
+ else if (oBal == 0)
+ {
+ a->balance = 0;
+ e->balance = 0;
+ }
+ else if (oBal < 0)
+ {
+ a->balance = 0;
+ e->balance = 1;
+ }
+ if (r)
+ {
+ if (f == r->child[RIGHT])
+ return r->RestoreBalances (1, racine);
+ if (f == r->child[LEFT])
+ return r->RestoreBalances (-1, racine);
+ }
+ return avl_no_err;
+ }
+ }
+ }
+ else if (balance == 0)
+ {
+ if (diff < 0)
+ {
+ balance = -1;
+ }
+ else if (diff == 0)
+ {
+ }
+ else if (diff > 0)
+ {
+ balance = 1;
+ }
+ return avl_no_err;
+ }
+ else if (balance < 0)
+ {
+ if (diff < 0)
+ {
+ if (child[RIGHT] == nullptr)
+ {
+// cout << "un probleme\n";
+ return avl_bal_err;
+ }
+ AVLTree *r = parent;
+ AVLTree *a = this;
+ AVLTree *b = child[LEFT];
+ AVLTree *e = child[RIGHT];
+ AVLTree *f = e->child[LEFT];
+ AVLTree *g = e->child[RIGHT];
+ if (e->balance < 0)
+ {
+ e->child[LEFT] = a;
+ e->child[RIGHT] = g;
+ a->child[LEFT] = b;
+ a->child[RIGHT] = f;
+ if (a)
+ a->parent = e;
+ if (g)
+ g->parent = e;
+ if (b)
+ b->parent = a;
+ if (f)
+ f->parent = a;
+ e->parent = r;
+ if (r)
+ {
+ if (r->child[LEFT] == a)
+ r->child[LEFT] = e;
+ if (r->child[RIGHT] == a)
+ r->child[RIGHT] = e;
+ }
+ if (racine == this)
+ racine = e;
+ e->balance = 0;
+ a->balance = 0;
+ if (r)
+ {
+ if (e == r->child[RIGHT])
+ return r->RestoreBalances (1, racine);
+ if (e == r->child[LEFT])
+ return r->RestoreBalances (-1, racine);
+ }
+ return avl_no_err;
+ }
+ else if (e->balance == 0)
+ {
+ e->child[LEFT] = a;
+ e->child[RIGHT] = g;
+ a->child[LEFT] = b;
+ a->child[RIGHT] = f;
+ if (a)
+ a->parent = e;
+ if (g)
+ g->parent = e;
+ if (b)
+ b->parent = a;
+ if (f)
+ f->parent = a;
+ e->parent = r;
+ if (r)
+ {
+ if (r->child[LEFT] == a)
+ r->child[LEFT] = e;
+ if (r->child[RIGHT] == a)
+ r->child[RIGHT] = e;
+ }
+ if (racine == this)
+ racine = e;
+ e->balance = 1;
+ a->balance = -1;
+ return avl_no_err;
+ }
+ else if (e->balance > 0)
+ {
+ if (child[RIGHT]->child[LEFT] == nullptr)
+ {
+// cout << "un probleme\n";
+ return avl_bal_err;
+ }
+ AVLTree *i = child[RIGHT]->child[LEFT]->child[LEFT];
+ AVLTree *j = child[RIGHT]->child[LEFT]->child[RIGHT];
+
+ f->child[LEFT] = a;
+ f->child[RIGHT] = e;
+ a->child[LEFT] = b;
+ a->child[RIGHT] = i;
+ e->child[LEFT] = j;
+ e->child[RIGHT] = g;
+ if (b)
+ b->parent = a;
+ if (i)
+ i->parent = a;
+ if (g)
+ g->parent = e;
+ if (j)
+ j->parent = e;
+ if (a)
+ a->parent = f;
+ if (e)
+ e->parent = f;
+ f->parent = r;
+ if (r)
+ {
+ if (r->child[LEFT] == a)
+ r->child[LEFT] = f;
+ if (r->child[RIGHT] == a)
+ r->child[RIGHT] = f;
+ }
+ if (racine == this)
+ racine = f;
+ int oBal = f->balance;
+ f->balance = 0;
+ if (oBal > 0)
+ {
+ a->balance = 0;
+ e->balance = -1;
+ }
+ else if (oBal == 0)
+ {
+ a->balance = 0;
+ e->balance = 0;
+ }
+ else if (oBal < 0)
+ {
+ a->balance = 1;
+ e->balance = 0;
+ }
+ if (r)
+ {
+ if (f == r->child[RIGHT])
+ return r->RestoreBalances (1, racine);
+ if (f == r->child[LEFT])
+ return r->RestoreBalances (-1, racine);
+ }
+ return avl_no_err;
+ }
+ }
+ else if (diff == 0)
+ {
+ }
+ else if (diff > 0)
+ {
+ balance = 0;
+ if (parent)
+ {
+ if (this == parent->child[RIGHT])
+ return parent->RestoreBalances (1, racine);
+ if (this == parent->child[LEFT])
+ return parent->RestoreBalances (-1, racine);
+ }
+ return avl_no_err;
+ }
+ }
+ return avl_no_err;
+}
+
+/*
+ * removal
+ */
+int
+AVLTree::Remove (AVLTree * &racine, bool rebalance)
+{
+ AVLTree *startNode = nullptr;
+ int remDiff = 0;
+ int res = Remove (racine, startNode, remDiff);
+ if (res == avl_no_err && rebalance && startNode)
+ res = startNode->RestoreBalances (remDiff, racine);
+ return res;
+}
+
+int
+AVLTree::Remove (AVLTree * &racine, AVLTree * &startNode, int &diff)
+{
+ if (elem[LEFT])
+ elem[LEFT]->elem[RIGHT] = elem[RIGHT];
+ if (elem[RIGHT])
+ elem[RIGHT]->elem[LEFT] = elem[LEFT];
+ elem[LEFT] = elem[RIGHT] = nullptr;
+
+ if (child[LEFT] && child[RIGHT])
+ {
+ AVLTree *newMe = child[LEFT]->leafFromParent(this, RIGHT);
+ if (newMe == nullptr || newMe->child[RIGHT])
+ {
+// cout << "pas normal\n";
+ return avl_rm_err;
+ }
+ if (newMe == child[LEFT])
+ {
+ startNode = newMe;
+ diff = -1;
+ newMe->child[RIGHT] = child[RIGHT];
+ child[RIGHT]->parent = newMe;
+ newMe->parent = parent;
+ if (parent)
+ {
+ if (parent->child[LEFT] == this)
+ parent->child[LEFT] = newMe;
+ if (parent->child[RIGHT] == this)
+ parent->child[RIGHT] = newMe;
+ }
+ }
+ else
+ {
+ AVLTree *oParent = newMe->parent;
+ startNode = oParent;
+ diff = 1;
+
+ oParent->child[RIGHT] = newMe->child[LEFT];
+ if (newMe->child[LEFT])
+ newMe->child[LEFT]->parent = oParent;
+
+ newMe->parent = parent;
+ newMe->child[LEFT] = child[LEFT];
+ newMe->child[RIGHT] = child[RIGHT];
+ if (parent)
+ {
+ if (parent->child[LEFT] == this)
+ parent->child[LEFT] = newMe;
+ if (parent->child[RIGHT] == this)
+ parent->child[RIGHT] = newMe;
+ }
+ if (child[LEFT])
+ child[LEFT]->parent = newMe;
+ if (child[RIGHT])
+ child[RIGHT]->parent = newMe;
+ }
+ newMe->balance = balance;
+ if (racine == this)
+ racine = newMe;
+ }
+ else if (child[LEFT])
+ {
+ startNode = parent;
+ diff = 0;
+ if (parent)
+ {
+ if (this == parent->child[LEFT])
+ diff = -1;
+ if (this == parent->child[RIGHT])
+ diff = 1;
+ }
+ if (parent)
+ {
+ if (parent->child[LEFT] == this)
+ parent->child[LEFT] = child[LEFT];
+ if (parent->child[RIGHT] == this)
+ parent->child[RIGHT] = child[LEFT];
+ }
+ if (child[LEFT]->parent == this)
+ child[LEFT]->parent = parent;
+ if (racine == this)
+ racine = child[LEFT];
+ }
+ else if (child[RIGHT])
+ {
+ startNode = parent;
+ diff = 0;
+ if (parent)
+ {
+ if (this == parent->child[LEFT])
+ diff = -1;
+ if (this == parent->child[RIGHT])
+ diff = 1;
+ }
+ if (parent)
+ {
+ if (parent->child[LEFT] == this)
+ parent->child[LEFT] = child[RIGHT];
+ if (parent->child[RIGHT] == this)
+ parent->child[RIGHT] = child[RIGHT];
+ }
+ if (child[RIGHT]->parent == this)
+ child[RIGHT]->parent = parent;
+ if (racine == this)
+ racine = child[RIGHT];
+ }
+ else
+ {
+ startNode = parent;
+ diff = 0;
+ if (parent)
+ {
+ if (this == parent->child[LEFT])
+ diff = -1;
+ if (this == parent->child[RIGHT])
+ diff = 1;
+ }
+ if (parent)
+ {
+ if (parent->child[LEFT] == this)
+ parent->child[LEFT] = nullptr;
+ if (parent->child[RIGHT] == this)
+ parent->child[RIGHT] = nullptr;
+ }
+ if (racine == this)
+ racine = nullptr;
+ }
+ parent = child[RIGHT] = child[LEFT] = nullptr;
+ balance = 0;
+ return avl_no_err;
+}
+
+/*
+ * insertion
+ */
+int
+AVLTree::Insert (AVLTree * &racine, int insertType, AVLTree * insertL,
+ AVLTree * insertR, bool rebalance)
+{
+ int res = Insert (racine, insertType, insertL, insertR);
+ if (res == avl_no_err && rebalance)
+ res = RestoreBalances ((AVLTree *) nullptr, racine);
+ return res;
+}
+
+int
+AVLTree::Insert (AVLTree * &racine, int insertType, AVLTree * insertL,
+ AVLTree * insertR)
+{
+ if (racine == nullptr)
+ {
+ racine = this;
+ return avl_no_err;
+ }
+ else
+ {
+ if (insertType == not_found)
+ {
+// cout << "pb avec l'arbre de raster\n";
+ return avl_ins_err;
+ }
+ else if (insertType == found_on_left)
+ {
+ if (insertR == nullptr || insertR->child[LEFT])
+ {
+// cout << "ngou?\n";
+ return avl_ins_err;
+ }
+ insertR->child[LEFT] = this;
+ parent = insertR;
+ insertOn(LEFT, insertR);
+ }
+ else if (insertType == found_on_right)
+ {
+ if (insertL == nullptr || insertL->child[RIGHT])
+ {
+// cout << "ngou?\n";
+ return avl_ins_err;
+ }
+ insertL->child[RIGHT] = this;
+ parent = insertL;
+ insertOn(RIGHT, insertL);
+ }
+ else if (insertType == found_between)
+ {
+ if (insertR == nullptr || insertL == nullptr
+ || (insertR->child[LEFT] != nullptr && insertL->child[RIGHT] != nullptr))
+ {
+// cout << "ngou?\n";
+ return avl_ins_err;
+ }
+ if (insertR->child[LEFT] == nullptr)
+ {
+ insertR->child[LEFT] = this;
+ parent = insertR;
+ }
+ else if (insertL->child[RIGHT] == nullptr)
+ {
+ insertL->child[RIGHT] = this;
+ parent = insertL;
+ }
+ insertBetween (insertL, insertR);
+ }
+ else if (insertType == found_exact)
+ {
+ if (insertL == nullptr)
+ {
+// cout << "ngou?\n";
+ return avl_ins_err;
+ }
+ // et on insere
+
+ if (insertL->child[RIGHT])
+ {
+ insertL = insertL->child[RIGHT]->leafFromParent(insertL, LEFT);
+ if (insertL->child[LEFT])
+ {
+// cout << "ngou?\n";
+ return avl_ins_err;
+ }
+ insertL->child[LEFT] = this;
+ this->parent = insertL;
+ insertBetween (insertL->elem[LEFT], insertL);
+ }
+ else
+ {
+ insertL->child[RIGHT] = this;
+ parent = insertL;
+ insertBetween (insertL, insertL->elem[RIGHT]);
+ }
+ }
+ else
+ {
+ // cout << "code incorrect\n";
+ return avl_ins_err;
+ }
+ }
+ return avl_no_err;
+}
+
+void
+AVLTree::Relocate (AVLTree * to)
+{
+ if (elem[LEFT])
+ elem[LEFT]->elem[RIGHT] = to;
+ if (elem[RIGHT])
+ elem[RIGHT]->elem[LEFT] = to;
+ to->elem[LEFT] = elem[LEFT];
+ to->elem[RIGHT] = elem[RIGHT];
+
+ if (parent)
+ {
+ if (parent->child[LEFT] == this)
+ parent->child[LEFT] = to;
+ if (parent->child[RIGHT] == this)
+ parent->child[RIGHT] = to;
+ }
+ if (child[RIGHT])
+ {
+ child[RIGHT]->parent = to;
+ }
+ if (child[LEFT])
+ {
+ child[LEFT]->parent = to;
+ }
+ to->parent = parent;
+ to->child[RIGHT] = child[RIGHT];
+ to->child[LEFT] = child[LEFT];
+}
+
+
+void AVLTree::insertOn(Side s, AVLTree *of)
+{
+ elem[1 - s] = of;
+ if (of)
+ of->elem[s] = this;
+}
+
+void AVLTree::insertBetween(AVLTree *l, AVLTree *r)
+{
+ if (l)
+ l->elem[RIGHT] = this;
+ if (r)
+ r->elem[LEFT] = this;
+ elem[LEFT] = l;
+ elem[RIGHT] = r;
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/livarot/AVL.h b/src/livarot/AVL.h
new file mode 100644
index 0000000..5e0856c
--- /dev/null
+++ b/src/livarot/AVL.h
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2014 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+/*
+ * AVL.h
+ * nlivarot
+ *
+ * Created by fred on Mon Jun 16 2003.
+ *
+ */
+
+#ifndef my_avl
+#define my_avl
+
+#include <cstdlib>
+#include "LivarotDefs.h"
+
+/*
+ * base class providing AVL tree functionnality, that is binary balanced tree
+ * there is no Find() function because the class only deal with topological info
+ * subclasses of this class have to implement a Find(), and most certainly to
+ * override the Insert() function
+ */
+
+class AVLTree
+{
+public:
+
+ AVLTree *elem[2];
+
+ // left most node (ie, with smallest key) in the subtree of this node
+ AVLTree *Leftmost();
+
+protected:
+
+ AVLTree *child[2];
+
+ AVLTree();
+ virtual ~AVLTree();
+
+ // constructor/destructor meant to be called for an array of AVLTree created by malloc
+ void MakeNew();
+ void MakeDelete();
+
+ // insertion of the present node in the tree
+ // insertType is the insertion type (defined in LivarotDefs.h: not_found, found_exact, found_on_left, etc)
+ // insertL is the node in the tree that is immediatly before the current one, NULL is the present node goes to the
+ // leftmost position. if insertType == found_exact, insertL should be the node with ak key
+ // equal to that of the present node
+ int Insert(AVLTree * &racine, int insertType, AVLTree *insertL,
+ AVLTree * insertR, bool rebalance);
+
+ // called when this node is relocated to a new position in memory, to update pointers to him
+ void Relocate(AVLTree *to);
+
+ // removal of the present element racine is the tree's root; it's a reference because if the
+ // node is the root, removal of the node will change the root
+ // rebalance==true if rebalancing is needed
+ int Remove(AVLTree * &racine, bool rebalance = true);
+
+private:
+
+ AVLTree *parent;
+
+ int balance;
+
+ // insertion gruntwork.
+ int Insert(AVLTree * &racine, int insertType, AVLTree *insertL, AVLTree *insertR);
+
+ // rebalancing functions. both are recursive, but the depth of the trees we'll use should not be a problem
+ // this one is for rebalancing after insertions
+ int RestoreBalances(AVLTree *from, AVLTree * &racine);
+ // this one is for removals
+ int RestoreBalances(int diff, AVLTree * &racine);
+
+ // startNode is the node where the rebalancing starts; rebalancing then moves up the tree to the root
+ // diff is the change in "subtree height", as needed for the rebalancing
+ // racine is the reference to the root, since rebalancing can change it too
+ int Remove(AVLTree * &racine, AVLTree * &startNode, int &diff);
+
+ void insertOn(Side s, AVLTree *of);
+ void insertBetween(AVLTree *l, AVLTree *r);
+ AVLTree *leaf(AVLTree *from, Side s);
+ AVLTree *leafFromParent(AVLTree *from, Side s);
+};
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/livarot/AlphaLigne.cpp b/src/livarot/AlphaLigne.cpp
new file mode 100644
index 0000000..7ae72c1
--- /dev/null
+++ b/src/livarot/AlphaLigne.cpp
@@ -0,0 +1,308 @@
+// 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 "AlphaLigne.h"
+
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <glib.h>
+
+AlphaLigne::AlphaLigne(int iMin,int iMax)
+{
+ min=iMin;
+ max=iMax;
+ if ( max < min+1 ) max=min+1;
+ steps=nullptr;
+ nbStep=maxStep=0;
+ before.x=min-1;
+ before.delta=0;
+ after.x=max+1;
+ after.delta=0;
+}
+AlphaLigne::~AlphaLigne()
+{
+ g_free(steps);
+ steps=nullptr;
+ nbStep=maxStep=0;
+}
+void AlphaLigne::Affiche()
+{
+ printf("%i steps\n",nbStep);
+ for (int i=0;i<nbStep;i++) {
+ printf("(%i %f) ",steps[i].x,steps[i].delta); // localization ok
+ }
+ printf("\n");
+}
+
+
+void AlphaLigne::Reset()
+{
+ // reset to empty line
+ // doesn't deallocate the steps array, to minimize memory operations
+ curMin=max;
+ curMax=min;
+ nbStep=0;
+ before.x=min-1;
+ before.delta=0;
+ after.x=max+1;
+ after.delta=0;
+}
+int AlphaLigne::AddBord(float spos,float sval,float epos,float eval,float tPente)
+{
+// printf("%f %f -> %f %f / %f\n",spos,sval,epos,eval,tPente);
+ if ( sval == eval ) return 0;
+ // compute the footprint of [spos,epos] on the line of pixels
+ float curStF=floor(spos);
+ float curEnF=floor(epos);
+ int curSt=(int)curStF;
+ int curEn=(int)curEnF;
+
+ // update curMin and curMax
+ if ( curSt > max ) {
+ // we're on the right of the visible portion of the line: bail out!
+ if ( eval < sval ) curMax=max;
+ return 0;
+ }
+ if ( curSt < curMin ) curMin=curSt;
+ if ( ceil(epos) > curMax ) curMax=(int)ceil(epos);
+
+ // clamp the changed portion to [min,max], no need for bigger
+ if ( curMax > max ) curMax=max;
+ if ( curMin < min ) curMin=min;
+
+ // total amount of change in pixel coverage from before the right to after the run
+ float needed=eval-sval;
+ float needC=/*(int)ldexpf(*/needed/*,24)*/;
+
+ if ( curEn < min ) {
+ // the added portion is entirely on the left, so we only have to change the initial coverage for the line
+ before.delta+=needC;
+ return 0;
+ }
+
+ // add the steps
+ // the pixels from [curSt..curEn] (included) intersect with [spos;epos]
+ // since we're dealing with delta in the coverage, there is also a curEn+1 delta, since the curEn pixel intersect
+ // with [spos;epos] and thus has some delta with respect to its next pixel
+ // lots of different cases... ugly
+ if ( curSt == curEn ) {
+ if ( curSt+1 < min ) {
+ before.delta+=needC;
+ } else {
+ if ( nbStep+2 >= maxStep ) {
+ maxStep=2*nbStep+2;
+ steps=(alpha_step*)g_realloc(steps,maxStep*sizeof(alpha_step));
+ }
+ float stC=/*(int)ldexpf(*/(eval-sval)*(0.5*(epos-spos)+curStF+1-epos)/*,24)*/;
+ steps[nbStep].x=curSt;
+ steps[nbStep].delta=stC;
+ nbStep++;
+ steps[nbStep].x=curSt+1;
+ steps[nbStep].delta=needC-stC; // au final, on a toujours le bon delta, meme avec une arete completement verticale
+ nbStep++;
+ }
+ } else if ( curEn == curSt+1 ) {
+ if ( curSt+2 < min ) {
+ before.delta+=needC;
+ } else {
+ if ( nbStep+3 >= maxStep ) {
+ maxStep=2*nbStep+3;
+ steps=(alpha_step*)g_realloc(steps,maxStep*sizeof(alpha_step));
+ }
+ float stC=/*(int)ldexpf(*/0.5*tPente*(curEnF-spos)*(curEnF-spos)/*,24)*/;
+ float enC=/*(int)ldexpf(*/tPente-0.5*tPente*((spos-curStF)*(spos-curStF)+(curEnF+1.0-epos)*(curEnF+1.0-epos))/*,24)*/;
+ steps[nbStep].x=curSt;
+ steps[nbStep].delta=stC;
+ nbStep++;
+ steps[nbStep].x=curEn;
+ steps[nbStep].delta=enC;
+ nbStep++;
+ steps[nbStep].x=curEn+1;
+ steps[nbStep].delta=needC-stC-enC;
+ nbStep++;
+ }
+ } else {
+ float stC=/*(int)ldexpf(*/0.5*tPente*(curStF+1-spos)*(curStF+1-spos)/*,24)*/;
+ float stFC=/*(int)ldexpf(*/tPente-0.5*tPente*(spos-curStF)*(spos-curStF)/*,24)*/;
+ float enC=/*(int)ldexpf(*/tPente-0.5*tPente*(curEnF+1.0-epos)*(curEnF+1.0-epos)/*,24)*/;
+ float miC=/*(int)ldexpf(*/tPente/*,24)*/;
+ if ( curSt < min ) {
+ if ( curEn > max ) {
+ if ( nbStep+(max-min) >= maxStep ) {
+ maxStep=2*nbStep+(max-min);
+ steps=(alpha_step*)g_realloc(steps,maxStep*sizeof(alpha_step));
+ }
+ float bfd=min-curSt-1;
+ bfd*=miC;
+ before.delta+=stC+bfd;
+ for (int i=min;i<max;i++) {
+ steps[nbStep].x=i;
+ steps[nbStep].delta=miC;
+ nbStep++;
+ }
+ } else {
+ if ( nbStep+(curEn-min)+2 >= maxStep ) {
+ maxStep=2*nbStep+(curEn-min)+2;
+ steps=(alpha_step*)g_realloc(steps,maxStep*sizeof(alpha_step));
+ }
+ float bfd=min-curSt-1;
+ bfd*=miC;
+ before.delta+=stC+bfd;
+ for (int i=min;i<curEn;i++) {
+ steps[nbStep].x=i;
+ steps[nbStep].delta=miC;
+ nbStep++;
+ }
+ steps[nbStep].x=curEn;
+ steps[nbStep].delta=enC;
+ nbStep++;
+ steps[nbStep].x=curEn+1;
+ steps[nbStep].delta=needC-stC-stFC-enC-(curEn-curSt-2)*miC;
+ nbStep++;
+ }
+ } else {
+ if ( curEn > max ) {
+ if ( nbStep+3+(max-curSt) >= maxStep ) {
+ maxStep=2*nbStep+3+(curEn-curSt);
+ steps=(alpha_step*)g_realloc(steps,maxStep*sizeof(alpha_step));
+ }
+ steps[nbStep].x=curSt;
+ steps[nbStep].delta=stC;
+ nbStep++;
+ steps[nbStep].x=curSt+1;
+ steps[nbStep].delta=stFC;
+ nbStep++;
+ for (int i=curSt+2;i<max;i++) {
+ steps[nbStep].x=i;
+ steps[nbStep].delta=miC;
+ nbStep++;
+ }
+ } else {
+ if ( nbStep+3+(curEn-curSt) >= maxStep ) {
+ maxStep=2*nbStep+3+(curEn-curSt);
+ steps=(alpha_step*)g_realloc(steps,maxStep*sizeof(alpha_step));
+ }
+ steps[nbStep].x=curSt;
+ steps[nbStep].delta=stC;
+ nbStep++;
+ steps[nbStep].x=curSt+1;
+ steps[nbStep].delta=stFC;
+ nbStep++;
+ for (int i=curSt+2;i<curEn;i++) {
+ steps[nbStep].x=i;
+ steps[nbStep].delta=miC;
+ nbStep++;
+ }
+ steps[nbStep].x=curEn;
+ steps[nbStep].delta=enC;
+ nbStep++;
+ steps[nbStep].x=curEn+1;
+ steps[nbStep].delta=needC-stC-stFC-enC-(curEn-curSt-2)*miC;
+ nbStep++;
+ }
+ }
+ }
+
+ return 0;
+}
+int AlphaLigne::AddBord(float spos,float sval,float epos,float eval)
+{
+ // pas de pente dans ce cas; on ajoute le delta au premier pixel
+ float tPente=(eval-sval);
+
+ float curStF=floor(spos);
+ float curEnF=floor(epos);
+ int curSt=(int)curStF;
+ int curEn=(int)curEnF;
+
+ if ( curSt > max ) {
+ if ( eval < sval ) curMax=max;
+ return 0; // en dehors des limites (attention a ne pas faire ca avec curEn)
+ }
+ if ( curEn < min ) {
+ before.delta+=eval-sval;
+ return 0; // en dehors des limites (attention a ne pas faire ca avec curEn)
+ }
+
+ if ( curSt < curMin ) curMin=curSt;
+// int curEn=(int)curEnF;
+ if ( ceil(epos) > curMax-1 ) curMax=1+(int)ceil(epos);
+ if ( curSt < min ) {
+ before.delta+=eval-sval;
+ } else {
+ AddRun(curSt,/*(int)ldexpf(*/(((float)(curSt+1))-spos)*tPente/*,24)*/);
+ AddRun(curSt+1,/*(int)ldexpf(*/(spos-((float)(curSt)))*tPente/*,24)*/);
+ }
+ return 0;
+}
+
+void AlphaLigne::Flatten()
+{
+ // just sort
+ if ( nbStep > 0 ) qsort(steps,nbStep,sizeof(alpha_step),CmpStep);
+}
+void AlphaLigne::AddRun(int st,float pente)
+{
+ if ( nbStep >= maxStep ) {
+ maxStep=2*nbStep+1;
+ steps=(alpha_step*)g_realloc(steps,maxStep*sizeof(alpha_step));
+ }
+ int nStep=nbStep++;
+ steps[nStep].x=st;
+ steps[nStep].delta=pente;
+}
+
+void AlphaLigne::Raster(raster_info &dest,void* color,RasterInRunFunc worker)
+{
+ // start by checking if there are actually pixels in need of rasterization
+ if ( curMax <= curMin ) return;
+ if ( dest.endPix <= curMin || dest.startPix >= curMax ) return;
+
+ int nMin=curMin,nMax=curMax;
+ float alpSum=before.delta; // alpSum will be the pixel coverage value, so we start at before.delta
+ int curStep=0;
+
+ // first add all the deltas up to the first pixel in need of rasterization
+ while ( curStep < nbStep && steps[curStep].x < nMin ) {
+ alpSum+=steps[curStep].delta;
+ curStep++;
+ }
+ // just in case, if the line bounds are greater than the buffer bounds.
+ if ( nMin < dest.startPix ) {
+ for (;( curStep < nbStep && steps[curStep].x < dest.startPix) ;curStep++) alpSum+=steps[curStep].delta;
+ nMin=dest.startPix;
+ }
+ if ( nMax > dest.endPix ) nMax=dest.endPix;
+
+ // raster!
+ int curPos=dest.startPix;
+ for (;curStep<nbStep;curStep++) {
+ if ( alpSum > 0 && steps[curStep].x > curPos ) {
+ // we're going to change the pixel position curPos, and alpSum is > 0: rasterization needed from
+ // the last position (curPos) up to the pixel we're moving to (steps[curStep].x)
+ int nst=curPos,nen=steps[curStep].x;
+//Buffer::RasterRun(dest,color,nst,alpSum,nen,alpSum);
+ (worker)(dest,color,nst,alpSum,nen,alpSum);
+ }
+ // add coverage deltas
+ alpSum+=steps[curStep].delta;
+ curPos=steps[curStep].x;
+ if ( curPos >= nMax ) break;
+ }
+ // if we ended the line with alpSum > 0, we need to raster from curPos to the right edge
+ if ( alpSum > 0 && curPos < nMax ) {
+ int nst=curPos,nen=max;
+ (worker)(dest,color,nst,alpSum,nen,alpSum);
+//Buffer::RasterRun(dest,color,nst,alpSum,nen,alpSum);
+ }
+}
diff --git a/src/livarot/AlphaLigne.h b/src/livarot/AlphaLigne.h
new file mode 100644
index 0000000..a192e1c
--- /dev/null
+++ b/src/livarot/AlphaLigne.h
@@ -0,0 +1,87 @@
+// 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.
+ */
+
+#ifndef my_alpha_ligne
+#define my_alpha_ligne
+
+#include "LivarotDefs.h"
+
+/*
+ * pixel coverage of a line, libart style: each pixel coverage is obtained from the coverage of the previous one by
+ * adding a delta given by a step. the goal is to have only a limited number of positions where the delta != 0, so that
+ * you only have to store a limited number of steps.
+ */
+
+// a step
+struct alpha_step {
+ int x; // position
+ float delta; // increase or decrease in pixel coverage with respect to the coverage of the previous pixel
+};
+
+
+class AlphaLigne {
+public:
+ // bounds of the line
+ // necessary since the visible portion of the canvas is bounded, and you need to compute
+ // the value of the pixel "just before the visible portion of the line"
+ int min,max;
+ int length;
+
+ // before is the step containing the delta relative to a pixel infinitely far on the left of the line
+ // thus the initial pixel coverage is before.delta
+ alpha_step before,after;
+ // array of steps
+ int nbStep,maxStep;
+ alpha_step* steps;
+
+ // bounds of the portion of the line that has received some coverage
+ int curMin,curMax;
+
+ // iMin and iMax are the bounds of the visible portion of the line
+ AlphaLigne(int iMin,int iMax);
+ virtual ~AlphaLigne();
+
+ // empties the line
+ void Reset();
+
+ // add some coverage.
+ // pente is (eval-sval)/(epos-spos), because you can compute it once per edge, and thus spare the
+ // CPU some potentially costly divisions
+ int AddBord(float spos,float sval,float epos,float eval,float iPente);
+ // version where you don't have the pente parameter
+ int AddBord(float spos,float sval,float epos,float eval);
+
+ // sorts the steps in increasing order. needed before you raster the line
+ void Flatten();
+
+ // debug dump of the steps
+ void Affiche();
+
+ // private
+ void AddRun(int st,float pente);
+
+ // raster the line in the buffer given in "dest", with the rasterization primitive worker
+ // worker() is given the color parameter each time it is called. the type of the function is
+ // defined in LivarotDefs.h
+ void Raster(raster_info &dest,void* color,RasterInRunFunc worker);
+
+ // also private. that's the comparison function given to qsort()
+ static int CmpStep(const void * p1, const void * p2) {
+ alpha_step* d1=(alpha_step*)p1;
+ alpha_step* d2=(alpha_step*)p2;
+ return d1->x - d2->x ;
+ };
+};
+
+
+#endif
+
diff --git a/src/livarot/BitLigne.cpp b/src/livarot/BitLigne.cpp
new file mode 100644
index 0000000..2e44359
--- /dev/null
+++ b/src/livarot/BitLigne.cpp
@@ -0,0 +1,180 @@
+// 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 "BitLigne.h"
+
+#include <cmath>
+#include <cstring>
+#include <cstdlib>
+#include <string>
+#include <cmath>
+#include <cstdio>
+#include <glib.h>
+
+BitLigne::BitLigne(int ist,int ien,float iScale)
+{
+ scale=iScale;
+ invScale=1/iScale;
+ st=ist;
+ en=ien;
+ if ( en <= st ) en=st+1;
+ stBit=(int)floor(((float)st)*invScale); // round to pixel boundaries in the canvas
+ enBit=(int)ceil(((float)en)*invScale);
+ int nbBit=enBit-stBit;
+ if ( nbBit&31 ) {
+ nbInt=nbBit/32+1;
+ } else {
+ nbInt=nbBit/32;
+ }
+ nbInt+=1;
+ fullB=(uint32_t*)g_malloc(nbInt*sizeof(uint32_t));
+ partB=(uint32_t*)g_malloc(nbInt*sizeof(uint32_t));
+
+ curMin=en;
+ curMax=st;
+}
+BitLigne::~BitLigne()
+{
+ g_free(fullB);
+ g_free(partB);
+}
+
+void BitLigne::Reset()
+{
+ curMin=en;
+ curMax=st+1;
+ memset(fullB,0,nbInt*sizeof(uint32_t));
+ memset(partB,0,nbInt*sizeof(uint32_t));
+}
+int BitLigne::AddBord(float spos,float epos,bool full)
+{
+ if ( spos >= epos ) return 0;
+
+ // separation of full and not entirely full bits is a bit useless
+ // the goal is to obtain a set of bits that are "on the edges" of the polygon, so that their coverage
+ // will be 1/2 on the average. in practice it's useless for anything but the even-odd fill rule
+ int ffBit,lfBit; // first and last bit of the portion of the line that is entirely covered
+ ffBit=(int)(ceil(invScale*spos));
+ lfBit=(int)(floor(invScale*epos));
+ int fpBit,lpBit; // first and last bit of the portion of the line that is not entirely but partially covered
+ fpBit=(int)(floor(invScale*spos));
+ lpBit=(int)(ceil(invScale*epos));
+
+ // update curMin and curMax to reflect the start and end pixel that need to be updated on the canvas
+ if ( floor(spos) < curMin ) curMin=(int)floor(spos);
+ if ( ceil(epos) > curMax ) curMax=(int)ceil(epos);
+
+ // clamp to the line
+ if ( ffBit < stBit ) ffBit=stBit;
+ if ( ffBit > enBit ) ffBit=enBit;
+ if ( lfBit < stBit ) lfBit=stBit;
+ if ( lfBit > enBit ) lfBit=enBit;
+ if ( fpBit < stBit ) fpBit=stBit;
+ if ( fpBit > enBit ) fpBit=enBit;
+ if ( lpBit < stBit ) lpBit=stBit;
+ if ( lpBit > enBit ) lpBit=enBit;
+
+ // offset to get actual bit position in the array
+ ffBit-=stBit;
+ lfBit-=stBit;
+ fpBit-=stBit;
+ lpBit-=stBit;
+
+ // get the end and start indices of the elements of fullB and partB that will receives coverage
+ int ffPos=ffBit>>5;
+ int lfPos=lfBit>>5;
+ int fpPos=fpBit>>5;
+ int lpPos=lpBit>>5;
+ // get bit numbers in the last and first changed elements of the fullB and partB arrays
+ int ffRem=ffBit&31;
+ int lfRem=lfBit&31;
+ int fpRem=fpBit&31;
+ int lpRem=lpBit&31;
+ // add the coverage
+ // note that the "full" bits are always a subset of the "not empty" bits, ie of the partial bits
+ // the function is a bit lame: since there is at most one bit that is partial but not full, or no full bit,
+ // it does 2 times the optimal amount of work when the coverage is full. but i'm too lazy to change that...
+ if ( fpPos == lpPos ) { // only one element of the arrays is modified
+ // compute the vector of changed bits in the element
+ uint32_t add=0xFFFFFFFF;
+ if ( lpRem < 32 ) {add>>=32-lpRem;add<<=32-lpRem; }
+ if ( lpRem <= 0 ) add=0;
+ if ( fpRem > 0) {add<<=fpRem;add>>=fpRem;}
+ // and put it in the line
+ fullB[fpPos]&=~(add); // partial is exclusive from full, so partial bits are removed from fullB
+ partB[fpPos]|=add; // and added to partB
+ if ( full ) { // if the coverage is full, add the vector of full bits
+ if ( ffBit <= lfBit ) {
+ add=0xFFFFFFFF;
+ if ( lfRem < 32 ) {add>>=32-lfRem;add<<=32-lfRem;}
+ if ( lfRem <= 0 ) add=0;
+ if ( ffRem > 0 ) {add<<=ffRem;add>>=ffRem;}
+ fullB[ffPos]|=add;
+ partB[ffPos]&=~(add);
+ }
+ }
+ } else {
+ // first and last elements are differents, so add what appropriate to each
+ uint32_t add=0xFFFFFFFF;
+ if ( fpRem > 0 ) {add<<=fpRem;add>>=fpRem;}
+ fullB[fpPos]&=~(add);
+ partB[fpPos]|=add;
+
+ add=0xFFFFFFFF;
+ if ( lpRem < 32 ) {add>>=32-lpRem;add<<=32-lpRem;}
+ if ( lpRem <= 0 ) add=0;
+ fullB[lpPos]&=~(add);
+ partB[lpPos]|=add;
+
+ // and fill what's in between with partial bits
+ if ( lpPos > fpPos+1 ) memset(fullB+(fpPos+1),0x00,(lpPos-fpPos-1)*sizeof(uint32_t));
+ if ( lpPos > fpPos+1 ) memset(partB+(fpPos+1),0xFF,(lpPos-fpPos-1)*sizeof(uint32_t));
+
+ if ( full ) { // is the coverage is full, do your magic
+ if ( ffBit <= lfBit ) {
+ if ( ffPos == lfPos ) {
+ add=0xFFFFFFFF;
+ if ( lfRem < 32 ) {add>>=32-lfRem;add<<=32-lfRem;}
+ if ( lfRem <= 0 ) add=0;
+ if ( ffRem > 0 ) {add<<=ffRem;add>>=ffRem;}
+ fullB[ffPos]|=add;
+ partB[ffPos]&=~(add);
+ } else {
+ add=0xFFFFFFFF;
+ if ( ffRem > 0 ) {add<<=ffRem;add>>=ffRem;}
+ fullB[ffPos]|=add;
+ partB[ffPos]&=~add;
+
+ add=0xFFFFFFFF;
+ if ( lfRem < 32 ) {add>>=32-lfRem;add<<=32-lfRem;}
+ if ( lfRem <= 0 ) add=0;
+ fullB[lfPos]|=add;
+ partB[lfPos]&=~add;
+
+ if ( lfPos > ffPos+1 ) memset(fullB+(ffPos+1),0xFF,(lfPos-ffPos-1)*sizeof(uint32_t));
+ if ( lfPos > ffPos+1 ) memset(partB+(ffPos+1),0x00,(lfPos-ffPos-1)*sizeof(uint32_t));
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+void BitLigne::Affiche()
+{
+ for (int i=0;i<nbInt;i++) printf(" %.8x",fullB[i]);
+ printf("\n");
+ for (int i=0;i<nbInt;i++) printf(" %.8x",partB[i]);
+ printf("\n\n");
+}
+
diff --git a/src/livarot/BitLigne.h b/src/livarot/BitLigne.h
new file mode 100644
index 0000000..7a7fcbe
--- /dev/null
+++ b/src/livarot/BitLigne.h
@@ -0,0 +1,64 @@
+// 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.
+ */
+
+#ifndef my_bit_ligne
+#define my_bit_ligne
+
+#include "LivarotDefs.h"
+
+/*
+ * a line of bits used for rasterizations of polygons
+ * the Scan() and QuickScan() functions fill the line with bits; after that you can use the Copy() function
+ * of the IntLigne class to have a set of pixel coverage runs
+ */
+
+class BitLigne {
+public:
+ // start and end pixels of the line
+ int st,en;
+ // start and end bits of the line
+ int stBit,enBit;
+ // size of the fullB and partB arrays
+ int nbInt;
+ // arrays of uint32_t used to store the bits
+ // bits of fullB mean "this pixel/bit is entirely covered"
+ // bits of partB mean "this pixel/bit is not entirely covered" (a better use would be: "this pixel is at least partially covered)
+ // so it's in fact a triage mask
+ uint32_t* fullB;
+ uint32_t* partB;
+
+ // when adding bits, these 2 values are updated to reflect which portion of the line has received coverage
+ int curMin,curMax;
+ // invScale is: canvas -> bit in the line
+ // scale is: bit -> canvas, ie the size (width) of a bit
+ float scale,invScale;
+
+ BitLigne(int ist,int ien,float iScale=0.25); // default scale is 1/4 for 4x4 supersampling
+ virtual ~BitLigne();
+
+ // reset the line to full empty
+ void Reset();
+
+ // put coverage from spos to epos (in canvas coordinates)
+ // full==true means that the bits from (fractional) position spos to epos are entirely covered
+ // full==false means the bits are not entirely covered, ie this is an edge
+ // see the Scan() and AvanceEdge() functions to see the difference
+ int AddBord(float spos,float epos,bool full);
+
+ // debug dump
+ void Affiche();
+
+};
+
+#endif
+
+
diff --git a/src/livarot/CMakeLists.txt b/src/livarot/CMakeLists.txt
new file mode 100644
index 0000000..185ae8a
--- /dev/null
+++ b/src/livarot/CMakeLists.txt
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+set(livarot_SRC
+ AlphaLigne.cpp
+ AVL.cpp
+ BitLigne.cpp
+ float-line.cpp
+ int-line.cpp
+ PathConversion.cpp
+ Path.cpp
+ PathCutting.cpp
+ path-description.cpp
+ PathOutline.cpp
+ PathSimplify.cpp
+ PathStroke.cpp
+ Shape.cpp
+ ShapeDraw.cpp
+ ShapeMisc.cpp
+ ShapeRaster.cpp
+ ShapeSweep.cpp
+ sweep-event.cpp
+ sweep-tree.cpp
+ sweep-tree-list.cpp
+
+
+ # -------
+ # Headers
+ AVL.h
+ AlphaLigne.h
+ BitLigne.h
+ Livarot.h
+ LivarotDefs.h
+ Path.h
+ Shape.h
+ float-line.h
+ int-line.h
+ path-description.h
+ sweep-event-queue.h
+ sweep-event.h
+ sweep-tree-list.h
+ sweep-tree.h
+)
+
+add_inkscape_lib(livarot_LIB "${livarot_SRC}")
diff --git a/src/livarot/Livarot.h b/src/livarot/Livarot.h
new file mode 100644
index 0000000..80cc6ba
--- /dev/null
+++ b/src/livarot/Livarot.h
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2010 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+/*
+ * Livarot.h
+ * nlivarot
+ *
+ * Created by fred on Sun Jul 27 2003.
+ *
+ */
+
+#include "LivarotDefs.h"
+
+#include "Shape.h"
+#include "Path.h"
+#include "Buffer.h"
+
+#include "Ligne.h"
+#include "AlphaLigne.h"
+#include "BitLigne.h"
+
+#include "Bounding.h"
+#include "Region.h"
+
+#include "VoronoiGraph.h"
+#include "VoronoiConstr.h"
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/livarot/LivarotDefs.h b/src/livarot/LivarotDefs.h
new file mode 100644
index 0000000..1cabb74
--- /dev/null
+++ b/src/livarot/LivarotDefs.h
@@ -0,0 +1,158 @@
+// 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.
+ */
+
+#ifndef my_defs
+#define my_defs
+
+#include <cstdint>
+
+// error codes (mostly obsolete)
+enum
+{
+ avl_no_err = 0, // 0 is the error code for "everything OK"
+ avl_bal_err = 1,
+ avl_rm_err = 2,
+ avl_ins_err = 3,
+ shape_euler_err = 4, // computations result in a non-eulerian graph, thus the function cannot do a proper polygon
+ // despite the rounding sheme, this still happen with uber-complex graphs
+ // note that coordinates are stored in double => double precision for the computation is not even
+ // enough to get exact results (need quadruple precision, i think).
+ shape_input_err = 5, // the function was given an incorrect input (not a polygon, or not eulerian)
+ shape_nothing_to_do = 6 // the function had nothing to do (zero offset, etc)
+};
+
+// return codes for the find function in the AVL tree (private)
+enum
+{
+ not_found = 0,
+ found_exact = 1,
+ found_on_left = 2,
+ found_on_right = 3,
+ found_between = 4
+};
+
+// types of cap for stroking polylines
+enum butt_typ
+{
+ butt_straight, // straight line
+ butt_square, // half square
+ butt_round, // half circle
+ butt_pointy // a little pointy hat
+};
+// types of joins for stroking paths
+enum join_typ
+{
+ join_straight, // a straight line
+ join_round, // arc of circle (in fact, one or two quadratic bezier curve chunks)
+ join_pointy // a miter join (uses the miter parameter)
+};
+typedef enum butt_typ ButtType;
+typedef enum join_typ JoinType;
+
+enum fill_typ
+{
+ fill_oddEven = 0,
+ fill_nonZero = 1,
+ fill_positive = 2,
+ fill_justDont = 3
+};
+typedef enum fill_typ FillRule;
+
+// stupid version of dashes: in dash x is plain, dash x+1 must be empty, so the gap field is extremely redundant
+struct one_dash
+{
+ bool gap;
+ double length;
+};
+
+// color definition structures for the rasterizations primitives (not present here)
+struct std_color
+{
+ uint32_t uCol;
+ uint16_t iColA, iColR, iColG, iColB;
+ double fColA, fColR, fColG, fColB;
+ uint32_t iColATab[256];
+};
+
+struct grad_stop
+{
+ double at;
+ double ca, cr, cg, cb;
+ double iSize;
+};
+
+// linear gradient for filling polygons
+struct lin_grad
+{
+ int type; // 0= gradient appears once
+ // 1= repeats itself start-end/start-end/start-end...
+ // 2= repeats itself start-end/end-start/start-end...
+ double u, v, w; // u*x+v*y+w = position in the gradient (clipped to [0;1])
+// double caa,car,cag,cab; // color at gradient position 0
+// double cba,cbr,cbg,cbb; // color at gradient position 1
+ int nbStop;
+ grad_stop stops[2];
+};
+
+// radial gradient (color is funciton of r^2, need to be corrected with a sqrt() to be r)
+struct rad_grad
+{
+ int type; // 0= gradient appears once
+ // 1= repeats itself start-end/start-end/start-end...
+ // 2= repeats itself start-end/end-start/start-end...
+ double mh, mv; // center
+ double rxx, rxy, ryx, ryy; // 1/radius
+ int nbStop;
+ grad_stop stops[2];
+};
+
+// functions types for an arbitrary filling shader
+typedef void (*InitColorFunc) (int ph, int pv, void *); // init for position ph,pv; the last parameter is a pointer
+ // on the gen_color structure
+typedef void (*NextPixelColorFunc) (void *); // go to next pixel and update the color
+typedef void (*NextLigneColorFunc) (void *); // go to next line (the h-coordinate must be the ph passed in
+ // the InitColorFunc)
+typedef void (*GotoPixelColorFunc) (int ph, void *); // move to h-coordinate ph
+typedef void (*GotoLigneColorFunc) (int pv, void *); // move to v-coordinate pv (the h-coordinate must be the ph passed
+ // in the InitColorFunc)
+
+// an arbitrary shader
+struct gen_color
+{
+ double colA, colR, colG, colB;
+ InitColorFunc iFunc;
+ NextPixelColorFunc npFunc;
+ NextLigneColorFunc nlFunc;
+ GotoPixelColorFunc gpFunc;
+ GotoLigneColorFunc glFunc;
+};
+
+// info for a run of pixel to fill
+struct raster_info {
+ int startPix,endPix; // start and end pixel from the polygon POV
+ int sth,stv; // coordinates for the first pixel in the run, in (possibly another) POV
+ uint32_t* buffer; // pointer to the first pixel in the run
+};
+typedef void (*RasterInRunFunc) (raster_info &dest,void *data,int nst,float vst,int nen,float ven); // init for position ph,pv; the last parameter is a pointer
+
+
+enum Side {
+ LEFT = 0,
+ RIGHT = 1
+};
+
+enum FirstOrLast {
+ FIRST = 0,
+ LAST = 1
+};
+
+#endif
diff --git a/src/livarot/Path.cpp b/src/livarot/Path.cpp
new file mode 100644
index 0000000..03e17db
--- /dev/null
+++ b/src/livarot/Path.cpp
@@ -0,0 +1,939 @@
+// 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 <glib.h>
+#include "Path.h"
+#include "livarot/path-description.h"
+
+/*
+ * manipulation of the path data: path description and polyline
+ * grunt work...
+ * at the end of this file, 2 utilitary functions to get the point and tangent to path associated with a (command no;abcissis)
+ */
+
+
+Path::Path()
+{
+ descr_flags = 0;
+ pending_bezier_cmd = -1;
+ pending_moveto_cmd = -1;
+
+ back = false;
+}
+
+Path::~Path()
+{
+ for (auto & i : descr_cmd) {
+ delete i;
+ }
+}
+
+// debug function do dump the path contents on stdout
+void Path::Affiche()
+{
+ std::cout << "path: " << descr_cmd.size() << " commands." << std::endl;
+ for (auto i : descr_cmd) {
+ i->dump(std::cout);
+ std::cout << std::endl;
+ }
+
+ std::cout << std::endl;
+}
+
+void Path::Reset()
+{
+ for (auto & i : descr_cmd) {
+ delete i;
+ }
+
+ descr_cmd.clear();
+ pending_bezier_cmd = -1;
+ pending_moveto_cmd = -1;
+ descr_flags = 0;
+}
+
+void Path::Copy(Path * who)
+{
+ ResetPoints();
+
+ for (auto & i : descr_cmd) {
+ delete i;
+ }
+
+ descr_cmd.clear();
+
+ for (auto i : who->descr_cmd)
+ {
+ descr_cmd.push_back(i->clone());
+ }
+}
+
+void Path::CloseSubpath()
+{
+ descr_flags &= ~(descr_doing_subpath);
+ pending_moveto_cmd = -1;
+}
+
+int Path::ForcePoint()
+{
+ if (descr_flags & descr_adding_bezier) {
+ EndBezierTo ();
+ }
+
+ if ( (descr_flags & descr_doing_subpath) == 0 ) {
+ return -1;
+ }
+
+ if (descr_cmd.empty()) {
+ return -1;
+ }
+
+ descr_cmd.push_back(new PathDescrForced);
+ return descr_cmd.size() - 1;
+}
+
+
+void Path::InsertForcePoint(int at)
+{
+ if ( at < 0 || at > int(descr_cmd.size()) ) {
+ return;
+ }
+
+ if ( at == int(descr_cmd.size()) ) {
+ ForcePoint();
+ return;
+ }
+
+ descr_cmd.insert(descr_cmd.begin() + at, new PathDescrForced);
+}
+
+int Path::Close()
+{
+ if ( descr_flags & descr_adding_bezier ) {
+ CancelBezier();
+ }
+ if ( descr_flags & descr_doing_subpath ) {
+ CloseSubpath();
+ } else {
+ // Nothing to close.
+ return -1;
+ }
+
+ descr_cmd.push_back(new PathDescrClose);
+
+ descr_flags &= ~(descr_doing_subpath);
+ pending_moveto_cmd = -1;
+
+ return descr_cmd.size() - 1;
+}
+
+int Path::MoveTo(Geom::Point const &iPt)
+{
+ if ( descr_flags & descr_adding_bezier ) {
+ EndBezierTo(iPt);
+ }
+ if ( descr_flags & descr_doing_subpath ) {
+ CloseSubpath();
+ }
+ pending_moveto_cmd = descr_cmd.size();
+
+ descr_cmd.push_back(new PathDescrMoveTo(iPt));
+
+ descr_flags |= descr_doing_subpath;
+ return descr_cmd.size() - 1;
+}
+
+void Path::InsertMoveTo(Geom::Point const &iPt, int at)
+{
+ if ( at < 0 || at > int(descr_cmd.size()) ) {
+ return;
+ }
+
+ if ( at == int(descr_cmd.size()) ) {
+ MoveTo(iPt);
+ return;
+ }
+
+ descr_cmd.insert(descr_cmd.begin() + at, new PathDescrMoveTo(iPt));
+}
+
+int Path::LineTo(Geom::Point const &iPt)
+{
+ if (descr_flags & descr_adding_bezier) {
+ EndBezierTo (iPt);
+ }
+ if (!( descr_flags & descr_doing_subpath )) {
+ return MoveTo (iPt);
+ }
+
+ descr_cmd.push_back(new PathDescrLineTo(iPt));
+ return descr_cmd.size() - 1;
+}
+
+void Path::InsertLineTo(Geom::Point const &iPt, int at)
+{
+ if ( at < 0 || at > int(descr_cmd.size()) ) {
+ return;
+ }
+
+ if ( at == int(descr_cmd.size()) ) {
+ LineTo(iPt);
+ return;
+ }
+
+ descr_cmd.insert(descr_cmd.begin() + at, new PathDescrLineTo(iPt));
+}
+
+int Path::CubicTo(Geom::Point const &iPt, Geom::Point const &iStD, Geom::Point const &iEnD)
+{
+ if (descr_flags & descr_adding_bezier) {
+ EndBezierTo(iPt);
+ }
+ if ( (descr_flags & descr_doing_subpath) == 0) {
+ return MoveTo (iPt);
+ }
+
+ descr_cmd.push_back(new PathDescrCubicTo(iPt, iStD, iEnD));
+ return descr_cmd.size() - 1;
+}
+
+
+void Path::InsertCubicTo(Geom::Point const &iPt, Geom::Point const &iStD, Geom::Point const &iEnD, int at)
+{
+ if ( at < 0 || at > int(descr_cmd.size()) ) {
+ return;
+ }
+
+ if ( at == int(descr_cmd.size()) ) {
+ CubicTo(iPt,iStD,iEnD);
+ return;
+ }
+
+ descr_cmd.insert(descr_cmd.begin() + at, new PathDescrCubicTo(iPt, iStD, iEnD));
+}
+
+int Path::ArcTo(Geom::Point const &iPt, double iRx, double iRy, double angle,
+ bool iLargeArc, bool iClockwise)
+{
+ if (descr_flags & descr_adding_bezier) {
+ EndBezierTo(iPt);
+ }
+ if ( (descr_flags & descr_doing_subpath) == 0 ) {
+ return MoveTo(iPt);
+ }
+
+ descr_cmd.push_back(new PathDescrArcTo(iPt, iRx, iRy, angle, iLargeArc, iClockwise));
+ return descr_cmd.size() - 1;
+}
+
+
+void Path::InsertArcTo(Geom::Point const &iPt, double iRx, double iRy, double angle,
+ bool iLargeArc, bool iClockwise, int at)
+{
+ if ( at < 0 || at > int(descr_cmd.size()) ) {
+ return;
+ }
+
+ if ( at == int(descr_cmd.size()) ) {
+ ArcTo(iPt, iRx, iRy, angle, iLargeArc, iClockwise);
+ return;
+ }
+
+ descr_cmd.insert(descr_cmd.begin() + at, new PathDescrArcTo(iPt, iRx, iRy,
+ angle, iLargeArc, iClockwise));
+}
+
+int Path::TempBezierTo()
+{
+ if (descr_flags & descr_adding_bezier) {
+ CancelBezier();
+ }
+ if ( (descr_flags & descr_doing_subpath) == 0) {
+ // No starting point -> bad.
+ return -1;
+ }
+ pending_bezier_cmd = descr_cmd.size();
+
+ descr_cmd.push_back(new PathDescrBezierTo(Geom::Point(0, 0), 0));
+ descr_flags |= descr_adding_bezier;
+ descr_flags |= descr_delayed_bezier;
+ return descr_cmd.size() - 1;
+}
+
+void Path::CancelBezier()
+{
+ descr_flags &= ~(descr_adding_bezier);
+ descr_flags &= ~(descr_delayed_bezier);
+ if (pending_bezier_cmd < 0) {
+ return;
+ }
+
+ /* FIXME: I think there's a memory leak here */
+ descr_cmd.resize(pending_bezier_cmd);
+ pending_bezier_cmd = -1;
+}
+
+int Path::EndBezierTo()
+{
+ if (descr_flags & descr_delayed_bezier) {
+ CancelBezier ();
+ } else {
+ pending_bezier_cmd = -1;
+ descr_flags &= ~(descr_adding_bezier);
+ descr_flags &= ~(descr_delayed_bezier);
+ }
+ return -1;
+}
+
+int Path::EndBezierTo(Geom::Point const &iPt)
+{
+ if ( (descr_flags & descr_adding_bezier) == 0 ) {
+ return LineTo(iPt);
+ }
+ if ( (descr_flags & descr_doing_subpath) == 0 ) {
+ return MoveTo(iPt);
+ }
+ if ( (descr_flags & descr_delayed_bezier) == 0 ) {
+ return EndBezierTo();
+ }
+ PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[pending_bezier_cmd]);
+ nData->p = iPt;
+ pending_bezier_cmd = -1;
+ descr_flags &= ~(descr_adding_bezier);
+ descr_flags &= ~(descr_delayed_bezier);
+ return -1;
+}
+
+
+int Path::IntermBezierTo(Geom::Point const &iPt)
+{
+ if ( (descr_flags & descr_adding_bezier) == 0 ) {
+ return LineTo (iPt);
+ }
+
+ if ( (descr_flags & descr_doing_subpath) == 0) {
+ return MoveTo (iPt);
+ }
+
+ descr_cmd.push_back(new PathDescrIntermBezierTo(iPt));
+
+ PathDescrBezierTo *nBData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[pending_bezier_cmd]);
+ nBData->nb++;
+ return descr_cmd.size() - 1;
+}
+
+
+void Path::InsertIntermBezierTo(Geom::Point const &iPt, int at)
+{
+ if ( at < 0 || at > int(descr_cmd.size()) ) {
+ return;
+ }
+
+ if ( at == int(descr_cmd.size()) ) {
+ IntermBezierTo(iPt);
+ return;
+ }
+
+ descr_cmd.insert(descr_cmd.begin() + at, new PathDescrIntermBezierTo(iPt));
+}
+
+
+int Path::BezierTo(Geom::Point const &iPt)
+{
+ if ( descr_flags & descr_adding_bezier ) {
+ EndBezierTo(iPt);
+ }
+
+ if ( (descr_flags & descr_doing_subpath) == 0 ) {
+ return MoveTo (iPt);
+ }
+
+ pending_bezier_cmd = descr_cmd.size();
+
+ descr_cmd.push_back(new PathDescrBezierTo(iPt, 0));
+ descr_flags |= descr_adding_bezier;
+ descr_flags &= ~(descr_delayed_bezier);
+ return descr_cmd.size() - 1;
+}
+
+
+void Path::InsertBezierTo(Geom::Point const &iPt, int iNb, int at)
+{
+ if ( at < 0 || at > int(descr_cmd.size()) ) {
+ return;
+ }
+
+ if ( at == int(descr_cmd.size()) ) {
+ BezierTo(iPt);
+ return;
+ }
+
+ descr_cmd.insert(descr_cmd.begin() + at, new PathDescrBezierTo(iPt, iNb));
+}
+
+
+/*
+ * points of the polyline
+ */
+void
+Path::SetBackData (bool nVal)
+{
+ if (! back) {
+ if (nVal) {
+ back = true;
+ ResetPoints();
+ }
+ } else {
+ if (! nVal) {
+ back = false;
+ ResetPoints();
+ }
+ }
+}
+
+
+void Path::ResetPoints()
+{
+ pts.clear();
+}
+
+
+int Path::AddPoint(Geom::Point const &iPt, bool mvto)
+{
+ if (back) {
+ return AddPoint (iPt, -1, 0.0, mvto);
+ }
+
+ if ( !mvto && !pts.empty() && pts.back().p == iPt ) {
+ return -1;
+ }
+
+ int const n = pts.size();
+ pts.emplace_back(mvto ? polyline_moveto : polyline_lineto, iPt);
+ return n;
+}
+
+
+int Path::ReplacePoint(Geom::Point const &iPt)
+{
+ if (pts.empty()) {
+ return -1;
+ }
+
+ int const n = pts.size() - 1;
+ pts[n] = path_lineto(polyline_lineto, iPt);
+ return n;
+}
+
+
+int Path::AddPoint(Geom::Point const &iPt, int ip, double it, bool mvto)
+{
+ if (! back) {
+ return AddPoint (iPt, mvto);
+ }
+
+ if ( !mvto && !pts.empty() && pts.back().p == iPt ) {
+ return -1;
+ }
+
+ int const n = pts.size();
+ pts.emplace_back(mvto ? polyline_moveto : polyline_lineto, iPt, ip, it);
+ return n;
+}
+
+int Path::AddForcedPoint(Geom::Point const &iPt)
+{
+ if (back) {
+ return AddForcedPoint (iPt, -1, 0.0);
+ }
+
+ if ( pts.empty() || pts.back().isMoveTo != polyline_lineto ) {
+ return -1;
+ }
+
+ int const n = pts.size();
+ pts.emplace_back(polyline_forced, pts[n - 1].p);
+ return n;
+}
+
+
+int Path::AddForcedPoint(Geom::Point const &iPt, int /*ip*/, double /*it*/)
+{
+ /* FIXME: ip & it aren't used. Is this deliberate? */
+ if (!back) {
+ return AddForcedPoint (iPt);
+ }
+
+ if ( pts.empty() || pts.back().isMoveTo != polyline_lineto ) {
+ return -1;
+ }
+
+ int const n = pts.size();
+ pts.emplace_back(polyline_forced, pts[n - 1].p, pts[n - 1].piece, pts[n - 1].t);
+ return n;
+}
+
+void Path::PolylineBoundingBox(double &l, double &t, double &r, double &b)
+{
+ l = t = r = b = 0.0;
+ if ( pts.empty() ) {
+ return;
+ }
+
+ std::vector<path_lineto>::const_iterator i = pts.begin();
+ l = r = i->p[Geom::X];
+ t = b = i->p[Geom::Y];
+ ++i;
+
+ for (; i != pts.end(); ++i) {
+ r = std::max(r, i->p[Geom::X]);
+ l = std::min(l, i->p[Geom::X]);
+ b = std::max(b, i->p[Geom::Y]);
+ t = std::min(t, i->p[Geom::Y]);
+ }
+}
+
+
+/**
+ * \param piece Index of a one of our commands.
+ * \param at Distance along the segment that corresponds to `piece' (0 <= at <= 1)
+ * \param pos Filled in with the point at `at' on `piece'.
+ */
+
+void Path::PointAt(int piece, double at, Geom::Point &pos)
+{
+ if (piece < 0 || piece >= int(descr_cmd.size())) {
+ // this shouldn't happen: the piece we are asked for doesn't
+ // exist in the path
+ pos = Geom::Point(0,0);
+ return;
+ }
+
+ PathDescr const *theD = descr_cmd[piece];
+ int const typ = theD->getType();
+ Geom::Point tgt;
+ double len;
+ double rad;
+
+ if (typ == descr_moveto) {
+
+ return PointAt (piece + 1, 0.0, pos);
+
+ } else if (typ == descr_close || typ == descr_forced) {
+
+ return PointAt (piece - 1, 1.0, pos);
+
+ } else if (typ == descr_lineto) {
+
+ PathDescrLineTo const *nData = dynamic_cast<PathDescrLineTo const *>(theD);
+ TangentOnSegAt(at, PrevPoint (piece - 1), *nData, pos, tgt, len);
+
+ } else if (typ == descr_arcto) {
+
+ PathDescrArcTo const *nData = dynamic_cast<PathDescrArcTo const *>(theD);
+ TangentOnArcAt(at,PrevPoint (piece - 1), *nData, pos, tgt, len, rad);
+
+ } else if (typ == descr_cubicto) {
+
+ PathDescrCubicTo const *nData = dynamic_cast<PathDescrCubicTo const *>(theD);
+ TangentOnCubAt(at, PrevPoint (piece - 1), *nData, false, pos, tgt, len, rad);
+
+ } else if (typ == descr_bezierto || typ == descr_interm_bezier) {
+
+ int bez_st = piece;
+ while (bez_st >= 0) {
+ int nt = descr_cmd[bez_st]->getType();
+ if (nt == descr_bezierto)
+ break;
+ bez_st--;
+ }
+ if ( bez_st < 0 ) {
+ // Didn't find the beginning of the spline (bad).
+ // [pas trouvé le dubut de la spline (mauvais)]
+ return PointAt(piece - 1, 1.0, pos);
+ }
+
+ PathDescrBezierTo *stB = dynamic_cast<PathDescrBezierTo *>(descr_cmd[bez_st]);
+ if ( piece > bez_st + stB->nb ) {
+ // The spline goes past the authorized number of commands (bad).
+ // [la spline sort du nombre de commandes autorisé (mauvais)]
+ return PointAt(piece - 1, 1.0, pos);
+ }
+
+ int k = piece - bez_st;
+ Geom::Point const bStPt = PrevPoint(bez_st - 1);
+ if (stB->nb == 1 || k <= 0) {
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + 1]);
+ TangentOnBezAt(at, bStPt, *nData, *stB, false, pos, tgt, len, rad);
+ } else {
+ // forcement plus grand que 1
+ if (k == 1) {
+ PathDescrIntermBezierTo *nextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + 1]);
+ PathDescrIntermBezierTo *nnextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + 2]);
+ PathDescrBezierTo fin(0.5 * (nextI->p + nnextI->p), 1);
+ TangentOnBezAt(at, bStPt, *nextI, fin, false, pos, tgt, len, rad);
+ } else if (k == stB->nb) {
+ PathDescrIntermBezierTo *nextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k]);
+ PathDescrIntermBezierTo *prevI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k - 1]);
+ Geom::Point stP = 0.5 * ( prevI->p + nextI->p );
+ TangentOnBezAt(at, stP, *nextI, *stB, false, pos, tgt, len, rad);
+ } else {
+ PathDescrIntermBezierTo *nextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k]);
+ PathDescrIntermBezierTo *prevI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k - 1]);
+ PathDescrIntermBezierTo *nnextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k + 1]);
+ Geom::Point stP = 0.5 * ( prevI->p + nextI->p );
+ PathDescrBezierTo fin(0.5 * (nextI->p + nnextI->p), 1);
+ TangentOnBezAt(at, stP, *nextI, fin, false, pos, tgt, len, rad);
+ }
+ }
+ }
+}
+
+
+void Path::PointAndTangentAt(int piece, double at, Geom::Point &pos, Geom::Point &tgt)
+{
+ if (piece < 0 || piece >= int(descr_cmd.size())) {
+ // this shouldn't happen: the piece we are asked for doesn't exist in the path
+ pos = Geom::Point(0, 0);
+ return;
+ }
+
+ PathDescr const *theD = descr_cmd[piece];
+ int typ = theD->getType();
+ double len;
+ double rad;
+ if (typ == descr_moveto) {
+
+ return PointAndTangentAt(piece + 1, 0.0, pos, tgt);
+
+ } else if (typ == descr_close ) {
+
+ int cp = piece - 1;
+ while ( cp >= 0 && (descr_cmd[cp]->getType()) != descr_moveto ) {
+ cp--;
+ }
+ if ( cp >= 0 ) {
+ PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[cp]);
+ PathDescrLineTo dst(nData->p);
+ TangentOnSegAt(at, PrevPoint (piece - 1), dst, pos, tgt, len);
+ }
+
+ } else if ( typ == descr_forced) {
+
+ return PointAndTangentAt(piece - 1, 1.0, pos,tgt);
+
+ } else if (typ == descr_lineto) {
+
+ PathDescrLineTo const *nData = dynamic_cast<PathDescrLineTo const *>(theD);
+ TangentOnSegAt(at, PrevPoint (piece - 1), *nData, pos, tgt, len);
+
+ } else if (typ == descr_arcto) {
+
+ PathDescrArcTo const *nData = dynamic_cast<PathDescrArcTo const *>(theD);
+ TangentOnArcAt (at,PrevPoint (piece - 1), *nData, pos, tgt, len, rad);
+
+ } else if (typ == descr_cubicto) {
+
+ PathDescrCubicTo const *nData = dynamic_cast<PathDescrCubicTo const *>(theD);
+ TangentOnCubAt (at, PrevPoint (piece - 1), *nData, false, pos, tgt, len, rad);
+
+ } else if (typ == descr_bezierto || typ == descr_interm_bezier) {
+ int bez_st = piece;
+ while (bez_st >= 0) {
+ int nt = descr_cmd[bez_st]->getType();
+ if (nt == descr_bezierto) break;
+ bez_st--;
+ }
+ if ( bez_st < 0 ) {
+ return PointAndTangentAt(piece - 1, 1.0, pos, tgt);
+ // Didn't find the beginning of the spline (bad).
+ // [pas trouvé le dubut de la spline (mauvais)]
+ }
+
+ PathDescrBezierTo* stB = dynamic_cast<PathDescrBezierTo*>(descr_cmd[bez_st]);
+ if ( piece > bez_st + stB->nb ) {
+ return PointAndTangentAt(piece - 1, 1.0, pos, tgt);
+ // The spline goes past the number of authorized commands (bad).
+ // [la spline sort du nombre de commandes autorisé (mauvais)]
+ }
+
+ int k = piece - bez_st;
+ Geom::Point const bStPt(PrevPoint( bez_st - 1 ));
+ if (stB->nb == 1 || k <= 0) {
+ PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + 1]);
+ TangentOnBezAt (at, bStPt, *nData, *stB, false, pos, tgt, len, rad);
+ } else {
+ // forcement plus grand que 1
+ if (k == 1) {
+ PathDescrIntermBezierTo *nextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + 1]);
+ PathDescrIntermBezierTo *nnextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + 2]);
+ PathDescrBezierTo fin(0.5 * (nextI->p + nnextI->p), 1);
+ TangentOnBezAt(at, bStPt, *nextI, fin, false, pos, tgt, len, rad);
+ } else if (k == stB->nb) {
+ PathDescrIntermBezierTo *prevI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k - 1]);
+ PathDescrIntermBezierTo *nextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k]);
+ Geom::Point stP = 0.5 * ( prevI->p + nextI->p );
+ TangentOnBezAt(at, stP, *nextI, *stB, false, pos, tgt, len, rad);
+ } else {
+ PathDescrIntermBezierTo *prevI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k - 1]);
+ PathDescrIntermBezierTo *nextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k]);
+ PathDescrIntermBezierTo *nnextI = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[bez_st + k + 1]);
+ Geom::Point stP = 0.5 * ( prevI->p + nextI->p );
+ PathDescrBezierTo fin(0.5 * (nnextI->p + nnextI->p), 1);
+ TangentOnBezAt(at, stP, *nextI, fin, false, pos, tgt, len, rad);
+ }
+ }
+ }
+}
+
+void Path::Transform(const Geom::Affine &trans)
+{
+ for (auto & i : descr_cmd) {
+ i->transform(trans);
+ }
+}
+
+void Path::FastBBox(double &l,double &t,double &r,double &b)
+{
+ l = t = r = b = 0;
+ bool empty = true;
+ Geom::Point lastP(0, 0);
+
+ for (auto & i : descr_cmd) {
+ int const typ = i->getType();
+ switch ( typ ) {
+ case descr_lineto:
+ {
+ PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(i);
+ if ( empty ) {
+ l = r = nData->p[Geom::X];
+ t = b = nData->p[Geom::Y];
+ empty = false;
+ } else {
+ if ( nData->p[Geom::X] < l ) {
+ l = nData->p[Geom::X];
+ }
+ if ( nData->p[Geom::X] > r ) {
+ r = nData->p[Geom::X];
+ }
+ if ( nData->p[Geom::Y] < t ) {
+ t = nData->p[Geom::Y];
+ }
+ if ( nData->p[Geom::Y] > b ) {
+ b = nData->p[Geom::Y];
+ }
+ }
+ lastP = nData->p;
+ }
+ break;
+
+ case descr_moveto:
+ {
+ PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo *>(i);
+ if ( empty ) {
+ l = r = nData->p[Geom::X];
+ t = b = nData->p[Geom::Y];
+ empty = false;
+ } else {
+ if ( nData->p[Geom::X] < l ) {
+ l = nData->p[Geom::X];
+ }
+ if ( nData->p[Geom::X] > r ) {
+ r = nData->p[Geom::X];
+ }
+ if ( nData->p[Geom::Y] < t ) {
+ t = nData->p[Geom::Y];
+ }
+ if ( nData->p[Geom::Y] > b ) {
+ b = nData->p[Geom::Y];
+ }
+ }
+ lastP = nData->p;
+ }
+ break;
+
+ case descr_arcto:
+ {
+ PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(i);
+ if ( empty ) {
+ l = r = nData->p[Geom::X];
+ t = b = nData->p[Geom::Y];
+ empty = false;
+ } else {
+ if ( nData->p[Geom::X] < l ) {
+ l = nData->p[Geom::X];
+ }
+ if ( nData->p[Geom::X] > r ) {
+ r = nData->p[Geom::X];
+ }
+ if ( nData->p[Geom::Y] < t ) {
+ t = nData->p[Geom::Y];
+ }
+ if ( nData->p[Geom::Y] > b ) {
+ b = nData->p[Geom::Y];
+ }
+ }
+ lastP = nData->p;
+ }
+ break;
+
+ case descr_cubicto:
+ {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(i);
+ if ( empty ) {
+ l = r = nData->p[Geom::X];
+ t = b = nData->p[Geom::Y];
+ empty = false;
+ } else {
+ if ( nData->p[Geom::X] < l ) {
+ l = nData->p[Geom::X];
+ }
+ if ( nData->p[Geom::X] > r ) {
+ r = nData->p[Geom::X];
+ }
+ if ( nData->p[Geom::Y] < t ) {
+ t = nData->p[Geom::Y];
+ }
+ if ( nData->p[Geom::Y] > b ) {
+ b = nData->p[Geom::Y];
+ }
+ }
+
+/* bug 249665: "...the calculation of the bounding-box for cubic-paths
+has some extra steps to make it work correctly in Win32 that unfortunately
+are unnecessary in Linux, generating wrong results. This only shows in
+Type1 fonts because they use cubic-paths instead of the
+bezier-paths used by True-Type fonts."
+*/
+
+#ifdef _WIN32
+ Geom::Point np = nData->p - nData->end;
+ if ( np[Geom::X] < l ) {
+ l = np[Geom::X];
+ }
+ if ( np[Geom::X] > r ) {
+ r = np[Geom::X];
+ }
+ if ( np[Geom::Y] < t ) {
+ t = np[Geom::Y];
+ }
+ if ( np[Geom::Y] > b ) {
+ b = np[Geom::Y];
+ }
+
+ np = lastP + nData->start;
+ if ( np[Geom::X] < l ) {
+ l = np[Geom::X];
+ }
+ if ( np[Geom::X] > r ) {
+ r = np[Geom::X];
+ }
+ if ( np[Geom::Y] < t ) {
+ t = np[Geom::Y];
+ }
+ if ( np[Geom::Y] > b ) {
+ b = np[Geom::Y];
+ }
+#endif
+
+ lastP = nData->p;
+ }
+ break;
+
+ case descr_bezierto:
+ {
+ PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(i);
+ if ( empty ) {
+ l = r = nData->p[Geom::X];
+ t = b = nData->p[Geom::Y];
+ empty = false;
+ } else {
+ if ( nData->p[Geom::X] < l ) {
+ l = nData->p[Geom::X];
+ }
+ if ( nData->p[Geom::X] > r ) {
+ r = nData->p[Geom::X];
+ }
+ if ( nData->p[Geom::Y] < t ) {
+ t = nData->p[Geom::Y];
+ }
+ if ( nData->p[Geom::Y] > b ) {
+ b = nData->p[Geom::Y];
+ }
+ }
+ lastP = nData->p;
+ }
+ break;
+
+ case descr_interm_bezier:
+ {
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(i);
+ if ( empty ) {
+ l = r = nData->p[Geom::X];
+ t = b = nData->p[Geom::Y];
+ empty = false;
+ } else {
+ if ( nData->p[Geom::X] < l ) {
+ l = nData->p[Geom::X];
+ }
+ if ( nData->p[Geom::X] > r ) {
+ r = nData->p[Geom::X];
+ }
+ if ( nData->p[Geom::Y] < t ) {
+ t = nData->p[Geom::Y];
+ }
+ if ( nData->p[Geom::Y] > b ) {
+ b = nData->p[Geom::Y];
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
+char *Path::svg_dump_path() const
+{
+ Inkscape::SVGOStringStream os;
+
+ for (int i = 0; i < int(descr_cmd.size()); i++) {
+ Geom::Point const p = (i == 0) ? Geom::Point(0, 0) : PrevPoint(i - 1);
+ descr_cmd[i]->dumpSVG(os, p);
+ }
+
+ return g_strdup (os.str().c_str());
+}
+
+// Find out if the segment that corresponds to 'piece' is a straight line
+bool Path::IsLineSegment(int piece)
+{
+ if (piece < 0 || piece >= int(descr_cmd.size())) {
+ return false;
+ }
+
+ PathDescr const *theD = descr_cmd[piece];
+ int const typ = theD->getType();
+
+ return (typ == descr_lineto);
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/livarot/Path.h b/src/livarot/Path.h
new file mode 100644
index 0000000..c965e36
--- /dev/null
+++ b/src/livarot/Path.h
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2014 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+/*
+ * Path.h
+ * nlivarot
+ *
+ * Created by fred on Tue Jun 17 2003.
+ *
+ */
+
+#ifndef my_path
+#define my_path
+
+#include <vector>
+#include "LivarotDefs.h"
+#include <2geom/point.h>
+
+struct PathDescr;
+struct PathDescrLineTo;
+struct PathDescrArcTo;
+struct PathDescrCubicTo;
+struct PathDescrBezierTo;
+struct PathDescrIntermBezierTo;
+
+class SPStyle;
+
+/*
+ * the Path class: a structure to hold path description and their polyline approximation (not kept in sync)
+ * the path description is built with regular commands like MoveTo() LineTo(), etc
+ * the polyline approximation is built by a call to Convert() or its variants
+ * another possibility would be to call directly the AddPoint() functions, but that is not encouraged
+ * the conversion to polyline can salvage data as to where on the path each polyline's point lies; use
+ * ConvertWithBackData() for this. after this call, it's easy to rewind the polyline: sequences of points
+ * of the same path command can be reassembled in a command
+ */
+
+// polyline description commands
+enum
+{
+ polyline_lineto = 0, // a lineto
+ polyline_moveto = 1, // a moveto
+ polyline_forced = 2 // a forced point, ie a point that was an angle or an intersection in a previous life
+ // or more realistically a control point in the path description that created the polyline
+ // forced points are used as "breakable" points for the polyline -> cubic bezier patch operations
+ // each time the bezier fitter encounters such a point in the polyline, it decreases its treshhold,
+ // so that it is more likely to cut the polyline at that position and produce a bezier patch
+};
+
+class Shape;
+
+// path creation: 2 phases: first the path is given as a succession of commands (MoveTo, LineTo, CurveTo...); then it
+// is converted in a polyline
+// a polylone can be stroked or filled to make a polygon
+class Path
+{
+ friend class Shape;
+
+public:
+
+ // flags for the path construction
+ enum
+ {
+ descr_ready = 0,
+ descr_adding_bezier = 1, // we're making a bezier spline, so you can expect pending_bezier_* to have a value
+ descr_doing_subpath = 2, // we're doing a path, so there is a moveto somewhere
+ descr_delayed_bezier = 4,// the bezier spline we're doing was initiated by a TempBezierTo(), so we'll need an endpoint
+ descr_dirty = 16 // the path description was modified
+ };
+
+ // some data for the construction: what's pending, and some flags
+ int descr_flags;
+ int pending_bezier_cmd;
+ int pending_bezier_data;
+ int pending_moveto_cmd;
+ int pending_moveto_data;
+ // the path description
+ std::vector<PathDescr*> descr_cmd;
+
+ // polyline storage: a series of coordinates (and maybe weights)
+ // also back data: info on where this polyline's segment comes from, ie which command in the path description: "piece"
+ // and what abcissis on the chunk of path for this command: "t"
+ // t=0 means it's at the start of the command's chunk, t=1 it's at the end
+ struct path_lineto
+ {
+ path_lineto(bool m, Geom::Point pp) : isMoveTo(m), p(pp), piece(-1), t(0), closed(false) {}
+ path_lineto(bool m, Geom::Point pp, int pie, double tt) : isMoveTo(m), p(pp), piece(pie), t(tt), closed(false) {}
+
+ int isMoveTo;
+ Geom::Point p;
+ int piece;
+ double t;
+ bool closed; // true if subpath is closed (this point is the last point of a closed subpath)
+ };
+
+ std::vector<path_lineto> pts;
+
+ bool back;
+
+ Path();
+ virtual ~Path();
+
+ // creation of the path description
+ void Reset(); // reset to the empty description
+ void Copy (Path * who);
+
+ // the commands...
+ int ForcePoint();
+ int Close();
+ int MoveTo ( Geom::Point const &ip);
+ int LineTo ( Geom::Point const &ip);
+ int CubicTo ( Geom::Point const &ip, Geom::Point const &iStD, Geom::Point const &iEnD);
+ int ArcTo ( Geom::Point const &ip, double iRx, double iRy, double angle, bool iLargeArc, bool iClockwise);
+ int IntermBezierTo ( Geom::Point const &ip); // add a quadratic bezier spline control point
+ int BezierTo ( Geom::Point const &ip); // quadratic bezier spline to this point (control points can be added after this)
+ int TempBezierTo(); // start a quadratic bezier spline (control points can be added after this)
+ int EndBezierTo();
+ int EndBezierTo ( Geom::Point const &ip); // ends a quadratic bezier spline (for curves started with TempBezierTo)
+
+ // transforms a description in a polyline (for stroking and filling)
+ // treshhold is the max length^2 (sort of)
+ void Convert (double treshhold);
+ void ConvertEvenLines (double treshhold); // decomposes line segments too, for later recomposition
+ // same function for use when you want to later recompose the curves from the polyline
+ void ConvertWithBackData (double treshhold);
+
+ // creation of the polyline (you can tinker with these function if you want)
+ void SetBackData (bool nVal); // has back data?
+ void ResetPoints(); // resets to the empty polyline
+ int AddPoint ( Geom::Point const &iPt, bool mvto = false); // add point
+ int AddPoint ( Geom::Point const &iPt, int ip, double it, bool mvto = false);
+ int AddForcedPoint ( Geom::Point const &iPt); // add point
+ int AddForcedPoint ( Geom::Point const &iPt, int ip, double it);
+ int ReplacePoint(Geom::Point const &iPt); // replace point
+
+ // transform in a polygon (in a graph, in fact; a subsequent call to ConvertToShape is needed)
+ // - fills the polyline; justAdd=true doesn't reset the Shape dest, but simply adds the polyline into it
+ // closeIfNeeded=false prevent the function from closing the path (resulting in a non-eulerian graph
+ // pathID is a identification number for the path, and is used for recomposing curves from polylines
+ // give each different Path a different ID, and feed the appropriate orig[] to the ConvertToForme() function
+ void Fill(Shape *dest, int pathID = -1, bool justAdd = false,
+ bool closeIfNeeded = true, bool invert = false);
+
+ // - stroke the path; usual parameters: type of cap=butt, type of join=join and miter (see LivarotDefs.h)
+ // doClose treat the path as closed (ie a loop)
+ void Stroke(Shape *dest, bool doClose, double width, JoinType join,
+ ButtType butt, double miter, bool justAdd = false);
+
+ // build a Path that is the outline of the Path instance's description (the result is stored in dest)
+ // it doesn't compute the exact offset (it's way too complicated, but an approximation made of cubic bezier patches
+ // and segments. the algorithm was found in a plugin for Impress (by Chris Cox), but i can't find it back...
+ void Outline(Path *dest, double width, JoinType join, ButtType butt,
+ double miter);
+
+ // half outline with edges having the same direction as the original
+ void OutsideOutline(Path *dest, double width, JoinType join, ButtType butt,
+ double miter);
+
+ // half outline with edges having the opposite direction as the original
+ void InsideOutline (Path * dest, double width, JoinType join, ButtType butt,
+ double miter);
+
+ // polyline to cubic bezier patches
+ void Simplify (double treshhold);
+
+ // description simplification
+ void Coalesce (double tresh);
+
+ // utilities
+ // piece is a command no in the command list
+ // "at" is an abcissis on the path portion associated with this command
+ // 0=beginning of portion, 1=end of portion.
+ void PointAt (int piece, double at, Geom::Point & pos);
+ void PointAndTangentAt (int piece, double at, Geom::Point & pos, Geom::Point & tgt);
+
+ // last control point before the command i (i included)
+ // used when dealing with quadratic bezier spline, cause these can contain arbitrarily many commands
+ const Geom::Point PrevPoint (const int i) const;
+
+ // dash the polyline
+ // the result is stored in the polyline, so you lose the original. make a copy before if needed
+ void DashPolyline(float head,float tail,float body,int nbD,float *dashs,bool stPlain,float stOffset);
+
+ void DashPolylineFromStyle(SPStyle *style, float scale, float min_len);
+
+ //utilitaire pour inkscape
+ void LoadPath(Geom::Path const &path, Geom::Affine const &tr, bool doTransformation, bool append = false);
+ void LoadPathVector(Geom::PathVector const &pv, Geom::Affine const &tr, bool doTransformation);
+ void LoadPathVector(Geom::PathVector const &pv);
+ Geom::PathVector* MakePathVector();
+
+ void Transform(const Geom::Affine &trans);
+
+ // decompose le chemin en ses sous-chemin
+ // killNoSurf=true -> oublie les chemins de surface nulle
+ Path** SubPaths(int &outNb,bool killNoSurf);
+ // pour recuperer les trous
+ // nbNest= nombre de contours
+ // conts= debut de chaque contour
+ // nesting= parent de chaque contour
+ Path** SubPathsWithNesting(int &outNb,bool killNoSurf,int nbNest,int* nesting,int* conts);
+ // surface du chemin (considere comme ferme)
+ double Surface();
+ void PolylineBoundingBox(double &l,double &t,double &r,double &b);
+ void FastBBox(double &l,double &t,double &r,double &b);
+ // longueur (totale des sous-chemins)
+ double Length();
+
+ void ConvertForcedToMoveTo();
+ void ConvertForcedToVoid();
+ struct cut_position {
+ int piece;
+ double t;
+ };
+ cut_position* CurvilignToPosition(int nbCv,double* cvAbs,int &nbCut);
+ cut_position PointToCurvilignPosition(Geom::Point const &pos, unsigned seg = 0) const;
+ //Should this take a cut_position as a param?
+ double PositionToLength(int piece, double t);
+
+ // caution: not tested on quadratic b-splines, most certainly buggy
+ void ConvertPositionsToMoveTo(int nbPos,cut_position* poss);
+ void ConvertPositionsToForced(int nbPos,cut_position* poss);
+
+ void Affiche();
+ char *svg_dump_path() const;
+
+ bool IsLineSegment(int piece);
+
+ private:
+ // utilitary functions for the path construction
+ void CancelBezier ();
+ void CloseSubpath();
+ void InsertMoveTo (Geom::Point const &iPt,int at);
+ void InsertForcePoint (int at);
+ void InsertLineTo (Geom::Point const &iPt,int at);
+ void InsertArcTo (Geom::Point const &ip, double iRx, double iRy, double angle, bool iLargeArc, bool iClockwise,int at);
+ void InsertCubicTo (Geom::Point const &ip, Geom::Point const &iStD, Geom::Point const &iEnD,int at);
+ void InsertBezierTo (Geom::Point const &iPt,int iNb,int at);
+ void InsertIntermBezierTo (Geom::Point const &iPt,int at);
+
+ // creation of dashes: take the polyline given by spP (length spL) and dash it according to head, body, etc. put the result in
+ // the polyline of this instance
+ void DashSubPath(int spL, int spP, std::vector<path_lineto> const &orig_pts, float head,float tail,float body,int nbD,float *dashs,bool stPlain,float stOffset);
+
+ // Functions used by the conversion.
+ // they append points to the polyline
+ void DoArc ( Geom::Point const &iS, Geom::Point const &iE, double rx, double ry,
+ double angle, bool large, bool wise, double tresh);
+ void RecCubicTo ( Geom::Point const &iS, Geom::Point const &iSd, Geom::Point const &iE, Geom::Point const &iEd, double tresh, int lev,
+ double maxL = -1.0);
+ void RecBezierTo ( Geom::Point const &iPt, Geom::Point const &iS, Geom::Point const &iE, double treshhold, int lev, double maxL = -1.0);
+
+ void DoArc ( Geom::Point const &iS, Geom::Point const &iE, double rx, double ry,
+ double angle, bool large, bool wise, double tresh, int piece);
+ void RecCubicTo ( Geom::Point const &iS, Geom::Point const &iSd, Geom::Point const &iE, Geom::Point const &iEd, double tresh, int lev,
+ double st, double et, int piece);
+ void RecBezierTo ( Geom::Point const &iPt, Geom::Point const &iS, const Geom::Point &iE, double treshhold, int lev, double st, double et,
+ int piece);
+
+ // don't pay attention
+ struct offset_orig
+ {
+ Path *orig;
+ int piece;
+ double tSt, tEn;
+ double off_dec;
+ };
+ void DoArc ( Geom::Point const &iS, Geom::Point const &iE, double rx, double ry,
+ double angle, bool large, bool wise, double tresh, int piece,
+ offset_orig & orig);
+ void RecCubicTo ( Geom::Point const &iS, Geom::Point const &iSd, Geom::Point const &iE, Geom::Point const &iEd, double tresh, int lev,
+ double st, double et, int piece, offset_orig & orig);
+ void RecBezierTo ( Geom::Point const &iPt, Geom::Point const &iS, Geom::Point const &iE, double treshhold, int lev, double st, double et,
+ int piece, offset_orig & orig);
+
+ static void ArcAngles ( Geom::Point const &iS, Geom::Point const &iE, double rx,
+ double ry, double angle, bool large, bool wise,
+ double &sang, double &eang);
+ static void QuadraticPoint (double t, Geom::Point &oPt, Geom::Point const &iS, Geom::Point const &iM, Geom::Point const &iE);
+ static void CubicTangent (double t, Geom::Point &oPt, Geom::Point const &iS,
+ Geom::Point const &iSd, Geom::Point const &iE,
+ Geom::Point const &iEd);
+
+ struct outline_callback_data
+ {
+ Path *orig;
+ int piece;
+ double tSt, tEn;
+ Path *dest;
+ double x1, y1, x2, y2;
+ union
+ {
+ struct
+ {
+ double dx1, dy1, dx2, dy2;
+ }
+ c;
+ struct
+ {
+ double mx, my;
+ }
+ b;
+ struct
+ {
+ double rx, ry, angle;
+ bool clock, large;
+ double stA, enA;
+ }
+ a;
+ }
+ d;
+ };
+
+ typedef void (outlineCallback) (outline_callback_data * data, double tol, double width);
+ struct outline_callbacks
+ {
+ outlineCallback *cubicto;
+ outlineCallback *bezierto;
+ outlineCallback *arcto;
+ };
+
+ void SubContractOutline (int off, int num_pd,
+ Path * dest, outline_callbacks & calls,
+ double tolerance, double width, JoinType join,
+ ButtType butt, double miter, bool closeIfNeeded,
+ bool skipMoveto, Geom::Point & lastP, Geom::Point & lastT);
+ void DoStroke(int off, int N, Shape *dest, bool doClose, double width, JoinType join,
+ ButtType butt, double miter, bool justAdd = false);
+
+ static void TangentOnSegAt(double at, Geom::Point const &iS, PathDescrLineTo const &fin,
+ Geom::Point &pos, Geom::Point &tgt, double &len);
+ static void TangentOnArcAt(double at, Geom::Point const &iS, PathDescrArcTo const &fin,
+ Geom::Point &pos, Geom::Point &tgt, double &len, double &rad);
+ static void TangentOnCubAt (double at, Geom::Point const &iS, PathDescrCubicTo const &fin, bool before,
+ Geom::Point &pos, Geom::Point &tgt, double &len, double &rad);
+ static void TangentOnBezAt (double at, Geom::Point const &iS,
+ PathDescrIntermBezierTo & mid,
+ PathDescrBezierTo & fin, bool before,
+ Geom::Point & pos, Geom::Point & tgt, double &len, double &rad);
+ static void OutlineJoin (Path * dest, Geom::Point pos, Geom::Point stNor, Geom::Point enNor,
+ double width, JoinType join, double miter, int nType);
+
+ static bool IsNulCurve (std::vector<PathDescr*> const &cmd, int curD, Geom::Point const &curX);
+
+ static void RecStdCubicTo (outline_callback_data * data, double tol,
+ double width, int lev);
+ static void StdCubicTo (outline_callback_data * data, double tol,
+ double width);
+ static void StdBezierTo (outline_callback_data * data, double tol,
+ double width);
+ static void RecStdArcTo (outline_callback_data * data, double tol,
+ double width, int lev);
+ static void StdArcTo (outline_callback_data * data, double tol, double width);
+
+
+ // fonctions annexes pour le stroke
+ static void DoButt (Shape * dest, double width, ButtType butt, Geom::Point pos,
+ Geom::Point dir, int &leftNo, int &rightNo);
+ static void DoJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
+ Geom::Point prev, Geom::Point next, double miter, double prevL,
+ double nextL, int *stNo, int *enNo);
+ static void DoLeftJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
+ Geom::Point prev, Geom::Point next, double miter, double prevL,
+ double nextL, int &leftStNo, int &leftEnNo,int pathID=-1,int pieceID=0,double tID=0.0);
+ static void DoRightJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
+ Geom::Point prev, Geom::Point next, double miter, double prevL,
+ double nextL, int &rightStNo, int &rightEnNo,int pathID=-1,int pieceID=0,double tID=0.0);
+ static void RecRound (Shape * dest, int sNo, int eNo,
+ Geom::Point const &iS, Geom::Point const &iE,
+ Geom::Point const &nS, Geom::Point const &nE,
+ Geom::Point &origine,float width);
+
+
+ void DoSimplify(int off, int N, double treshhold);
+ bool AttemptSimplify(int off, int N, double treshhold, PathDescrCubicTo &res, int &worstP);
+ static bool FitCubic(Geom::Point const &start,
+ PathDescrCubicTo &res,
+ double *Xk, double *Yk, double *Qk, double *tk, int nbPt);
+
+ struct fitting_tables {
+ int nbPt,maxPt,inPt;
+ double *Xk;
+ double *Yk;
+ double *Qk;
+ double *tk;
+ double *lk;
+ char *fk;
+ double totLen;
+ };
+ bool AttemptSimplify (fitting_tables &data,double treshhold, PathDescrCubicTo & res,int &worstP);
+ bool ExtendFit(int off, int N, fitting_tables &data,double treshhold, PathDescrCubicTo & res,int &worstP);
+ double RaffineTk (Geom::Point pt, Geom::Point p0, Geom::Point p1, Geom::Point p2, Geom::Point p3, double it);
+ void FlushPendingAddition(Path* dest,PathDescr *lastAddition,PathDescrCubicTo &lastCubic,int lastAD);
+
+private:
+ void AddCurve(Geom::Curve const &c);
+
+};
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/livarot/PathConversion.cpp b/src/livarot/PathConversion.cpp
new file mode 100644
index 0000000..737ecbe
--- /dev/null
+++ b/src/livarot/PathConversion.cpp
@@ -0,0 +1,1579 @@
+// 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 <glib.h>
+#include <2geom/transforms.h>
+#include "Path.h"
+#include "Shape.h"
+#include "livarot/path-description.h"
+
+/*
+ * path description -> polyline
+ * and Path -> Shape (the Fill() function at the bottom)
+ * nathing fancy here: take each command and append an approximation of it to the polyline
+ */
+
+void Path::ConvertWithBackData(double treshhold)
+{
+ if ( descr_flags & descr_adding_bezier ) {
+ CancelBezier();
+ }
+
+ if ( descr_flags & descr_doing_subpath ) {
+ CloseSubpath();
+ }
+
+ SetBackData(true);
+ ResetPoints();
+ if ( descr_cmd.empty() ) {
+ return;
+ }
+
+ Geom::Point curX;
+ int curP = 1;
+ int lastMoveTo = -1;
+
+ // The initial moveto.
+ {
+ int const firstTyp = descr_cmd[0]->getType();
+ if ( firstTyp == descr_moveto ) {
+ curX = dynamic_cast<PathDescrMoveTo *>(descr_cmd[0])->p;
+ } else {
+ curP = 0;
+ curX[Geom::X] = curX[Geom::Y] = 0;
+ }
+ lastMoveTo = AddPoint(curX, 0, 0.0, true);
+ }
+
+ // And the rest, one by one.
+ while ( curP < int(descr_cmd.size()) ) {
+
+ int const nType = descr_cmd[curP]->getType();
+ Geom::Point nextX;
+
+ switch (nType) {
+ case descr_forced: {
+ AddForcedPoint(curX, curP, 1.0);
+ curP++;
+ break;
+ }
+
+ case descr_moveto: {
+ PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo*>(descr_cmd[curP]);
+ nextX = nData->p;
+ lastMoveTo = AddPoint(nextX, curP, 0.0, true);
+ // et on avance
+ curP++;
+ break;
+ }
+
+ case descr_close: {
+ nextX = pts[lastMoveTo].p;
+ int n = AddPoint(nextX, curP, 1.0, false);
+ if (n > 0) pts[n].closed = true;
+ curP++;
+ break;
+ }
+
+ case descr_lineto: {
+ PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[curP]);
+ nextX = nData->p;
+ AddPoint(nextX,curP,1.0,false);
+ // et on avance
+ curP++;
+ break;
+ }
+
+ case descr_cubicto: {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[curP]);
+ nextX = nData->p;
+ RecCubicTo(curX, nData->start, nextX, nData->end, treshhold, 8, 0.0, 1.0, curP);
+ AddPoint(nextX, curP, 1.0, false);
+ // et on avance
+ curP++;
+ break;
+ }
+
+ case descr_arcto: {
+ PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[curP]);
+ nextX = nData->p;
+ DoArc(curX, nextX, nData->rx, nData->ry, nData->angle, nData->large, nData->clockwise, treshhold, curP);
+ AddPoint(nextX, curP, 1.0, false);
+ // et on avance
+ curP++;
+ break;
+ }
+
+ case descr_bezierto: {
+ PathDescrBezierTo *nBData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[curP]);
+ int nbInterm = nBData->nb;
+ nextX = nBData->p;
+
+ int ip = curP + 1;
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
+
+ if ( nbInterm >= 1 ) {
+ Geom::Point bx = curX;
+ Geom::Point dx = nData->p;
+ Geom::Point cx = 2 * bx - dx;
+
+ ip++;
+ nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
+
+ for (int k = 0; k < nbInterm - 1; k++) {
+ bx = cx;
+ cx = dx;
+
+ dx = nData->p;
+ ip++;
+ nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
+
+ Geom::Point stx;
+ stx = (bx + cx) / 2;
+ if ( k > 0 ) {
+ AddPoint(stx,curP - 1+k,1.0,false);
+ }
+
+ {
+ Geom::Point mx;
+ mx = (cx + dx) / 2;
+ RecBezierTo(cx, stx, mx, treshhold, 8, 0.0, 1.0, curP + k);
+ }
+ }
+ {
+ bx = cx;
+ cx = dx;
+
+ dx = nextX;
+ dx = 2 * dx - cx;
+
+ Geom::Point stx;
+ stx = (bx + cx) / 2;
+
+ if ( nbInterm > 1 ) {
+ AddPoint(stx, curP + nbInterm - 2, 1.0, false);
+ }
+
+ {
+ Geom::Point mx;
+ mx = (cx + dx) / 2;
+ RecBezierTo(cx, stx, mx, treshhold, 8, 0.0, 1.0, curP + nbInterm - 1);
+ }
+ }
+
+ }
+
+
+ AddPoint(nextX, curP - 1 + nbInterm, 1.0, false);
+
+ // et on avance
+ curP += 1 + nbInterm;
+ break;
+ }
+ }
+ curX = nextX;
+ }
+}
+
+
+void Path::Convert(double treshhold)
+{
+ if ( descr_flags & descr_adding_bezier ) {
+ CancelBezier();
+ }
+
+ if ( descr_flags & descr_doing_subpath ) {
+ CloseSubpath();
+ }
+
+ SetBackData(false);
+ ResetPoints();
+ if ( descr_cmd.empty() ) {
+ return;
+ }
+
+ Geom::Point curX;
+ int curP = 1;
+ int lastMoveTo = 0;
+
+ // le moveto
+ {
+ int const firstTyp = descr_cmd[0]->getType();
+ if ( firstTyp == descr_moveto ) {
+ curX = dynamic_cast<PathDescrMoveTo *>(descr_cmd[0])->p;
+ } else {
+ curP = 0;
+ curX[0] = curX[1] = 0;
+ }
+ lastMoveTo = AddPoint(curX, true);
+ }
+ descr_cmd[0]->associated = lastMoveTo;
+
+ // et le reste, 1 par 1
+ while ( curP < int(descr_cmd.size()) ) {
+
+ int const nType = descr_cmd[curP]->getType();
+ Geom::Point nextX;
+
+ switch (nType) {
+ case descr_forced: {
+ descr_cmd[curP]->associated = AddForcedPoint(curX);
+ curP++;
+ break;
+ }
+
+ case descr_moveto: {
+ PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[curP]);
+ nextX = nData->p;
+ lastMoveTo = AddPoint(nextX, true);
+ descr_cmd[curP]->associated = lastMoveTo;
+
+ // et on avance
+ curP++;
+ break;
+ }
+
+ case descr_close: {
+ nextX = pts[lastMoveTo].p;
+ descr_cmd[curP]->associated = AddPoint(nextX, false);
+ if ( descr_cmd[curP]->associated < 0 ) {
+ if ( curP == 0 ) {
+ descr_cmd[curP]->associated = 0;
+ } else {
+ descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
+ }
+ }
+ if ( descr_cmd[curP]->associated > 0 ) {
+ pts[descr_cmd[curP]->associated].closed = true;
+ }
+ curP++;
+ break;
+ }
+
+ case descr_lineto: {
+ PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[curP]);
+ nextX = nData->p;
+ descr_cmd[curP]->associated = AddPoint(nextX, false);
+ if ( descr_cmd[curP]->associated < 0 ) {
+ if ( curP == 0 ) {
+ descr_cmd[curP]->associated = 0;
+ } else {
+ descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
+ }
+ }
+ // et on avance
+ curP++;
+ break;
+ }
+
+ case descr_cubicto: {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[curP]);
+ nextX = nData->p;
+ RecCubicTo(curX, nData->start, nextX, nData->end, treshhold, 8);
+ descr_cmd[curP]->associated = AddPoint(nextX,false);
+ if ( descr_cmd[curP]->associated < 0 ) {
+ if ( curP == 0 ) {
+ descr_cmd[curP]->associated = 0;
+ } else {
+ descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
+ }
+ }
+ // et on avance
+ curP++;
+ break;
+ }
+
+ case descr_arcto: {
+ PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[curP]);
+ nextX = nData->p;
+ DoArc(curX, nextX, nData->rx, nData->ry, nData->angle, nData->large, nData->clockwise, treshhold);
+ descr_cmd[curP]->associated = AddPoint(nextX, false);
+ if ( descr_cmd[curP]->associated < 0 ) {
+ if ( curP == 0 ) {
+ descr_cmd[curP]->associated = 0;
+ } else {
+ descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
+ }
+ }
+ // et on avance
+ curP++;
+ break;
+ }
+
+ case descr_bezierto: {
+ PathDescrBezierTo *nBData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[curP]);
+ int nbInterm = nBData->nb;
+ nextX = nBData->p;
+ int curBD = curP;
+
+ curP++;
+ int ip = curP;
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
+
+ if ( nbInterm == 1 ) {
+ Geom::Point const midX = nData->p;
+ RecBezierTo(midX, curX, nextX, treshhold, 8);
+ } else if ( nbInterm > 1 ) {
+ Geom::Point bx = curX;
+ Geom::Point dx = nData->p;
+ Geom::Point cx = 2 * bx - dx;
+
+ ip++;
+ nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
+
+ for (int k = 0; k < nbInterm - 1; k++) {
+ bx = cx;
+ cx = dx;
+
+ dx = nData->p;
+ ip++;
+ nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
+
+ Geom::Point stx = (bx + cx) / 2;
+ if ( k > 0 ) {
+ descr_cmd[ip - 2]->associated = AddPoint(stx, false);
+ if ( descr_cmd[ip - 2]->associated < 0 ) {
+ if ( curP == 0 ) {
+ descr_cmd[ip - 2]->associated = 0;
+ } else {
+ descr_cmd[ip - 2]->associated = descr_cmd[ip - 3]->associated;
+ }
+ }
+ }
+
+ {
+ Geom::Point const mx = (cx + dx) / 2;
+ RecBezierTo(cx, stx, mx, treshhold, 8);
+ }
+ }
+
+ {
+ bx = cx;
+ cx = dx;
+
+ dx = nextX;
+ dx = 2 * dx - cx;
+
+ Geom::Point stx = (bx + cx) / 2;
+
+ descr_cmd[ip - 1]->associated = AddPoint(stx, false);
+ if ( descr_cmd[ip - 1]->associated < 0 ) {
+ if ( curP == 0 ) {
+ descr_cmd[ip - 1]->associated = 0;
+ } else {
+ descr_cmd[ip - 1]->associated = descr_cmd[ip - 2]->associated;
+ }
+ }
+
+ {
+ Geom::Point mx = (cx + dx) / 2;
+ RecBezierTo(cx, stx, mx, treshhold, 8);
+ }
+ }
+ }
+
+ descr_cmd[curBD]->associated = AddPoint(nextX, false);
+ if ( descr_cmd[curBD]->associated < 0 ) {
+ if ( curP == 0 ) {
+ descr_cmd[curBD]->associated = 0;
+ } else {
+ descr_cmd[curBD]->associated = descr_cmd[curBD - 1]->associated;
+ }
+ }
+
+ // et on avance
+ curP += nbInterm;
+ break;
+ }
+ }
+
+ curX = nextX;
+ }
+}
+
+void Path::ConvertEvenLines(double treshhold)
+{
+ if ( descr_flags & descr_adding_bezier ) {
+ CancelBezier();
+ }
+
+ if ( descr_flags & descr_doing_subpath ) {
+ CloseSubpath();
+ }
+
+ SetBackData(false);
+ ResetPoints();
+ if ( descr_cmd.empty() ) {
+ return;
+ }
+
+ Geom::Point curX;
+ int curP = 1;
+ int lastMoveTo = 0;
+
+ // le moveto
+ {
+ int const firstTyp = descr_cmd[0]->getType();
+ if ( firstTyp == descr_moveto ) {
+ curX = dynamic_cast<PathDescrMoveTo *>(descr_cmd[0])->p;
+ } else {
+ curP = 0;
+ curX[0] = curX[1] = 0;
+ }
+ lastMoveTo = AddPoint(curX, true);
+ }
+ descr_cmd[0]->associated = lastMoveTo;
+
+ // et le reste, 1 par 1
+ while ( curP < int(descr_cmd.size()) ) {
+
+ int const nType = descr_cmd[curP]->getType();
+ Geom::Point nextX;
+
+ switch (nType) {
+ case descr_forced: {
+ descr_cmd[curP]->associated = AddForcedPoint(curX);
+ curP++;
+ break;
+ }
+
+ case descr_moveto: {
+ PathDescrMoveTo* nData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[curP]);
+ nextX = nData->p;
+ lastMoveTo = AddPoint(nextX,true);
+ descr_cmd[curP]->associated = lastMoveTo;
+
+ // et on avance
+ curP++;
+ break;
+ }
+
+ case descr_close: {
+ nextX = pts[lastMoveTo].p;
+ {
+ Geom::Point nexcur;
+ nexcur = nextX - curX;
+ const double segL = Geom::L2(nexcur);
+ if ( (segL > treshhold) && (treshhold > 0) ) {
+ for (double i = treshhold; i < segL; i += treshhold) {
+ Geom::Point nX;
+ nX = (segL - i) * curX + i * nextX;
+ nX /= segL;
+ AddPoint(nX);
+ }
+ }
+ }
+
+ descr_cmd[curP]->associated = AddPoint(nextX,false);
+ if ( descr_cmd[curP]->associated < 0 ) {
+ if ( curP == 0 ) {
+ descr_cmd[curP]->associated = 0;
+ } else {
+ descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
+ }
+ }
+ if ( descr_cmd[curP]->associated > 0 ) {
+ pts[descr_cmd[curP]->associated].closed = true;
+ }
+ curP++;
+ break;
+ }
+
+ case descr_lineto: {
+ PathDescrLineTo* nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[curP]);
+ nextX = nData->p;
+ Geom::Point nexcur = nextX - curX;
+ const double segL = L2(nexcur);
+ if ( (segL > treshhold) && (treshhold > 0)) {
+ for (double i = treshhold; i < segL; i += treshhold) {
+ Geom::Point nX = ((segL - i) * curX + i * nextX) / segL;
+ AddPoint(nX);
+ }
+ }
+
+ descr_cmd[curP]->associated = AddPoint(nextX,false);
+ if ( descr_cmd[curP]->associated < 0 ) {
+ if ( curP == 0 ) {
+ descr_cmd[curP]->associated = 0;
+ } else {
+ descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
+ }
+ }
+ // et on avance
+ curP++;
+ break;
+ }
+
+ case descr_cubicto: {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[curP]);
+ nextX = nData->p;
+ RecCubicTo(curX, nData->start, nextX, nData->end, treshhold, 8, 4 * treshhold);
+ descr_cmd[curP]->associated = AddPoint(nextX, false);
+ if ( descr_cmd[curP]->associated < 0 ) {
+ if ( curP == 0 ) {
+ descr_cmd[curP]->associated = 0;
+ } else {
+ descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
+ }
+ }
+ // et on avance
+ curP++;
+ break;
+ }
+
+ case descr_arcto: {
+ PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[curP]);
+ nextX = nData->p;
+ DoArc(curX, nextX, nData->rx, nData->ry, nData->angle, nData->large, nData->clockwise, treshhold);
+ descr_cmd[curP]->associated =AddPoint(nextX, false);
+ if ( descr_cmd[curP]->associated < 0 ) {
+ if ( curP == 0 ) {
+ descr_cmd[curP]->associated = 0;
+ } else {
+ descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
+ }
+ }
+
+ // et on avance
+ curP++;
+ break;
+ }
+
+ case descr_bezierto: {
+ PathDescrBezierTo *nBData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[curP]);
+ int nbInterm = nBData->nb;
+ nextX = nBData->p;
+ int curBD = curP;
+
+ curP++;
+ int ip = curP;
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
+
+ if ( nbInterm == 1 ) {
+ Geom::Point const midX = nData->p;
+ RecBezierTo(midX, curX, nextX, treshhold, 8, 4 * treshhold);
+ } else if ( nbInterm > 1 ) {
+ Geom::Point bx = curX;
+ Geom::Point dx = nData->p;
+ Geom::Point cx = 2 * bx - dx;
+
+ ip++;
+ nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
+
+ for (int k = 0; k < nbInterm - 1; k++) {
+ bx = cx;
+ cx = dx;
+ dx = nData->p;
+
+ ip++;
+ nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
+
+ Geom::Point stx = (bx+cx) / 2;
+ if ( k > 0 ) {
+ descr_cmd[ip - 2]->associated = AddPoint(stx, false);
+ if ( descr_cmd[ip - 2]->associated < 0 ) {
+ if ( curP == 0 ) {
+ descr_cmd[ip- 2]->associated = 0;
+ } else {
+ descr_cmd[ip - 2]->associated = descr_cmd[ip - 3]->associated;
+ }
+ }
+ }
+
+ {
+ Geom::Point const mx = (cx + dx) / 2;
+ RecBezierTo(cx, stx, mx, treshhold, 8, 4 * treshhold);
+ }
+ }
+
+ {
+ bx = cx;
+ cx = dx;
+
+ dx = nextX;
+ dx = 2 * dx - cx;
+
+ Geom::Point const stx = (bx + cx) / 2;
+
+ descr_cmd[ip - 1]->associated = AddPoint(stx, false);
+ if ( descr_cmd[ip - 1]->associated < 0 ) {
+ if ( curP == 0 ) {
+ descr_cmd[ip - 1]->associated = 0;
+ } else {
+ descr_cmd[ip - 1]->associated = descr_cmd[ip - 2]->associated;
+ }
+ }
+
+ {
+ Geom::Point const mx = (cx + dx) / 2;
+ RecBezierTo(cx, stx, mx, treshhold, 8, 4 * treshhold);
+ }
+ }
+ }
+
+ descr_cmd[curBD]->associated = AddPoint(nextX, false);
+ if ( descr_cmd[curBD]->associated < 0 ) {
+ if ( curP == 0 ) {
+ descr_cmd[curBD]->associated = 0;
+ } else {
+ descr_cmd[curBD]->associated = descr_cmd[curBD - 1]->associated;
+ }
+ }
+
+ // et on avance
+ curP += nbInterm;
+ break;
+ }
+ }
+ if ( Geom::LInfty(curX - nextX) > 0.00001 ) {
+ curX = nextX;
+ }
+ }
+}
+
+const Geom::Point Path::PrevPoint(int i) const
+{
+ /* TODO: I suspect this should assert `(unsigned) i < descr_nb'. We can probably change
+ the argument to unsigned. descr_nb should probably be changed to unsigned too. */
+ g_assert( i >= 0 );
+ switch ( descr_cmd[i]->getType() ) {
+ case descr_moveto: {
+ PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[i]);
+ return nData->p;
+ }
+ case descr_lineto: {
+ PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[i]);
+ return nData->p;
+ }
+ case descr_arcto: {
+ PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[i]);
+ return nData->p;
+ }
+ case descr_cubicto: {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[i]);
+ return nData->p;
+ }
+ case descr_bezierto: {
+ PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[i]);
+ return nData->p;
+ }
+ case descr_interm_bezier:
+ case descr_close:
+ case descr_forced:
+ return PrevPoint(i - 1);
+ default:
+ g_assert_not_reached();
+ return Geom::Point(0, 0);
+ }
+}
+
+// utilitaries: given a quadratic bezier curve (start point, control point, end point, ie that's a clamped curve),
+// and an abcissis on it, get the point with that abcissis.
+// warning: it's NOT a curvilign abcissis (or whatever you call that in english), so "t" is NOT the length of "start point"->"result point"
+void Path::QuadraticPoint(double t, Geom::Point &oPt,
+ const Geom::Point &iS, const Geom::Point &iM, const Geom::Point &iE)
+{
+ Geom::Point const ax = iE - 2 * iM + iS;
+ Geom::Point const bx = 2 * iM - 2 * iS;
+ Geom::Point const cx = iS;
+
+ oPt = t * t * ax + t * bx + cx;
+}
+// idem for cubic bezier patch
+void Path::CubicTangent(double t, Geom::Point &oPt, const Geom::Point &iS, const Geom::Point &isD,
+ const Geom::Point &iE, const Geom::Point &ieD)
+{
+ Geom::Point const ax = ieD - 2 * iE + 2 * iS + isD;
+ Geom::Point const bx = 3 * iE - ieD - 2 * isD - 3 * iS;
+ Geom::Point const cx = isD;
+
+ oPt = 3 * t * t * ax + 2 * t * bx + cx;
+}
+
+// extract interesting info of a SVG arc description
+static void ArcAnglesAndCenter(Geom::Point const &iS, Geom::Point const &iE,
+ double rx, double ry, double angle,
+ bool large, bool wise,
+ double &sang, double &eang, Geom::Point &dr);
+
+void Path::ArcAngles(const Geom::Point &iS, const Geom::Point &iE,
+ double rx, double ry, double angle, bool large, bool wise, double &sang, double &eang)
+{
+ Geom::Point dr;
+ ArcAnglesAndCenter(iS, iE, rx, ry, angle, large, wise, sang, eang, dr);
+}
+
+/* N.B. If iS == iE then sang,eang,dr each become NaN. Probably a bug. */
+static void ArcAnglesAndCenter(Geom::Point const &iS, Geom::Point const &iE,
+ double rx, double ry, double angle,
+ bool large, bool wise,
+ double &sang, double &eang, Geom::Point &dr)
+{
+ Geom::Point se = iE - iS;
+ Geom::Point ca(cos(angle), sin(angle));
+ Geom::Point cse(dot(ca, se), cross(ca, se));
+ cse[0] /= rx;
+ cse[1] /= ry;
+ double const lensq = dot(cse,cse);
+ Geom::Point csd = ( ( lensq < 4
+ ? sqrt( 1/lensq - .25 )
+ : 0.0 )
+ * cse.ccw() );
+
+ Geom::Point ra = -csd - 0.5 * cse;
+ if ( ra[0] <= -1 ) {
+ sang = M_PI;
+ } else if ( ra[0] >= 1 ) {
+ sang = 0;
+ } else {
+ sang = acos(ra[0]);
+ if ( ra[1] < 0 ) {
+ sang = 2 * M_PI - sang;
+ }
+ }
+
+ ra = -csd + 0.5 * cse;
+ if ( ra[0] <= -1 ) {
+ eang = M_PI;
+ } else if ( ra[0] >= 1 ) {
+ eang = 0;
+ } else {
+ eang = acos(ra[0]);
+ if ( ra[1] < 0 ) {
+ eang = 2 * M_PI - eang;
+ }
+ }
+
+ csd[0] *= rx;
+ csd[1] *= ry;
+ ca[1] = -ca[1]; // because it's the inverse rotation
+
+ dr[0] = dot(ca, csd);
+ dr[1] = cross(ca, csd);
+
+ ca[1] = -ca[1];
+
+ if ( wise ) {
+
+ if (large) {
+ dr = -dr;
+ double swap = eang;
+ eang = sang;
+ sang = swap;
+ eang += M_PI;
+ sang += M_PI;
+ if ( eang >= 2*M_PI ) {
+ eang -= 2*M_PI;
+ }
+ if ( sang >= 2*M_PI ) {
+ sang -= 2*M_PI;
+ }
+ }
+
+ } else {
+ if (!large) {
+ dr = -dr;
+ double swap = eang;
+ eang = sang;
+ sang = swap;
+ eang += M_PI;
+ sang += M_PI;
+ if ( eang >= 2*M_PI ) {
+ eang -= 2 * M_PI;
+ }
+ if ( sang >= 2*M_PI ) {
+ sang -= 2 * M_PI;
+ }
+ }
+ }
+
+ dr += 0.5 * (iS + iE);
+}
+
+
+
+void Path::DoArc(Geom::Point const &iS, Geom::Point const &iE,
+ double const rx, double const ry, double const angle,
+ bool const large, bool const wise, double const /*tresh*/)
+{
+ /* TODO: Check that our behaviour is standards-conformant if iS and iE are (much) further
+ apart than the diameter. Also check that we do the right thing for negative radius.
+ (Same for the other DoArc functions in this file.) */
+ if ( rx <= 0.0001 || ry <= 0.0001 ) {
+ return;
+ // We always add a lineto afterwards, so this is fine.
+ // [on ajoute toujours un lineto apres, donc c bon]
+ }
+
+ double sang;
+ double eang;
+ Geom::Point dr_temp;
+ ArcAnglesAndCenter(iS, iE, rx, ry, angle*M_PI/180.0, large, wise, sang, eang, dr_temp);
+ Geom::Point dr = dr_temp;
+ /* TODO: This isn't as good numerically as treating iS and iE as primary. E.g. consider
+ the case of low curvature (i.e. very large radius). */
+
+ Geom::Scale const ar(rx, ry);
+ Geom::Rotate cb(sang);
+ Geom::Rotate cbangle(angle*M_PI/180.0);
+
+ if (wise) {
+
+ double const incr = -0.1/sqrt(ar.vector().length());
+ if ( sang < eang ) {
+ sang += 2*M_PI;
+ }
+ Geom::Rotate const omega(incr);
+ for (double b = sang + incr ; b > eang ; b += incr) {
+ cb = omega * cb;
+ AddPoint( cb.vector() * ar * cbangle + dr );
+ }
+
+ } else {
+
+ double const incr = 0.1/sqrt(ar.vector().length());
+ if ( sang > eang ) {
+ sang -= 2*M_PI;
+ }
+ Geom::Rotate const omega(incr);
+ for (double b = sang + incr ; b < eang ; b += incr) {
+ cb = omega * cb;
+ AddPoint( cb.vector() * ar * cbangle + dr);
+ }
+ }
+}
+
+
+void Path::RecCubicTo( Geom::Point const &iS, Geom::Point const &isD,
+ Geom::Point const &iE, Geom::Point const &ieD,
+ double tresh, int lev, double maxL)
+{
+ Geom::Point se = iE - iS;
+ const double dC = Geom::L2(se);
+ if ( dC < 0.01 ) {
+
+ const double sC = dot(isD,isD);
+ const double eC = dot(ieD,ieD);
+ if ( sC < tresh && eC < tresh ) {
+ return;
+ }
+
+ } else {
+ const double sC = fabs(cross(se, isD)) / dC;
+ const double eC = fabs(cross(se, ieD)) / dC;
+ if ( sC < tresh && eC < tresh ) {
+ // presque tt droit -> attention si on nous demande de bien subdiviser les petits segments
+ if ( maxL > 0 && dC > maxL ) {
+ if ( lev <= 0 ) {
+ return;
+ }
+ Geom::Point m = 0.5 * (iS + iE) + 0.125 * (isD - ieD);
+ Geom::Point md = 0.75 * (iE - iS) - 0.125 * (isD + ieD);
+
+ Geom::Point hisD = 0.5 * isD;
+ Geom::Point hieD = 0.5 * ieD;
+
+ RecCubicTo(iS, hisD, m, md, tresh, lev - 1, maxL);
+ AddPoint(m);
+ RecCubicTo(m, md, iE, hieD, tresh, lev - 1,maxL);
+ }
+ return;
+ }
+ }
+
+ if ( lev <= 0 ) {
+ return;
+ }
+
+ {
+ Geom::Point m = 0.5 * (iS + iE) + 0.125 * (isD - ieD);
+ Geom::Point md = 0.75 * (iE - iS) - 0.125 * (isD + ieD);
+
+ Geom::Point hisD = 0.5 * isD;
+ Geom::Point hieD = 0.5 * ieD;
+
+ RecCubicTo(iS, hisD, m, md, tresh, lev - 1, maxL);
+ AddPoint(m);
+ RecCubicTo(m, md, iE, hieD, tresh, lev - 1,maxL);
+ }
+}
+
+
+
+void Path::RecBezierTo(const Geom::Point &iP,
+ const Geom::Point &iS,
+ const Geom::Point &iE,
+ double tresh, int lev, double maxL)
+{
+ if ( lev <= 0 ) {
+ return;
+ }
+
+ Geom::Point ps = iS - iP;
+ Geom::Point pe = iE - iP;
+ Geom::Point se = iE - iS;
+ double s = fabs(cross(pe, ps));
+ if ( s < tresh ) {
+ const double l = L2(se);
+ if ( maxL > 0 && l > maxL ) {
+ const Geom::Point m = 0.25 * (iS + iE + 2 * iP);
+ Geom::Point md = 0.5 * (iS + iP);
+ RecBezierTo(md, iS, m, tresh, lev - 1, maxL);
+ AddPoint(m);
+ md = 0.5 * (iP + iE);
+ RecBezierTo(md, m, iE, tresh, lev - 1, maxL);
+ }
+ return;
+ }
+
+ {
+ const Geom::Point m = 0.25 * (iS + iE + 2 * iP);
+ Geom::Point md = 0.5 * (iS + iP);
+ RecBezierTo(md, iS, m, tresh, lev - 1, maxL);
+ AddPoint(m);
+ md = 0.5 * (iP + iE);
+ RecBezierTo(md, m, iE, tresh, lev - 1, maxL);
+ }
+}
+
+
+void Path::DoArc(Geom::Point const &iS, Geom::Point const &iE,
+ double const rx, double const ry, double const angle,
+ bool const large, bool const wise, double const /*tresh*/, int const piece)
+{
+ /* TODO: Check that our behaviour is standards-conformant if iS and iE are (much) further
+ apart than the diameter. Also check that we do the right thing for negative radius.
+ (Same for the other DoArc functions in this file.) */
+ if ( rx <= 0.0001 || ry <= 0.0001 ) {
+ return;
+ // We always add a lineto afterwards, so this is fine.
+ // [on ajoute toujours un lineto apres, donc c bon]
+ }
+
+ double sang;
+ double eang;
+ Geom::Point dr_temp;
+ ArcAnglesAndCenter(iS, iE, rx, ry, angle*M_PI/180.0, large, wise, sang, eang, dr_temp);
+ Geom::Point dr = dr_temp;
+ /* TODO: This isn't as good numerically as treating iS and iE as primary. E.g. consider
+ the case of low curvature (i.e. very large radius). */
+
+ Geom::Scale const ar(rx, ry);
+ Geom::Rotate cb(sang);
+ Geom::Rotate cbangle(angle*M_PI/180.0);
+
+ if (wise) {
+ double const incr = -0.1/sqrt(ar.vector().length());
+ if ( sang < eang ) {
+ sang += 2*M_PI;
+ }
+ Geom::Rotate const omega(incr);
+ for (double b = sang + incr; b > eang; b += incr) {
+ cb = omega * cb;
+ AddPoint(cb.vector() * ar * cbangle + dr, piece, (sang - b) / (sang - eang));
+ }
+
+ } else {
+
+ double const incr = 0.1/sqrt(ar.vector().length());
+ if ( sang > eang ) {
+ sang -= 2 * M_PI;
+ }
+ Geom::Rotate const omega(incr);
+ for (double b = sang + incr ; b < eang ; b += incr) {
+ cb = omega * cb;
+ AddPoint(cb.vector() * ar * cbangle + dr, piece, (b - sang) / (eang - sang));
+ }
+ }
+}
+
+void Path::RecCubicTo(Geom::Point const &iS, Geom::Point const &isD,
+ Geom::Point const &iE, Geom::Point const &ieD,
+ double tresh, int lev, double st, double et, int piece)
+{
+ const Geom::Point se = iE - iS;
+ const double dC = Geom::L2(se);
+ if ( dC < 0.01 ) {
+ const double sC = dot(isD, isD);
+ const double eC = dot(ieD, ieD);
+ if ( sC < tresh && eC < tresh ) {
+ return;
+ }
+ } else {
+ const double sC = fabs(cross(se, isD)) / dC;
+ const double eC = fabs(cross(se, ieD)) / dC;
+ if ( sC < tresh && eC < tresh ) {
+ return;
+ }
+ }
+
+ if ( lev <= 0 ) {
+ return;
+ }
+
+ Geom::Point m = 0.5 * (iS + iE) + 0.125 * (isD - ieD);
+ Geom::Point md = 0.75 * (iE - iS) - 0.125 * (isD + ieD);
+ double mt = (st + et) / 2;
+
+ Geom::Point hisD = 0.5 * isD;
+ Geom::Point hieD = 0.5 * ieD;
+
+ RecCubicTo(iS, hisD, m, md, tresh, lev - 1, st, mt, piece);
+ AddPoint(m, piece, mt);
+ RecCubicTo(m, md, iE, hieD, tresh, lev - 1, mt, et, piece);
+
+}
+
+
+
+void Path::RecBezierTo(Geom::Point const &iP,
+ Geom::Point const &iS,
+ Geom::Point const &iE,
+ double tresh, int lev, double st, double et, int piece)
+{
+ if ( lev <= 0 ) {
+ return;
+ }
+
+ Geom::Point ps = iS - iP;
+ Geom::Point pe = iE - iP;
+ const double s = fabs(cross(pe, ps));
+ if ( s < tresh ) {
+ return;
+ }
+
+ {
+ const double mt = (st + et) / 2;
+ const Geom::Point m = 0.25 * (iS + iE + 2 * iP);
+ RecBezierTo(0.5 * (iS + iP), iS, m, tresh, lev - 1, st, mt, piece);
+ AddPoint(m, piece, mt);
+ RecBezierTo(0.5 * (iP + iE), m, iE, tresh, lev - 1, mt, et, piece);
+ }
+}
+
+
+
+void Path::DoArc(Geom::Point const &iS, Geom::Point const &iE,
+ double const rx, double const ry, double const angle,
+ bool const large, bool const wise, double const /*tresh*/,
+ int const piece, offset_orig &/*orig*/)
+{
+ // Will never arrive here, as offsets are made of cubics.
+ // [on n'arrivera jamais ici, puisque les offsets sont fait de cubiques]
+ /* TODO: Check that our behaviour is standards-conformant if iS and iE are (much) further
+ apart than the diameter. Also check that we do the right thing for negative radius.
+ (Same for the other DoArc functions in this file.) */
+ if ( rx <= 0.0001 || ry <= 0.0001 ) {
+ return;
+ // We always add a lineto afterwards, so this is fine.
+ // [on ajoute toujours un lineto apres, donc c bon]
+ }
+
+ double sang;
+ double eang;
+ Geom::Point dr_temp;
+ ArcAnglesAndCenter(iS, iE, rx, ry, angle*M_PI/180.0, large, wise, sang, eang, dr_temp);
+ Geom::Point dr = dr_temp;
+ /* TODO: This isn't as good numerically as treating iS and iE as primary. E.g. consider
+ the case of low curvature (i.e. very large radius). */
+
+ Geom::Scale const ar(rx, ry);
+ Geom::Rotate cb(sang);
+ Geom::Rotate cbangle(angle*M_PI/180.0);
+ if (wise) {
+
+ double const incr = -0.1/sqrt(ar.vector().length());
+ if ( sang < eang ) {
+ sang += 2*M_PI;
+ }
+ Geom::Rotate const omega(incr);
+ for (double b = sang + incr; b > eang ;b += incr) {
+ cb = omega * cb;
+ AddPoint(cb.vector() * ar * cbangle + dr, piece, (sang - b) / (sang - eang));
+ }
+
+ } else {
+ double const incr = 0.1/sqrt(ar.vector().length());
+ if ( sang > eang ) {
+ sang -= 2*M_PI;
+ }
+ Geom::Rotate const omega(incr);
+ for (double b = sang + incr ; b < eang ; b += incr) {
+ cb = omega * cb;
+ AddPoint(cb.vector() * ar * cbangle + dr, piece, (b - sang) / (eang - sang));
+ }
+ }
+}
+
+
+void Path::RecCubicTo(Geom::Point const &iS, Geom::Point const &isD,
+ Geom::Point const &iE, Geom::Point const &ieD,
+ double tresh, int lev, double st, double et,
+ int piece, offset_orig &orig)
+{
+ const Geom::Point se = iE - iS;
+ const double dC = Geom::L2(se);
+ bool doneSub = false;
+ if ( dC < 0.01 ) {
+ const double sC = dot(isD, isD);
+ const double eC = dot(ieD, ieD);
+ if ( sC < tresh && eC < tresh ) {
+ return;
+ }
+ } else {
+ const double sC = fabs(cross(se, isD)) / dC;
+ const double eC = fabs(cross(se, ieD)) / dC;
+ if ( sC < tresh && eC < tresh ) {
+ doneSub = true;
+ }
+ }
+
+ if ( lev <= 0 ) {
+ doneSub = true;
+ }
+
+ // test des inversions
+ bool stInv = false;
+ bool enInv = false;
+ {
+ Geom::Point os_pos;
+ Geom::Point os_tgt;
+ Geom::Point oe_pos;
+ Geom::Point oe_tgt;
+
+ orig.orig->PointAndTangentAt(orig.piece, orig.tSt * (1 - st) + orig.tEn * st, os_pos, os_tgt);
+ orig.orig->PointAndTangentAt(orig.piece, orig.tSt * (1 - et) + orig.tEn * et, oe_pos, oe_tgt);
+
+
+ Geom::Point n_tgt = isD;
+ double si = dot(n_tgt, os_tgt);
+ if ( si < 0 ) {
+ stInv = true;
+ }
+ n_tgt = ieD;
+ si = dot(n_tgt, oe_tgt);
+ if ( si < 0 ) {
+ enInv = true;
+ }
+ if ( stInv && enInv ) {
+
+ AddPoint(os_pos, -1, 0.0);
+ AddPoint(iE, piece, et);
+ AddPoint(iS, piece, st);
+ AddPoint(oe_pos, -1, 0.0);
+ return;
+
+ } else if ( ( stInv && !enInv ) || ( !stInv && enInv ) ) {
+ return;
+ }
+
+ }
+
+ if ( ( !stInv && !enInv && doneSub ) || lev <= 0 ) {
+ return;
+ }
+
+ {
+ const Geom::Point m = 0.5 * (iS+iE) + 0.125 * (isD - ieD);
+ const Geom::Point md = 0.75 * (iE - iS) - 0.125 * (isD + ieD);
+ const double mt = (st + et) / 2;
+ const Geom::Point hisD = 0.5 * isD;
+ const Geom::Point hieD = 0.5 * ieD;
+
+ RecCubicTo(iS, hisD, m, md, tresh, lev - 1, st, mt, piece, orig);
+ AddPoint(m, piece, mt);
+ RecCubicTo(m, md, iE, hieD, tresh, lev - 1, mt, et, piece, orig);
+ }
+}
+
+
+
+void Path::RecBezierTo(Geom::Point const &iP, Geom::Point const &iS,Geom::Point const &iE,
+ double tresh, int lev, double st, double et,
+ int piece, offset_orig& orig)
+{
+ bool doneSub = false;
+ if ( lev <= 0 ) {
+ return;
+ }
+
+ const Geom::Point ps = iS - iP;
+ const Geom::Point pe = iE - iP;
+ const double s = fabs(cross(pe, ps));
+ if ( s < tresh ) {
+ doneSub = true ;
+ }
+
+ // test des inversions
+ bool stInv = false;
+ bool enInv = false;
+ {
+ Geom::Point os_pos;
+ Geom::Point os_tgt;
+ Geom::Point oe_pos;
+ Geom::Point oe_tgt;
+ Geom::Point n_tgt;
+ Geom::Point n_pos;
+
+ double n_len;
+ double n_rad;
+ PathDescrIntermBezierTo mid(iP);
+ PathDescrBezierTo fin(iE, 1);
+
+ TangentOnBezAt(0.0, iS, mid, fin, false, n_pos, n_tgt, n_len, n_rad);
+ orig.orig->PointAndTangentAt(orig.piece, orig.tSt * (1 - st) + orig.tEn * st, os_pos, os_tgt);
+ double si = dot(n_tgt, os_tgt);
+ if ( si < 0 ) {
+ stInv = true;
+ }
+
+ TangentOnBezAt(1.0, iS, mid, fin, false, n_pos, n_tgt, n_len, n_rad);
+ orig.orig->PointAndTangentAt(orig.piece, orig.tSt * (1 - et) + orig.tEn * et, oe_pos, oe_tgt);
+ si = dot(n_tgt, oe_tgt);
+ if ( si < 0 ) {
+ enInv = true;
+ }
+
+ if ( stInv && enInv ) {
+ AddPoint(os_pos, -1, 0.0);
+ AddPoint(iE, piece, et);
+ AddPoint(iS, piece, st);
+ AddPoint(oe_pos, -1, 0.0);
+ return;
+ }
+ }
+
+ if ( !stInv && !enInv && doneSub ) {
+ return;
+ }
+
+ {
+ double mt = (st + et) / 2;
+ Geom::Point m = 0.25 * (iS + iE + 2 * iP);
+ Geom::Point md = 0.5 * (iS + iP);
+ RecBezierTo(md, iS, m, tresh, lev - 1, st, mt, piece, orig);
+ AddPoint(m, piece, mt);
+ md = 0.5 * (iP + iE);
+ RecBezierTo(md, m, iE, tresh, lev - 1, mt, et, piece, orig);
+ }
+}
+
+
+/*
+ * put a polyline in a Shape instance, for further fun
+ * pathID is the ID you want this Path instance to be associated with, for when you're going to recompose the polyline
+ * in a path description ( you need to have prepared the back data for that, of course)
+ */
+
+void Path::Fill(Shape* dest, int pathID, bool justAdd, bool closeIfNeeded, bool invert)
+{
+ if ( dest == nullptr ) {
+ return;
+ }
+
+ if ( justAdd == false ) {
+ dest->Reset(pts.size(), pts.size());
+ }
+
+ if ( pts.size() <= 1 ) {
+ return;
+ }
+
+ int first = dest->numberOfPoints();
+
+ if ( back ) {
+ dest->MakeBackData(true);
+ }
+
+ if ( invert ) {
+ if ( back ) {
+ {
+ // invert && back && !weighted
+ for (auto & pt : pts) {
+ dest->AddPoint(pt.p);
+ }
+ int lastM = 0;
+ int curP = 1;
+ int pathEnd = 0;
+ bool closed = false;
+ int lEdge = -1;
+
+ while ( curP < int(pts.size()) ) {
+ int sbp = curP;
+ int lm = lastM;
+ int prp = pathEnd;
+
+ if ( pts[sbp].isMoveTo == polyline_moveto ) {
+
+ if ( closeIfNeeded ) {
+ if ( closed && lEdge >= 0 ) {
+ dest->DisconnectStart(lEdge);
+ dest->ConnectStart(first + lastM, lEdge);
+ } else {
+ lEdge = dest->AddEdge(first + lastM, first+pathEnd);
+ if ( lEdge >= 0 ) {
+ dest->ebData[lEdge].pathID = pathID;
+ dest->ebData[lEdge].pieceID = pts[lm].piece;
+ dest->ebData[lEdge].tSt = 1.0;
+ dest->ebData[lEdge].tEn = 0.0;
+ }
+ }
+ }
+
+ lastM = curP;
+ pathEnd = curP;
+ closed = false;
+ lEdge = -1;
+
+ } else {
+
+ if ( Geom::LInfty(pts[sbp].p - pts[prp].p) >= 0.00001 ) {
+ lEdge = dest->AddEdge(first + curP, first + pathEnd);
+ if ( lEdge >= 0 ) {
+ dest->ebData[lEdge].pathID = pathID;
+ dest->ebData[lEdge].pieceID = pts[sbp].piece;
+ if ( pts[sbp].piece == pts[prp].piece ) {
+ dest->ebData[lEdge].tSt = pts[sbp].t;
+ dest->ebData[lEdge].tEn = pts[prp].t;
+ } else {
+ dest->ebData[lEdge].tSt = pts[sbp].t;
+ dest->ebData[lEdge].tEn = 0.0;
+ }
+ }
+ pathEnd = curP;
+ if ( Geom::LInfty(pts[sbp].p - pts[lm].p) < 0.00001 ) {
+ closed = true;
+ } else {
+ closed = false;
+ }
+ }
+ }
+
+ curP++;
+ }
+
+ if ( closeIfNeeded ) {
+ if ( closed && lEdge >= 0 ) {
+ dest->DisconnectStart(lEdge);
+ dest->ConnectStart(first + lastM, lEdge);
+ } else {
+ int lm = lastM;
+ lEdge = dest->AddEdge(first + lastM, first + pathEnd);
+ if ( lEdge >= 0 ) {
+ dest->ebData[lEdge].pathID = pathID;
+ dest->ebData[lEdge].pieceID = pts[lm].piece;
+ dest->ebData[lEdge].tSt = 1.0;
+ dest->ebData[lEdge].tEn = 0.0;
+ }
+ }
+ }
+ }
+
+ } else {
+
+ {
+ // invert && !back && !weighted
+ for (auto & pt : pts) {
+ dest->AddPoint(pt.p);
+ }
+ int lastM = 0;
+ int curP = 1;
+ int pathEnd = 0;
+ bool closed = false;
+ int lEdge = -1;
+ while ( curP < int(pts.size()) ) {
+ int sbp = curP;
+ int lm = lastM;
+ int prp = pathEnd;
+ if ( pts[sbp].isMoveTo == polyline_moveto ) {
+ if ( closeIfNeeded ) {
+ if ( closed && lEdge >= 0 ) {
+ dest->DisconnectStart(lEdge);
+ dest->ConnectStart(first + lastM, lEdge);
+ } else {
+ dest->AddEdge(first + lastM, first + pathEnd);
+ }
+ }
+ lastM = curP;
+ pathEnd = curP;
+ closed = false;
+ lEdge = -1;
+ } else {
+ if ( Geom::LInfty(pts[sbp].p - pts[prp].p) >= 0.00001 ) {
+ lEdge = dest->AddEdge(first+curP, first+pathEnd);
+ pathEnd = curP;
+ if ( Geom::LInfty(pts[sbp].p - pts[lm].p) < 0.00001 ) {
+ closed = true;
+ } else {
+ closed = false;
+ }
+ }
+ }
+ curP++;
+ }
+
+ if ( closeIfNeeded ) {
+ if ( closed && lEdge >= 0 ) {
+ dest->DisconnectStart(lEdge);
+ dest->ConnectStart(first + lastM, lEdge);
+ } else {
+ dest->AddEdge(first + lastM, first + pathEnd);
+ }
+ }
+
+ }
+ }
+
+ } else {
+
+ if ( back ) {
+ {
+ // !invert && back && !weighted
+ for (auto & pt : pts) {
+ dest->AddPoint(pt.p);
+ }
+
+ int lastM = 0;
+ int curP = 1;
+ int pathEnd = 0;
+ bool closed = false;
+ int lEdge = -1;
+ while ( curP < int(pts.size()) ) {
+ int sbp = curP;
+ int lm = lastM;
+ int prp = pathEnd;
+ if ( pts[sbp].isMoveTo == polyline_moveto ) {
+ if ( closeIfNeeded ) {
+ if ( closed && lEdge >= 0 ) {
+ dest->DisconnectEnd(lEdge);
+ dest->ConnectEnd(first + lastM, lEdge);
+ } else {
+ lEdge = dest->AddEdge(first + pathEnd, first+lastM);
+ if ( lEdge >= 0 ) {
+ dest->ebData[lEdge].pathID = pathID;
+ dest->ebData[lEdge].pieceID = pts[lm].piece;
+ dest->ebData[lEdge].tSt = 0.0;
+ dest->ebData[lEdge].tEn = 1.0;
+ }
+ }
+ }
+ lastM = curP;
+ pathEnd = curP;
+ closed = false;
+ lEdge = -1;
+ } else {
+ if ( Geom::LInfty(pts[sbp].p - pts[prp].p) >= 0.00001 ) {
+ lEdge = dest->AddEdge(first + pathEnd, first + curP);
+ dest->ebData[lEdge].pathID = pathID;
+ dest->ebData[lEdge].pieceID = pts[sbp].piece;
+ if ( pts[sbp].piece == pts[prp].piece ) {
+ dest->ebData[lEdge].tSt = pts[prp].t;
+ dest->ebData[lEdge].tEn = pts[sbp].t;
+ } else {
+ dest->ebData[lEdge].tSt = 0.0;
+ dest->ebData[lEdge].tEn = pts[sbp].t;
+ }
+ pathEnd = curP;
+ if ( Geom::LInfty(pts[sbp].p - pts[lm].p) < 0.00001 ) {
+ closed = true;
+ } else {
+ closed = false;
+ }
+ }
+ }
+ curP++;
+ }
+
+ if ( closeIfNeeded ) {
+ if ( closed && lEdge >= 0 ) {
+ dest->DisconnectEnd(lEdge);
+ dest->ConnectEnd(first + lastM, lEdge);
+ } else {
+ int lm = lastM;
+ lEdge = dest->AddEdge(first + pathEnd, first + lastM);
+ if ( lEdge >= 0 ) {
+ dest->ebData[lEdge].pathID = pathID;
+ dest->ebData[lEdge].pieceID = pts[lm].piece;
+ dest->ebData[lEdge].tSt = 0.0;
+ dest->ebData[lEdge].tEn = 1.0;
+ }
+ }
+ }
+ }
+
+ } else {
+ {
+ // !invert && !back && !weighted
+ for (auto & pt : pts) {
+ dest->AddPoint(pt.p);
+ }
+
+ int lastM = 0;
+ int curP = 1;
+ int pathEnd = 0;
+ bool closed = false;
+ int lEdge = -1;
+ while ( curP < int(pts.size()) ) {
+ int sbp = curP;
+ int lm = lastM;
+ int prp = pathEnd;
+ if ( pts[sbp].isMoveTo == polyline_moveto ) {
+ if ( closeIfNeeded ) {
+ if ( closed && lEdge >= 0 ) {
+ dest->DisconnectEnd(lEdge);
+ dest->ConnectEnd(first + lastM, lEdge);
+ } else {
+ dest->AddEdge(first + pathEnd, first + lastM);
+ }
+ }
+ lastM = curP;
+ pathEnd = curP;
+ closed = false;
+ lEdge = -1;
+ } else {
+ if ( Geom::LInfty(pts[sbp].p - pts[prp].p) >= 0.00001 ) {
+ lEdge = dest->AddEdge(first+pathEnd, first+curP);
+ pathEnd = curP;
+ if ( Geom::LInfty(pts[sbp].p - pts[lm].p) < 0.00001 ) {
+ closed = true;
+ } else {
+ closed = false;
+ }
+ }
+ }
+ curP++;
+ }
+
+ if ( closeIfNeeded ) {
+ if ( closed && lEdge >= 0 ) {
+ dest->DisconnectEnd(lEdge);
+ dest->ConnectEnd(first + lastM, lEdge);
+ } else {
+ dest->AddEdge(first + pathEnd, first + lastM);
+ }
+ }
+
+ }
+ }
+ }
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/livarot/PathCutting.cpp b/src/livarot/PathCutting.cpp
new file mode 100644
index 0000000..ae164c4
--- /dev/null
+++ b/src/livarot/PathCutting.cpp
@@ -0,0 +1,1534 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PathCutting.cpp
+ * nlivarot
+ *
+ * Created by fred on someday in 2004.
+ * public domain
+ *
+ * Additional Code by Authors:
+ * Richard Hughes <cyreve@users.sf.net>
+ *
+ * Copyright (C) 2005 Richard Hughes
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <cstring>
+#include <string>
+#include <cstdio>
+#include <typeinfo>
+#include "Path.h"
+#include "style.h"
+#include "livarot/path-description.h"
+#include <2geom/pathvector.h>
+#include <2geom/point.h>
+#include <2geom/affine.h>
+#include <2geom/sbasis-to-bezier.h>
+#include <2geom/curves.h>
+#include "helper/geom-curves.h"
+#include "helper/geom.h"
+
+#include "svg/svg.h"
+
+void Path::DashPolyline(float head,float tail,float body,int nbD,float *dashs,bool stPlain,float stOffset)
+{
+ if ( nbD <= 0 || body <= 0.0001 ) return; // pas de tirets, en fait
+
+ std::vector<path_lineto> orig_pts = pts;
+ pts.clear();
+
+ int lastMI=-1;
+ int curP = 0;
+ int lastMP = -1;
+
+ for (int i = 0; i < int(orig_pts.size()); i++) {
+ if ( orig_pts[curP].isMoveTo == polyline_moveto ) {
+ if ( lastMI >= 0 && lastMI < i-1 ) { // au moins 2 points
+ DashSubPath(i-lastMI,lastMP, orig_pts, head,tail,body,nbD,dashs,stPlain,stOffset);
+ }
+ lastMI=i;
+ lastMP=curP;
+ }
+ curP++;
+ }
+ if ( lastMI >= 0 && lastMI < int(orig_pts.size()) - 1 ) {
+ DashSubPath(orig_pts.size() - lastMI, lastMP, orig_pts, head, tail, body, nbD, dashs, stPlain, stOffset);
+ }
+}
+
+void Path::DashPolylineFromStyle(SPStyle *style, float scale, float min_len)
+{
+ if (!style->stroke_dasharray.values.empty()) {
+
+ double dlen = 0.0;
+ // Find total length
+ for (auto & value : style->stroke_dasharray.values) {
+ dlen += value.value * scale;
+ }
+ if (dlen >= min_len) {
+ // Extract out dash pattern (relative positions)
+ double dash_offset = style->stroke_dashoffset.value * scale;
+ size_t n_dash = style->stroke_dasharray.values.size();
+ double *dash = g_new(double, n_dash);
+ for (unsigned i = 0; i < n_dash; i++) {
+ dash[i] = style->stroke_dasharray.values[i].value * scale;
+ }
+
+ // Convert relative positions to absolute positions
+ int nbD = n_dash;
+ float *dashs=(float*)malloc((nbD+1)*sizeof(float));
+ while ( dash_offset >= dlen ) dash_offset-=dlen;
+ dashs[0]=dash[0];
+ for (int i=1; i<nbD; i++) {
+ dashs[i]=dashs[i-1]+dash[i];
+ }
+
+ // modulo dlen
+ this->DashPolyline(0.0, 0.0, dlen, nbD, dashs, true, dash_offset);
+
+ free(dashs);
+ g_free(dash);
+ }
+ }
+}
+
+
+void Path::DashSubPath(int spL, int spP, std::vector<path_lineto> const &orig_pts, float head,float tail,float body,int nbD,float *dashs,bool stPlain,float stOffset)
+{
+ if ( spL <= 0 || spP == -1 ) return;
+
+ double totLength=0;
+ Geom::Point lastP;
+ lastP = orig_pts[spP].p;
+ for (int i=1;i<spL;i++) {
+ Geom::Point const n = orig_pts[spP + i].p;
+ Geom::Point d=n-lastP;
+ double nl=Geom::L2(d);
+ if ( nl > 0.0001 ) {
+ totLength+=nl;
+ lastP=n;
+ }
+ }
+
+ if ( totLength <= head+tail ) return; // tout mange par la tete et la queue
+
+ double curLength=0;
+ double dashPos=0;
+ int dashInd=0;
+ bool dashPlain=false;
+ double lastT=0;
+ int lastPiece=-1;
+ lastP = orig_pts[spP].p;
+ for (int i=1;i<spL;i++) {
+ Geom::Point n;
+ int nPiece=-1;
+ double nT=0;
+ if ( back ) {
+ n = orig_pts[spP + i].p;
+ nPiece = orig_pts[spP + i].piece;
+ nT = orig_pts[spP + i].t;
+ } else {
+ n = orig_pts[spP + i].p;
+ }
+ Geom::Point d=n-lastP;
+ double nl=Geom::L2(d);
+ if ( nl > 0.0001 ) {
+ double stLength=curLength;
+ double enLength=curLength+nl;
+ // couper les bouts en trop
+ if ( curLength <= head && curLength+nl > head ) {
+ nl-=head-curLength;
+ curLength=head;
+ dashInd=0;
+ dashPos=stOffset;
+ bool nPlain=stPlain;
+ while ( dashs[dashInd] < stOffset ) {
+ dashInd++;
+ nPlain=!(nPlain);
+ if ( dashInd >= nbD ) {
+ dashPos=0;
+ dashInd=0;
+ break;
+ }
+ }
+ if ( nPlain == true && dashPlain == false ) {
+ Geom::Point p=(enLength-curLength)*lastP+(curLength-stLength)*n;
+ p/=(enLength-stLength);
+ if ( back ) {
+ double pT=0;
+ if ( nPiece == lastPiece ) {
+ pT=(lastT*(enLength-curLength)+nT*(curLength-stLength))/(enLength-stLength);
+ } else {
+ pT=(nPiece*(curLength-stLength))/(enLength-stLength);
+ }
+ AddPoint(p,nPiece,pT,true);
+ } else {
+ AddPoint(p,true);
+ }
+ } else if ( nPlain == false && dashPlain == true ) {
+ }
+ dashPlain=nPlain;
+ }
+ // faire les tirets
+ if ( curLength >= head /*&& curLength+nl <= totLength-tail*/ ) {
+ while ( curLength <= totLength-tail && nl > 0 ) {
+ if ( enLength <= totLength-tail ) nl=enLength-curLength; else nl=totLength-tail-curLength;
+ double leftInDash=body-dashPos;
+ if ( dashInd < nbD ) {
+ leftInDash=dashs[dashInd]-dashPos;
+ }
+ if ( leftInDash <= nl ) {
+ bool nPlain=false;
+ if ( dashInd < nbD ) {
+ dashPos=dashs[dashInd];
+ dashInd++;
+ if ( dashPlain ) nPlain=false; else nPlain=true;
+ } else {
+ dashInd=0;
+ dashPos=0;
+ //nPlain=stPlain;
+ nPlain=dashPlain;
+ }
+ if ( nPlain == true && dashPlain == false ) {
+ Geom::Point p=(enLength-curLength-leftInDash)*lastP+(curLength+leftInDash-stLength)*n;
+ p/=(enLength-stLength);
+ if ( back ) {
+ double pT=0;
+ if ( nPiece == lastPiece ) {
+ pT=(lastT*(enLength-curLength-leftInDash)+nT*(curLength+leftInDash-stLength))/(enLength-stLength);
+ } else {
+ pT=(nPiece*(curLength+leftInDash-stLength))/(enLength-stLength);
+ }
+ AddPoint(p,nPiece,pT,true);
+ } else {
+ AddPoint(p,true);
+ }
+ } else if ( nPlain == false && dashPlain == true ) {
+ Geom::Point p=(enLength-curLength-leftInDash)*lastP+(curLength+leftInDash-stLength)*n;
+ p/=(enLength-stLength);
+ if ( back ) {
+ double pT=0;
+ if ( nPiece == lastPiece ) {
+ pT=(lastT*(enLength-curLength-leftInDash)+nT*(curLength+leftInDash-stLength))/(enLength-stLength);
+ } else {
+ pT=(nPiece*(curLength+leftInDash-stLength))/(enLength-stLength);
+ }
+ AddPoint(p,nPiece,pT,false);
+ } else {
+ AddPoint(p,false);
+ }
+ }
+ dashPlain=nPlain;
+
+ curLength+=leftInDash;
+ nl-=leftInDash;
+ } else {
+ dashPos+=nl;
+ curLength+=nl;
+ nl=0;
+ }
+ }
+ if ( dashPlain ) {
+ if ( back ) {
+ AddPoint(n,nPiece,nT,false);
+ } else {
+ AddPoint(n,false);
+ }
+ }
+ nl=enLength-curLength;
+ }
+ if ( curLength <= totLength-tail && curLength+nl > totLength-tail ) {
+ nl=totLength-tail-curLength;
+ dashInd=0;
+ dashPos=0;
+ bool nPlain=false;
+ if ( nPlain == true && dashPlain == false ) {
+ } else if ( nPlain == false && dashPlain == true ) {
+ Geom::Point p=(enLength-curLength)*lastP+(curLength-stLength)*n;
+ p/=(enLength-stLength);
+ if ( back ) {
+ double pT=0;
+ if ( nPiece == lastPiece ) {
+ pT=(lastT*(enLength-curLength)+nT*(curLength-stLength))/(enLength-stLength);
+ } else {
+ pT=(nPiece*(curLength-stLength))/(enLength-stLength);
+ }
+ AddPoint(p,nPiece,pT,false);
+ } else {
+ AddPoint(p,false);
+ }
+ }
+ dashPlain=nPlain;
+ }
+ // continuer
+ curLength=enLength;
+ lastP=n;
+ lastPiece=nPiece;
+ lastT=nT;
+ }
+ }
+}
+
+Geom::PathVector *
+Path::MakePathVector()
+{
+ Geom::PathVector *pv = new Geom::PathVector();
+ Geom::Path * currentpath = nullptr;
+
+ Geom::Point lastP,bezSt,bezEn;
+ int bezNb=0;
+ for (int i=0;i<int(descr_cmd.size());i++) {
+ int const typ = descr_cmd[i]->getType();
+ switch ( typ ) {
+ case descr_close:
+ {
+ currentpath->close(true);
+ }
+ break;
+
+ case descr_lineto:
+ {
+ PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[i]);
+ currentpath->appendNew<Geom::LineSegment>(Geom::Point(nData->p[0], nData->p[1]));
+ lastP = nData->p;
+ }
+ break;
+
+ case descr_moveto:
+ {
+ PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[i]);
+ pv->push_back(Geom::Path());
+ currentpath = &pv->back();
+ currentpath->start(Geom::Point(nData->p[0], nData->p[1]));
+ lastP = nData->p;
+ }
+ break;
+
+ case descr_arcto:
+ {
+ /* TODO: add testcase for this descr_arcto case */
+ PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[i]);
+ currentpath->appendNew<Geom::EllipticalArc>( nData->rx, nData->ry, nData->angle*M_PI/180.0, nData->large, !nData->clockwise, nData->p );
+ lastP = nData->p;
+ }
+ break;
+
+ case descr_cubicto:
+ {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[i]);
+ gdouble x1=lastP[0]+0.333333*nData->start[0];
+ gdouble y1=lastP[1]+0.333333*nData->start[1];
+ gdouble x2=nData->p[0]-0.333333*nData->end[0];
+ gdouble y2=nData->p[1]-0.333333*nData->end[1];
+ gdouble x3=nData->p[0];
+ gdouble y3=nData->p[1];
+ currentpath->appendNew<Geom::CubicBezier>( Geom::Point(x1,y1) , Geom::Point(x2,y2) , Geom::Point(x3,y3) );
+ lastP = nData->p;
+ }
+ break;
+
+ case descr_bezierto:
+ {
+ PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[i]);
+ if ( nData->nb <= 0 ) {
+ currentpath->appendNew<Geom::LineSegment>( Geom::Point(nData->p[0], nData->p[1]) );
+ bezNb=0;
+ } else if ( nData->nb == 1 ){
+ PathDescrIntermBezierTo *iData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[i+1]);
+ gdouble x1=0.333333*(lastP[0]+2*iData->p[0]);
+ gdouble y1=0.333333*(lastP[1]+2*iData->p[1]);
+ gdouble x2=0.333333*(nData->p[0]+2*iData->p[0]);
+ gdouble y2=0.333333*(nData->p[1]+2*iData->p[1]);
+ gdouble x3=nData->p[0];
+ gdouble y3=nData->p[1];
+ currentpath->appendNew<Geom::CubicBezier>( Geom::Point(x1,y1) , Geom::Point(x2,y2) , Geom::Point(x3,y3) );
+ bezNb=0;
+ } else {
+ bezSt = 2*lastP-nData->p;
+ bezEn = nData->p;
+ bezNb = nData->nb;
+ }
+ lastP = nData->p;
+ }
+ break;
+
+ case descr_interm_bezier:
+ {
+ if ( bezNb > 0 ) {
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[i]);
+ Geom::Point p_m=nData->p,p_s=0.5*(bezSt+p_m),p_e;
+ if ( bezNb > 1 ) {
+ PathDescrIntermBezierTo *iData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[i+1]);
+ p_e=0.5*(p_m+iData->p);
+ } else {
+ p_e=bezEn;
+ }
+
+ Geom::Point cp1=0.333333*(p_s+2*p_m),cp2=0.333333*(2*p_m+p_e);
+ gdouble x1=cp1[0];
+ gdouble y1=cp1[1];
+ gdouble x2=cp2[0];
+ gdouble y2=cp2[1];
+ gdouble x3=p_e[0];
+ gdouble y3=p_e[1];
+ currentpath->appendNew<Geom::CubicBezier>( Geom::Point(x1,y1) , Geom::Point(x2,y2) , Geom::Point(x3,y3) );
+
+ bezNb--;
+ }
+ }
+ break;
+ }
+ }
+
+ return pv;
+}
+
+void Path::AddCurve(Geom::Curve const &c)
+{
+ if( is_straight_curve(c) )
+ {
+ LineTo( c.finalPoint() );
+ }
+ /*
+ else if(Geom::QuadraticBezier const *quadratic_bezier = dynamic_cast<Geom::QuadraticBezier const *>(c)) {
+ ...
+ }
+ */
+ else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const *>(&c)) {
+ Geom::Point tmp = (*cubic_bezier)[3];
+ Geom::Point tms = 3 * ((*cubic_bezier)[1] - (*cubic_bezier)[0]);
+ Geom::Point tme = 3 * ((*cubic_bezier)[3] - (*cubic_bezier)[2]);
+ CubicTo (tmp, tms, tme);
+ }
+ else if(Geom::EllipticalArc const *elliptical_arc = dynamic_cast<Geom::EllipticalArc const *>(&c)) {
+ ArcTo( elliptical_arc->finalPoint(),
+ elliptical_arc->ray(Geom::X), elliptical_arc->ray(Geom::Y),
+ elliptical_arc->rotationAngle()*180.0/M_PI, // convert from radians to degrees
+ elliptical_arc->largeArc(), !elliptical_arc->sweep() );
+ } else {
+ //this case handles sbasis as well as all other curve types
+ Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c.toSBasis(), 0.1);
+
+ //recurse to convert the new path resulting from the sbasis to svgd
+ for(const auto & iter : sbasis_path) {
+ AddCurve(iter);
+ }
+ }
+}
+
+/** append is false by default: it means that the path should be resetted. If it is true, the path is not resetted and Geom::Path will be appended as a new path
+ */
+void Path::LoadPath(Geom::Path const &path, Geom::Affine const &tr, bool doTransformation, bool append)
+{
+ if (!append) {
+ SetBackData (false);
+ Reset();
+ }
+ if (path.empty())
+ return;
+
+ // TODO: this can be optimized by not generating a new path here, but doing the transform in AddCurve
+ // directly on the curve parameters
+
+ Geom::Path const pathtr = doTransformation ? path * tr : path;
+
+ MoveTo( pathtr.initialPoint() );
+
+ for(const auto & cit : pathtr) {
+ AddCurve(cit);
+ }
+
+ if (pathtr.closed()) {
+ Close();
+ }
+}
+
+void Path::LoadPathVector(Geom::PathVector const &pv)
+{
+ LoadPathVector(pv, Geom::Affine(), false);
+}
+
+void Path::LoadPathVector(Geom::PathVector const &pv, Geom::Affine const &tr, bool doTransformation)
+{
+ SetBackData (false);
+ Reset();
+
+ // FIXME: 2geom is currently unable to maintain SVGElliptical arcs through transformation, and
+ // sometimes it crashes on a continuity error during conversions, therefore convert to beziers here.
+ // (the fix is of course to fix 2geom and then remove this if-statement, and just execute the 'else'-clause)
+ if (doTransformation) {
+ Geom::PathVector pvbezier = pathv_to_linear_and_cubic_beziers(pv);
+ for(const auto & it : pvbezier) {
+ LoadPath(it, tr, doTransformation, true);
+ }
+ } else {
+ for(const auto & it : pv) {
+ LoadPath(it, tr, doTransformation, true);
+ }
+ }
+}
+
+/**
+ * \return Length of the lines in the pts vector.
+ */
+
+double Path::Length()
+{
+ if ( pts.empty() ) {
+ return 0;
+ }
+
+ Geom::Point lastP = pts[0].p;
+
+ double len = 0;
+ for (const auto & pt : pts) {
+
+ if ( pt.isMoveTo != polyline_moveto ) {
+ len += Geom::L2(pt.p - lastP);
+ }
+
+ lastP = pt.p;
+ }
+
+ return len;
+}
+
+
+double Path::Surface()
+{
+ if ( pts.empty() ) {
+ return 0;
+ }
+
+ Geom::Point lastM = pts[0].p;
+ Geom::Point lastP = lastM;
+
+ double surf = 0;
+ for (const auto & pt : pts) {
+
+ if ( pt.isMoveTo == polyline_moveto ) {
+ surf += Geom::cross(lastM, lastM - lastP);
+ lastP = lastM = pt.p;
+ } else {
+ surf += Geom::cross(pt.p, pt.p - lastP);
+ lastP = pt.p;
+ }
+
+ }
+
+ return surf;
+}
+
+
+Path** Path::SubPaths(int &outNb,bool killNoSurf)
+{
+ int nbRes=0;
+ Path** res=nullptr;
+ Path* curAdd=nullptr;
+
+ for (auto & i : descr_cmd) {
+ int const typ = i->getType();
+ switch ( typ ) {
+ case descr_moveto:
+ if ( curAdd ) {
+ if ( curAdd->descr_cmd.size() > 1 ) {
+ curAdd->Convert(1.0);
+ double addSurf=curAdd->Surface();
+ if ( fabs(addSurf) > 0.0001 || killNoSurf == false ) {
+ res=(Path**)g_realloc(res,(nbRes+1)*sizeof(Path*));
+ res[nbRes++]=curAdd;
+ } else {
+ delete curAdd;
+ }
+ } else {
+ delete curAdd;
+ }
+ curAdd=nullptr;
+ }
+ curAdd=new Path;
+ curAdd->SetBackData(false);
+ {
+ PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo *>(i);
+ curAdd->MoveTo(nData->p);
+ }
+ break;
+ case descr_close:
+ {
+ curAdd->Close();
+ }
+ break;
+ case descr_lineto:
+ {
+ PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(i);
+ curAdd->LineTo(nData->p);
+ }
+ break;
+ case descr_cubicto:
+ {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(i);
+ curAdd->CubicTo(nData->p,nData->start,nData->end);
+ }
+ break;
+ case descr_arcto:
+ {
+ PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(i);
+ curAdd->ArcTo(nData->p,nData->rx,nData->ry,nData->angle,nData->large,nData->clockwise);
+ }
+ break;
+ case descr_bezierto:
+ {
+ PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(i);
+ curAdd->BezierTo(nData->p);
+ }
+ break;
+ case descr_interm_bezier:
+ {
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(i);
+ curAdd->IntermBezierTo(nData->p);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if ( curAdd ) {
+ if ( curAdd->descr_cmd.size() > 1 ) {
+ curAdd->Convert(1.0);
+ double addSurf=curAdd->Surface();
+ if ( fabs(addSurf) > 0.0001 || killNoSurf == false ) {
+ res=(Path**)g_realloc(res,(nbRes+1)*sizeof(Path*));
+ res[nbRes++]=curAdd;
+ } else {
+ delete curAdd;
+ }
+ } else {
+ delete curAdd;
+ }
+ }
+ curAdd=nullptr;
+
+ outNb=nbRes;
+ return res;
+}
+Path** Path::SubPathsWithNesting(int &outNb,bool killNoSurf,int nbNest,int* nesting,int* conts)
+{
+ int nbRes=0;
+ Path** res=nullptr;
+ Path* curAdd=nullptr;
+ bool increment=false;
+
+ for (int i=0;i<int(descr_cmd.size());i++) {
+ int const typ = descr_cmd[i]->getType();
+ switch ( typ ) {
+ case descr_moveto:
+ {
+ if ( curAdd && increment == false ) {
+ if ( curAdd->descr_cmd.size() > 1 ) {
+ // sauvegarder descr_cmd[0]->associated
+ int savA=curAdd->descr_cmd[0]->associated;
+ curAdd->Convert(1.0);
+ curAdd->descr_cmd[0]->associated=savA; // associated n'est pas utilise apres
+ double addSurf=curAdd->Surface();
+ if ( fabs(addSurf) > 0.0001 || killNoSurf == false ) {
+ res=(Path**)g_realloc(res,(nbRes+1)*sizeof(Path*));
+ res[nbRes++]=curAdd;
+ } else {
+ delete curAdd;
+ }
+ } else {
+ delete curAdd;
+ }
+ curAdd=nullptr;
+ }
+ Path* hasParent=nullptr;
+ for (int j=0;j<nbNest;j++) {
+ if ( conts[j] == i && nesting[j] >= 0 ) {
+ int parentMvt=conts[nesting[j]];
+ for (int k=0;k<nbRes;k++) {
+ if ( res[k] && res[k]->descr_cmd.empty() == false && res[k]->descr_cmd[0]->associated == parentMvt ) {
+ hasParent=res[k];
+ break;
+ }
+ }
+ }
+ if ( conts[j] > i ) break;
+ }
+ if ( hasParent ) {
+ curAdd=hasParent;
+ increment=true;
+ } else {
+ curAdd=new Path;
+ curAdd->SetBackData(false);
+ increment=false;
+ }
+ PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[i]);
+ int mNo=curAdd->MoveTo(nData->p);
+ curAdd->descr_cmd[mNo]->associated=i;
+ }
+ break;
+ case descr_close:
+ {
+ curAdd->Close();
+ }
+ break;
+ case descr_lineto:
+ {
+ PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[i]);
+ curAdd->LineTo(nData->p);
+ }
+ break;
+ case descr_cubicto:
+ {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[i]);
+ curAdd->CubicTo(nData->p,nData->start,nData->end);
+ }
+ break;
+ case descr_arcto:
+ {
+ PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[i]);
+ curAdd->ArcTo(nData->p,nData->rx,nData->ry,nData->angle,nData->large,nData->clockwise);
+ }
+ break;
+ case descr_bezierto:
+ {
+ PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[i]);
+ curAdd->BezierTo(nData->p);
+ }
+ break;
+ case descr_interm_bezier:
+ {
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[i]);
+ curAdd->IntermBezierTo(nData->p);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if ( curAdd && increment == false ) {
+ if ( curAdd->descr_cmd.size() > 1 ) {
+ curAdd->Convert(1.0);
+ double addSurf=curAdd->Surface();
+ if ( fabs(addSurf) > 0.0001 || killNoSurf == false ) {
+ res=(Path**)g_realloc(res,(nbRes+1)*sizeof(Path*));
+ res[nbRes++]=curAdd;
+ } else {
+ delete curAdd;
+ }
+ } else {
+ delete curAdd;
+ }
+ }
+ curAdd=nullptr;
+
+ outNb=nbRes;
+ return res;
+}
+
+
+void Path::ConvertForcedToVoid()
+{
+ for (int i=0; i < int(descr_cmd.size()); i++) {
+ if ( descr_cmd[i]->getType() == descr_forced) {
+ delete descr_cmd[i];
+ descr_cmd.erase(descr_cmd.begin() + i);
+ }
+ }
+}
+
+
+void Path::ConvertForcedToMoveTo()
+{
+ Geom::Point lastSeen(0, 0);
+ Geom::Point lastMove(0, 0);
+
+ {
+ Geom::Point lastPos(0, 0);
+ for (int i = int(descr_cmd.size()) - 1; i >= 0; i--) {
+ int const typ = descr_cmd[i]->getType();
+ switch ( typ ) {
+ case descr_forced:
+ {
+ PathDescrForced *d = dynamic_cast<PathDescrForced *>(descr_cmd[i]);
+ d->p = lastPos;
+ break;
+ }
+ case descr_close:
+ {
+ PathDescrClose *d = dynamic_cast<PathDescrClose *>(descr_cmd[i]);
+ d->p = lastPos;
+ break;
+ }
+ case descr_moveto:
+ {
+ PathDescrMoveTo *d = dynamic_cast<PathDescrMoveTo *>(descr_cmd[i]);
+ lastPos = d->p;
+ break;
+ }
+ case descr_lineto:
+ {
+ PathDescrLineTo *d = dynamic_cast<PathDescrLineTo *>(descr_cmd[i]);
+ lastPos = d->p;
+ break;
+ }
+ case descr_arcto:
+ {
+ PathDescrArcTo *d = dynamic_cast<PathDescrArcTo *>(descr_cmd[i]);
+ lastPos = d->p;
+ break;
+ }
+ case descr_cubicto:
+ {
+ PathDescrCubicTo *d = dynamic_cast<PathDescrCubicTo *>(descr_cmd[i]);
+ lastPos = d->p;
+ break;
+ }
+ case descr_bezierto:
+ {
+ PathDescrBezierTo *d = dynamic_cast<PathDescrBezierTo *>(descr_cmd[i]);
+ lastPos = d->p;
+ break;
+ }
+ case descr_interm_bezier:
+ {
+ PathDescrIntermBezierTo *d = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[i]);
+ lastPos = d->p;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ bool hasMoved = false;
+ for (int i = 0; i < int(descr_cmd.size()); i++) {
+ int const typ = descr_cmd[i]->getType();
+ switch ( typ ) {
+ case descr_forced:
+ if ( i < int(descr_cmd.size()) - 1 && hasMoved ) { // sinon il termine le chemin
+
+ delete descr_cmd[i];
+ descr_cmd[i] = new PathDescrMoveTo(lastSeen);
+ lastMove = lastSeen;
+ hasMoved = true;
+ }
+ break;
+
+ case descr_moveto:
+ {
+ PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[i]);
+ lastMove = lastSeen = nData->p;
+ hasMoved = true;
+ }
+ break;
+ case descr_close:
+ {
+ lastSeen=lastMove;
+ }
+ break;
+ case descr_lineto:
+ {
+ PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[i]);
+ lastSeen=nData->p;
+ }
+ break;
+ case descr_cubicto:
+ {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[i]);
+ lastSeen=nData->p;
+ }
+ break;
+ case descr_arcto:
+ {
+ PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[i]);
+ lastSeen=nData->p;
+ }
+ break;
+ case descr_bezierto:
+ {
+ PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[i]);
+ lastSeen=nData->p;
+ }
+ break;
+ case descr_interm_bezier:
+ {
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[i]);
+ lastSeen=nData->p;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+static int CmpPosition(const void * p1, const void * p2) {
+ Path::cut_position *cp1=(Path::cut_position*)p1;
+ Path::cut_position *cp2=(Path::cut_position*)p2;
+ if ( cp1->piece < cp2->piece ) return -1;
+ if ( cp1->piece > cp2->piece ) return 1;
+ if ( cp1->t < cp2->t ) return -1;
+ if ( cp1->t > cp2->t ) return 1;
+ return 0;
+}
+static int CmpCurv(const void * p1, const void * p2) {
+ double *cp1=(double*)p1;
+ double *cp2=(double*)p2;
+ if ( *cp1 < *cp2 ) return -1;
+ if ( *cp1 > *cp2 ) return 1;
+ return 0;
+}
+
+
+Path::cut_position* Path::CurvilignToPosition(int nbCv, double *cvAbs, int &nbCut)
+{
+ if ( nbCv <= 0 || pts.empty() || back == false ) {
+ return nullptr;
+ }
+
+ qsort(cvAbs, nbCv, sizeof(double), CmpCurv);
+
+ cut_position *res = nullptr;
+ nbCut = 0;
+ int curCv = 0;
+
+ double len = 0;
+ double lastT = 0;
+ int lastPiece = -1;
+
+ Geom::Point lastM = pts[0].p;
+ Geom::Point lastP = lastM;
+
+ for (const auto & pt : pts) {
+
+ if ( pt.isMoveTo == polyline_moveto ) {
+
+ lastP = lastM = pt.p;
+ lastT = pt.t;
+ lastPiece = pt.piece;
+
+ } else {
+
+ double const add = Geom::L2(pt.p - lastP);
+ double curPos = len;
+ double curAdd = add;
+
+ while ( curAdd > 0.0001 && curCv < nbCv && curPos + curAdd >= cvAbs[curCv] ) {
+ double const theta = (cvAbs[curCv] - len) / add;
+ res = (cut_position*) g_realloc(res, (nbCut + 1) * sizeof(cut_position));
+ res[nbCut].piece = pt.piece;
+ res[nbCut].t = theta * pt.t + (1 - theta) * ( (lastPiece != pt.piece) ? 0 : lastT);
+ nbCut++;
+ curAdd -= cvAbs[curCv] - curPos;
+ curPos = cvAbs[curCv];
+ curCv++;
+ }
+
+ len += add;
+ lastPiece = pt.piece;
+ lastP = pt.p;
+ lastT = pt.t;
+ }
+ }
+
+ return res;
+}
+
+/*
+Moved from Layout-TNG-OutIter.cpp
+TODO: clean up uses of the original function and remove
+
+Original Comment:
+"this function really belongs to Path. I'll probably move it there eventually,
+hence the Path-esque coding style"
+
+*/
+template<typename T> inline static T square(T x) {return x*x;}
+Path::cut_position Path::PointToCurvilignPosition(Geom::Point const &pos, unsigned seg) const
+{
+ // if the parameter "seg" == 0, then all segments will be considered
+ // In however e.g. "seg" == 6 , then only the 6th segment will be considered
+
+ unsigned bestSeg = 0;
+ double bestRangeSquared = DBL_MAX;
+ double bestT = 0.0; // you need a sentinel, or make sure that you prime with correct values.
+
+ for (unsigned i = 1 ; i < pts.size() ; i++) {
+ if (pts[i].isMoveTo == polyline_moveto || (seg > 0 && i != seg)) continue;
+ Geom::Point p1, p2, localPos;
+ double thisRangeSquared;
+ double t;
+
+ if (pts[i - 1].p == pts[i].p) {
+ thisRangeSquared = square(pts[i].p[Geom::X] - pos[Geom::X]) + square(pts[i].p[Geom::Y] - pos[Geom::Y]);
+ t = 0.0;
+ } else {
+ // we rotate all our coordinates so we're always looking at a mostly vertical line.
+ if (fabs(pts[i - 1].p[Geom::X] - pts[i].p[Geom::X]) < fabs(pts[i - 1].p[Geom::Y] - pts[i].p[Geom::Y])) {
+ p1 = pts[i - 1].p;
+ p2 = pts[i].p;
+ localPos = pos;
+ } else {
+ p1 = pts[i - 1].p.cw();
+ p2 = pts[i].p.cw();
+ localPos = pos.cw();
+ }
+ double gradient = (p2[Geom::X] - p1[Geom::X]) / (p2[Geom::Y] - p1[Geom::Y]);
+ double intersection = p1[Geom::X] - gradient * p1[Geom::Y];
+ /*
+ orthogonalGradient = -1.0 / gradient; // you are going to have numerical problems here.
+ orthogonalIntersection = localPos[Geom::X] - orthogonalGradient * localPos[Geom::Y];
+ nearestY = (orthogonalIntersection - intersection) / (gradient - orthogonalGradient);
+
+ expand out nearestY fully :
+ nearestY = (localPos[Geom::X] - (-1.0 / gradient) * localPos[Geom::Y] - intersection) / (gradient - (-1.0 / gradient));
+
+ multiply top and bottom by gradient:
+ nearestY = (localPos[Geom::X] * gradient - (-1.0) * localPos[Geom::Y] - intersection * gradient) / (gradient * gradient - (-1.0));
+
+ and simplify to get:
+ */
+ double nearestY = (localPos[Geom::X] * gradient + localPos[Geom::Y] - intersection * gradient)
+ / (gradient * gradient + 1.0);
+ t = (nearestY - p1[Geom::Y]) / (p2[Geom::Y] - p1[Geom::Y]);
+ if (t <= 0.0) {
+ thisRangeSquared = square(p1[Geom::X] - localPos[Geom::X]) + square(p1[Geom::Y] - localPos[Geom::Y]);
+ t = 0.0;
+ } else if (t >= 1.0) {
+ thisRangeSquared = square(p2[Geom::X] - localPos[Geom::X]) + square(p2[Geom::Y] - localPos[Geom::Y]);
+ t = 1.0;
+ } else {
+ thisRangeSquared = square(nearestY * gradient + intersection - localPos[Geom::X]) + square(nearestY - localPos[Geom::Y]);
+ }
+ }
+
+ if (thisRangeSquared < bestRangeSquared) {
+ bestSeg = i;
+ bestRangeSquared = thisRangeSquared;
+ bestT = t;
+ }
+ }
+ Path::cut_position result;
+ if (bestSeg == 0) {
+ result.piece = 0;
+ result.t = 0.0;
+ } else {
+ result.piece = pts[bestSeg].piece;
+ if (result.piece == pts[bestSeg - 1].piece) {
+ result.t = pts[bestSeg - 1].t * (1.0 - bestT) + pts[bestSeg].t * bestT;
+ } else {
+ result.t = pts[bestSeg].t * bestT;
+ }
+ }
+ return result;
+}
+/*
+ this one also belongs to Path
+ returns the length of the path up to the position indicated by t (0..1)
+
+ TODO: clean up uses of the original function and remove
+
+ should this take a cut_position as a parameter?
+*/
+double Path::PositionToLength(int piece, double t)
+{
+ double length = 0.0;
+ for (unsigned i = 1 ; i < pts.size() ; i++) {
+ if (pts[i].isMoveTo == polyline_moveto) continue;
+ if (pts[i].piece == piece && t < pts[i].t) {
+ length += Geom::L2((t - pts[i - 1].t) / (pts[i].t - pts[i - 1].t) * (pts[i].p - pts[i - 1].p));
+ break;
+ }
+ length += Geom::L2(pts[i].p - pts[i - 1].p);
+ }
+ return length;
+}
+
+void Path::ConvertPositionsToForced(int nbPos, cut_position *poss)
+{
+ if ( nbPos <= 0 ) {
+ return;
+ }
+
+ {
+ Geom::Point lastPos(0, 0);
+ for (int i = int(descr_cmd.size()) - 1; i >= 0; i--) {
+ int const typ = descr_cmd[i]->getType();
+ switch ( typ ) {
+
+ case descr_forced:
+ {
+ PathDescrForced *d = dynamic_cast<PathDescrForced *>(descr_cmd[i]);
+ d->p = lastPos;
+ break;
+ }
+
+ case descr_close:
+ {
+ delete descr_cmd[i];
+ descr_cmd[i] = new PathDescrLineTo(Geom::Point(0, 0));
+
+ int fp = i - 1;
+ while ( fp >= 0 && (descr_cmd[fp]->getType()) != descr_moveto ) {
+ fp--;
+ }
+
+ if ( fp >= 0 ) {
+ PathDescrMoveTo *oData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[fp]);
+ dynamic_cast<PathDescrLineTo*>(descr_cmd[i])->p = oData->p;
+ }
+ }
+ break;
+
+ case descr_bezierto:
+ {
+ PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[i]);
+ Geom::Point theP = nData->p;
+ if ( nData->nb == 0 ) {
+ lastPos = theP;
+ }
+ }
+ break;
+
+ case descr_moveto:
+ {
+ PathDescrMoveTo *d = dynamic_cast<PathDescrMoveTo *>(descr_cmd[i]);
+ lastPos = d->p;
+ break;
+ }
+ case descr_lineto:
+ {
+ PathDescrLineTo *d = dynamic_cast<PathDescrLineTo *>(descr_cmd[i]);
+ lastPos = d->p;
+ break;
+ }
+ case descr_arcto:
+ {
+ PathDescrArcTo *d = dynamic_cast<PathDescrArcTo *>(descr_cmd[i]);
+ lastPos = d->p;
+ break;
+ }
+ case descr_cubicto:
+ {
+ PathDescrCubicTo *d = dynamic_cast<PathDescrCubicTo *>(descr_cmd[i]);
+ lastPos = d->p;
+ break;
+ }
+ case descr_interm_bezier:
+ {
+ PathDescrIntermBezierTo *d = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[i]);
+ lastPos = d->p;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ if (descr_cmd[0]->getType() == descr_moveto)
+ descr_flags |= descr_doing_subpath; // see LP Bug 166302
+
+ qsort(poss, nbPos, sizeof(cut_position), CmpPosition);
+
+ for (int curP=0;curP<nbPos;curP++) {
+ int cp=poss[curP].piece;
+ if ( cp < 0 || cp >= int(descr_cmd.size()) ) break;
+ float ct=poss[curP].t;
+ if ( ct < 0 ) continue;
+ if ( ct > 1 ) continue;
+
+ int const typ = descr_cmd[cp]->getType();
+ if ( typ == descr_moveto || typ == descr_forced || typ == descr_close ) {
+ // ponctuel= rien a faire
+ } else if ( typ == descr_lineto || typ == descr_arcto || typ == descr_cubicto ) {
+ // facile: creation d'un morceau et d'un forced -> 2 commandes
+ Geom::Point theP;
+ Geom::Point theT;
+ Geom::Point startP;
+ startP=PrevPoint(cp-1);
+ if ( typ == descr_cubicto ) {
+ double len,rad;
+ Geom::Point stD,enD,endP;
+ {
+ PathDescrCubicTo *oData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[cp]);
+ stD=oData->start;
+ enD=oData->end;
+ endP=oData->p;
+ TangentOnCubAt (ct, startP, *oData,true, theP,theT,len,rad);
+ }
+
+ theT*=len;
+
+ InsertCubicTo(endP,(1-ct)*theT,(1-ct)*enD,cp+1);
+ InsertForcePoint(cp+1);
+ {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[cp]);
+ nData->start=ct*stD;
+ nData->end=ct*theT;
+ nData->p=theP;
+ }
+ // decalages dans le tableau des positions de coupe
+ for (int j=curP+1;j<nbPos;j++) {
+ if ( poss[j].piece == cp ) {
+ poss[j].piece+=2;
+ poss[j].t=(poss[j].t-ct)/(1-ct);
+ } else {
+ poss[j].piece+=2;
+ }
+ }
+ } else if ( typ == descr_lineto ) {
+ Geom::Point endP;
+ {
+ PathDescrLineTo *oData = dynamic_cast<PathDescrLineTo *>(descr_cmd[cp]);
+ endP=oData->p;
+ }
+
+ theP=ct*endP+(1-ct)*startP;
+
+ InsertLineTo(endP,cp+1);
+ InsertForcePoint(cp+1);
+ {
+ PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[cp]);
+ nData->p=theP;
+ }
+ // decalages dans le tableau des positions de coupe
+ for (int j=curP+1;j<nbPos;j++) {
+ if ( poss[j].piece == cp ) {
+ poss[j].piece+=2;
+ poss[j].t=(poss[j].t-ct)/(1-ct);
+ } else {
+ poss[j].piece+=2;
+ }
+ }
+ } else if ( typ == descr_arcto ) {
+ Geom::Point endP;
+ double rx,ry,angle;
+ bool clockw,large;
+ double delta=0;
+ {
+ PathDescrArcTo *oData = dynamic_cast<PathDescrArcTo *>(descr_cmd[cp]);
+ endP=oData->p;
+ rx=oData->rx;
+ ry=oData->ry;
+ angle=oData->angle;
+ clockw=oData->clockwise;
+ large=oData->large;
+ }
+ {
+ double sang,eang;
+ ArcAngles(startP,endP,rx,ry,angle*M_PI/180.0,large,clockw,sang,eang);
+
+ if (clockw) {
+ if ( sang < eang ) sang += 2*M_PI;
+ delta=eang-sang;
+ } else {
+ if ( sang > eang ) sang -= 2*M_PI;
+ delta=eang-sang;
+ }
+ if ( delta < 0 ) delta=-delta;
+ }
+
+ PointAt (cp,ct, theP);
+
+ if ( delta*(1-ct) > M_PI ) {
+ InsertArcTo(endP,rx,ry,angle,true,clockw,cp+1);
+ } else {
+ InsertArcTo(endP,rx,ry,angle,false,clockw,cp+1);
+ }
+ InsertForcePoint(cp+1);
+ {
+ PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[cp]);
+ nData->p=theP;
+ if ( delta*ct > M_PI ) {
+ nData->large=true;
+ } else {
+ nData->large=false;
+ }
+ }
+ // decalages dans le tableau des positions de coupe
+ for (int j=curP+1;j<nbPos;j++) {
+ if ( poss[j].piece == cp ) {
+ poss[j].piece+=2;
+ poss[j].t=(poss[j].t-ct)/(1-ct);
+ } else {
+ poss[j].piece+=2;
+ }
+ }
+ }
+ } else if ( typ == descr_bezierto || typ == descr_interm_bezier ) {
+ // dur
+ int theBDI=cp;
+ while ( theBDI >= 0 && (descr_cmd[theBDI]->getType()) != descr_bezierto ) theBDI--;
+ if ( (descr_cmd[theBDI]->getType()) == descr_bezierto ) {
+ PathDescrBezierTo theBD=*(dynamic_cast<PathDescrBezierTo *>(descr_cmd[theBDI]));
+ if ( cp >= theBDI && cp < theBDI+theBD.nb ) {
+ if ( theBD.nb == 1 ) {
+ Geom::Point endP=theBD.p;
+ Geom::Point midP;
+ Geom::Point startP;
+ startP=PrevPoint(theBDI-1);
+ {
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[theBDI+1]);
+ midP=nData->p;
+ }
+ Geom::Point aP=ct*midP+(1-ct)*startP;
+ Geom::Point bP=ct*endP+(1-ct)*midP;
+ Geom::Point knotP=ct*bP+(1-ct)*aP;
+
+ InsertIntermBezierTo(bP,theBDI+2);
+ InsertBezierTo(knotP,1,theBDI+2);
+ InsertForcePoint(theBDI+2);
+ {
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[theBDI+1]);
+ nData->p=aP;
+ }
+ {
+ PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[theBDI]);
+ nData->p=knotP;
+ }
+ // decalages dans le tableau des positions de coupe
+ for (int j=curP+1;j<nbPos;j++) {
+ if ( poss[j].piece == cp ) {
+ poss[j].piece+=3;
+ poss[j].t=(poss[j].t-ct)/(1-ct);
+ } else {
+ poss[j].piece+=3;
+ }
+ }
+
+ } else {
+ // decouper puis repasser
+ if ( cp > theBDI ) {
+ Geom::Point pcP,ncP;
+ {
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[cp]);
+ pcP=nData->p;
+ }
+ {
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[cp+1]);
+ ncP=nData->p;
+ }
+ Geom::Point knotP=0.5*(pcP+ncP);
+
+ InsertBezierTo(knotP,theBD.nb-(cp-theBDI),cp+1);
+ {
+ PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[theBDI]);
+ nData->nb=cp-theBDI;
+ }
+
+ // decalages dans le tableau des positions de coupe
+ for (int j=curP;j<nbPos;j++) {
+ if ( poss[j].piece == cp ) {
+ poss[j].piece+=1;
+ } else {
+ poss[j].piece+=1;
+ }
+ }
+ curP--;
+ } else {
+ Geom::Point pcP,ncP;
+ {
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[cp+1]);
+ pcP=nData->p;
+ }
+ {
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[cp+2]);
+ ncP=nData->p;
+ }
+ Geom::Point knotP=0.5*(pcP+ncP);
+
+ InsertBezierTo(knotP,theBD.nb-1,cp+2);
+ {
+ PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[theBDI]);
+ nData->nb=1;
+ }
+
+ // decalages dans le tableau des positions de coupe
+ for (int j=curP;j<nbPos;j++) {
+ if ( poss[j].piece == cp ) {
+// poss[j].piece+=1;
+ } else {
+ poss[j].piece+=1;
+ }
+ }
+ curP--;
+ }
+ }
+ } else {
+ // on laisse aussi tomber
+ }
+ } else {
+ // on laisse tomber
+ }
+ }
+ }
+}
+
+void Path::ConvertPositionsToMoveTo(int nbPos,cut_position* poss)
+{
+ ConvertPositionsToForced(nbPos,poss);
+// ConvertForcedToMoveTo();
+ // on fait une version customizee a la place
+
+ Path* res=new Path;
+
+ Geom::Point lastP(0,0);
+ for (int i=0;i<int(descr_cmd.size());i++) {
+ int const typ = descr_cmd[i]->getType();
+ if ( typ == descr_moveto ) {
+ Geom::Point np;
+ {
+ PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[i]);
+ np=nData->p;
+ }
+ Geom::Point endP;
+ bool hasClose=false;
+ int hasForced=-1;
+ bool doesClose=false;
+ int j=i+1;
+ for (;j<int(descr_cmd.size());j++) {
+ int const ntyp = descr_cmd[j]->getType();
+ if ( ntyp == descr_moveto ) {
+ j--;
+ break;
+ } else if ( ntyp == descr_forced ) {
+ if ( hasForced < 0 ) hasForced=j;
+ } else if ( ntyp == descr_close ) {
+ hasClose=true;
+ break;
+ } else if ( ntyp == descr_lineto ) {
+ PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[j]);
+ endP=nData->p;
+ } else if ( ntyp == descr_arcto ) {
+ PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[j]);
+ endP=nData->p;
+ } else if ( ntyp == descr_cubicto ) {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[j]);
+ endP=nData->p;
+ } else if ( ntyp == descr_bezierto ) {
+ PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[j]);
+ endP=nData->p;
+ } else {
+ }
+ }
+ if ( Geom::LInfty(endP-np) < 0.00001 ) {
+ doesClose=true;
+ }
+ if ( ( doesClose || hasClose ) && hasForced >= 0 ) {
+ // printf("nasty i=%i j=%i frc=%i\n",i,j,hasForced);
+ // aghhh.
+ Geom::Point nMvtP=PrevPoint(hasForced);
+ res->MoveTo(nMvtP);
+ Geom::Point nLastP=nMvtP;
+ for (int k = hasForced + 1; k < j; k++) {
+ int ntyp=descr_cmd[k]->getType();
+ if ( ntyp == descr_moveto ) {
+ // ne doit pas arriver
+ } else if ( ntyp == descr_forced ) {
+ res->MoveTo(nLastP);
+ } else if ( ntyp == descr_close ) {
+ // rien a faire ici; de plus il ne peut y en avoir qu'un
+ } else if ( ntyp == descr_lineto ) {
+ PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[k]);
+ res->LineTo(nData->p);
+ nLastP=nData->p;
+ } else if ( ntyp == descr_arcto ) {
+ PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[k]);
+ res->ArcTo(nData->p,nData->rx,nData->ry,nData->angle,nData->large,nData->clockwise);
+ nLastP=nData->p;
+ } else if ( ntyp == descr_cubicto ) {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[k]);
+ res->CubicTo(nData->p,nData->start,nData->end);
+ nLastP=nData->p;
+ } else if ( ntyp == descr_bezierto ) {
+ PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[k]);
+ res->BezierTo(nData->p);
+ nLastP=nData->p;
+ } else if ( ntyp == descr_interm_bezier ) {
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[k]);
+ res->IntermBezierTo(nData->p);
+ } else {
+ }
+ }
+ if ( doesClose == false ) res->LineTo(np);
+ nLastP=np;
+ for (int k=i+1;k<hasForced;k++) {
+ int ntyp=descr_cmd[k]->getType();
+ if ( ntyp == descr_moveto ) {
+ // ne doit pas arriver
+ } else if ( ntyp == descr_forced ) {
+ res->MoveTo(nLastP);
+ } else if ( ntyp == descr_close ) {
+ // rien a faire ici; de plus il ne peut y en avoir qu'un
+ } else if ( ntyp == descr_lineto ) {
+ PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[k]);
+ res->LineTo(nData->p);
+ nLastP=nData->p;
+ } else if ( ntyp == descr_arcto ) {
+ PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[k]);
+ res->ArcTo(nData->p,nData->rx,nData->ry,nData->angle,nData->large,nData->clockwise);
+ nLastP=nData->p;
+ } else if ( ntyp == descr_cubicto ) {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[k]);
+ res->CubicTo(nData->p,nData->start,nData->end);
+ nLastP=nData->p;
+ } else if ( ntyp == descr_bezierto ) {
+ PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[k]);
+ res->BezierTo(nData->p);
+ nLastP=nData->p;
+ } else if ( ntyp == descr_interm_bezier ) {
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[k]);
+ res->IntermBezierTo(nData->p);
+ } else {
+ }
+ }
+ lastP=nMvtP;
+ i=j;
+ } else {
+ // regular, just move on
+ res->MoveTo(np);
+ lastP=np;
+ }
+ } else if ( typ == descr_close ) {
+ res->Close();
+ } else if ( typ == descr_forced ) {
+ res->MoveTo(lastP);
+ } else if ( typ == descr_lineto ) {
+ PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[i]);
+ res->LineTo(nData->p);
+ lastP=nData->p;
+ } else if ( typ == descr_arcto ) {
+ PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[i]);
+ res->ArcTo(nData->p,nData->rx,nData->ry,nData->angle,nData->large,nData->clockwise);
+ lastP=nData->p;
+ } else if ( typ == descr_cubicto ) {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[i]);
+ res->CubicTo(nData->p,nData->start,nData->end);
+ lastP=nData->p;
+ } else if ( typ == descr_bezierto ) {
+ PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[i]);
+ res->BezierTo(nData->p);
+ lastP=nData->p;
+ } else if ( typ == descr_interm_bezier ) {
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[i]);
+ res->IntermBezierTo(nData->p);
+ } else {
+ }
+ }
+
+ Copy(res);
+ delete res;
+ return;
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/livarot/PathOutline.cpp b/src/livarot/PathOutline.cpp
new file mode 100644
index 0000000..c7fb226
--- /dev/null
+++ b/src/livarot/PathOutline.cpp
@@ -0,0 +1,1526 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "livarot/Path.h"
+#include "livarot/path-description.h"
+
+/*
+ * the "outliner"
+ * takes a sequence of path commands and produces a set of commands that approximates the offset
+ * result is stored in dest (that paremeter is handed to all the subfunctions)
+ * not that the result is in general not mathematically correct; you can end up with unwanted holes in your
+ * beautiful offset. a better way is to do path->polyline->polygon->offset of polygon->polyline(=contours of the polygon)->path
+ * but computing offsets of the path is faster...
+ */
+
+// outline of a path.
+// computed by making 2 offsets, one of the "left" side of the path, one of the right side, and then glueing the two
+// the left side has to be reversed to make a contour
+void Path::Outline(Path *dest, double width, JoinType join, ButtType butt, double miter)
+{
+ if ( descr_flags & descr_adding_bezier ) {
+ CancelBezier();
+ }
+ if ( descr_flags & descr_doing_subpath ) {
+ CloseSubpath();
+ }
+ if ( descr_cmd.size() <= 1 ) {
+ return;
+ }
+ if ( dest == nullptr ) {
+ return;
+ }
+
+ dest->Reset();
+ dest->SetBackData(false);
+
+ outline_callbacks calls;
+ Geom::Point endButt;
+ Geom::Point endPos;
+ calls.cubicto = StdCubicTo;
+ calls.bezierto = StdBezierTo;
+ calls.arcto = StdArcTo;
+
+ Path *rev = new Path;
+
+ // we repeat the offset contour creation for each subpath
+ int curP = 0;
+ do {
+ int lastM = curP;
+ do {
+ curP++;
+ if (curP >= int(descr_cmd.size())) {
+ break;
+ }
+ int typ = descr_cmd[curP]->getType();
+ if (typ == descr_moveto) {
+ break;
+ }
+ } while (curP < int(descr_cmd.size()));
+
+ if (curP >= int(descr_cmd.size())) {
+ curP = descr_cmd.size();
+ }
+
+ if (curP > lastM + 1) {
+ // we have isolated a subpath, now we make a reversed version of it
+ // we do so by taking the subpath in the reverse and constructing a path as appropriate
+ // the construct is stored in "rev"
+ int curD = curP - 1;
+ Geom::Point curX;
+ Geom::Point nextX;
+ int firstTyp = descr_cmd[curD]->getType();
+ bool const needClose = (firstTyp == descr_close);
+ while (curD > lastM && descr_cmd[curD]->getType() == descr_close) {
+ curD--;
+ }
+
+ int realP = curD + 1;
+ if (curD > lastM) {
+ curX = PrevPoint(curD);
+ rev->Reset ();
+ rev->MoveTo(curX);
+ while (curD > lastM) {
+ int const typ = descr_cmd[curD]->getType();
+ if (typ == descr_moveto) {
+ // rev->Close();
+ curD--;
+ } else if (typ == descr_forced) {
+ // rev->Close();
+ curD--;
+ } else if (typ == descr_lineto) {
+ nextX = PrevPoint (curD - 1);
+ rev->LineTo (nextX);
+ curX = nextX;
+ curD--;
+ } else if (typ == descr_cubicto) {
+ PathDescrCubicTo* nData = dynamic_cast<PathDescrCubicTo*>(descr_cmd[curD]);
+ nextX = PrevPoint (curD - 1);
+ Geom::Point isD=-nData->start;
+ Geom::Point ieD=-nData->end;
+ rev->CubicTo (nextX, ieD,isD);
+ curX = nextX;
+ curD--;
+ } else if (typ == descr_arcto) {
+ PathDescrArcTo* nData = dynamic_cast<PathDescrArcTo*>(descr_cmd[curD]);
+ nextX = PrevPoint (curD - 1);
+ rev->ArcTo (nextX, nData->rx,nData->ry,nData->angle,nData->large,nData->clockwise);
+ curX = nextX;
+ curD--;
+ } else if (typ == descr_bezierto) {
+ nextX = PrevPoint (curD - 1);
+ rev->LineTo (nextX);
+ curX = nextX;
+ curD--;
+ } else if (typ == descr_interm_bezier) {
+ int nD = curD - 1;
+ while (nD > lastM && descr_cmd[nD]->getType() != descr_bezierto) nD--;
+ if ((descr_cmd[nD]->getType()) != descr_bezierto) {
+ // pas trouve le debut!?
+ // Not find the start?!
+ nextX = PrevPoint (nD);
+ rev->LineTo (nextX);
+ curX = nextX;
+ } else {
+ nextX = PrevPoint (nD - 1);
+ rev->BezierTo (nextX);
+ for (int i = curD; i > nD; i--) {
+ PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(descr_cmd[i]);
+ rev->IntermBezierTo (nData->p);
+ }
+ rev->EndBezierTo ();
+ curX = nextX;
+ }
+ curD = nD - 1;
+ } else {
+ curD--;
+ }
+ }
+
+ // offset the paths and glue everything
+ // actual offseting is done in SubContractOutline()
+ if (needClose) {
+ rev->Close ();
+ rev->SubContractOutline (0, rev->descr_cmd.size(),
+ dest, calls, 0.0025 * width * width, width,
+ join, butt, miter, true, false, endPos, endButt);
+ SubContractOutline (lastM, realP + 1 - lastM,
+ dest, calls, 0.0025 * width * width,
+ width, join, butt, miter, true, false, endPos, endButt);
+ } else {
+ rev->SubContractOutline (0, rev->descr_cmd.size(),
+ dest, calls, 0.0025 * width * width, width,
+ join, butt, miter, false, false, endPos, endButt);
+ Geom::Point endNor=endButt.ccw();
+ if (butt == butt_round) {
+ dest->ArcTo (endPos+width*endButt, width, width, 0.0, false, true);
+ dest->ArcTo (endPos+width*endNor, width, width, 0.0, false, true);
+ } else if (butt == butt_square) {
+ dest->LineTo (endPos-width*endNor+width*endButt);
+ dest->LineTo (endPos+width*endNor+width*endButt);
+ dest->LineTo (endPos+width*endNor);
+ } else if (butt == butt_pointy) {
+ dest->LineTo (endPos+width*endButt);
+ dest->LineTo (endPos+width*endNor);
+ } else {
+ dest->LineTo (endPos+width*endNor);
+ }
+ SubContractOutline (lastM, realP - lastM,
+ dest, calls, 0.0025 * width * width, width, join, butt,
+ miter, false, true, endPos, endButt);
+
+ endNor=endButt.ccw();
+ if (butt == butt_round) {
+ dest->ArcTo (endPos+width*endButt, width, width, 0.0, false, true);
+ dest->ArcTo (endPos+width*endNor, width, width, 0.0, false, true);
+ } else if (butt == butt_square) {
+ dest->LineTo (endPos-width*endNor+width*endButt);
+ dest->LineTo (endPos+width*endNor+width*endButt);
+ dest->LineTo (endPos+width*endNor);
+ } else if (butt == butt_pointy) {
+ dest->LineTo (endPos+width*endButt);
+ dest->LineTo (endPos+width*endNor);
+ } else {
+ dest->LineTo (endPos+width*endNor);
+ }
+ dest->Close ();
+ }
+ } // if (curD > lastM)
+ } // if (curP > lastM + 1)
+
+ } while (curP < int(descr_cmd.size()));
+
+ delete rev;
+}
+
+// versions for outlining closed path: they only make one side of the offset contour
+void
+Path::OutsideOutline (Path * dest, double width, JoinType join, ButtType butt,
+ double miter)
+{
+ if (descr_flags & descr_adding_bezier) {
+ CancelBezier();
+ }
+ if (descr_flags & descr_doing_subpath) {
+ CloseSubpath();
+ }
+ if (int(descr_cmd.size()) <= 1) return;
+ if (dest == nullptr) return;
+ dest->Reset ();
+ dest->SetBackData (false);
+
+ outline_callbacks calls;
+ Geom::Point endButt, endPos;
+ calls.cubicto = StdCubicTo;
+ calls.bezierto = StdBezierTo;
+ calls.arcto = StdArcTo;
+ SubContractOutline (0, descr_cmd.size(),
+ dest, calls, 0.0025 * width * width, width, join, butt,
+ miter, true, false, endPos, endButt);
+}
+
+void
+Path::InsideOutline (Path * dest, double width, JoinType join, ButtType butt,
+ double miter)
+{
+ if ( descr_flags & descr_adding_bezier ) {
+ CancelBezier();
+ }
+ if ( descr_flags & descr_doing_subpath ) {
+ CloseSubpath();
+ }
+ if (int(descr_cmd.size()) <= 1) return;
+ if (dest == nullptr) return;
+ dest->Reset ();
+ dest->SetBackData (false);
+
+ outline_callbacks calls;
+ Geom::Point endButt, endPos;
+ calls.cubicto = StdCubicTo;
+ calls.bezierto = StdBezierTo;
+ calls.arcto = StdArcTo;
+
+ Path *rev = new Path;
+
+ int curP = 0;
+ do {
+ int lastM = curP;
+ do {
+ curP++;
+ if (curP >= int(descr_cmd.size())) break;
+ int typ = descr_cmd[curP]->getType();
+ if (typ == descr_moveto) break;
+ } while (curP < int(descr_cmd.size()));
+ if (curP >= int(descr_cmd.size())) curP = descr_cmd.size();
+ if (curP > lastM + 1) {
+ // Otherwise there's only one point. (tr: or "only a point")
+ // [sinon il n'y a qu'un point]
+ int curD = curP - 1;
+ Geom::Point curX;
+ Geom::Point nextX;
+ while (curD > lastM && (descr_cmd[curD]->getType()) == descr_close) curD--;
+ if (curD > lastM) {
+ curX = PrevPoint (curD);
+ rev->Reset ();
+ rev->MoveTo (curX);
+ while (curD > lastM) {
+ int typ = descr_cmd[curD]->getType();
+ if (typ == descr_moveto) {
+ rev->Close ();
+ curD--;
+ } else if (typ == descr_forced) {
+ curD--;
+ } else if (typ == descr_lineto) {
+ nextX = PrevPoint (curD - 1);
+ rev->LineTo (nextX);
+ curX = nextX;
+ curD--;
+ } else if (typ == descr_cubicto) {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo*>(descr_cmd[curD]);
+ nextX = PrevPoint (curD - 1);
+ Geom::Point isD=-nData->start;
+ Geom::Point ieD=-nData->end;
+ rev->CubicTo (nextX, ieD,isD);
+ curX = nextX;
+ curD--;
+ } else if (typ == descr_arcto) {
+ PathDescrArcTo* nData = dynamic_cast<PathDescrArcTo*>(descr_cmd[curD]);
+ nextX = PrevPoint (curD - 1);
+ rev->ArcTo (nextX, nData->rx,nData->ry,nData->angle,nData->large,nData->clockwise);
+ curX = nextX;
+ curD--;
+ } else if (typ == descr_bezierto) {
+ nextX = PrevPoint (curD - 1);
+ rev->LineTo (nextX);
+ curX = nextX;
+ curD--;
+ } else if (typ == descr_interm_bezier) {
+ int nD = curD - 1;
+ while (nD > lastM && (descr_cmd[nD]->getType()) != descr_bezierto) nD--;
+ if (descr_cmd[nD]->getType() != descr_bezierto) {
+ // pas trouve le debut!?
+ nextX = PrevPoint (nD);
+ rev->LineTo (nextX);
+ curX = nextX;
+ } else {
+ nextX = PrevPoint (nD - 1);
+ rev->BezierTo (nextX);
+ for (int i = curD; i > nD; i--) {
+ PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(descr_cmd[i]);
+ rev->IntermBezierTo (nData->p);
+ }
+ rev->EndBezierTo ();
+ curX = nextX;
+ }
+ curD = nD - 1;
+ } else {
+ curD--;
+ }
+ }
+ rev->Close ();
+ rev->SubContractOutline (0, rev->descr_cmd.size(),
+ dest, calls, 0.0025 * width * width,
+ width, join, butt, miter, true, false,
+ endPos, endButt);
+ }
+ }
+ } while (curP < int(descr_cmd.size()));
+
+ delete rev;
+}
+
+
+// the offset
+// take each command and offset it.
+// the bezier spline is split in a sequence of bezier curves, and these are transformed in cubic bezier (which is
+// not hard since they are quadratic bezier)
+// joins are put where needed
+void Path::SubContractOutline(int off, int num_pd,
+ Path *dest, outline_callbacks & calls,
+ double tolerance, double width, JoinType join,
+ ButtType /*butt*/, double miter, bool closeIfNeeded,
+ bool skipMoveto, Geom::Point &lastP, Geom::Point &lastT)
+{
+ outline_callback_data callsData;
+
+ callsData.orig = this;
+ callsData.dest = dest;
+ int curP = 1;
+
+ // le moveto
+ Geom::Point curX;
+ {
+ int firstTyp = descr_cmd[off]->getType();
+ if ( firstTyp != descr_moveto ) {
+ curX[0] = curX[1] = 0;
+ curP = 0;
+ } else {
+ PathDescrMoveTo* nData = dynamic_cast<PathDescrMoveTo*>(descr_cmd[off]);
+ curX = nData->p;
+ }
+ }
+ Geom::Point curT(0, 0);
+
+ bool doFirst = true;
+ Geom::Point firstP(0, 0);
+ Geom::Point firstT(0, 0);
+
+ // et le reste, 1 par 1
+ while (curP < num_pd)
+ {
+ int curD = off + curP;
+ int nType = descr_cmd[curD]->getType();
+ Geom::Point nextX;
+ Geom::Point stPos, enPos, stTgt, enTgt, stNor, enNor;
+ double stRad, enRad, stTle, enTle;
+ if (nType == descr_forced) {
+ curP++;
+ } else if (nType == descr_moveto) {
+ PathDescrMoveTo* nData = dynamic_cast<PathDescrMoveTo*>(descr_cmd[curD]);
+ nextX = nData->p;
+ // et on avance
+ if (doFirst) {
+ } else {
+ if (closeIfNeeded) {
+ if ( Geom::LInfty (curX- firstP) < 0.0001 ) {
+ OutlineJoin (dest, firstP, curT, firstT, width, join,
+ miter, nType);
+ dest->Close ();
+ } else {
+ PathDescrLineTo temp(firstP);
+
+ TangentOnSegAt (0.0, curX, temp, stPos, stTgt,
+ stTle);
+ TangentOnSegAt (1.0, curX, temp, enPos, enTgt,
+ enTle);
+ stNor=stTgt.cw();
+ enNor=enTgt.cw();
+
+ // jointure
+ {
+ Geom::Point pos;
+ pos = curX;
+ OutlineJoin (dest, pos, curT, stNor, width, join,
+ miter, nType);
+ }
+ dest->LineTo (enPos+width*enNor);
+
+ // jointure
+ {
+ Geom::Point pos;
+ pos = firstP;
+ OutlineJoin (dest, enPos, enNor, firstT, width, join,
+ miter, nType);
+ dest->Close ();
+ }
+ }
+ }
+ }
+ firstP = nextX;
+ curP++;
+ }
+ else if (nType == descr_close)
+ {
+ if (! doFirst)
+ {
+ if (Geom::LInfty (curX - firstP) < 0.0001)
+ {
+ OutlineJoin (dest, firstP, curT, firstT, width, join,
+ miter, nType);
+ dest->Close ();
+ }
+ else
+ {
+ PathDescrLineTo temp(firstP);
+ nextX = firstP;
+
+ TangentOnSegAt (0.0, curX, temp, stPos, stTgt, stTle);
+ TangentOnSegAt (1.0, curX, temp, enPos, enTgt, enTle);
+ stNor=stTgt.cw();
+ enNor=enTgt.cw();
+
+ // jointure
+ {
+ OutlineJoin (dest, stPos, curT, stNor, width, join,
+ miter, nType);
+ }
+
+ dest->LineTo (enPos+width*enNor);
+
+ // jointure
+ {
+ OutlineJoin (dest, enPos, enNor, firstT, width, join,
+ miter, nType);
+ dest->Close ();
+ }
+ }
+ }
+ doFirst = true;
+ curP++;
+ }
+ else if (nType == descr_lineto)
+ {
+ PathDescrLineTo* nData = dynamic_cast<PathDescrLineTo*>(descr_cmd[curD]);
+ nextX = nData->p;
+ // et on avance
+ TangentOnSegAt (0.0, curX, *nData, stPos, stTgt, stTle);
+ TangentOnSegAt (1.0, curX, *nData, enPos, enTgt, enTle);
+ // test de nullité du segment
+ if (IsNulCurve (descr_cmd, curD, curX))
+ {
+ if (descr_cmd.size() == 2) { // single point, see LP Bug 1006666
+ stTgt = dest->descr_cmd.size() ? Geom::Point(1, 0) : Geom::Point(-1, 0); // reverse direction
+ enTgt = stTgt;
+ } else {
+ curP++;
+ continue;
+ }
+ }
+ stNor=stTgt.cw();
+ enNor=enTgt.cw();
+
+ lastP = enPos;
+ lastT = enTgt;
+
+ if (doFirst)
+ {
+ doFirst = false;
+ firstP = stPos;
+ firstT = stNor;
+ if (skipMoveto)
+ {
+ skipMoveto = false;
+ }
+ else
+ dest->MoveTo (curX+width*stNor);
+ }
+ else
+ {
+ // jointure
+ Geom::Point pos;
+ pos = curX;
+ OutlineJoin (dest, pos, curT, stNor, width, join, miter, nType);
+ }
+
+ int n_d = dest->LineTo (nextX+width*enNor);
+ if (n_d >= 0)
+ {
+ dest->descr_cmd[n_d]->associated = curP;
+ dest->descr_cmd[n_d]->tSt = 0.0;
+ dest->descr_cmd[n_d]->tEn = 1.0;
+ }
+ curP++;
+ }
+ else if (nType == descr_cubicto)
+ {
+ PathDescrCubicTo* nData = dynamic_cast<PathDescrCubicTo*>(descr_cmd[curD]);
+ nextX = nData->p;
+ // test de nullite du segment
+ if (IsNulCurve (descr_cmd, curD, curX))
+ {
+ curP++;
+ continue;
+ }
+ // et on avance
+ TangentOnCubAt (0.0, curX, *nData, false, stPos, stTgt,
+ stTle, stRad);
+ TangentOnCubAt (1.0, curX, *nData, true, enPos, enTgt,
+ enTle, enRad);
+ stNor=stTgt.cw();
+ enNor=enTgt.cw();
+
+ lastP = enPos;
+ lastT = enTgt;
+
+ if (doFirst)
+ {
+ doFirst = false;
+ firstP = stPos;
+ firstT = stNor;
+ if (skipMoveto)
+ {
+ skipMoveto = false;
+ }
+ else
+ dest->MoveTo (curX+width*stNor);
+ }
+ else
+ {
+ // jointure
+ Geom::Point pos;
+ pos = curX;
+ OutlineJoin (dest, pos, curT, stNor, width, join, miter, nType);
+ }
+
+ callsData.piece = curP;
+ callsData.tSt = 0.0;
+ callsData.tEn = 1.0;
+ callsData.x1 = curX[0];
+ callsData.y1 = curX[1];
+ callsData.x2 = nextX[0];
+ callsData.y2 = nextX[1];
+ callsData.d.c.dx1 = nData->start[0];
+ callsData.d.c.dy1 = nData->start[1];
+ callsData.d.c.dx2 = nData->end[0];
+ callsData.d.c.dy2 = nData->end[1];
+ (calls.cubicto) (&callsData, tolerance, width);
+
+ curP++;
+ }
+ else if (nType == descr_arcto)
+ {
+ PathDescrArcTo* nData = dynamic_cast<PathDescrArcTo*>(descr_cmd[curD]);
+ nextX = nData->p;
+ // test de nullité du segment
+ if (IsNulCurve (descr_cmd, curD, curX))
+ {
+ curP++;
+ continue;
+ }
+ // et on avance
+ TangentOnArcAt (0.0, curX, *nData, stPos, stTgt, stTle,
+ stRad);
+ TangentOnArcAt (1.0, curX, *nData, enPos, enTgt, enTle,
+ enRad);
+ stNor=stTgt.cw();
+ enNor=enTgt.cw();
+
+ lastP = enPos;
+ lastT = enTgt; // tjs definie
+
+ if (doFirst)
+ {
+ doFirst = false;
+ firstP = stPos;
+ firstT = stNor;
+ if (skipMoveto)
+ {
+ skipMoveto = false;
+ }
+ else
+ dest->MoveTo (curX+width*stNor);
+ }
+ else
+ {
+ // jointure
+ Geom::Point pos;
+ pos = curX;
+ OutlineJoin (dest, pos, curT, stNor, width, join, miter, nType);
+ }
+
+ callsData.piece = curP;
+ callsData.tSt = 0.0;
+ callsData.tEn = 1.0;
+ callsData.x1 = curX[0];
+ callsData.y1 = curX[1];
+ callsData.x2 = nextX[0];
+ callsData.y2 = nextX[1];
+ callsData.d.a.rx = nData->rx;
+ callsData.d.a.ry = nData->ry;
+ callsData.d.a.angle = nData->angle;
+ callsData.d.a.clock = nData->clockwise;
+ callsData.d.a.large = nData->large;
+ (calls.arcto) (&callsData, tolerance, width);
+
+ curP++;
+ }
+ else if (nType == descr_bezierto)
+ {
+ PathDescrBezierTo* nBData = dynamic_cast<PathDescrBezierTo*>(descr_cmd[curD]);
+ int nbInterm = nBData->nb;
+ nextX = nBData->p;
+
+ if (IsNulCurve (descr_cmd, curD, curX)) {
+ curP += nbInterm + 1;
+ continue;
+ }
+
+ curP++;
+
+ curD = off + curP;
+ int ip = curD;
+ PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(descr_cmd[ip]);
+
+ if (nbInterm <= 0) {
+ // et on avance
+ PathDescrLineTo temp(nextX);
+ TangentOnSegAt (0.0, curX, temp, stPos, stTgt, stTle);
+ TangentOnSegAt (1.0, curX, temp, enPos, enTgt, enTle);
+ stNor=stTgt.cw();
+ enNor=enTgt.cw();
+
+ lastP = enPos;
+ lastT = enTgt;
+
+ if (doFirst) {
+ doFirst = false;
+ firstP = stPos;
+ firstT = stNor;
+ if (skipMoveto) {
+ skipMoveto = false;
+ } else dest->MoveTo (curX+width*stNor);
+ } else {
+ // jointure
+ Geom::Point pos;
+ pos = curX;
+ if (stTle > 0) OutlineJoin (dest, pos, curT, stNor, width, join, miter, nType);
+ }
+ int n_d = dest->LineTo (nextX+width*enNor);
+ if (n_d >= 0) {
+ dest->descr_cmd[n_d]->associated = curP - 1;
+ dest->descr_cmd[n_d]->tSt = 0.0;
+ dest->descr_cmd[n_d]->tEn = 1.0;
+ }
+ } else if (nbInterm == 1) {
+ Geom::Point midX;
+ midX = nData->p;
+ // et on avance
+ TangentOnBezAt (0.0, curX, *nData, *nBData, false, stPos, stTgt, stTle, stRad);
+ TangentOnBezAt (1.0, curX, *nData, *nBData, true, enPos, enTgt, enTle, enRad);
+ stNor=stTgt.cw();
+ enNor=enTgt.cw();
+
+ lastP = enPos;
+ lastT = enTgt;
+
+ if (doFirst) {
+ doFirst = false;
+ firstP = stPos;
+ firstT = stNor;
+ if (skipMoveto) {
+ skipMoveto = false;
+ } else dest->MoveTo (curX+width*stNor);
+ } else {
+ // jointure
+ Geom::Point pos;
+ pos = curX;
+ OutlineJoin (dest, pos, curT, stNor, width, join, miter, nType);
+ }
+
+ callsData.piece = curP;
+ callsData.tSt = 0.0;
+ callsData.tEn = 1.0;
+ callsData.x1 = curX[0];
+ callsData.y1 = curX[1];
+ callsData.x2 = nextX[0];
+ callsData.y2 = nextX[1];
+ callsData.d.b.mx = midX[0];
+ callsData.d.b.my = midX[1];
+ (calls.bezierto) (&callsData, tolerance, width);
+
+ } else if (nbInterm > 1) {
+ Geom::Point bx=curX;
+ Geom::Point cx=curX;
+ Geom::Point dx=nData->p;
+
+ TangentOnBezAt (0.0, curX, *nData, *nBData, false, stPos, stTgt, stTle, stRad);
+ stNor=stTgt.cw();
+
+ ip++;
+ nData = dynamic_cast<PathDescrIntermBezierTo*>(descr_cmd[ip]);
+ // et on avance
+ if (stTle > 0) {
+ if (doFirst) {
+ doFirst = false;
+ firstP = stPos;
+ firstT = stNor;
+ if (skipMoveto) {
+ skipMoveto = false;
+ } else dest->MoveTo (curX+width*stNor);
+ } else {
+ // jointure
+ Geom::Point pos=curX;
+ OutlineJoin (dest, pos, stTgt, stNor, width, join, miter, nType);
+ // dest->LineTo(curX+width*stNor.x,curY+width*stNor.y);
+ }
+ }
+
+ cx = 2 * bx - dx;
+
+ for (int k = 0; k < nbInterm - 1; k++) {
+ bx = cx;
+ cx = dx;
+
+ dx = nData->p;
+ ip++;
+ nData = dynamic_cast<PathDescrIntermBezierTo*>(descr_cmd[ip]);
+ Geom::Point stx = (bx + cx) / 2;
+ // double stw=(bw+cw)/2;
+
+ PathDescrBezierTo tempb((cx + dx) / 2, 1);
+ PathDescrIntermBezierTo tempi(cx);
+ TangentOnBezAt (1.0, stx, tempi, tempb, true, enPos, enTgt, enTle, enRad);
+ enNor=enTgt.cw();
+
+ lastP = enPos;
+ lastT = enTgt;
+
+ callsData.piece = curP + k;
+ callsData.tSt = 0.0;
+ callsData.tEn = 1.0;
+ callsData.x1 = stx[0];
+ callsData.y1 = stx[1];
+ callsData.x2 = (cx[0] + dx[0]) / 2;
+ callsData.y2 = (cx[1] + dx[1]) / 2;
+ callsData.d.b.mx = cx[0];
+ callsData.d.b.my = cx[1];
+ (calls.bezierto) (&callsData, tolerance, width);
+ }
+ {
+ bx = cx;
+ cx = dx;
+
+ dx = nextX;
+ dx = 2 * dx - cx;
+
+ Geom::Point stx = (bx + cx) / 2;
+ // double stw=(bw+cw)/2;
+
+ PathDescrBezierTo tempb((cx + dx) / 2, 1);
+ PathDescrIntermBezierTo tempi(cx);
+ TangentOnBezAt (1.0, stx, tempi, tempb, true, enPos,
+ enTgt, enTle, enRad);
+ enNor=enTgt.cw();
+
+ lastP = enPos;
+ lastT = enTgt;
+
+ callsData.piece = curP + nbInterm - 1;
+ callsData.tSt = 0.0;
+ callsData.tEn = 1.0;
+ callsData.x1 = stx[0];
+ callsData.y1 = stx[1];
+ callsData.x2 = (cx[0] + dx[0]) / 2;
+ callsData.y2 = (cx[1] + dx[1]) / 2;
+ callsData.d.b.mx = cx[0];
+ callsData.d.b.my = cx[1];
+ (calls.bezierto) (&callsData, tolerance, width);
+
+ }
+ }
+
+ // et on avance
+ curP += nbInterm;
+ }
+ curX = nextX;
+ curT = enNor; // sera tjs bien definie
+ }
+ if (closeIfNeeded)
+ {
+ if (! doFirst)
+ {
+ }
+ }
+
+}
+
+/*
+ *
+ * utilitaires pour l'outline
+ *
+ */
+
+// like the name says: check whether the path command is actually more than a dumb point.
+bool
+Path::IsNulCurve (std::vector<PathDescr*> const &cmd, int curD, Geom::Point const &curX)
+{
+ switch(cmd[curD]->getType()) {
+ case descr_lineto:
+ {
+ PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo*>(cmd[curD]);
+ if (Geom::LInfty(nData->p - curX) < 0.00001) {
+ return true;
+ }
+ return false;
+ }
+ case descr_cubicto:
+ {
+ PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo*>(cmd[curD]);
+ Geom::Point A = nData->start + nData->end + 2*(curX - nData->p);
+ Geom::Point B = 3*(nData->p - curX) - 2*nData->start - nData->end;
+ Geom::Point C = nData->start;
+ if (Geom::LInfty(A) < 0.0001
+ && Geom::LInfty(B) < 0.0001
+ && Geom::LInfty (C) < 0.0001) {
+ return true;
+ }
+ return false;
+ }
+ case descr_arcto:
+ {
+ PathDescrArcTo* nData = dynamic_cast<PathDescrArcTo*>(cmd[curD]);
+ if ( Geom::LInfty(nData->p - curX) < 0.00001) {
+ if ((! nData->large)
+ || (fabs (nData->rx) < 0.00001
+ || fabs (nData->ry) < 0.00001)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ case descr_bezierto:
+ {
+ PathDescrBezierTo* nBData = dynamic_cast<PathDescrBezierTo*>(cmd[curD]);
+ if (nBData->nb <= 0)
+ {
+ if (Geom::LInfty(nBData->p - curX) < 0.00001) {
+ return true;
+ }
+ return false;
+ }
+ else if (nBData->nb == 1)
+ {
+ if (Geom::LInfty(nBData->p - curX) < 0.00001) {
+ int ip = curD + 1;
+ PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(cmd[ip]);
+ if (Geom::LInfty(nData->p - curX) < 0.00001) {
+ return true;
+ }
+ }
+ return false;
+ } else if (Geom::LInfty(nBData->p - curX) < 0.00001) {
+ for (int i = 1; i <= nBData->nb; i++) {
+ int ip = curD + i;
+ PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(cmd[ip]);
+ if (Geom::LInfty(nData->p - curX) > 0.00001) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ default:
+ return true;
+ }
+}
+
+// tangents and curvarture computing, for the different path command types.
+// the need for tangent is obvious: it gives the normal, along which we offset points
+// curvature is used to do strength correction on the length of the tangents to the offset (see
+// cubic offset)
+
+/**
+ * \param at Distance along a tangent (0 <= at <= 1).
+ * \param iS Start point.
+ * \param fin LineTo description containing end point.
+ * \param pos Filled in with the position of `at' on the segment.
+ * \param tgt Filled in with the normalised tangent vector.
+ * \param len Filled in with the length of the segment.
+ */
+
+void Path::TangentOnSegAt(double at, Geom::Point const &iS, PathDescrLineTo const &fin,
+ Geom::Point &pos, Geom::Point &tgt, double &len)
+{
+ Geom::Point const iE = fin.p;
+ Geom::Point const seg = iE - iS;
+ double const l = L2(seg);
+ if (l <= 0.000001) {
+ pos = iS;
+ tgt = Geom::Point(0, 0);
+ len = 0;
+ } else {
+ tgt = seg / l;
+ pos = (1 - at) * iS + at * iE; // in other words, pos = iS + at * seg
+ len = l;
+ }
+}
+
+// barf
+void Path::TangentOnArcAt(double at, const Geom::Point &iS, PathDescrArcTo const &fin,
+ Geom::Point &pos, Geom::Point &tgt, double &len, double &rad)
+{
+ Geom::Point const iE = fin.p;
+ double const rx = fin.rx;
+ double const ry = fin.ry;
+ double const angle = fin.angle*M_PI/180.0;
+ bool const large = fin.large;
+ bool const wise = fin.clockwise;
+
+ pos = iS;
+ tgt[0] = tgt[1] = 0;
+ if (rx <= 0.0001 || ry <= 0.0001)
+ return;
+
+ double const sex = iE[0] - iS[0], sey = iE[1] - iS[1];
+ double const ca = cos (angle), sa = sin (angle);
+ double csex = ca * sex + sa * sey;
+ double csey = -sa * sex + ca * sey;
+ csex /= rx;
+ csey /= ry;
+ double l = csex * csex + csey * csey;
+ double const d = sqrt(std::max(1 - l / 4, 0.0));
+ double csdx = csey;
+ double csdy = -csex;
+ l = sqrt(l);
+ csdx /= l;
+ csdy /= l;
+ csdx *= d;
+ csdy *= d;
+
+ double sang;
+ double eang;
+ double rax = -csdx - csex / 2;
+ double ray = -csdy - csey / 2;
+ if (rax < -1)
+ {
+ sang = M_PI;
+ }
+ else if (rax > 1)
+ {
+ sang = 0;
+ }
+ else
+ {
+ sang = acos (rax);
+ if (ray < 0)
+ sang = 2 * M_PI - sang;
+ }
+ rax = -csdx + csex / 2;
+ ray = -csdy + csey / 2;
+ if (rax < -1)
+ {
+ eang = M_PI;
+ }
+ else if (rax > 1)
+ {
+ eang = 0;
+ }
+ else
+ {
+ eang = acos (rax);
+ if (ray < 0)
+ eang = 2 * M_PI - eang;
+ }
+
+ csdx *= rx;
+ csdy *= ry;
+ double drx = ca * csdx - sa * csdy;
+ double dry = sa * csdx + ca * csdy;
+
+ if (wise)
+ {
+ if (large)
+ {
+ drx = -drx;
+ dry = -dry;
+ double swap = eang;
+ eang = sang;
+ sang = swap;
+ eang += M_PI;
+ sang += M_PI;
+ if (eang >= 2 * M_PI)
+ eang -= 2 * M_PI;
+ if (sang >= 2 * M_PI)
+ sang -= 2 * M_PI;
+ }
+ }
+ else
+ {
+ if (! large)
+ {
+ drx = -drx;
+ dry = -dry;
+ double swap = eang;
+ eang = sang;
+ sang = swap;
+ eang += M_PI;
+ sang += M_PI;
+ if (eang >= 2 * M_PI)
+ eang -= 2 * M_PI;
+ if (sang >= 2 * M_PI)
+ sang -= 2 * M_PI;
+ }
+ }
+ drx += (iS[0] + iE[0]) / 2;
+ dry += (iS[1] + iE[1]) / 2;
+
+ if (wise) {
+ if (sang < eang)
+ sang += 2 * M_PI;
+ double b = sang * (1 - at) + eang * at;
+ double cb = cos (b), sb = sin (b);
+ pos[0] = drx + ca * rx * cb - sa * ry * sb;
+ pos[1] = dry + sa * rx * cb + ca * ry * sb;
+ tgt[0] = ca * rx * sb + sa * ry * cb;
+ tgt[1] = sa * rx * sb - ca * ry * cb;
+ Geom::Point dtgt;
+ dtgt[0] = -ca * rx * cb + sa * ry * sb;
+ dtgt[1] = -sa * rx * cb - ca * ry * sb;
+ len = L2(tgt);
+ rad = -len * dot(tgt, tgt) / (tgt[0] * dtgt[1] - tgt[1] * dtgt[0]);
+ tgt /= len;
+ }
+ else
+ {
+ if (sang > eang)
+ sang -= 2 * M_PI;
+ double b = sang * (1 - at) + eang * at;
+ double cb = cos (b), sb = sin (b);
+ pos[0] = drx + ca * rx * cb - sa * ry * sb;
+ pos[1] = dry + sa * rx * cb + ca * ry * sb;
+ tgt[0] = ca * rx * sb + sa * ry * cb;
+ tgt[1] = sa * rx * sb - ca * ry * cb;
+ Geom::Point dtgt;
+ dtgt[0] = -ca * rx * cb + sa * ry * sb;
+ dtgt[1] = -sa * rx * cb - ca * ry * sb;
+ len = L2(tgt);
+ rad = len * dot(tgt, tgt) / (tgt[0] * dtgt[1] - tgt[1] * dtgt[0]);
+ tgt /= len;
+ }
+}
+void
+Path::TangentOnCubAt (double at, Geom::Point const &iS, PathDescrCubicTo const &fin, bool before,
+ Geom::Point &pos, Geom::Point &tgt, double &len, double &rad)
+{
+ const Geom::Point E = fin.p;
+ const Geom::Point Sd = fin.start;
+ const Geom::Point Ed = fin.end;
+
+ pos = iS;
+ tgt = Geom::Point(0,0);
+ len = rad = 0;
+
+ const Geom::Point A = Sd + Ed - 2*E + 2*iS;
+ const Geom::Point B = 0.5*(Ed - Sd);
+ const Geom::Point C = 0.25*(6*E - 6*iS - Sd - Ed);
+ const Geom::Point D = 0.125*(4*iS + 4*E - Ed + Sd);
+ const double atb = at - 0.5;
+ pos = (atb * atb * atb)*A + (atb * atb)*B + atb*C + D;
+ const Geom::Point der = (3 * atb * atb)*A + (2 * atb)*B + C;
+ const Geom::Point dder = (6 * atb)*A + 2*B;
+ const Geom::Point ddder = 6 * A;
+
+ double l = Geom::L2 (der);
+ // lots of nasty cases. inversion points are sadly too common...
+ if (l <= 0.0001) {
+ len = 0;
+ l = L2(dder);
+ if (l <= 0.0001) {
+ l = L2(ddder);
+ if (l <= 0.0001) {
+ // pas de segment....
+ return;
+ }
+ rad = 100000000;
+ tgt = ddder / l;
+ if (before) {
+ tgt = -tgt;
+ }
+ return;
+ }
+ rad = -l * (dot(dder,dder)) / (cross(dder, ddder));
+ tgt = dder / l;
+ if (before) {
+ tgt = -tgt;
+ }
+ return;
+ }
+ len = l;
+
+ rad = -l * (dot(der,der)) / (cross(der, dder));
+
+ tgt = der / l;
+}
+
+void
+Path::TangentOnBezAt (double at, Geom::Point const &iS,
+ PathDescrIntermBezierTo & mid,
+ PathDescrBezierTo & fin, bool before, Geom::Point & pos,
+ Geom::Point & tgt, double &len, double &rad)
+{
+ pos = iS;
+ tgt = Geom::Point(0,0);
+ len = rad = 0;
+
+ const Geom::Point A = fin.p + iS - 2*mid.p;
+ const Geom::Point B = 2*mid.p - 2 * iS;
+ const Geom::Point C = iS;
+
+ pos = at * at * A + at * B + C;
+ const Geom::Point der = 2 * at * A + B;
+ const Geom::Point dder = 2 * A;
+ double l = Geom::L2(der);
+
+ if (l <= 0.0001) {
+ l = Geom::L2(dder);
+ if (l <= 0.0001) {
+ // pas de segment....
+ // Not a segment.
+ return;
+ }
+ rad = 100000000; // Why this number?
+ tgt = dder / l;
+ if (before) {
+ tgt = -tgt;
+ }
+ return;
+ }
+ len = l;
+ rad = -l * (dot(der,der)) / (cross(der, dder));
+
+ tgt = der / l;
+}
+
+void
+Path::OutlineJoin (Path * dest, Geom::Point pos, Geom::Point stNor, Geom::Point enNor, double width,
+ JoinType join, double miter, int nType)
+{
+ /*
+ Arbitrarily decide if we're on the inside or outside of a half turn.
+ A turn of 180 degrees (line path leaves the node in the same direction as it arrived)
+ is symmetric and has no real inside and outside. However when outlining we shall handle
+ one path as inside and the reverse path as outside. Handling both as inside joins (as
+ was done previously) will cut off round joins. Handling both as outside joins could
+ ideally work because both should fall together, but it seems that this causes many
+ extra nodes (due to rounding errors). Solution: for the 'half turn'-case toggle
+ inside/outside each time the same node is processed 2 consecutive times.
+ */
+ static bool TurnInside = true;
+ static Geom::Point PrevPos(0, 0);
+ TurnInside ^= PrevPos == pos;
+ PrevPos = pos;
+
+ const double angSi = cross (stNor, enNor);
+ const double angCo = dot (stNor, enNor);
+
+ if ((fabs(angSi) < .0000001) && angCo > 0) { // The join is straight -> nothing to do.
+ } else {
+ if ((angSi > 0 && width >= 0)
+ || (angSi < 0 && width < 0)) { // This is an inside join -> join is independent of chosen JoinType.
+ if ((dest->descr_cmd[dest->descr_cmd.size() - 1]->getType() == descr_lineto) && (nType == descr_lineto)) {
+ Geom::Point const biss = unit_vector(Geom::rot90( stNor - enNor ));
+ double c2 = Geom::dot (biss, enNor);
+ if (fabs(c2) > M_SQRT1_2) { // apply only to obtuse angles
+ double l = width / c2;
+ PathDescrLineTo* nLine = dynamic_cast<PathDescrLineTo*>(dest->descr_cmd[dest->descr_cmd.size() - 1]);
+ nLine->p = pos + l*biss; // relocate to bisector
+ } else {
+ dest->LineTo (pos + width*enNor);
+ }
+ } else {
+// dest->LineTo (pos); // redundant
+ dest->LineTo (pos + width*enNor);
+ }
+ } else if (angSi == 0 && TurnInside) { // Half turn (180 degrees) ... inside (see above).
+ dest->LineTo (pos + width*enNor);
+ } else { // This is an outside join -> chosen JoinType should be applied.
+ if (join == join_round) {
+ // Use the ends of the cubic: approximate the arc at the
+ // point where .., and support better the rounding of
+ // coordinates of the end points.
+
+ // utiliser des bouts de cubique: approximation de l'arc (au point ou on en est...), et supporte mieux
+ // l'arrondi des coordonnees des extremites
+ /* double angle=acos(angCo);
+ if ( angCo >= 0 ) {
+ Geom::Point stTgt,enTgt;
+ RotCCWTo(stNor,stTgt);
+ RotCCWTo(enNor,enTgt);
+ dest->CubicTo(pos.x+width*enNor.x,pos.y+width*enNor.y,
+ angle*width*stTgt.x,angle*width*stTgt.y,
+ angle*width*enTgt.x,angle*width*enTgt.y);
+ } else {
+ Geom::Point biNor;
+ Geom::Point stTgt,enTgt,biTgt;
+ biNor.x=stNor.x+enNor.x;
+ biNor.y=stNor.y+enNor.y;
+ double biL=sqrt(biNor.x*biNor.x+biNor.y*biNor.y);
+ biNor.x/=biL;
+ biNor.y/=biL;
+ RotCCWTo(stNor,stTgt);
+ RotCCWTo(enNor,enTgt);
+ RotCCWTo(biNor,biTgt);
+ dest->CubicTo(pos.x+width*biNor.x,pos.y+width*biNor.y,
+ angle*width*stTgt.x,angle*width*stTgt.y,
+ angle*width*biTgt.x,angle*width*biTgt.y);
+ dest->CubicTo(pos.x+width*enNor.x,pos.y+width*enNor.y,
+ angle*width*biTgt.x,angle*width*biTgt.y,
+ angle*width*enTgt.x,angle*width*enTgt.y);
+ }*/
+ if (width > 0) {
+ dest->ArcTo (pos + width*enNor,
+ 1.0001 * width, 1.0001 * width, 0.0, false, true);
+ } else {
+ dest->ArcTo (pos + width*enNor,
+ -1.0001 * width, -1.0001 * width, 0.0, false,
+ false);
+ }
+ } else if (join == join_pointy) {
+ Geom::Point const biss = unit_vector(Geom::rot90( stNor - enNor ));
+ double c2 = Geom::dot (biss, enNor);
+ double l = width / c2;
+ if ( fabs(l) > miter) {
+ dest->LineTo (pos + width*enNor);
+ } else {
+ if (dest->descr_cmd[dest->descr_cmd.size() - 1]->getType() == descr_lineto) {
+ PathDescrLineTo* nLine = dynamic_cast<PathDescrLineTo*>(dest->descr_cmd[dest->descr_cmd.size() - 1]);
+ nLine->p = pos+l*biss; // relocate to bisector
+ } else {
+ dest->LineTo (pos+l*biss);
+ }
+ if (nType != descr_lineto)
+ dest->LineTo (pos+width*enNor);
+ }
+ } else { // Bevel join
+ dest->LineTo (pos + width*enNor);
+ }
+ }
+ }
+}
+
+// les callbacks
+
+// see http://www.home.unix-ag.org/simon/sketch/pathstroke.py to understand what's happening here
+
+void
+Path::RecStdCubicTo (outline_callback_data * data, double tol, double width,
+ int lev)
+{
+ Geom::Point stPos, miPos, enPos;
+ Geom::Point stTgt, enTgt, miTgt, stNor, enNor, miNor;
+ double stRad, miRad, enRad;
+ double stTle, miTle, enTle;
+ // un cubic
+ {
+ PathDescrCubicTo temp(Geom::Point(data->x2, data->y2),
+ Geom::Point(data->d.c.dx1, data->d.c.dy1),
+ Geom::Point(data->d.c.dx2, data->d.c.dy2));
+
+ Geom::Point initial_point(data->x1, data->y1);
+ TangentOnCubAt (0.0, initial_point, temp, false, stPos, stTgt, stTle,
+ stRad);
+ TangentOnCubAt (0.5, initial_point, temp, false, miPos, miTgt, miTle,
+ miRad);
+ TangentOnCubAt (1.0, initial_point, temp, true, enPos, enTgt, enTle,
+ enRad);
+ stNor=stTgt.cw();
+ miNor=miTgt.cw();
+ enNor=enTgt.cw();
+ }
+
+ double stGue = 1, miGue = 1, enGue = 1;
+ // correction of the lengths of the tangent to the offset
+ // if you don't see why i wrote that, draw a little figure and everything will be clear
+ if (fabs (stRad) > 0.01)
+ stGue += width / stRad;
+ if (fabs (miRad) > 0.01)
+ miGue += width / miRad;
+ if (fabs (enRad) > 0.01)
+ enGue += width / enRad;
+ stGue *= stTle;
+ miGue *= miTle;
+ enGue *= enTle;
+
+
+ if (lev <= 0) {
+ int n_d = data->dest->CubicTo (enPos + width*enNor,
+ stGue*stTgt,
+ enGue*enTgt);
+ if (n_d >= 0) {
+ data->dest->descr_cmd[n_d]->associated = data->piece;
+ data->dest->descr_cmd[n_d]->tSt = data->tSt;
+ data->dest->descr_cmd[n_d]->tEn = data->tEn;
+ }
+ return;
+ }
+
+ Geom::Point chk;
+ const Geom::Point req = miPos + width * miNor;
+ {
+ PathDescrCubicTo temp(enPos + width * enNor,
+ stGue * stTgt,
+ enGue * enTgt);
+ double chTle, chRad;
+ Geom::Point chTgt;
+ TangentOnCubAt (0.5, stPos+width*stNor,
+ temp, false, chk, chTgt, chTle, chRad);
+ }
+ const Geom::Point diff = req - chk;
+ const double err = dot(diff,diff);
+ if (err <= tol ) { // tolerance is given as a quadratic value, no need to use tol*tol here
+// printf("%f <= %f %i\n",err,tol,lev);
+ int n_d = data->dest->CubicTo (enPos + width*enNor,
+ stGue*stTgt,
+ enGue*enTgt);
+ if (n_d >= 0) {
+ data->dest->descr_cmd[n_d]->associated = data->piece;
+ data->dest->descr_cmd[n_d]->tSt = data->tSt;
+ data->dest->descr_cmd[n_d]->tEn = data->tEn;
+ }
+ } else {
+ outline_callback_data desc = *data;
+
+ desc.tSt = data->tSt;
+ desc.tEn = (data->tSt + data->tEn) / 2;
+ desc.x1 = data->x1;
+ desc.y1 = data->y1;
+ desc.x2 = miPos[0];
+ desc.y2 = miPos[1];
+ desc.d.c.dx1 = 0.5 * stTle * stTgt[0];
+ desc.d.c.dy1 = 0.5 * stTle * stTgt[1];
+ desc.d.c.dx2 = 0.5 * miTle * miTgt[0];
+ desc.d.c.dy2 = 0.5 * miTle * miTgt[1];
+ RecStdCubicTo (&desc, tol, width, lev - 1);
+
+ desc.tSt = (data->tSt + data->tEn) / 2;
+ desc.tEn = data->tEn;
+ desc.x1 = miPos[0];
+ desc.y1 = miPos[1];
+ desc.x2 = data->x2;
+ desc.y2 = data->y2;
+ desc.d.c.dx1 = 0.5 * miTle * miTgt[0];
+ desc.d.c.dy1 = 0.5 * miTle * miTgt[1];
+ desc.d.c.dx2 = 0.5 * enTle * enTgt[0];
+ desc.d.c.dy2 = 0.5 * enTle * enTgt[1];
+ RecStdCubicTo (&desc, tol, width, lev - 1);
+ }
+}
+
+void
+Path::StdCubicTo (Path::outline_callback_data * data, double tol, double width)
+{
+// fflush (stdout);
+ RecStdCubicTo (data, tol, width, 8);
+}
+
+void
+Path::StdBezierTo (Path::outline_callback_data * data, double tol, double width)
+{
+ PathDescrBezierTo tempb(Geom::Point(data->x2, data->y2), 1);
+ PathDescrIntermBezierTo tempi(Geom::Point(data->d.b.mx, data->d.b.my));
+ Geom::Point stPos, enPos, stTgt, enTgt;
+ double stRad, enRad, stTle, enTle;
+ Geom::Point tmp(data->x1,data->y1);
+ TangentOnBezAt (0.0, tmp, tempi, tempb, false, stPos, stTgt,
+ stTle, stRad);
+ TangentOnBezAt (1.0, tmp, tempi, tempb, true, enPos, enTgt,
+ enTle, enRad);
+ data->d.c.dx1 = stTle * stTgt[0];
+ data->d.c.dy1 = stTle * stTgt[1];
+ data->d.c.dx2 = enTle * enTgt[0];
+ data->d.c.dy2 = enTle * enTgt[1];
+ RecStdCubicTo (data, tol, width, 8);
+}
+
+void
+Path::RecStdArcTo (outline_callback_data * data, double tol, double width,
+ int lev)
+{
+ Geom::Point stPos, miPos, enPos;
+ Geom::Point stTgt, enTgt, miTgt, stNor, enNor, miNor;
+ double stRad, miRad, enRad;
+ double stTle, miTle, enTle;
+ // un cubic
+ {
+ PathDescrArcTo temp(Geom::Point(data->x2, data->y2),
+ data->d.a.rx, data->d.a.ry,
+ data->d.a.angle, data->d.a.large, data->d.a.clock);
+
+ Geom::Point tmp(data->x1,data->y1);
+ TangentOnArcAt (data->d.a.stA, tmp, temp, stPos, stTgt,
+ stTle, stRad);
+ TangentOnArcAt ((data->d.a.stA + data->d.a.enA) / 2, tmp,
+ temp, miPos, miTgt, miTle, miRad);
+ TangentOnArcAt (data->d.a.enA, tmp, temp, enPos, enTgt,
+ enTle, enRad);
+ stNor=stTgt.cw();
+ miNor=miTgt.cw();
+ enNor=enTgt.cw();
+ }
+
+ double stGue = 1, miGue = 1, enGue = 1;
+ if (fabs (stRad) > 0.01)
+ stGue += width / stRad;
+ if (fabs (miRad) > 0.01)
+ miGue += width / miRad;
+ if (fabs (enRad) > 0.01)
+ enGue += width / enRad;
+ stGue *= stTle;
+ miGue *= miTle;
+ enGue *= enTle;
+ double sang, eang;
+ {
+ Geom::Point tms(data->x1,data->y1),tme(data->x2,data->y2);
+ ArcAngles (tms,tme, data->d.a.rx,
+ data->d.a.ry, data->d.a.angle*M_PI/180.0, data->d.a.large, !data->d.a.clock,
+ sang, eang);
+ }
+ double scal = eang - sang;
+ if (scal < 0)
+ scal += 2 * M_PI;
+ if (scal > 2 * M_PI)
+ scal -= 2 * M_PI;
+ scal *= data->d.a.enA - data->d.a.stA;
+
+ if (lev <= 0)
+ {
+ int n_d = data->dest->CubicTo (enPos + width*enNor,
+ stGue*scal*stTgt,
+ enGue*scal*enTgt);
+ if (n_d >= 0) {
+ data->dest->descr_cmd[n_d]->associated = data->piece;
+ data->dest->descr_cmd[n_d]->tSt = data->d.a.stA;
+ data->dest->descr_cmd[n_d]->tEn = data->d.a.enA;
+ }
+ return;
+ }
+
+ Geom::Point chk;
+ const Geom::Point req = miPos + width*miNor;
+ {
+ PathDescrCubicTo temp(enPos + width * enNor, stGue * scal * stTgt, enGue * scal * enTgt);
+ double chTle, chRad;
+ Geom::Point chTgt;
+ TangentOnCubAt (0.5, stPos+width*stNor,
+ temp, false, chk, chTgt, chTle, chRad);
+ }
+ const Geom::Point diff = req - chk;
+ const double err = (dot(diff,diff));
+ if (err <= tol) // tolerance is given as a quadratic value, no need to use tol*tol here
+ {
+ int n_d = data->dest->CubicTo (enPos + width*enNor,
+ stGue*scal*stTgt,
+ enGue*scal*enTgt);
+ if (n_d >= 0) {
+ data->dest->descr_cmd[n_d]->associated = data->piece;
+ data->dest->descr_cmd[n_d]->tSt = data->d.a.stA;
+ data->dest->descr_cmd[n_d]->tEn = data->d.a.enA;
+ }
+ } else {
+ outline_callback_data desc = *data;
+
+ desc.d.a.stA = data->d.a.stA;
+ desc.d.a.enA = (data->d.a.stA + data->d.a.enA) / 2;
+ RecStdArcTo (&desc, tol, width, lev - 1);
+
+ desc.d.a.stA = (data->d.a.stA + data->d.a.enA) / 2;
+ desc.d.a.enA = data->d.a.enA;
+ RecStdArcTo (&desc, tol, width, lev - 1);
+ }
+}
+
+void
+Path::StdArcTo (Path::outline_callback_data * data, double tol, double width)
+{
+ data->d.a.stA = 0.0;
+ data->d.a.enA = 1.0;
+ RecStdArcTo (data, tol, width, 8);
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/livarot/PathSimplify.cpp b/src/livarot/PathSimplify.cpp
new file mode 100644
index 0000000..bf3e200
--- /dev/null
+++ b/src/livarot/PathSimplify.cpp
@@ -0,0 +1,1404 @@
+// 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 <memory>
+#include <glib.h>
+#include <2geom/affine.h>
+#include "livarot/Path.h"
+#include "livarot/path-description.h"
+
+/*
+ * Reassembling polyline segments into cubic bezier patches
+ * thes functions do not need the back data. but they are slower than recomposing
+ * path descriptions when you have said back data (it's always easier with a model)
+ * there's a bezier fitter in bezier-utils.cpp too. the main difference is the way bezier patch are split
+ * here: walk on the polyline, trying to extend the portion you can fit by respecting the treshhold, split when
+ * treshhold is exceeded. when encountering a "forced" point, lower the treshhold to favor splitting at that point
+ * in bezier-utils: fit the whole polyline, get the position with the higher deviation to the fitted curve, split
+ * there and recurse
+ */
+
+
+// algo d'origine: http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/INT-APP/CURVE-APP-global.html
+
+// need the b-spline basis for cubic splines
+// pas oublier que c'est une b-spline clampee
+// et que ca correspond a une courbe de bezier normale
+#define N03(t) ((1.0-t)*(1.0-t)*(1.0-t))
+#define N13(t) (3*t*(1.0-t)*(1.0-t))
+#define N23(t) (3*t*t*(1.0-t))
+#define N33(t) (t*t*t)
+// quadratic b-splines (jsut in case)
+#define N02(t) ((1.0-t)*(1.0-t))
+#define N12(t) (2*t*(1.0-t))
+#define N22(t) (t*t)
+// linear interpolation b-splines
+#define N01(t) ((1.0-t))
+#define N11(t) (t)
+
+
+
+void Path::Simplify(double treshhold)
+{
+ if (pts.size() <= 1) {
+ return;
+ }
+
+ Reset();
+
+ int lastM = 0;
+ while (lastM < int(pts.size())) {
+ int lastP = lastM + 1;
+ while (lastP < int(pts.size())
+ && (pts[lastP].isMoveTo == polyline_lineto
+ || pts[lastP].isMoveTo == polyline_forced))
+ {
+ lastP++;
+ }
+
+ DoSimplify(lastM, lastP - lastM, treshhold);
+
+ lastM = lastP;
+ }
+}
+
+
+#if 0
+// dichomtomic method to get distance to curve approximation
+// a real polynomial solver would get the minimum more efficiently, but since the polynom
+// would likely be of degree >= 5, that would imply using some generic solver, liek using the sturm method
+static double RecDistanceToCubic(Geom::Point const &iS, Geom::Point const &isD,
+ Geom::Point const &iE, Geom::Point const &ieD,
+ Geom::Point &pt, double current, int lev, double st, double et)
+{
+ if ( lev <= 0 ) {
+ return current;
+ }
+
+ Geom::Point const m = 0.5 * (iS + iE) + 0.125 * (isD - ieD);
+ Geom::Point const md = 0.75 * (iE - iS) - 0.125 * (isD + ieD);
+ double const mt = (st + et) / 2;
+
+ Geom::Point const hisD = 0.5 * isD;
+ Geom::Point const hieD = 0.5 * ieD;
+
+ Geom::Point const mp = pt - m;
+ double nle = Geom::dot(mp, mp);
+
+ if ( nle < current ) {
+
+ current = nle;
+ nle = RecDistanceToCubic(iS, hisD, m, md, pt, current, lev - 1, st, mt);
+ if ( nle < current ) {
+ current = nle;
+ }
+ nle = RecDistanceToCubic(m, md, iE, hieD, pt, current, lev - 1, mt, et);
+ if ( nle < current ) {
+ current = nle;
+ }
+
+ } else if ( nle < 2 * current ) {
+
+ nle = RecDistanceToCubic(iS, hisD, m, md, pt, current, lev - 1, st, mt);
+ if ( nle < current ) {
+ current = nle;
+ }
+ nle = RecDistanceToCubic(m, md, iE, hieD, pt, current, lev - 1, mt, et);
+ if ( nle < current ) {
+ current = nle;
+ }
+ }
+
+ return current;
+}
+#endif
+
+static double DistanceToCubic(Geom::Point const &start, PathDescrCubicTo res, Geom::Point &pt)
+{
+ Geom::Point const sp = pt - start;
+ Geom::Point const ep = pt - res.p;
+ double nle = Geom::dot(sp, sp);
+ double nnle = Geom::dot(ep, ep);
+ if ( nnle < nle ) {
+ nle = nnle;
+ }
+
+ Geom::Point seg = res.p - start;
+ nnle = Geom::cross(sp, seg);
+ nnle *= nnle;
+ nnle /= Geom::dot(seg, seg);
+ if ( nnle < nle ) {
+ if ( Geom::dot(sp,seg) >= 0 ) {
+ seg = start - res.p;
+ if ( Geom::dot(ep,seg) >= 0 ) {
+ nle = nnle;
+ }
+ }
+ }
+
+ return nle;
+}
+
+
+/**
+ * Simplification on a subpath.
+ */
+
+void Path::DoSimplify(int off, int N, double treshhold)
+{
+ // non-dichotomic method: grow an interval of points approximated by a curve, until you reach the treshhold, and repeat
+ if (N <= 1) {
+ return;
+ }
+
+ int curP = 0;
+
+ fitting_tables data;
+ data.Xk = data.Yk = data.Qk = nullptr;
+ data.tk = data.lk = nullptr;
+ data.fk = nullptr;
+ data.totLen = 0;
+ data.nbPt = data.maxPt = data.inPt = 0;
+
+ Geom::Point const moveToPt = pts[off].p;
+ MoveTo(moveToPt);
+ Geom::Point endToPt = moveToPt;
+
+ while (curP < N - 1) {
+
+ int lastP = curP + 1;
+ int M = 2;
+
+ // remettre a zero
+ data.inPt = data.nbPt = 0;
+
+ PathDescrCubicTo res(Geom::Point(0, 0), Geom::Point(0, 0), Geom::Point(0, 0));
+ bool contains_forced = false;
+ int step = 64;
+
+ while ( step > 0 ) {
+ int forced_pt = -1;
+ int worstP = -1;
+
+ do {
+ if (pts[off + lastP].isMoveTo == polyline_forced) {
+ contains_forced = true;
+ }
+ forced_pt = lastP;
+ lastP += step;
+ M += step;
+ } while (lastP < N && ExtendFit(off + curP, M, data,
+ (contains_forced) ? 0.05 * treshhold : treshhold,
+ res, worstP) );
+ if (lastP >= N) {
+
+ lastP -= step;
+ M -= step;
+
+ } else {
+ // le dernier a echoue
+ lastP -= step;
+ M -= step;
+
+ if ( contains_forced ) {
+ lastP = forced_pt;
+ M = lastP - curP + 1;
+ }
+
+ AttemptSimplify(off + curP, M, treshhold, res, worstP); // ca passe forcement
+ }
+ step /= 2;
+ }
+
+ endToPt = pts[off + lastP].p;
+ if (M <= 2) {
+ LineTo(endToPt);
+ } else {
+ CubicTo(endToPt, res.start, res.end);
+ }
+
+ curP = lastP;
+ }
+
+ if (Geom::LInfty(endToPt - moveToPt) < 0.00001) {
+ Close();
+ }
+
+ g_free(data.Xk);
+ g_free(data.Yk);
+ g_free(data.Qk);
+ g_free(data.tk);
+ g_free(data.lk);
+ g_free(data.fk);
+}
+
+
+// warning: slow
+// idea behind this feature: splotches appear when trying to fit a small number of points: you can
+// get a cubic bezier that fits the points very well but doesn't fit the polyline itself
+// so we add a bit of the error at the middle of each segment of the polyline
+// also we restrict this to <=20 points, to avoid unnecessary computations
+#define with_splotch_killer
+
+// primitive= calc the cubic bezier patche that fits Xk and Yk best
+// Qk est deja alloue
+// retourne false si probleme (matrice non-inversible)
+bool Path::FitCubic(Geom::Point const &start, PathDescrCubicTo &res,
+ double *Xk, double *Yk, double *Qk, double *tk, int nbPt)
+{
+ Geom::Point const end = res.p;
+
+ // la matrice tNN
+ Geom::Affine M(0, 0, 0, 0, 0, 0);
+ for (int i = 1; i < nbPt - 1; i++) {
+ M[0] += N13(tk[i]) * N13(tk[i]);
+ M[1] += N23(tk[i]) * N13(tk[i]);
+ M[2] += N13(tk[i]) * N23(tk[i]);
+ M[3] += N23(tk[i]) * N23(tk[i]);
+ }
+
+ double const det = M.det();
+ if (fabs(det) < 0.000001) {
+ res.start[0]=res.start[1]=0.0;
+ res.end[0]=res.end[1]=0.0;
+ return false;
+ }
+
+ Geom::Affine const iM = M.inverse();
+ M = iM;
+
+ // phase 1: abcisses
+ // calcul des Qk
+ Xk[0] = start[0];
+ Yk[0] = start[1];
+ Xk[nbPt - 1] = end[0];
+ Yk[nbPt - 1] = end[1];
+
+ for (int i = 1; i < nbPt - 1; i++) {
+ Qk[i] = Xk[i] - N03 (tk[i]) * Xk[0] - N33 (tk[i]) * Xk[nbPt - 1];
+ }
+
+ // le vecteur Q
+ Geom::Point Q(0, 0);
+ for (int i = 1; i < nbPt - 1; i++) {
+ Q[0] += N13 (tk[i]) * Qk[i];
+ Q[1] += N23 (tk[i]) * Qk[i];
+ }
+
+ Geom::Point P = Q * M;
+ Geom::Point cp1;
+ Geom::Point cp2;
+ cp1[Geom::X] = P[Geom::X];
+ cp2[Geom::X] = P[Geom::Y];
+
+ // phase 2: les ordonnees
+ for (int i = 1; i < nbPt - 1; i++) {
+ Qk[i] = Yk[i] - N03 (tk[i]) * Yk[0] - N33 (tk[i]) * Yk[nbPt - 1];
+ }
+
+ // le vecteur Q
+ Q = Geom::Point(0, 0);
+ for (int i = 1; i < nbPt - 1; i++) {
+ Q[0] += N13 (tk[i]) * Qk[i];
+ Q[1] += N23 (tk[i]) * Qk[i];
+ }
+
+ P = Q * M;
+ cp1[Geom::Y] = P[Geom::X];
+ cp2[Geom::Y] = P[Geom::Y];
+
+ res.start = 3.0 * (cp1 - start);
+ res.end = 3.0 * (end - cp2 );
+
+ return true;
+}
+
+
+bool Path::ExtendFit(int off, int N, fitting_tables &data, double treshhold, PathDescrCubicTo &res, int &worstP)
+{
+ if ( N >= data.maxPt ) {
+ data.maxPt = 2 * N + 1;
+ data.Xk = (double *) g_realloc(data.Xk, data.maxPt * sizeof(double));
+ data.Yk = (double *) g_realloc(data.Yk, data.maxPt * sizeof(double));
+ data.Qk = (double *) g_realloc(data.Qk, data.maxPt * sizeof(double));
+ data.tk = (double *) g_realloc(data.tk, data.maxPt * sizeof(double));
+ data.lk = (double *) g_realloc(data.lk, data.maxPt * sizeof(double));
+ data.fk = (char *) g_realloc(data.fk, data.maxPt * sizeof(char));
+ }
+
+ if ( N > data.inPt ) {
+ for (int i = data.inPt; i < N; i++) {
+ data.Xk[i] = pts[off + i].p[Geom::X];
+ data.Yk[i] = pts[off + i].p[Geom::Y];
+ data.fk[i] = ( pts[off + i].isMoveTo == polyline_forced ) ? 0x01 : 0x00;
+ }
+ data.lk[0] = 0;
+ data.tk[0] = 0;
+
+ double prevLen = 0;
+ for (int i = 0; i < data.inPt; i++) {
+ prevLen += data.lk[i];
+ }
+ data.totLen = prevLen;
+
+ for (int i = ( (data.inPt > 0) ? data.inPt : 1); i < N; i++) {
+ Geom::Point diff;
+ diff[Geom::X] = data.Xk[i] - data.Xk[i - 1];
+ diff[Geom::Y] = data.Yk[i] - data.Yk[i - 1];
+ data.lk[i] = Geom::L2(diff);
+ data.totLen += data.lk[i];
+ data.tk[i] = data.totLen;
+ }
+
+ for (int i = 0; i < data.inPt; i++) {
+ data.tk[i] *= prevLen;
+ data.tk[i] /= data.totLen;
+ }
+
+ for (int i = data.inPt; i < N; i++) {
+ data.tk[i] /= data.totLen;
+ }
+ data.inPt = N;
+ }
+
+ if ( N < data.nbPt ) {
+ // We've gone too far; we'll have to recalulate the .tk.
+ data.totLen = 0;
+ data.tk[0] = 0;
+ data.lk[0] = 0;
+ for (int i = 1; i < N; i++) {
+ data.totLen += data.lk[i];
+ data.tk[i] = data.totLen;
+ }
+
+ for (int i = 1; i < N; i++) {
+ data.tk[i] /= data.totLen;
+ }
+ }
+
+ data.nbPt = N;
+
+ if ( data.nbPt <= 0 ) {
+ return false;
+ }
+
+ res.p[0] = data.Xk[data.nbPt - 1];
+ res.p[1] = data.Yk[data.nbPt - 1];
+ res.start[0] = res.start[1] = 0;
+ res.end[0] = res.end[1] = 0;
+ worstP = 1;
+ if ( N <= 2 ) {
+ return true;
+ }
+
+ if ( data.totLen < 0.0001 ) {
+ double worstD = 0;
+ Geom::Point start;
+ worstP = -1;
+ start[0] = data.Xk[0];
+ start[1] = data.Yk[0];
+ for (int i = 1; i < N; i++) {
+ Geom::Point nPt;
+ bool isForced = data.fk[i];
+ nPt[0] = data.Xk[i];
+ nPt[1] = data.Yk[i];
+
+ double nle = DistanceToCubic(start, res, nPt);
+ if ( isForced ) {
+ // forced points are favored for splitting the recursion; we do this by increasing their distance
+ if ( worstP < 0 || 2*nle > worstD ) {
+ worstP = i;
+ worstD = 2*nle;
+ }
+ } else {
+ if ( worstP < 0 || nle > worstD ) {
+ worstP = i;
+ worstD = nle;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ return AttemptSimplify(data, treshhold, res, worstP);
+}
+
+
+// fit a polyline to a bezier patch, return true is treshhold not exceeded (ie: you can continue)
+// version that uses tables from the previous iteration, to minimize amount of work done
+bool Path::AttemptSimplify (fitting_tables &data,double treshhold, PathDescrCubicTo & res,int &worstP)
+{
+ Geom::Point start,end;
+ // pour une coordonnee
+ Geom::Point cp1, cp2;
+
+ worstP = 1;
+ if (pts.size() == 2) {
+ return true;
+ }
+
+ start[0] = data.Xk[0];
+ start[1] = data.Yk[0];
+ cp1[0] = data.Xk[1];
+ cp1[1] = data.Yk[1];
+ end[0] = data.Xk[data.nbPt - 1];
+ end[1] = data.Yk[data.nbPt - 1];
+ cp2 = cp1;
+
+ if (pts.size() == 3) {
+ // start -> cp1 -> end
+ res.start = cp1 - start;
+ res.end = end - cp1;
+ worstP = 1;
+ return true;
+ }
+
+ if ( FitCubic(start, res, data.Xk, data.Yk, data.Qk, data.tk, data.nbPt) ) {
+ cp1 = start + res.start / 3;
+ cp2 = end - res.end / 3;
+ } else {
+ // aie, non-inversible
+ double worstD = 0;
+ worstP = -1;
+ for (int i = 1; i < data.nbPt; i++) {
+ Geom::Point nPt;
+ nPt[Geom::X] = data.Xk[i];
+ nPt[Geom::Y] = data.Yk[i];
+ double nle = DistanceToCubic(start, res, nPt);
+ if ( data.fk[i] ) {
+ // forced points are favored for splitting the recursion; we do this by increasing their distance
+ if ( worstP < 0 || 2 * nle > worstD ) {
+ worstP = i;
+ worstD = 2 * nle;
+ }
+ } else {
+ if ( worstP < 0 || nle > worstD ) {
+ worstP = i;
+ worstD = nle;
+ }
+ }
+ }
+ return false;
+ }
+
+ // calcul du delta= pondere par les longueurs des segments
+ double delta = 0;
+ {
+ double worstD = 0;
+ worstP = -1;
+ Geom::Point prevAppP;
+ Geom::Point prevP;
+ double prevDist;
+ prevP[Geom::X] = data.Xk[0];
+ prevP[Geom::Y] = data.Yk[0];
+ prevAppP = prevP; // le premier seulement
+ prevDist = 0;
+#ifdef with_splotch_killer
+ if ( data.nbPt <= 20 ) {
+ for (int i = 1; i < data.nbPt - 1; i++) {
+ Geom::Point curAppP;
+ Geom::Point curP;
+ double curDist;
+ Geom::Point midAppP;
+ Geom::Point midP;
+ double midDist;
+
+ curAppP[Geom::X] = N13(data.tk[i]) * cp1[Geom::X] +
+ N23(data.tk[i]) * cp2[Geom::X] +
+ N03(data.tk[i]) * data.Xk[0] +
+ N33(data.tk[i]) * data.Xk[data.nbPt - 1];
+
+ curAppP[Geom::Y] = N13(data.tk[i]) * cp1[Geom::Y] +
+ N23(data.tk[i]) * cp2[Geom::Y] +
+ N03(data.tk[i]) * data.Yk[0] +
+ N33(data.tk[i]) * data.Yk[data.nbPt - 1];
+
+ curP[Geom::X] = data.Xk[i];
+ curP[Geom::Y] = data.Yk[i];
+ double mtk = 0.5 * (data.tk[i] + data.tk[i - 1]);
+
+ midAppP[Geom::X] = N13(mtk) * cp1[Geom::X] +
+ N23(mtk) * cp2[Geom::X] +
+ N03(mtk) * data.Xk[0] +
+ N33(mtk) * data.Xk[data.nbPt - 1];
+
+ midAppP[Geom::Y] = N13(mtk) * cp1[Geom::Y] +
+ N23(mtk) * cp2[Geom::Y] +
+ N03(mtk) * data.Yk[0] +
+ N33(mtk) * data.Yk[data.nbPt - 1];
+
+ midP = 0.5 * (curP + prevP);
+
+ Geom::Point diff = curAppP - curP;
+ curDist = dot(diff, diff);
+ diff = midAppP - midP;
+ midDist = dot(diff, diff);
+
+ delta += 0.3333 * (curDist + prevDist + midDist) * data.lk[i];
+ if ( curDist > worstD ) {
+ worstD = curDist;
+ worstP = i;
+ } else if ( data.fk[i] && 2 * curDist > worstD ) {
+ worstD = 2*curDist;
+ worstP = i;
+ }
+ prevP = curP;
+ prevAppP = curAppP;
+ prevDist = curDist;
+ }
+ delta /= data.totLen;
+
+ } else {
+#endif
+ for (int i = 1; i < data.nbPt - 1; i++) {
+ Geom::Point curAppP;
+ Geom::Point curP;
+ double curDist;
+
+ curAppP[Geom::X] = N13(data.tk[i]) * cp1[Geom::X] +
+ N23(data.tk[i]) * cp2[Geom::X] +
+ N03(data.tk[i]) * data.Xk[0] +
+ N33(data.tk[i]) * data.Xk[data.nbPt - 1];
+
+ curAppP[Geom::Y] = N13(data.tk[i]) * cp1[Geom::Y] +
+ N23(data.tk[i]) * cp2[Geom::Y] +
+ N03(data.tk[i]) * data.Yk[0] +
+ N33(data.tk[i]) * data.Yk[data.nbPt - 1];
+
+ curP[Geom::X] = data.Xk[i];
+ curP[Geom::Y] = data.Yk[i];
+
+ Geom::Point diff = curAppP-curP;
+ curDist = dot(diff, diff);
+ delta += curDist;
+
+ if ( curDist > worstD ) {
+ worstD = curDist;
+ worstP = i;
+ } else if ( data.fk[i] && 2 * curDist > worstD ) {
+ worstD = 2*curDist;
+ worstP = i;
+ }
+ prevP = curP;
+ prevAppP = curAppP;
+ prevDist = curDist;
+ }
+#ifdef with_splotch_killer
+ }
+#endif
+ }
+
+ if (delta < treshhold * treshhold) {
+ // premier jet
+
+ // Refine a little.
+ for (int i = 1; i < data.nbPt - 1; i++) {
+ Geom::Point pt(data.Xk[i], data.Yk[i]);
+ data.tk[i] = RaffineTk(pt, start, cp1, cp2, end, data.tk[i]);
+ if (data.tk[i] < data.tk[i - 1]) {
+ // Force tk to be monotonic non-decreasing.
+ data.tk[i] = data.tk[i - 1];
+ }
+ }
+
+ if ( FitCubic(start, res, data.Xk, data.Yk, data.Qk, data.tk, data.nbPt) == false) {
+ // ca devrait jamais arriver, mais bon
+ res.start = 3.0 * (cp1 - start);
+ res.end = 3.0 * (end - cp2 );
+ return true;
+ }
+
+ double ndelta = 0;
+ {
+ double worstD = 0;
+ worstP = -1;
+ Geom::Point prevAppP;
+ Geom::Point prevP(data.Xk[0], data.Yk[0]);
+ double prevDist = 0;
+ prevAppP = prevP; // le premier seulement
+#ifdef with_splotch_killer
+ if ( data.nbPt <= 20 ) {
+ for (int i = 1; i < data.nbPt - 1; i++) {
+ Geom::Point curAppP;
+ Geom::Point curP;
+ double curDist;
+ Geom::Point midAppP;
+ Geom::Point midP;
+ double midDist;
+
+ curAppP[Geom::X] = N13(data.tk[i]) * cp1[Geom::X] +
+ N23(data.tk[i]) * cp2[Geom::X] +
+ N03(data.tk[i]) * data.Xk[0] +
+ N33(data.tk[i]) * data.Xk[data.nbPt - 1];
+
+ curAppP[Geom::Y] = N13(data.tk[i]) * cp1[Geom::Y] +
+ N23(data.tk[i]) * cp2[Geom::Y] +
+ N03(data.tk[i]) * data.Yk[0] +
+ N33(data.tk[i]) * data.Yk[data.nbPt - 1];
+
+ curP[Geom::X] = data.Xk[i];
+ curP[Geom::Y] = data.Yk[i];
+ double mtk = 0.5 * (data.tk[i] + data.tk[i - 1]);
+
+ midAppP[Geom::X] = N13(mtk) * cp1[Geom::X] +
+ N23(mtk) * cp2[Geom::X] +
+ N03(mtk) * data.Xk[0] +
+ N33(mtk) * data.Xk[data.nbPt - 1];
+
+ midAppP[Geom::Y] = N13(mtk) * cp1[Geom::Y] +
+ N23(mtk) * cp2[Geom::Y] +
+ N03(mtk) * data.Yk[0] +
+ N33(mtk) * data.Yk[data.nbPt - 1];
+
+ midP = 0.5 * (curP + prevP);
+
+ Geom::Point diff = curAppP - curP;
+ curDist = dot(diff, diff);
+
+ diff = midAppP - midP;
+ midDist = dot(diff, diff);
+
+ ndelta += 0.3333 * (curDist + prevDist + midDist) * data.lk[i];
+
+ if ( curDist > worstD ) {
+ worstD = curDist;
+ worstP = i;
+ } else if ( data.fk[i] && 2 * curDist > worstD ) {
+ worstD = 2*curDist;
+ worstP = i;
+ }
+
+ prevP = curP;
+ prevAppP = curAppP;
+ prevDist = curDist;
+ }
+ ndelta /= data.totLen;
+ } else {
+#endif
+ for (int i = 1; i < data.nbPt - 1; i++) {
+ Geom::Point curAppP;
+ Geom::Point curP;
+ double curDist;
+
+ curAppP[Geom::X] = N13(data.tk[i]) * cp1[Geom::X] +
+ N23(data.tk[i]) * cp2[Geom::X] +
+ N03(data.tk[i]) * data.Xk[0] +
+ N33(data.tk[i]) * data.Xk[data.nbPt - 1];
+
+ curAppP[Geom::Y] = N13(data.tk[i]) * cp1[Geom::Y] +
+ N23(data.tk[i]) * cp2[1] +
+ N03(data.tk[i]) * data.Yk[0] +
+ N33(data.tk[i]) * data.Yk[data.nbPt - 1];
+
+ curP[Geom::X] = data.Xk[i];
+ curP[Geom::Y] = data.Yk[i];
+
+ Geom::Point diff = curAppP - curP;
+ curDist = dot(diff, diff);
+
+ ndelta += curDist;
+
+ if ( curDist > worstD ) {
+ worstD = curDist;
+ worstP = i;
+ } else if ( data.fk[i] && 2 * curDist > worstD ) {
+ worstD = 2 * curDist;
+ worstP = i;
+ }
+ prevP = curP;
+ prevAppP = curAppP;
+ prevDist = curDist;
+ }
+#ifdef with_splotch_killer
+ }
+#endif
+ }
+
+ if (ndelta < delta + 0.00001) {
+ return true;
+ } else {
+ // nothing better to do
+ res.start = 3.0 * (cp1 - start);
+ res.end = 3.0 * (end - cp2 );
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+
+bool Path::AttemptSimplify(int off, int N, double treshhold, PathDescrCubicTo &res,int &worstP)
+{
+ Geom::Point start;
+ Geom::Point end;
+
+ // pour une coordonnee
+ double *Xk; // la coordonnee traitee (x puis y)
+ double *Yk; // la coordonnee traitee (x puis y)
+ double *lk; // les longueurs de chaque segment
+ double *tk; // les tk
+ double *Qk; // les Qk
+ char *fk; // si point force
+
+ Geom::Point cp1;
+ Geom::Point cp2;
+
+ if (N == 2) {
+ worstP = 1;
+ return true;
+ }
+
+ start = pts[off].p;
+ cp1 = pts[off + 1].p;
+ end = pts[off + N - 1].p;
+
+ res.p = end;
+ res.start[0] = res.start[1] = 0;
+ res.end[0] = res.end[1] = 0;
+ if (N == 3) {
+ // start -> cp1 -> end
+ res.start = cp1 - start;
+ res.end = end - cp1;
+ worstP = 1;
+ return true;
+ }
+
+ // Totally inefficient, allocates & deallocates all the time.
+ tk = (double *) g_malloc(N * sizeof(double));
+ Qk = (double *) g_malloc(N * sizeof(double));
+ Xk = (double *) g_malloc(N * sizeof(double));
+ Yk = (double *) g_malloc(N * sizeof(double));
+ lk = (double *) g_malloc(N * sizeof(double));
+ fk = (char *) g_malloc(N * sizeof(char));
+
+ // chord length method
+ tk[0] = 0.0;
+ lk[0] = 0.0;
+ {
+ Geom::Point prevP = start;
+ for (int i = 1; i < N; i++) {
+ Xk[i] = pts[off + i].p[Geom::X];
+ Yk[i] = pts[off + i].p[Geom::Y];
+
+ if ( pts[off + i].isMoveTo == polyline_forced ) {
+ fk[i] = 0x01;
+ } else {
+ fk[i] = 0;
+ }
+
+ Geom::Point diff(Xk[i] - prevP[Geom::X], Yk[i] - prevP[1]);
+ prevP[0] = Xk[i];
+ prevP[1] = Yk[i];
+ lk[i] = Geom::L2(diff);
+ tk[i] = tk[i - 1] + lk[i];
+ }
+ }
+
+ if (tk[N - 1] < 0.00001) {
+ // longueur nulle
+ res.start[0] = res.start[1] = 0;
+ res.end[0] = res.end[1] = 0;
+ double worstD = 0;
+ worstP = -1;
+ for (int i = 1; i < N; i++) {
+ Geom::Point nPt;
+ bool isForced = fk[i];
+ nPt[0] = Xk[i];
+ nPt[1] = Yk[i];
+
+ double nle = DistanceToCubic(start, res, nPt);
+ if ( isForced ) {
+ // forced points are favored for splitting the recursion; we do this by increasing their distance
+ if ( worstP < 0 || 2 * nle > worstD ) {
+ worstP = i;
+ worstD = 2 * nle;
+ }
+ } else {
+ if ( worstP < 0 || nle > worstD ) {
+ worstP = i;
+ worstD = nle;
+ }
+ }
+ }
+
+ g_free(tk);
+ g_free(Qk);
+ g_free(Xk);
+ g_free(Yk);
+ g_free(fk);
+ g_free(lk);
+
+ return false;
+ }
+
+ double totLen = tk[N - 1];
+ for (int i = 1; i < N - 1; i++) {
+ tk[i] /= totLen;
+ }
+
+ res.p = end;
+ if ( FitCubic(start, res, Xk, Yk, Qk, tk, N) ) {
+ cp1 = start + res.start / 3;
+ cp2 = end + res.end / 3;
+ } else {
+ // aie, non-inversible
+ res.start[0] = res.start[1] = 0;
+ res.end[0] = res.end[1] = 0;
+ double worstD = 0;
+ worstP = -1;
+ for (int i = 1; i < N; i++) {
+ Geom::Point nPt(Xk[i], Yk[i]);
+ bool isForced = fk[i];
+ double nle = DistanceToCubic(start, res, nPt);
+ if ( isForced ) {
+ // forced points are favored for splitting the recursion; we do this by increasing their distance
+ if ( worstP < 0 || 2 * nle > worstD ) {
+ worstP = i;
+ worstD = 2 * nle;
+ }
+ } else {
+ if ( worstP < 0 || nle > worstD ) {
+ worstP = i;
+ worstD = nle;
+ }
+ }
+ }
+
+ g_free(tk);
+ g_free(Qk);
+ g_free(Xk);
+ g_free(Yk);
+ g_free(fk);
+ g_free(lk);
+ return false;
+ }
+
+ // calcul du delta= pondere par les longueurs des segments
+ double delta = 0;
+ {
+ double worstD = 0;
+ worstP = -1;
+ Geom::Point prevAppP;
+ Geom::Point prevP;
+ double prevDist;
+ prevP[0] = Xk[0];
+ prevP[1] = Yk[0];
+ prevAppP = prevP; // le premier seulement
+ prevDist = 0;
+#ifdef with_splotch_killer
+ if ( N <= 20 ) {
+ for (int i = 1; i < N - 1; i++)
+ {
+ Geom::Point curAppP;
+ Geom::Point curP;
+ double curDist;
+ Geom::Point midAppP;
+ Geom::Point midP;
+ double midDist;
+
+ curAppP[0] = N13 (tk[i]) * cp1[0] + N23 (tk[i]) * cp2[0] + N03 (tk[i]) * Xk[0] + N33 (tk[i]) * Xk[N - 1];
+ curAppP[1] = N13 (tk[i]) * cp1[1] + N23 (tk[i]) * cp2[1] + N03 (tk[i]) * Yk[0] + N33 (tk[i]) * Yk[N - 1];
+ curP[0] = Xk[i];
+ curP[1] = Yk[i];
+ midAppP[0] = N13 (0.5*(tk[i]+tk[i-1])) * cp1[0] + N23 (0.5*(tk[i]+tk[i-1])) * cp2[0] + N03 (0.5*(tk[i]+tk[i-1])) * Xk[0] + N33 (0.5*(tk[i]+tk[i-1])) * Xk[N - 1];
+ midAppP[1] = N13 (0.5*(tk[i]+tk[i-1])) * cp1[1] + N23 (0.5*(tk[i]+tk[i-1])) * cp2[1] + N03 (0.5*(tk[i]+tk[i-1])) * Yk[0] + N33 (0.5*(tk[i]+tk[i-1])) * Yk[N - 1];
+ midP=0.5*(curP+prevP);
+
+ Geom::Point diff;
+ diff = curAppP-curP;
+ curDist = dot(diff,diff);
+
+ diff = midAppP-midP;
+ midDist = dot(diff,diff);
+
+ delta+=0.3333*(curDist+prevDist+midDist)/**lk[i]*/;
+
+ if ( curDist > worstD ) {
+ worstD = curDist;
+ worstP = i;
+ } else if ( fk[i] && 2*curDist > worstD ) {
+ worstD = 2*curDist;
+ worstP = i;
+ }
+ prevP = curP;
+ prevAppP = curAppP;
+ prevDist = curDist;
+ }
+ delta/=totLen;
+ } else {
+#endif
+ for (int i = 1; i < N - 1; i++)
+ {
+ Geom::Point curAppP;
+ Geom::Point curP;
+ double curDist;
+
+ curAppP[0] = N13 (tk[i]) * cp1[0] + N23 (tk[i]) * cp2[0] + N03 (tk[i]) * Xk[0] + N33 (tk[i]) * Xk[N - 1];
+ curAppP[1] = N13 (tk[i]) * cp1[1] + N23 (tk[i]) * cp2[1] + N03 (tk[i]) * Yk[0] + N33 (tk[i]) * Yk[N - 1];
+ curP[0] = Xk[i];
+ curP[1] = Yk[i];
+
+ Geom::Point diff;
+ diff = curAppP-curP;
+ curDist = dot(diff,diff);
+ delta += curDist;
+ if ( curDist > worstD ) {
+ worstD = curDist;
+ worstP = i;
+ } else if ( fk[i] && 2*curDist > worstD ) {
+ worstD = 2*curDist;
+ worstP = i;
+ }
+ prevP = curP;
+ prevAppP = curAppP;
+ prevDist = curDist;
+ }
+#ifdef with_splotch_killer
+ }
+#endif
+ }
+
+ if (delta < treshhold * treshhold)
+ {
+ // premier jet
+ res.start = 3.0 * (cp1 - start);
+ res.end = -3.0 * (cp2 - end);
+ res.p = end;
+
+ // Refine a little.
+ for (int i = 1; i < N - 1; i++)
+ {
+ Geom::Point
+ pt;
+ pt[0] = Xk[i];
+ pt[1] = Yk[i];
+ tk[i] = RaffineTk (pt, start, cp1, cp2, end, tk[i]);
+ if (tk[i] < tk[i - 1])
+ {
+ // Force tk to be monotonic non-decreasing.
+ tk[i] = tk[i - 1];
+ }
+ }
+
+ if ( FitCubic(start,res,Xk,Yk,Qk,tk,N) ) {
+ } else {
+ // ca devrait jamais arriver, mais bon
+ res.start = 3.0 * (cp1 - start);
+ res.end = -3.0 * (cp2 - end);
+ g_free(tk);
+ g_free(Qk);
+ g_free(Xk);
+ g_free(Yk);
+ g_free(fk);
+ g_free(lk);
+ return true;
+ }
+ double ndelta = 0;
+ {
+ double worstD = 0;
+ worstP = -1;
+ Geom::Point prevAppP;
+ Geom::Point prevP;
+ double prevDist;
+ prevP[0] = Xk[0];
+ prevP[1] = Yk[0];
+ prevAppP = prevP; // le premier seulement
+ prevDist = 0;
+#ifdef with_splotch_killer
+ if ( N <= 20 ) {
+ for (int i = 1; i < N - 1; i++)
+ {
+ Geom::Point curAppP;
+ Geom::Point curP;
+ double curDist;
+ Geom::Point midAppP;
+ Geom::Point midP;
+ double midDist;
+
+ curAppP[0] = N13 (tk[i]) * cp1[0] + N23 (tk[i]) * cp2[0] + N03 (tk[i]) * Xk[0] + N33 (tk[i]) * Xk[N - 1];
+ curAppP[1] = N13 (tk[i]) * cp1[1] + N23 (tk[i]) * cp2[1] + N03 (tk[i]) * Yk[0] + N33 (tk[i]) * Yk[N - 1];
+ curP[0] = Xk[i];
+ curP[1] = Yk[i];
+ midAppP[0] = N13 (0.5*(tk[i]+tk[i-1])) * cp1[0] + N23 (0.5*(tk[i]+tk[i-1])) * cp2[0] + N03 (0.5*(tk[i]+tk[i-1])) * Xk[0] + N33 (0.5*(tk[i]+tk[i-1])) * Xk[N - 1];
+ midAppP[1] = N13 (0.5*(tk[i]+tk[i-1])) * cp1[1] + N23 (0.5*(tk[i]+tk[i-1])) * cp2[1] + N03 (0.5*(tk[i]+tk[i-1])) * Yk[0] + N33 (0.5*(tk[i]+tk[i-1])) * Yk[N - 1];
+ midP = 0.5*(curP+prevP);
+
+ Geom::Point diff;
+ diff = curAppP-curP;
+ curDist = dot(diff,diff);
+ diff = midAppP-midP;
+ midDist = dot(diff,diff);
+
+ ndelta+=0.3333*(curDist+prevDist+midDist)/**lk[i]*/;
+
+ if ( curDist > worstD ) {
+ worstD = curDist;
+ worstP = i;
+ } else if ( fk[i] && 2*curDist > worstD ) {
+ worstD = 2*curDist;
+ worstP = i;
+ }
+ prevP = curP;
+ prevAppP = curAppP;
+ prevDist = curDist;
+ }
+ ndelta /= totLen;
+ } else {
+#endif
+ for (int i = 1; i < N - 1; i++)
+ {
+ Geom::Point curAppP;
+ Geom::Point curP;
+ double curDist;
+
+ curAppP[0] = N13 (tk[i]) * cp1[0] + N23 (tk[i]) * cp2[0] + N03 (tk[i]) * Xk[0] + N33 (tk[i]) * Xk[N - 1];
+ curAppP[1] = N13 (tk[i]) * cp1[1] + N23 (tk[i]) * cp2[1] + N03 (tk[i]) * Yk[0] + N33 (tk[i]) * Yk[N - 1];
+ curP[0]=Xk[i];
+ curP[1]=Yk[i];
+
+ Geom::Point diff;
+ diff=curAppP-curP;
+ curDist=dot(diff,diff);
+ ndelta+=curDist;
+
+ if ( curDist > worstD ) {
+ worstD=curDist;
+ worstP=i;
+ } else if ( fk[i] && 2*curDist > worstD ) {
+ worstD=2*curDist;
+ worstP=i;
+ }
+ prevP=curP;
+ prevAppP=curAppP;
+ prevDist=curDist;
+ }
+#ifdef with_splotch_killer
+ }
+#endif
+ }
+
+ g_free(tk);
+ g_free(Qk);
+ g_free(Xk);
+ g_free(Yk);
+ g_free(fk);
+ g_free(lk);
+
+ if (ndelta < delta + 0.00001)
+ {
+ return true;
+ } else {
+ // nothing better to do
+ res.start = 3.0 * (cp1 - start);
+ res.end = -3.0 * (cp2 - end);
+ }
+ return true;
+ } else {
+ // nothing better to do
+ }
+
+ g_free(tk);
+ g_free(Qk);
+ g_free(Xk);
+ g_free(Yk);
+ g_free(fk);
+ g_free(lk);
+ return false;
+}
+
+double Path::RaffineTk (Geom::Point pt, Geom::Point p0, Geom::Point p1, Geom::Point p2, Geom::Point p3, double it)
+{
+ // Refinement of the tk values.
+ // Just one iteration of Newtow Raphson, given that we're approaching the curve anyway.
+ // [fr: vu que de toute facon la courbe est approchC)e]
+ double const Ax = pt[Geom::X] -
+ p0[Geom::X] * N03(it) -
+ p1[Geom::X] * N13(it) -
+ p2[Geom::X] * N23(it) -
+ p3[Geom::X] * N33(it);
+
+ double const Bx = (p1[Geom::X] - p0[Geom::X]) * N02(it) +
+ (p2[Geom::X] - p1[Geom::X]) * N12(it) +
+ (p3[Geom::X] - p2[Geom::X]) * N22(it);
+
+ double const Cx = (p0[Geom::X] - 2 * p1[Geom::X] + p2[Geom::X]) * N01(it) +
+ (p3[Geom::X] - 2 * p2[Geom::X] + p1[Geom::X]) * N11(it);
+
+ double const Ay = pt[Geom::Y] -
+ p0[Geom::Y] * N03(it) -
+ p1[Geom::Y] * N13(it) -
+ p2[Geom::Y] * N23(it) -
+ p3[Geom::Y] * N33(it);
+
+ double const By = (p1[Geom::Y] - p0[Geom::Y]) * N02(it) +
+ (p2[Geom::Y] - p1[Geom::Y]) * N12(it) +
+ (p3[Geom::Y] - p2[Geom::Y]) * N22(it);
+
+ double const Cy = (p0[Geom::Y] - 2 * p1[Geom::Y] + p2[Geom::Y]) * N01(it) +
+ (p3[Geom::Y] - 2 * p2[Geom::Y] + p1[Geom::Y]) * N11(it);
+
+ double const dF = -6 * (Ax * Bx + Ay * By);
+ double const ddF = 18 * (Bx * Bx + By * By) - 12 * (Ax * Cx + Ay * Cy);
+ if (fabs (ddF) > 0.0000001) {
+ return it - dF / ddF;
+ }
+
+ return it;
+}
+
+// Variation on the fitting theme: try to merge path commands into cubic bezier patches.
+// The goal is to reduce the number of path commands, especially when operations on path produce
+// lots of small path elements; ideally you could get rid of very small segments at reduced visual cost.
+void Path::Coalesce(double tresh)
+{
+ if ( descr_flags & descr_adding_bezier ) {
+ CancelBezier();
+ }
+
+ if ( descr_flags & descr_doing_subpath ) {
+ CloseSubpath();
+ }
+
+ if (descr_cmd.size() <= 2) {
+ return;
+ }
+
+ SetBackData(false);
+ Path* tempDest = new Path();
+ tempDest->SetBackData(false);
+
+ ConvertEvenLines(0.25*tresh);
+
+ int lastP = 0;
+ int lastAP = -1;
+ // As the elements are stored in a separate array, it's no longer worth optimizing
+ // the rewriting in the same array.
+ // [[comme les elements sont stockes dans un tableau a part, plus la peine d'optimiser
+ // la réécriture dans la meme tableau]]
+
+ int lastA = descr_cmd[0]->associated;
+ int prevA = lastA;
+ Geom::Point firstP;
+
+ /* FIXME: the use of this variable probably causes a leak or two.
+ ** It's a hack anyway, and probably only needs to be a type rather than
+ ** a full PathDescr.
+ */
+ std::unique_ptr<PathDescr> lastAddition(new PathDescrMoveTo(Geom::Point(0, 0)));
+ bool containsForced = false;
+ PathDescrCubicTo pending_cubic(Geom::Point(0, 0), Geom::Point(0, 0), Geom::Point(0, 0));
+
+ for (int curP = 0; curP < int(descr_cmd.size()); curP++) {
+ int typ = descr_cmd[curP]->getType();
+ int nextA = lastA;
+
+ if (typ == descr_moveto) {
+
+ if (lastAddition->flags != descr_moveto) {
+ FlushPendingAddition(tempDest,lastAddition.get(),pending_cubic,lastAP);
+ }
+ lastAddition.reset(descr_cmd[curP]->clone());
+ lastAP = curP;
+ FlushPendingAddition(tempDest, lastAddition.get(), pending_cubic, lastAP);
+ // Added automatically (too bad about multiple moveto's).
+ // [fr: (tant pis pour les moveto multiples)]
+ containsForced = false;
+
+ PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[curP]);
+ firstP = nData->p;
+ lastA = descr_cmd[curP]->associated;
+ prevA = lastA;
+ lastP = curP;
+
+ } else if (typ == descr_close) {
+ nextA = descr_cmd[curP]->associated;
+ if (lastAddition->flags != descr_moveto) {
+
+ PathDescrCubicTo res(Geom::Point(0, 0), Geom::Point(0, 0), Geom::Point(0, 0));
+ int worstP = -1;
+ if (AttemptSimplify(lastA, nextA - lastA + 1, (containsForced) ? 0.05 * tresh : tresh, res, worstP)) {
+ lastAddition.reset(new PathDescrCubicTo(Geom::Point(0, 0),
+ Geom::Point(0, 0),
+ Geom::Point(0, 0)));
+ pending_cubic = res;
+ lastAP = -1;
+ }
+
+ FlushPendingAddition(tempDest, lastAddition.get(), pending_cubic, lastAP);
+ FlushPendingAddition(tempDest, descr_cmd[curP], pending_cubic, curP);
+
+ } else {
+ FlushPendingAddition(tempDest,descr_cmd[curP],pending_cubic,curP);
+ }
+
+ containsForced = false;
+ lastAddition.reset(new PathDescrMoveTo(Geom::Point(0, 0)));
+ prevA = lastA = nextA;
+ lastP = curP;
+ lastAP = curP;
+
+ } else if (typ == descr_forced) {
+
+ nextA = descr_cmd[curP]->associated;
+ if (lastAddition->flags != descr_moveto) {
+
+ PathDescrCubicTo res(Geom::Point(0, 0), Geom::Point(0, 0), Geom::Point(0, 0));
+ int worstP = -1;
+ if (AttemptSimplify(lastA, nextA - lastA + 1, 0.05 * tresh, res, worstP)) {
+ // plus sensible parce que point force
+ // ca passe
+ /* (Possible translation: More sensitive because contains a forced point.) */
+ containsForced = true;
+ } else {
+ // Force the addition.
+ FlushPendingAddition(tempDest, lastAddition.get(), pending_cubic, lastAP);
+ lastAddition.reset(new PathDescrMoveTo(Geom::Point(0, 0)));
+ prevA = lastA = nextA;
+ lastP = curP;
+ lastAP = curP;
+ containsForced = false;
+ }
+ }
+
+ } else if (typ == descr_lineto || typ == descr_cubicto || typ == descr_arcto) {
+
+ nextA = descr_cmd[curP]->associated;
+ if (lastAddition->flags != descr_moveto) {
+
+ PathDescrCubicTo res(Geom::Point(0, 0), Geom::Point(0, 0), Geom::Point(0, 0));
+ int worstP = -1;
+ if (AttemptSimplify(lastA, nextA - lastA + 1, tresh, res, worstP)) {
+ lastAddition.reset(new PathDescrCubicTo(Geom::Point(0, 0),
+ Geom::Point(0, 0),
+ Geom::Point(0, 0)));
+ pending_cubic = res;
+ lastAddition->associated = lastA;
+ lastP = curP;
+ lastAP = -1;
+ } else {
+ lastA = descr_cmd[lastP]->associated; // pourrait etre surecrit par la ligne suivante
+ /* (possible translation: Could be overwritten by the next line.) */
+ FlushPendingAddition(tempDest, lastAddition.get(), pending_cubic, lastAP);
+ lastAddition.reset(descr_cmd[curP]->clone());
+ if ( typ == descr_cubicto ) {
+ pending_cubic = *(dynamic_cast<PathDescrCubicTo*>(descr_cmd[curP]));
+ }
+ lastAP = curP;
+ containsForced = false;
+ }
+
+ } else {
+ lastA = prevA /*descr_cmd[curP-1]->associated */ ;
+ lastAddition.reset(descr_cmd[curP]->clone());
+ if ( typ == descr_cubicto ) {
+ pending_cubic = *(dynamic_cast<PathDescrCubicTo*>(descr_cmd[curP]));
+ }
+ lastAP = curP;
+ containsForced = false;
+ }
+ prevA = nextA;
+
+ } else if (typ == descr_bezierto) {
+
+ if (lastAddition->flags != descr_moveto) {
+ FlushPendingAddition(tempDest, lastAddition.get(), pending_cubic, lastAP);
+ lastAddition.reset(new PathDescrMoveTo(Geom::Point(0, 0)));
+ }
+ lastAP = -1;
+ lastA = descr_cmd[curP]->associated;
+ lastP = curP;
+ PathDescrBezierTo *nBData = dynamic_cast<PathDescrBezierTo*>(descr_cmd[curP]);
+ for (int i = 1; i <= nBData->nb; i++) {
+ FlushPendingAddition(tempDest, descr_cmd[curP + i], pending_cubic, curP + i);
+ }
+ curP += nBData->nb;
+ prevA = nextA;
+
+ } else if (typ == descr_interm_bezier) {
+ continue;
+ } else {
+ continue;
+ }
+ }
+
+ if (lastAddition->flags != descr_moveto) {
+ FlushPendingAddition(tempDest, lastAddition.get(), pending_cubic, lastAP);
+ }
+
+ Copy(tempDest);
+ delete tempDest;
+}
+
+
+void Path::FlushPendingAddition(Path *dest, PathDescr *lastAddition,
+ PathDescrCubicTo &lastCubic, int lastAP)
+{
+ switch (lastAddition->getType()) {
+
+ case descr_moveto:
+ if ( lastAP >= 0 ) {
+ PathDescrMoveTo* nData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[lastAP]);
+ dest->MoveTo(nData->p);
+ }
+ break;
+
+ case descr_close:
+ dest->Close();
+ break;
+
+ case descr_cubicto:
+ dest->CubicTo(lastCubic.p, lastCubic.start, lastCubic.end);
+ break;
+
+ case descr_lineto:
+ if ( lastAP >= 0 ) {
+ PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[lastAP]);
+ dest->LineTo(nData->p);
+ }
+ break;
+
+ case descr_arcto:
+ if ( lastAP >= 0 ) {
+ PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[lastAP]);
+ dest->ArcTo(nData->p, nData->rx, nData->ry, nData->angle, nData->large, nData->clockwise);
+ }
+ break;
+
+ case descr_bezierto:
+ if ( lastAP >= 0 ) {
+ PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[lastAP]);
+ dest->BezierTo(nData->p);
+ }
+ break;
+
+ case descr_interm_bezier:
+ if ( lastAP >= 0 ) {
+ PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo*>(descr_cmd[lastAP]);
+ dest->IntermBezierTo(nData->p);
+ }
+ break;
+ }
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/livarot/PathStroke.cpp b/src/livarot/PathStroke.cpp
new file mode 100644
index 0000000..e70ce36
--- /dev/null
+++ b/src/livarot/PathStroke.cpp
@@ -0,0 +1,763 @@
+// 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 "Path.h"
+#include "Shape.h"
+#include <2geom/transforms.h>
+
+/*
+ * stroking polylines into a Shape instance
+ * grunt work.
+ * if the goal is to raster the stroke, polyline stroke->polygon->uncrossed polygon->raster is grossly
+ * inefficient (but reuse the intersector, so that's what a lazy programmer like me does). the correct way would be
+ * to set up a supersampled buffer, raster each polyline stroke's part (one part per segment in the polyline, plus
+ * each join) because these are all convex polygons, then transform in alpha values
+ */
+
+// until i find something better
+static Geom::Point StrokeNormalize(const Geom::Point value) {
+ double length = L2(value);
+ if ( length < 0.0000001 ) {
+ return Geom::Point(0, 0);
+ } else {
+ return value/length;
+ }
+}
+
+// faster version if length is known
+static Geom::Point StrokeNormalize(const Geom::Point value, double length) {
+ if ( length < 0.0000001 ) {
+ return Geom::Point(0, 0);
+ } else {
+ return value/length;
+ }
+}
+
+void Path::Stroke(Shape *dest, bool doClose, double width, JoinType join,
+ ButtType butt, double miter, bool justAdd)
+{
+ if (dest == nullptr) {
+ return;
+ }
+
+ if (justAdd == false) {
+ dest->Reset(3 * pts.size(), 3 * pts.size());
+ }
+
+ dest->MakeBackData(false);
+
+ int lastM = 0;
+ while (lastM < int(pts.size())) {
+
+ int lastP = lastM + 1;
+ while (lastP < int(pts.size()) // select one subpath
+ && (pts[lastP].isMoveTo == polyline_lineto
+ || pts[lastP].isMoveTo == polyline_forced))
+ {
+ lastP++;
+ }
+
+ if ( lastP > lastM+1 ) {
+ Geom::Point sbStart = pts[lastM].p;
+ Geom::Point sbEnd = pts[lastP - 1].p;
+ // if ( pts[lastP - 1].closed ) { // this is correct, but this bugs text rendering (doesn't close text stroke)...
+ if ( Geom::LInfty(sbEnd-sbStart) < 0.00001 ) { // why close lines that shouldn't be closed?
+ // ah I see, because close is defined here for
+ // a whole path and should be defined per subpath.
+ // debut==fin => ferme (on devrait garder un element pour les close(), mais tant pis)
+ DoStroke(lastM, lastP - lastM, dest, true, width, join, butt, miter, true);
+ } else {
+ DoStroke(lastM, lastP - lastM, dest, doClose, width, join, butt, miter, true);
+ }
+ } else if (butt == butt_round) { // special case: zero length round butt is a circle
+ int last[2] = { -1, -1 };
+ Geom::Point dir;
+ dir[0] = 1;
+ dir[1] = 0;
+ Geom::Point pos = pts[lastM].p;
+ DoButt(dest, width, butt, pos, dir, last[RIGHT], last[LEFT]);
+ int end[2];
+ dir = -dir;
+ DoButt(dest, width, butt, pos, dir, end[LEFT], end[RIGHT]);
+ dest->AddEdge (end[LEFT], last[LEFT]);
+ dest->AddEdge (last[RIGHT], end[RIGHT]);
+ }
+ lastM = lastP;
+ }
+}
+
+void Path::DoStroke(int off, int N, Shape *dest, bool doClose, double width, JoinType join,
+ ButtType butt, double miter, bool /*justAdd*/)
+{
+ if (N <= 1) {
+ return;
+ }
+
+ Geom::Point prevP, nextP;
+ int prevI, nextI;
+ int upTo;
+
+ int curI = 0;
+ Geom::Point curP = pts[off].p;
+
+ if (doClose) {
+
+ prevI = N - 1;
+ while (prevI > 0) {
+ prevP = pts[off + prevI].p;
+ Geom::Point diff = curP - prevP;
+ double dist = dot(diff, diff);
+ if (dist > 0.001) {
+ break;
+ }
+ prevI--;
+ }
+ if (prevI <= 0) {
+ return;
+ }
+ upTo = prevI;
+
+ } else {
+
+ prevP = curP;
+ prevI = curI;
+ upTo = N - 1;
+ }
+
+ {
+ nextI = 1;
+ while (nextI <= upTo) {
+ nextP = pts[off + nextI].p;
+ Geom::Point diff = curP - nextP;
+ double dist = dot(diff, diff);
+ if (dist > 0.0) { // more tolerance for the first distance, to give the cap the right direction
+ break;
+ }
+ nextI++;
+ }
+ if (nextI > upTo) {
+ if (butt == butt_round) { // special case: zero length round butt is a circle
+ int last[2] = { -1, -1 };
+ Geom::Point dir;
+ dir[0] = 1;
+ dir[1] = 0;
+ DoButt(dest, width, butt, curP, dir, last[RIGHT], last[LEFT]);
+ int end[2];
+ dir = -dir;
+ DoButt(dest, width, butt, curP, dir, end[LEFT], end[RIGHT]);
+ dest->AddEdge (end[LEFT], last[LEFT]);
+ dest->AddEdge (last[RIGHT], end[RIGHT]);
+ }
+ return;
+ }
+ }
+
+ int start[2] = { -1, -1 };
+ int last[2] = { -1, -1 };
+ Geom::Point prevD = curP - prevP;
+ Geom::Point nextD = nextP - curP;
+ double prevLe = Geom::L2(prevD);
+ double nextLe = Geom::L2(nextD);
+ prevD = StrokeNormalize(prevD, prevLe);
+ nextD = StrokeNormalize(nextD, nextLe);
+
+ if (doClose) {
+ DoJoin(dest, width, join, curP, prevD, nextD, miter, prevLe, nextLe, start, last);
+ } else {
+ nextD = -nextD;
+ DoButt(dest, width, butt, curP, nextD, last[RIGHT], last[LEFT]);
+ nextD = -nextD;
+ }
+
+ do {
+ prevP = curP;
+ prevI = curI;
+ curP = nextP;
+ curI = nextI;
+ prevD = nextD;
+ prevLe = nextLe;
+ nextI++;
+ while (nextI <= upTo) {
+ nextP = pts[off + nextI].p;
+ Geom::Point diff = curP - nextP;
+ double dist = dot(diff, diff);
+ if (dist > 0.001 || (nextI == upTo && dist > 0.0)) { // more tolerance for the last distance too, for the right cap direction
+ break;
+ }
+ nextI++;
+ }
+ if (nextI > upTo) {
+ break;
+ }
+
+ nextD = nextP - curP;
+ nextLe = Geom::L2(nextD);
+ nextD = StrokeNormalize(nextD, nextLe);
+ int nSt[2] = { -1, -1 };
+ int nEn[2] = { -1, -1 };
+ DoJoin(dest, width, join, curP, prevD, nextD, miter, prevLe, nextLe, nSt, nEn);
+ dest->AddEdge(nSt[LEFT], last[LEFT]);
+ last[LEFT] = nEn[LEFT];
+ dest->AddEdge(last[RIGHT], nSt[RIGHT]);
+ last[RIGHT] = nEn[RIGHT];
+ } while (nextI <= upTo);
+
+ if (doClose) {
+ /* prevP=curP;
+ prevI=curI;
+ curP=nextP;
+ curI=nextI;
+ prevD=nextD;*/
+ nextP = pts[off].p;
+
+ nextD = nextP - curP;
+ nextLe = Geom::L2(nextD);
+ nextD = StrokeNormalize(nextD, nextLe);
+ int nSt[2] = { -1, -1 };
+ int nEn[2] = { -1, -1 };
+ DoJoin(dest, width, join, curP, prevD, nextD, miter, prevLe, nextLe, nSt, nEn);
+ dest->AddEdge (nSt[LEFT], last[LEFT]);
+ last[LEFT] = nEn[LEFT];
+ dest->AddEdge (last[RIGHT], nSt[RIGHT]);
+ last[RIGHT] = nEn[RIGHT];
+
+ dest->AddEdge (start[LEFT], last[LEFT]);
+ dest->AddEdge (last[RIGHT], start[RIGHT]);
+
+ } else {
+
+ int end[2];
+ DoButt (dest, width, butt, curP, prevD, end[LEFT], end[RIGHT]);
+ dest->AddEdge (end[LEFT], last[LEFT]);
+ dest->AddEdge (last[RIGHT], end[RIGHT]);
+ }
+}
+
+
+void Path::DoButt(Shape *dest, double width, ButtType butt, Geom::Point pos, Geom::Point dir,
+ int &leftNo, int &rightNo)
+{
+ Geom::Point nor;
+ nor = dir.ccw();
+
+ if (butt == butt_square)
+ {
+ Geom::Point x;
+ x = pos + width * dir + width * nor;
+ int bleftNo = dest->AddPoint (x);
+ x = pos + width * dir - width * nor;
+ int brightNo = dest->AddPoint (x);
+ x = pos + width * nor;
+ leftNo = dest->AddPoint (x);
+ x = pos - width * nor;
+ rightNo = dest->AddPoint (x);
+ dest->AddEdge (rightNo, brightNo);
+ dest->AddEdge (brightNo, bleftNo);
+ dest->AddEdge (bleftNo, leftNo);
+ }
+ else if (butt == butt_pointy)
+ {
+ leftNo = dest->AddPoint (pos + width * nor);
+ rightNo = dest->AddPoint (pos - width * nor);
+ int mid = dest->AddPoint (pos + width * dir);
+ dest->AddEdge (rightNo, mid);
+ dest->AddEdge (mid, leftNo);
+ }
+ else if (butt == butt_round)
+ {
+ const Geom::Point sx = pos + width * nor;
+ const Geom::Point ex = pos - width * nor;
+ leftNo = dest->AddPoint (sx);
+ rightNo = dest->AddPoint (ex);
+
+ RecRound (dest, rightNo, leftNo, ex, sx, -nor, nor, pos, width);
+ }
+ else
+ {
+ leftNo = dest->AddPoint (pos + width * nor);
+ rightNo = dest->AddPoint (pos - width * nor);
+ dest->AddEdge (rightNo, leftNo);
+ }
+}
+
+
+void Path::DoJoin (Shape *dest, double width, JoinType join, Geom::Point pos, Geom::Point prev,
+ Geom::Point next, double miter, double /*prevL*/, double /*nextL*/,
+ int *stNo, int *enNo)
+{
+ Geom::Point pnor = prev.ccw();
+ Geom::Point nnor = next.ccw();
+ double angSi = cross(prev, next);
+
+ /* FIXED: this special case caused bug 1028953 */
+ if (angSi > -0.0001 && angSi < 0.0001) {
+ double angCo = dot (prev, next);
+ if (angCo > 0.9999) {
+ // tout droit
+ stNo[LEFT] = enNo[LEFT] = dest->AddPoint(pos + width * pnor);
+ stNo[RIGHT] = enNo[RIGHT] = dest->AddPoint(pos - width * pnor);
+ } else {
+ // demi-tour
+ const Geom::Point sx = pos + width * pnor;
+ const Geom::Point ex = pos - width * pnor;
+ stNo[LEFT] = enNo[RIGHT] = dest->AddPoint (sx);
+ stNo[RIGHT] = enNo[LEFT] = dest->AddPoint (ex);
+ if (join == join_round) {
+ RecRound (dest, enNo[LEFT], stNo[LEFT], ex, sx, -pnor, pnor, pos, width);
+ dest->AddEdge(stNo[RIGHT], enNo[RIGHT]);
+ } else {
+ dest->AddEdge(enNo[LEFT], stNo[LEFT]);
+ dest->AddEdge(stNo[RIGHT], enNo[RIGHT]); // two times because both are crossing each other
+ }
+ }
+ return;
+ }
+
+ if (angSi < 0) {
+ int midNo = dest->AddPoint(pos);
+ stNo[LEFT] = dest->AddPoint(pos + width * pnor);
+ enNo[LEFT] = dest->AddPoint(pos + width * nnor);
+ dest->AddEdge(enNo[LEFT], midNo);
+ dest->AddEdge(midNo, stNo[LEFT]);
+
+ if (join == join_pointy) {
+
+ stNo[RIGHT] = dest->AddPoint(pos - width * pnor);
+ enNo[RIGHT] = dest->AddPoint(pos - width * nnor);
+
+ const Geom::Point biss = StrokeNormalize(prev - next);
+ double c2 = dot(biss, nnor);
+ double l = width / c2;
+ double emiter = width * c2;
+ if (emiter < miter) {
+ emiter = miter;
+ }
+
+ if (fabs(l) < miter) {
+ int const n = dest->AddPoint(pos - l * biss);
+ dest->AddEdge(stNo[RIGHT], n);
+ dest->AddEdge(n, enNo[RIGHT]);
+ } else {
+ dest->AddEdge(stNo[RIGHT], enNo[RIGHT]);
+ }
+
+ } else if (join == join_round) {
+ Geom::Point sx = pos - width * pnor;
+ stNo[RIGHT] = dest->AddPoint(sx);
+ Geom::Point ex = pos - width * nnor;
+ enNo[RIGHT] = dest->AddPoint(ex);
+
+ RecRound(dest, stNo[RIGHT], enNo[RIGHT],
+ sx, ex, -pnor, -nnor, pos, width);
+
+ } else {
+ stNo[RIGHT] = dest->AddPoint(pos - width * pnor);
+ enNo[RIGHT] = dest->AddPoint(pos - width * nnor);
+ dest->AddEdge(stNo[RIGHT], enNo[RIGHT]);
+ }
+
+ } else {
+
+ int midNo = dest->AddPoint(pos);
+ stNo[RIGHT] = dest->AddPoint(pos - width * pnor);
+ enNo[RIGHT] = dest->AddPoint(pos - width * nnor);
+ dest->AddEdge(stNo[RIGHT], midNo);
+ dest->AddEdge(midNo, enNo[RIGHT]);
+
+ if (join == join_pointy) {
+
+ stNo[LEFT] = dest->AddPoint(pos + width * pnor);
+ enNo[LEFT] = dest->AddPoint(pos + width * nnor);
+
+ const Geom::Point biss = StrokeNormalize(next - prev);
+ double c2 = dot(biss, nnor);
+ double l = width / c2;
+ double emiter = width * c2;
+ if (emiter < miter) {
+ emiter = miter;
+ }
+ if ( fabs(l) < miter) {
+ int const n = dest->AddPoint (pos + l * biss);
+ dest->AddEdge (enNo[LEFT], n);
+ dest->AddEdge (n, stNo[LEFT]);
+ }
+ else
+ {
+ dest->AddEdge (enNo[LEFT], stNo[LEFT]);
+ }
+
+ } else if (join == join_round) {
+
+ Geom::Point sx = pos + width * pnor;
+ stNo[LEFT] = dest->AddPoint(sx);
+ Geom::Point ex = pos + width * nnor;
+ enNo[LEFT] = dest->AddPoint(ex);
+
+ RecRound(dest, enNo[LEFT], stNo[LEFT],
+ ex, sx, nnor, pnor, pos, width);
+
+ } else {
+ stNo[LEFT] = dest->AddPoint(pos + width * pnor);
+ enNo[LEFT] = dest->AddPoint(pos + width * nnor);
+ dest->AddEdge(enNo[LEFT], stNo[LEFT]);
+ }
+ }
+}
+
+ void
+Path::DoLeftJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
+ Geom::Point prev, Geom::Point next, double miter, double /*prevL*/, double /*nextL*/,
+ int &leftStNo, int &leftEnNo,int pathID,int pieceID,double tID)
+{
+ Geom::Point pnor=prev.ccw();
+ Geom::Point nnor=next.ccw();
+ double angSi = cross(prev, next);
+ if (angSi > -0.0001 && angSi < 0.0001)
+ {
+ double angCo = dot (prev, next);
+ if (angCo > 0.9999)
+ {
+ // tout droit
+ leftEnNo = leftStNo = dest->AddPoint (pos + width * pnor);
+ }
+ else
+ {
+ // demi-tour
+ leftStNo = dest->AddPoint (pos + width * pnor);
+ leftEnNo = dest->AddPoint (pos - width * pnor);
+ int nEdge=dest->AddEdge (leftEnNo, leftStNo);
+ if ( dest->hasBackData() ) {
+ dest->ebData[nEdge].pathID=pathID;
+ dest->ebData[nEdge].pieceID=pieceID;
+ dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+ }
+ }
+ return;
+ }
+ if (angSi < 0)
+ {
+ /* Geom::Point biss;
+ biss.x=next.x-prev.x;
+ biss.y=next.y-prev.y;
+ double c2=cross(next, biss);
+ double l=width/c2;
+ double projn=l*(dot(biss,next));
+ double projp=-l*(dot(biss,prev));
+ if ( projp <= 0.5*prevL && projn <= 0.5*nextL ) {
+ double x,y;
+ x=pos.x+l*biss.x;
+ y=pos.y+l*biss.y;
+ leftEnNo=leftStNo=dest->AddPoint(x,y);
+ } else {*/
+ leftStNo = dest->AddPoint (pos + width * pnor);
+ leftEnNo = dest->AddPoint (pos + width * nnor);
+// int midNo = dest->AddPoint (pos);
+// int nEdge=dest->AddEdge (leftEnNo, midNo);
+ int nEdge=dest->AddEdge (leftEnNo, leftStNo);
+ if ( dest->hasBackData() ) {
+ dest->ebData[nEdge].pathID=pathID;
+ dest->ebData[nEdge].pieceID=pieceID;
+ dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+ }
+// nEdge=dest->AddEdge (midNo, leftStNo);
+// if ( dest->hasBackData() ) {
+// dest->ebData[nEdge].pathID=pathID;
+// dest->ebData[nEdge].pieceID=pieceID;
+// dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+// }
+ // }
+ }
+ else
+ {
+ if (join == join_pointy)
+ {
+ leftStNo = dest->AddPoint (pos + width * pnor);
+ leftEnNo = dest->AddPoint (pos + width * nnor);
+
+ const Geom::Point biss = StrokeNormalize (pnor + nnor);
+ double c2 = dot (biss, nnor);
+ double l = width / c2;
+ double emiter = width * c2;
+ if (emiter < miter)
+ emiter = miter;
+ if (l <= emiter)
+ {
+ int nleftStNo = dest->AddPoint (pos + l * biss);
+ int nEdge=dest->AddEdge (leftEnNo, nleftStNo);
+ if ( dest->hasBackData() ) {
+ dest->ebData[nEdge].pathID=pathID;
+ dest->ebData[nEdge].pieceID=pieceID;
+ dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+ }
+ nEdge=dest->AddEdge (nleftStNo, leftStNo);
+ if ( dest->hasBackData() ) {
+ dest->ebData[nEdge].pathID=pathID;
+ dest->ebData[nEdge].pieceID=pieceID;
+ dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+ }
+ }
+ else
+ {
+ double s2 = cross(nnor, biss);
+ double dec = (l - emiter) * c2 / s2;
+ const Geom::Point tbiss=biss.ccw();
+
+ int nleftStNo = dest->AddPoint (pos + emiter * biss + dec * tbiss);
+ int nleftEnNo = dest->AddPoint (pos + emiter * biss - dec * tbiss);
+ int nEdge=dest->AddEdge (nleftEnNo, nleftStNo);
+ if ( dest->hasBackData() ) {
+ dest->ebData[nEdge].pathID=pathID;
+ dest->ebData[nEdge].pieceID=pieceID;
+ dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+ }
+ nEdge=dest->AddEdge (leftEnNo, nleftEnNo);
+ if ( dest->hasBackData() ) {
+ dest->ebData[nEdge].pathID=pathID;
+ dest->ebData[nEdge].pieceID=pieceID;
+ dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+ }
+ nEdge=dest->AddEdge (nleftStNo, leftStNo);
+ if ( dest->hasBackData() ) {
+ dest->ebData[nEdge].pathID=pathID;
+ dest->ebData[nEdge].pieceID=pieceID;
+ dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+ }
+ }
+ }
+ else if (join == join_round)
+ {
+ const Geom::Point sx = pos + width * pnor;
+ leftStNo = dest->AddPoint (sx);
+ const Geom::Point ex = pos + width * nnor;
+ leftEnNo = dest->AddPoint (ex);
+
+ RecRound(dest, leftEnNo, leftStNo,
+ sx, ex, pnor, nnor ,pos, width);
+
+ }
+ else
+ {
+ leftStNo = dest->AddPoint (pos + width * pnor);
+ leftEnNo = dest->AddPoint (pos + width * nnor);
+ int nEdge=dest->AddEdge (leftEnNo, leftStNo);
+ if ( dest->hasBackData() ) {
+ dest->ebData[nEdge].pathID=pathID;
+ dest->ebData[nEdge].pieceID=pieceID;
+ dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+ }
+ }
+ }
+}
+ void
+Path::DoRightJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
+ Geom::Point prev, Geom::Point next, double miter, double /*prevL*/,
+ double /*nextL*/, int &rightStNo, int &rightEnNo,int pathID,int pieceID,double tID)
+{
+ const Geom::Point pnor=prev.ccw();
+ const Geom::Point nnor=next.ccw();
+ double angSi = cross(prev, next);
+ if (angSi > -0.0001 && angSi < 0.0001)
+ {
+ double angCo = dot (prev, next);
+ if (angCo > 0.9999)
+ {
+ // tout droit
+ rightEnNo = rightStNo = dest->AddPoint (pos - width*pnor);
+ }
+ else
+ {
+ // demi-tour
+ rightEnNo = dest->AddPoint (pos + width*pnor);
+ rightStNo = dest->AddPoint (pos - width*pnor);
+ int nEdge=dest->AddEdge (rightStNo, rightEnNo);
+ if ( dest->hasBackData() ) {
+ dest->ebData[nEdge].pathID=pathID;
+ dest->ebData[nEdge].pieceID=pieceID;
+ dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+ }
+ }
+ return;
+ }
+ if (angSi < 0)
+ {
+ if (join == join_pointy)
+ {
+ rightStNo = dest->AddPoint (pos - width*pnor);
+ rightEnNo = dest->AddPoint (pos - width*nnor);
+
+ const Geom::Point biss = StrokeNormalize (pnor + nnor);
+ double c2 = dot (biss, nnor);
+ double l = width / c2;
+ double emiter = width * c2;
+ if (emiter < miter)
+ emiter = miter;
+ if (l <= emiter)
+ {
+ int nrightStNo = dest->AddPoint (pos - l * biss);
+ int nEdge=dest->AddEdge (rightStNo, nrightStNo);
+ if ( dest->hasBackData() ) {
+ dest->ebData[nEdge].pathID=pathID;
+ dest->ebData[nEdge].pieceID=pieceID;
+ dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+ }
+ nEdge=dest->AddEdge (nrightStNo, rightEnNo);
+ if ( dest->hasBackData() ) {
+ dest->ebData[nEdge].pathID=pathID;
+ dest->ebData[nEdge].pieceID=pieceID;
+ dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+ }
+ }
+ else
+ {
+ double s2 = cross(nnor, biss);
+ double dec = (l - emiter) * c2 / s2;
+ const Geom::Point tbiss=biss.ccw();
+
+ int nrightStNo = dest->AddPoint (pos - emiter*biss - dec*tbiss);
+ int nrightEnNo = dest->AddPoint (pos - emiter*biss + dec*tbiss);
+ int nEdge=dest->AddEdge (rightStNo, nrightStNo);
+ if ( dest->hasBackData() ) {
+ dest->ebData[nEdge].pathID=pathID;
+ dest->ebData[nEdge].pieceID=pieceID;
+ dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+ }
+ nEdge=dest->AddEdge (nrightStNo, nrightEnNo);
+ if ( dest->hasBackData() ) {
+ dest->ebData[nEdge].pathID=pathID;
+ dest->ebData[nEdge].pieceID=pieceID;
+ dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+ }
+ nEdge=dest->AddEdge (nrightEnNo, rightEnNo);
+ if ( dest->hasBackData() ) {
+ dest->ebData[nEdge].pathID=pathID;
+ dest->ebData[nEdge].pieceID=pieceID;
+ dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+ }
+ }
+ }
+ else if (join == join_round)
+ {
+ const Geom::Point sx = pos - width * pnor;
+ rightStNo = dest->AddPoint (sx);
+ const Geom::Point ex = pos - width * nnor;
+ rightEnNo = dest->AddPoint (ex);
+
+ RecRound(dest, rightStNo, rightEnNo,
+ sx, ex, -pnor, -nnor ,pos, width);
+ }
+ else
+ {
+ rightStNo = dest->AddPoint (pos - width * pnor);
+ rightEnNo = dest->AddPoint (pos - width * nnor);
+ int nEdge=dest->AddEdge (rightStNo, rightEnNo);
+ if ( dest->hasBackData() ) {
+ dest->ebData[nEdge].pathID=pathID;
+ dest->ebData[nEdge].pieceID=pieceID;
+ dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+ }
+ }
+ }
+ else
+ {
+ /* Geom::Point biss;
+ biss=next.x-prev.x;
+ biss.y=next.y-prev.y;
+ double c2=cross(biss, next);
+ double l=width/c2;
+ double projn=l*(dot(biss,next));
+ double projp=-l*(dot(biss,prev));
+ if ( projp <= 0.5*prevL && projn <= 0.5*nextL ) {
+ double x,y;
+ x=pos.x+l*biss.x;
+ y=pos.y+l*biss.y;
+ rightEnNo=rightStNo=dest->AddPoint(x,y);
+ } else {*/
+ rightStNo = dest->AddPoint (pos - width*pnor);
+ rightEnNo = dest->AddPoint (pos - width*nnor);
+// int midNo = dest->AddPoint (pos);
+// int nEdge=dest->AddEdge (rightStNo, midNo);
+ int nEdge=dest->AddEdge (rightStNo, rightEnNo);
+ if ( dest->hasBackData() ) {
+ dest->ebData[nEdge].pathID=pathID;
+ dest->ebData[nEdge].pieceID=pieceID;
+ dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+ }
+// nEdge=dest->AddEdge (midNo, rightEnNo);
+// if ( dest->hasBackData() ) {
+// dest->ebData[nEdge].pathID=pathID;
+// dest->ebData[nEdge].pieceID=pieceID;
+// dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
+// }
+ // }
+ }
+}
+
+
+// a very ugly way to produce round joins: doing one (or two, depend on the angle of the join) quadratic bezier curves
+// but since most joins are going to be small, nobody will notice -- but somebody noticed and now the ugly stuff is gone! so:
+
+// a very nice way to produce round joins, caps or dots
+void Path::RecRound(Shape *dest, int sNo, int eNo, // start and end index
+ Geom::Point const &iS, Geom::Point const &iE, // start and end point
+ Geom::Point const &nS, Geom::Point const &nE, // start and end normal vector
+ Geom::Point &origine, float width) // center and radius of round
+{
+ //Geom::Point diff = iS - iE;
+ //double dist = dot(diff, diff);
+ if (width < 0.5 || dot(iS - iE, iS - iE)/width < 2.0) {
+ dest->AddEdge(sNo, eNo);
+ return;
+ }
+ double ang, sia, lod;
+ if (nS == -nE) {
+ ang = M_PI;
+ sia = 1;
+ } else {
+ double coa = dot(nS, nE);
+ sia = cross(nE, nS);
+ ang = acos(coa);
+ if ( coa >= 1 ) {
+ ang = 0;
+ }
+ if ( coa <= -1 ) {
+ ang = M_PI;
+ }
+ }
+ lod = 0.02 + 10 / (10 + width); // limit detail to about 2 degrees (180 * 0.02/Pi degrees)
+ ang /= lod;
+
+ int nbS = (int) floor(ang);
+ Geom::Rotate omega(((sia > 0) ? -lod : lod));
+ Geom::Point cur = iS - origine;
+ // StrokeNormalize(cur);
+ // cur*=width;
+ int lastNo = sNo;
+ for (int i = 0; i < nbS; i++) {
+ cur = cur * omega;
+ Geom::Point m = origine + cur;
+ int mNo = dest->AddPoint(m);
+ dest->AddEdge(lastNo, mNo);
+ lastNo = mNo;
+ }
+ dest->AddEdge(lastNo, eNo);
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/livarot/README b/src/livarot/README
new file mode 100644
index 0000000..3e6f138
--- /dev/null
+++ b/src/livarot/README
@@ -0,0 +1,17 @@
+
+
+This directory contains path and shape related code. This code is
+partially replaced by lib2geom. We still rely on this code for:
+
+* Binary path operations.
+* Path offsetting.
+* Finding start and end positions for lines text inside a shape.
+
+To do:
+
+* Move needed functionality to lib2geom or independent functions.
+* Delete directory.
+
+(Livarot is a pungent French cow milk cheese.)
+
+
diff --git a/src/livarot/Shape.cpp b/src/livarot/Shape.cpp
new file mode 100644
index 0000000..4b287ca
--- /dev/null
+++ b/src/livarot/Shape.cpp
@@ -0,0 +1,2317 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors:
+ * see git history
+ * Fred
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <cstdio>
+#include <cstdlib>
+#include <glib.h>
+#include "Shape.h"
+#include "livarot/sweep-event-queue.h"
+#include "livarot/sweep-tree-list.h"
+
+/*
+ * Shape instances handling.
+ * never (i repeat: never) modify edges and points links; use Connect() and Disconnect() instead
+ * the graph is stored as a set of points and edges, with edges in a doubly-linked list for each point.
+ */
+
+Shape::Shape()
+ : nbQRas(0),
+ firstQRas(-1),
+ lastQRas(-1),
+ qrsData(nullptr),
+ nbInc(0),
+ maxInc(0),
+ iData(nullptr),
+ sTree(nullptr),
+ sEvts(nullptr),
+ _need_points_sorting(false),
+ _need_edges_sorting(false),
+ _has_points_data(false),
+ _point_data_initialised(false),
+ _has_edges_data(false),
+ _has_sweep_src_data(false),
+ _has_sweep_dest_data(false),
+ _has_raster_data(false),
+ _has_quick_raster_data(false),
+ _has_back_data(false),
+ _has_voronoi_data(false),
+ _bbox_up_to_date(false)
+{
+ leftX = topY = rightX = bottomY = 0;
+ maxPt = 0;
+ maxAr = 0;
+
+ type = shape_polygon;
+}
+Shape::~Shape ()
+{
+ maxPt = 0;
+ maxAr = 0;
+ free(qrsData);
+}
+
+void Shape::Affiche()
+{
+ printf("sh=%p nbPt=%i nbAr=%i\n", this, static_cast<int>(_pts.size()), static_cast<int>(_aretes.size())); // localizing ok
+ for (unsigned int i=0; i<_pts.size(); i++) {
+ printf("pt %u : x=(%f %f) dI=%i dO=%i\n",i, _pts[i].x[0], _pts[i].x[1], _pts[i].dI, _pts[i].dO); // localizing ok
+ }
+ for (unsigned int i=0; i<_aretes.size(); i++) {
+ printf("ar %u : dx=(%f %f) st=%i en=%i\n",i, _aretes[i].dx[0], _aretes[i].dx[1], _aretes[i].st, _aretes[i].en); // localizing ok
+ }
+}
+
+/**
+ * Allocates space for point cache or clears the cache
+ \param nVal Allocate a cache (true) or clear it (false)
+ */
+void
+Shape::MakePointData (bool nVal)
+{
+ if (nVal)
+ {
+ if (_has_points_data == false)
+ {
+ _has_points_data = true;
+ _point_data_initialised = false;
+ _bbox_up_to_date = false;
+ pData.resize(maxPt);
+ }
+ }
+ /* no need to clean point data - keep it cached*/
+}
+
+void
+Shape::MakeEdgeData (bool nVal)
+{
+ if (nVal)
+ {
+ if (_has_edges_data == false)
+ {
+ _has_edges_data = true;
+ eData.resize(maxAr);
+ }
+ }
+ else
+ {
+ if (_has_edges_data)
+ {
+ _has_edges_data = false;
+ eData.clear();
+ }
+ }
+}
+
+void
+Shape::MakeRasterData (bool nVal)
+{
+ if (nVal)
+ {
+ if (_has_raster_data == false)
+ {
+ _has_raster_data = true;
+ swrData.resize(maxAr);
+ }
+ }
+ else
+ {
+ if (_has_raster_data)
+ {
+ _has_raster_data = false;
+ swrData.clear();
+ }
+ }
+}
+void
+Shape::MakeQuickRasterData (bool nVal)
+{
+ if (nVal)
+ {
+ if (_has_quick_raster_data == false)
+ {
+ _has_quick_raster_data = true;
+ quick_raster_data* new_qrsData = static_cast<quick_raster_data*>(realloc(qrsData, maxAr * sizeof(quick_raster_data)));
+ if (!new_qrsData) {
+ g_error("Not enough memory available for reallocating Shape::qrsData");
+ } else {
+ qrsData = new_qrsData;
+ }
+ }
+ }
+ else
+ {
+ if (_has_quick_raster_data)
+ {
+ _has_quick_raster_data = false;
+ }
+ }
+}
+void
+Shape::MakeSweepSrcData (bool nVal)
+{
+ if (nVal)
+ {
+ if (_has_sweep_src_data == false)
+ {
+ _has_sweep_src_data = true;
+ swsData.resize(maxAr);
+ }
+ }
+ else
+ {
+ if (_has_sweep_src_data)
+ {
+ _has_sweep_src_data = false;
+ swsData.clear();
+ }
+ }
+}
+void
+Shape::MakeSweepDestData (bool nVal)
+{
+ if (nVal)
+ {
+ if (_has_sweep_dest_data == false)
+ {
+ _has_sweep_dest_data = true;
+ swdData.resize(maxAr);
+ }
+ }
+ else
+ {
+ if (_has_sweep_dest_data)
+ {
+ _has_sweep_dest_data = false;
+ swdData.clear();
+ }
+ }
+}
+void
+Shape::MakeBackData (bool nVal)
+{
+ if (nVal)
+ {
+ if (_has_back_data == false)
+ {
+ _has_back_data = true;
+ ebData.resize(maxAr);
+ }
+ }
+ else
+ {
+ if (_has_back_data)
+ {
+ _has_back_data = false;
+ ebData.clear();
+ }
+ }
+}
+void
+Shape::MakeVoronoiData (bool nVal)
+{
+ if (nVal)
+ {
+ if (_has_voronoi_data == false)
+ {
+ _has_voronoi_data = true;
+ vorpData.resize(maxPt);
+ voreData.resize(maxAr);
+ }
+ }
+ else
+ {
+ if (_has_voronoi_data)
+ {
+ _has_voronoi_data = false;
+ vorpData.clear();
+ voreData.clear();
+ }
+ }
+}
+
+
+/**
+ * Copy point and edge data from `who' into this object, discarding
+ * any cached data that we have.
+ */
+void
+Shape::Copy (Shape * who)
+{
+ if (who == nullptr)
+ {
+ Reset (0, 0);
+ return;
+ }
+ MakePointData (false);
+ MakeEdgeData (false);
+ MakeSweepSrcData (false);
+ MakeSweepDestData (false);
+ MakeRasterData (false);
+ MakeQuickRasterData (false);
+ MakeBackData (false);
+
+ delete sTree;
+ sTree = nullptr;
+ delete sEvts;
+ sEvts = nullptr;
+
+ Reset (who->numberOfPoints(), who->numberOfEdges());
+ type = who->type;
+ _need_points_sorting = who->_need_points_sorting;
+ _need_edges_sorting = who->_need_edges_sorting;
+ _has_points_data = false;
+ _point_data_initialised = false;
+ _has_edges_data = false;
+ _has_sweep_src_data = false;
+ _has_sweep_dest_data = false;
+ _has_raster_data = false;
+ _has_quick_raster_data = false;
+ _has_back_data = false;
+ _has_voronoi_data = false;
+ _bbox_up_to_date = false;
+
+ _pts = who->_pts;
+ _aretes = who->_aretes;
+}
+
+/**
+ * Clear points and edges and prepare internal data using new size.
+ */
+void
+Shape::Reset (int pointCount, int edgeCount)
+{
+ _pts.clear();
+ _aretes.clear();
+
+ type = shape_polygon;
+ if (pointCount > maxPt)
+ {
+ maxPt = pointCount;
+ if (_has_points_data)
+ pData.resize(maxPt);
+ if (_has_voronoi_data)
+ vorpData.resize(maxPt);
+ }
+ if (edgeCount > maxAr)
+ {
+ maxAr = edgeCount;
+ if (_has_edges_data)
+ eData.resize(maxAr);
+ if (_has_sweep_dest_data)
+ swdData.resize(maxAr);
+ if (_has_sweep_src_data)
+ swsData.resize(maxAr);
+ if (_has_back_data)
+ ebData.resize(maxAr);
+ if (_has_voronoi_data)
+ voreData.resize(maxAr);
+ }
+ _need_points_sorting = false;
+ _need_edges_sorting = false;
+ _point_data_initialised = false;
+ _bbox_up_to_date = false;
+}
+
+int
+Shape::AddPoint (const Geom::Point x)
+{
+ if (numberOfPoints() >= maxPt)
+ {
+ maxPt = 2 * numberOfPoints() + 1;
+ if (_has_points_data)
+ pData.resize(maxPt);
+ if (_has_voronoi_data)
+ vorpData.resize(maxPt);
+ }
+
+ dg_point p;
+ p.x = x;
+ p.dI = p.dO = 0;
+ p.incidentEdge[FIRST] = p.incidentEdge[LAST] = -1;
+ p.oldDegree = -1;
+ _pts.push_back(p);
+ int const n = _pts.size() - 1;
+
+ if (_has_points_data)
+ {
+ pData[n].pending = 0;
+ pData[n].edgeOnLeft = -1;
+ pData[n].nextLinkedPoint = -1;
+ pData[n].askForWindingS = nullptr;
+ pData[n].askForWindingB = -1;
+ pData[n].rx[0] = Round(p.x[0]);
+ pData[n].rx[1] = Round(p.x[1]);
+ }
+ if (_has_voronoi_data)
+ {
+ vorpData[n].value = 0.0;
+ vorpData[n].winding = -2;
+ }
+ _need_points_sorting = true;
+
+ return n;
+}
+
+void
+Shape::SubPoint (int p)
+{
+ if (p < 0 || p >= numberOfPoints())
+ return;
+ _need_points_sorting = true;
+ int cb;
+ cb = getPoint(p).incidentEdge[FIRST];
+ while (cb >= 0 && cb < numberOfEdges())
+ {
+ if (getEdge(cb).st == p)
+ {
+ int ncb = getEdge(cb).nextS;
+ _aretes[cb].nextS = _aretes[cb].prevS = -1;
+ _aretes[cb].st = -1;
+ cb = ncb;
+ }
+ else if (getEdge(cb).en == p)
+ {
+ int ncb = getEdge(cb).nextE;
+ _aretes[cb].nextE = _aretes[cb].prevE = -1;
+ _aretes[cb].en = -1;
+ cb = ncb;
+ }
+ else
+ {
+ break;
+ }
+ }
+ _pts[p].incidentEdge[FIRST] = _pts[p].incidentEdge[LAST] = -1;
+ if (p < numberOfPoints() - 1)
+ SwapPoints (p, numberOfPoints() - 1);
+ _pts.pop_back();
+}
+
+void
+Shape::SwapPoints (int a, int b)
+{
+ if (a == b)
+ return;
+ if (getPoint(a).totalDegree() == 2 && getPoint(b).totalDegree() == 2)
+ {
+ int cb = getPoint(a).incidentEdge[FIRST];
+ if (getEdge(cb).st == a)
+ {
+ _aretes[cb].st = numberOfPoints();
+ }
+ else if (getEdge(cb).en == a)
+ {
+ _aretes[cb].en = numberOfPoints();
+ }
+ cb = getPoint(a).incidentEdge[LAST];
+ if (getEdge(cb).st == a)
+ {
+ _aretes[cb].st = numberOfPoints();
+ }
+ else if (getEdge(cb).en == a)
+ {
+ _aretes[cb].en = numberOfPoints();
+ }
+
+ cb = getPoint(b).incidentEdge[FIRST];
+ if (getEdge(cb).st == b)
+ {
+ _aretes[cb].st = a;
+ }
+ else if (getEdge(cb).en == b)
+ {
+ _aretes[cb].en = a;
+ }
+ cb = getPoint(b).incidentEdge[LAST];
+ if (getEdge(cb).st == b)
+ {
+ _aretes[cb].st = a;
+ }
+ else if (getEdge(cb).en == b)
+ {
+ _aretes[cb].en = a;
+ }
+
+ cb = getPoint(a).incidentEdge[FIRST];
+ if (getEdge(cb).st == numberOfPoints())
+ {
+ _aretes[cb].st = b;
+ }
+ else if (getEdge(cb).en == numberOfPoints())
+ {
+ _aretes[cb].en = b;
+ }
+ cb = getPoint(a).incidentEdge[LAST];
+ if (getEdge(cb).st == numberOfPoints())
+ {
+ _aretes[cb].st = b;
+ }
+ else if (getEdge(cb).en == numberOfPoints())
+ {
+ _aretes[cb].en = b;
+ }
+
+ }
+ else
+ {
+ int cb;
+ cb = getPoint(a).incidentEdge[FIRST];
+ while (cb >= 0)
+ {
+ int ncb = NextAt (a, cb);
+ if (getEdge(cb).st == a)
+ {
+ _aretes[cb].st = numberOfPoints();
+ }
+ else if (getEdge(cb).en == a)
+ {
+ _aretes[cb].en = numberOfPoints();
+ }
+ cb = ncb;
+ }
+ cb = getPoint(b).incidentEdge[FIRST];
+ while (cb >= 0)
+ {
+ int ncb = NextAt (b, cb);
+ if (getEdge(cb).st == b)
+ {
+ _aretes[cb].st = a;
+ }
+ else if (getEdge(cb).en == b)
+ {
+ _aretes[cb].en = a;
+ }
+ cb = ncb;
+ }
+ cb = getPoint(a).incidentEdge[FIRST];
+ while (cb >= 0)
+ {
+ int ncb = NextAt (numberOfPoints(), cb);
+ if (getEdge(cb).st == numberOfPoints())
+ {
+ _aretes[cb].st = b;
+ }
+ else if (getEdge(cb).en == numberOfPoints())
+ {
+ _aretes[cb].en = b;
+ }
+ cb = ncb;
+ }
+ }
+ {
+ dg_point swap = getPoint(a);
+ _pts[a] = getPoint(b);
+ _pts[b] = swap;
+ }
+ if (_has_points_data)
+ {
+ point_data swad = pData[a];
+ pData[a] = pData[b];
+ pData[b] = swad;
+ // pData[pData[a].oldInd].newInd=a;
+ // pData[pData[b].oldInd].newInd=b;
+ }
+ if (_has_voronoi_data)
+ {
+ voronoi_point swav = vorpData[a];
+ vorpData[a] = vorpData[b];
+ vorpData[b] = swav;
+ }
+}
+void
+Shape::SwapPoints (int a, int b, int c)
+{
+ if (a == b || b == c || a == c)
+ return;
+ SwapPoints (a, b);
+ SwapPoints (b, c);
+}
+
+void
+Shape::SortPoints ()
+{
+ if (_need_points_sorting && hasPoints())
+ SortPoints (0, numberOfPoints() - 1);
+ _need_points_sorting = false;
+}
+
+void
+Shape::SortPointsRounded ()
+{
+ if (hasPoints())
+ SortPointsRounded (0, numberOfPoints() - 1);
+}
+
+void
+Shape::SortPoints (int s, int e)
+{
+ if (s >= e)
+ return;
+ if (e == s + 1)
+ {
+ if (getPoint(s).x[1] > getPoint(e).x[1]
+ || (getPoint(s).x[1] == getPoint(e).x[1] && getPoint(s).x[0] > getPoint(e).x[0]))
+ SwapPoints (s, e);
+ return;
+ }
+
+ int ppos = (s + e) / 2;
+ int plast = ppos;
+ double pvalx = getPoint(ppos).x[0];
+ double pvaly = getPoint(ppos).x[1];
+
+ int le = s, ri = e;
+ while (le < ppos || ri > plast)
+ {
+ if (le < ppos)
+ {
+ do
+ {
+ int test = 0;
+ if (getPoint(le).x[1] > pvaly)
+ {
+ test = 1;
+ }
+ else if (getPoint(le).x[1] == pvaly)
+ {
+ if (getPoint(le).x[0] > pvalx)
+ {
+ test = 1;
+ }
+ else if (getPoint(le).x[0] == pvalx)
+ {
+ test = 0;
+ }
+ else
+ {
+ test = -1;
+ }
+ }
+ else
+ {
+ test = -1;
+ }
+ if (test == 0)
+ {
+ // on colle les valeurs egales au pivot ensemble
+ if (le < ppos - 1)
+ {
+ SwapPoints (le, ppos - 1, ppos);
+ ppos--;
+ continue; // sans changer le
+ }
+ else if (le == ppos - 1)
+ {
+ ppos--;
+ break;
+ }
+ else
+ {
+ // oupsie
+ break;
+ }
+ }
+ if (test > 0)
+ {
+ break;
+ }
+ le++;
+ }
+ while (le < ppos);
+ }
+ if (ri > plast)
+ {
+ do
+ {
+ int test = 0;
+ if (getPoint(ri).x[1] > pvaly)
+ {
+ test = 1;
+ }
+ else if (getPoint(ri).x[1] == pvaly)
+ {
+ if (getPoint(ri).x[0] > pvalx)
+ {
+ test = 1;
+ }
+ else if (getPoint(ri).x[0] == pvalx)
+ {
+ test = 0;
+ }
+ else
+ {
+ test = -1;
+ }
+ }
+ else
+ {
+ test = -1;
+ }
+ if (test == 0)
+ {
+ // on colle les valeurs egales au pivot ensemble
+ if (ri > plast + 1)
+ {
+ SwapPoints (ri, plast + 1, plast);
+ plast++;
+ continue; // sans changer ri
+ }
+ else if (ri == plast + 1)
+ {
+ plast++;
+ break;
+ }
+ else
+ {
+ // oupsie
+ break;
+ }
+ }
+ if (test < 0)
+ {
+ break;
+ }
+ ri--;
+ }
+ while (ri > plast);
+ }
+ if (le < ppos)
+ {
+ if (ri > plast)
+ {
+ SwapPoints (le, ri);
+ le++;
+ ri--;
+ }
+ else
+ {
+ if (le < ppos - 1)
+ {
+ SwapPoints (ppos - 1, plast, le);
+ ppos--;
+ plast--;
+ }
+ else if (le == ppos - 1)
+ {
+ SwapPoints (plast, le);
+ ppos--;
+ plast--;
+ }
+ }
+ }
+ else
+ {
+ if (ri > plast + 1)
+ {
+ SwapPoints (plast + 1, ppos, ri);
+ ppos++;
+ plast++;
+ }
+ else if (ri == plast + 1)
+ {
+ SwapPoints (ppos, ri);
+ ppos++;
+ plast++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ SortPoints (s, ppos - 1);
+ SortPoints (plast + 1, e);
+}
+
+void
+Shape::SortPointsByOldInd (int s, int e)
+{
+ if (s >= e)
+ return;
+ if (e == s + 1)
+ {
+ if (getPoint(s).x[1] > getPoint(e).x[1] || (getPoint(s).x[1] == getPoint(e).x[1] && getPoint(s).x[0] > getPoint(e).x[0])
+ || (getPoint(s).x[1] == getPoint(e).x[1] && getPoint(s).x[0] == getPoint(e).x[0]
+ && pData[s].oldInd > pData[e].oldInd))
+ SwapPoints (s, e);
+ return;
+ }
+
+ int ppos = (s + e) / 2;
+ int plast = ppos;
+ double pvalx = getPoint(ppos).x[0];
+ double pvaly = getPoint(ppos).x[1];
+ int pvali = pData[ppos].oldInd;
+
+ int le = s, ri = e;
+ while (le < ppos || ri > plast)
+ {
+ if (le < ppos)
+ {
+ do
+ {
+ int test = 0;
+ if (getPoint(le).x[1] > pvaly)
+ {
+ test = 1;
+ }
+ else if (getPoint(le).x[1] == pvaly)
+ {
+ if (getPoint(le).x[0] > pvalx)
+ {
+ test = 1;
+ }
+ else if (getPoint(le).x[0] == pvalx)
+ {
+ if (pData[le].oldInd > pvali)
+ {
+ test = 1;
+ }
+ else if (pData[le].oldInd == pvali)
+ {
+ test = 0;
+ }
+ else
+ {
+ test = -1;
+ }
+ }
+ else
+ {
+ test = -1;
+ }
+ }
+ else
+ {
+ test = -1;
+ }
+ if (test == 0)
+ {
+ // on colle les valeurs egales au pivot ensemble
+ if (le < ppos - 1)
+ {
+ SwapPoints (le, ppos - 1, ppos);
+ ppos--;
+ continue; // sans changer le
+ }
+ else if (le == ppos - 1)
+ {
+ ppos--;
+ break;
+ }
+ else
+ {
+ // oupsie
+ break;
+ }
+ }
+ if (test > 0)
+ {
+ break;
+ }
+ le++;
+ }
+ while (le < ppos);
+ }
+ if (ri > plast)
+ {
+ do
+ {
+ int test = 0;
+ if (getPoint(ri).x[1] > pvaly)
+ {
+ test = 1;
+ }
+ else if (getPoint(ri).x[1] == pvaly)
+ {
+ if (getPoint(ri).x[0] > pvalx)
+ {
+ test = 1;
+ }
+ else if (getPoint(ri).x[0] == pvalx)
+ {
+ if (pData[ri].oldInd > pvali)
+ {
+ test = 1;
+ }
+ else if (pData[ri].oldInd == pvali)
+ {
+ test = 0;
+ }
+ else
+ {
+ test = -1;
+ }
+ }
+ else
+ {
+ test = -1;
+ }
+ }
+ else
+ {
+ test = -1;
+ }
+ if (test == 0)
+ {
+ // on colle les valeurs egales au pivot ensemble
+ if (ri > plast + 1)
+ {
+ SwapPoints (ri, plast + 1, plast);
+ plast++;
+ continue; // sans changer ri
+ }
+ else if (ri == plast + 1)
+ {
+ plast++;
+ break;
+ }
+ else
+ {
+ // oupsie
+ break;
+ }
+ }
+ if (test < 0)
+ {
+ break;
+ }
+ ri--;
+ }
+ while (ri > plast);
+ }
+ if (le < ppos)
+ {
+ if (ri > plast)
+ {
+ SwapPoints (le, ri);
+ le++;
+ ri--;
+ }
+ else
+ {
+ if (le < ppos - 1)
+ {
+ SwapPoints (ppos - 1, plast, le);
+ ppos--;
+ plast--;
+ }
+ else if (le == ppos - 1)
+ {
+ SwapPoints (plast, le);
+ ppos--;
+ plast--;
+ }
+ }
+ }
+ else
+ {
+ if (ri > plast + 1)
+ {
+ SwapPoints (plast + 1, ppos, ri);
+ ppos++;
+ plast++;
+ }
+ else if (ri == plast + 1)
+ {
+ SwapPoints (ppos, ri);
+ ppos++;
+ plast++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ SortPointsByOldInd (s, ppos - 1);
+ SortPointsByOldInd (plast + 1, e);
+}
+
+void
+Shape::SortPointsRounded (int s, int e)
+{
+ if (s >= e)
+ return;
+ if (e == s + 1)
+ {
+ if (pData[s].rx[1] > pData[e].rx[1]
+ || (pData[s].rx[1] == pData[e].rx[1] && pData[s].rx[0] > pData[e].rx[0]))
+ SwapPoints (s, e);
+ return;
+ }
+
+ int ppos = (s + e) / 2;
+ int plast = ppos;
+ double pvalx = pData[ppos].rx[0];
+ double pvaly = pData[ppos].rx[1];
+
+ int le = s, ri = e;
+ while (le < ppos || ri > plast)
+ {
+ if (le < ppos)
+ {
+ do
+ {
+ int test = 0;
+ if (pData[le].rx[1] > pvaly)
+ {
+ test = 1;
+ }
+ else if (pData[le].rx[1] == pvaly)
+ {
+ if (pData[le].rx[0] > pvalx)
+ {
+ test = 1;
+ }
+ else if (pData[le].rx[0] == pvalx)
+ {
+ test = 0;
+ }
+ else
+ {
+ test = -1;
+ }
+ }
+ else
+ {
+ test = -1;
+ }
+ if (test == 0)
+ {
+ // on colle les valeurs egales au pivot ensemble
+ if (le < ppos - 1)
+ {
+ SwapPoints (le, ppos - 1, ppos);
+ ppos--;
+ continue; // sans changer le
+ }
+ else if (le == ppos - 1)
+ {
+ ppos--;
+ break;
+ }
+ else
+ {
+ // oupsie
+ break;
+ }
+ }
+ if (test > 0)
+ {
+ break;
+ }
+ le++;
+ }
+ while (le < ppos);
+ }
+ if (ri > plast)
+ {
+ do
+ {
+ int test = 0;
+ if (pData[ri].rx[1] > pvaly)
+ {
+ test = 1;
+ }
+ else if (pData[ri].rx[1] == pvaly)
+ {
+ if (pData[ri].rx[0] > pvalx)
+ {
+ test = 1;
+ }
+ else if (pData[ri].rx[0] == pvalx)
+ {
+ test = 0;
+ }
+ else
+ {
+ test = -1;
+ }
+ }
+ else
+ {
+ test = -1;
+ }
+ if (test == 0)
+ {
+ // on colle les valeurs egales au pivot ensemble
+ if (ri > plast + 1)
+ {
+ SwapPoints (ri, plast + 1, plast);
+ plast++;
+ continue; // sans changer ri
+ }
+ else if (ri == plast + 1)
+ {
+ plast++;
+ break;
+ }
+ else
+ {
+ // oupsie
+ break;
+ }
+ }
+ if (test < 0)
+ {
+ break;
+ }
+ ri--;
+ }
+ while (ri > plast);
+ }
+ if (le < ppos)
+ {
+ if (ri > plast)
+ {
+ SwapPoints (le, ri);
+ le++;
+ ri--;
+ }
+ else
+ {
+ if (le < ppos - 1)
+ {
+ SwapPoints (ppos - 1, plast, le);
+ ppos--;
+ plast--;
+ }
+ else if (le == ppos - 1)
+ {
+ SwapPoints (plast, le);
+ ppos--;
+ plast--;
+ }
+ }
+ }
+ else
+ {
+ if (ri > plast + 1)
+ {
+ SwapPoints (plast + 1, ppos, ri);
+ ppos++;
+ plast++;
+ }
+ else if (ri == plast + 1)
+ {
+ SwapPoints (ppos, ri);
+ ppos++;
+ plast++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ SortPointsRounded (s, ppos - 1);
+ SortPointsRounded (plast + 1, e);
+}
+
+/*
+ *
+ */
+int
+Shape::AddEdge (int st, int en)
+{
+ if (st == en)
+ return -1;
+ if (st < 0 || en < 0)
+ return -1;
+ type = shape_graph;
+ if (numberOfEdges() >= maxAr)
+ {
+ maxAr = 2 * numberOfEdges() + 1;
+ if (_has_edges_data)
+ eData.resize(maxAr);
+ if (_has_sweep_src_data)
+ swsData.resize(maxAr);
+ if (_has_sweep_dest_data)
+ swdData.resize(maxAr);
+ if (_has_raster_data)
+ swrData.resize(maxAr);
+ if (_has_back_data)
+ ebData.resize(maxAr);
+ if (_has_voronoi_data)
+ voreData.resize(maxAr);
+ }
+
+ dg_arete a;
+ a.dx = Geom::Point(0, 0);
+ a.st = a.en = -1;
+ a.prevS = a.nextS = -1;
+ a.prevE = a.nextE = -1;
+ if (st >= 0 && en >= 0) {
+ a.dx = getPoint(en).x - getPoint(st).x;
+ }
+
+ _aretes.push_back(a);
+ int const n = numberOfEdges() - 1;
+
+ ConnectStart (st, n);
+ ConnectEnd (en, n);
+ if (_has_edges_data)
+ {
+ eData[n].weight = 1;
+ eData[n].rdx = getEdge(n).dx;
+ }
+ if (_has_sweep_src_data)
+ {
+ swsData[n].misc = nullptr;
+ swsData[n].firstLinkedPoint = -1;
+ }
+ if (_has_back_data)
+ {
+ ebData[n].pathID = -1;
+ ebData[n].pieceID = -1;
+ ebData[n].tSt = ebData[n].tEn = 0;
+ }
+ if (_has_voronoi_data)
+ {
+ voreData[n].leF = -1;
+ voreData[n].riF = -1;
+ }
+ _need_edges_sorting = true;
+ return n;
+}
+
+int
+Shape::AddEdge (int st, int en, int leF, int riF)
+{
+ if (st == en)
+ return -1;
+ if (st < 0 || en < 0)
+ return -1;
+ {
+ int cb = getPoint(st).incidentEdge[FIRST];
+ while (cb >= 0)
+ {
+ if (getEdge(cb).st == st && getEdge(cb).en == en)
+ return -1; // doublon
+ if (getEdge(cb).st == en && getEdge(cb).en == st)
+ return -1; // doublon
+ cb = NextAt (st, cb);
+ }
+ }
+ type = shape_graph;
+ if (numberOfEdges() >= maxAr)
+ {
+ maxAr = 2 * numberOfEdges() + 1;
+ if (_has_edges_data)
+ eData.resize(maxAr);
+ if (_has_sweep_src_data)
+ swsData.resize(maxAr);
+ if (_has_sweep_dest_data)
+ swdData.resize(maxAr);
+ if (_has_raster_data)
+ swrData.resize(maxAr);
+ if (_has_back_data)
+ ebData.resize(maxAr);
+ if (_has_voronoi_data)
+ voreData.resize(maxAr);
+ }
+
+ dg_arete a;
+ a.dx = Geom::Point(0, 0);
+ a.st = a.en = -1;
+ a.prevS = a.nextS = -1;
+ a.prevE = a.nextE = -1;
+ if (st >= 0 && en >= 0) {
+ a.dx = getPoint(en).x - getPoint(st).x;
+ }
+
+ _aretes.push_back(a);
+ int const n = numberOfEdges() - 1;
+
+ ConnectStart (st, n);
+ ConnectEnd (en, n);
+ if (_has_edges_data)
+ {
+ eData[n].weight = 1;
+ eData[n].rdx = getEdge(n).dx;
+ }
+ if (_has_sweep_src_data)
+ {
+ swsData[n].misc = nullptr;
+ swsData[n].firstLinkedPoint = -1;
+ }
+ if (_has_back_data)
+ {
+ ebData[n].pathID = -1;
+ ebData[n].pieceID = -1;
+ ebData[n].tSt = ebData[n].tEn = 0;
+ }
+ if (_has_voronoi_data)
+ {
+ voreData[n].leF = leF;
+ voreData[n].riF = riF;
+ }
+ _need_edges_sorting = true;
+ return n;
+}
+
+void
+Shape::SubEdge (int e)
+{
+ if (e < 0 || e >= numberOfEdges())
+ return;
+ type = shape_graph;
+ DisconnectStart (e);
+ DisconnectEnd (e);
+ if (e < numberOfEdges() - 1)
+ SwapEdges (e, numberOfEdges() - 1);
+ _aretes.pop_back();
+ _need_edges_sorting = true;
+}
+
+void
+Shape::SwapEdges (int a, int b)
+{
+ if (a == b)
+ return;
+ if (getEdge(a).prevS >= 0 && getEdge(a).prevS != b)
+ {
+ if (getEdge(getEdge(a).prevS).st == getEdge(a).st)
+ {
+ _aretes[getEdge(a).prevS].nextS = b;
+ }
+ else if (getEdge(getEdge(a).prevS).en == getEdge(a).st)
+ {
+ _aretes[getEdge(a).prevS].nextE = b;
+ }
+ }
+ if (getEdge(a).nextS >= 0 && getEdge(a).nextS != b)
+ {
+ if (getEdge(getEdge(a).nextS).st == getEdge(a).st)
+ {
+ _aretes[getEdge(a).nextS].prevS = b;
+ }
+ else if (getEdge(getEdge(a).nextS).en == getEdge(a).st)
+ {
+ _aretes[getEdge(a).nextS].prevE = b;
+ }
+ }
+ if (getEdge(a).prevE >= 0 && getEdge(a).prevE != b)
+ {
+ if (getEdge(getEdge(a).prevE).st == getEdge(a).en)
+ {
+ _aretes[getEdge(a).prevE].nextS = b;
+ }
+ else if (getEdge(getEdge(a).prevE).en == getEdge(a).en)
+ {
+ _aretes[getEdge(a).prevE].nextE = b;
+ }
+ }
+ if (getEdge(a).nextE >= 0 && getEdge(a).nextE != b)
+ {
+ if (getEdge(getEdge(a).nextE).st == getEdge(a).en)
+ {
+ _aretes[getEdge(a).nextE].prevS = b;
+ }
+ else if (getEdge(getEdge(a).nextE).en == getEdge(a).en)
+ {
+ _aretes[getEdge(a).nextE].prevE = b;
+ }
+ }
+ if (getEdge(a).st >= 0)
+ {
+ if (getPoint(getEdge(a).st).incidentEdge[FIRST] == a)
+ _pts[getEdge(a).st].incidentEdge[FIRST] = numberOfEdges();
+ if (getPoint(getEdge(a).st).incidentEdge[LAST] == a)
+ _pts[getEdge(a).st].incidentEdge[LAST] = numberOfEdges();
+ }
+ if (getEdge(a).en >= 0)
+ {
+ if (getPoint(getEdge(a).en).incidentEdge[FIRST] == a)
+ _pts[getEdge(a).en].incidentEdge[FIRST] = numberOfEdges();
+ if (getPoint(getEdge(a).en).incidentEdge[LAST] == a)
+ _pts[getEdge(a).en].incidentEdge[LAST] = numberOfEdges();
+ }
+
+
+ if (getEdge(b).prevS >= 0 && getEdge(b).prevS != a)
+ {
+ if (getEdge(getEdge(b).prevS).st == getEdge(b).st)
+ {
+ _aretes[getEdge(b).prevS].nextS = a;
+ }
+ else if (getEdge(getEdge(b).prevS).en == getEdge(b).st)
+ {
+ _aretes[getEdge(b).prevS].nextE = a;
+ }
+ }
+ if (getEdge(b).nextS >= 0 && getEdge(b).nextS != a)
+ {
+ if (getEdge(getEdge(b).nextS).st == getEdge(b).st)
+ {
+ _aretes[getEdge(b).nextS].prevS = a;
+ }
+ else if (getEdge(getEdge(b).nextS).en == getEdge(b).st)
+ {
+ _aretes[getEdge(b).nextS].prevE = a;
+ }
+ }
+ if (getEdge(b).prevE >= 0 && getEdge(b).prevE != a)
+ {
+ if (getEdge(getEdge(b).prevE).st == getEdge(b).en)
+ {
+ _aretes[getEdge(b).prevE].nextS = a;
+ }
+ else if (getEdge(getEdge(b).prevE).en == getEdge(b).en)
+ {
+ _aretes[getEdge(b).prevE].nextE = a;
+ }
+ }
+ if (getEdge(b).nextE >= 0 && getEdge(b).nextE != a)
+ {
+ if (getEdge(getEdge(b).nextE).st == getEdge(b).en)
+ {
+ _aretes[getEdge(b).nextE].prevS = a;
+ }
+ else if (getEdge(getEdge(b).nextE).en == getEdge(b).en)
+ {
+ _aretes[getEdge(b).nextE].prevE = a;
+ }
+ }
+
+
+ for (int i = 0; i < 2; i++) {
+ int p = getEdge(b).st;
+ if (p >= 0) {
+ if (getPoint(p).incidentEdge[i] == b) {
+ _pts[p].incidentEdge[i] = a;
+ }
+ }
+
+ p = getEdge(b).en;
+ if (p >= 0) {
+ if (getPoint(p).incidentEdge[i] == b) {
+ _pts[p].incidentEdge[i] = a;
+ }
+ }
+
+ p = getEdge(a).st;
+ if (p >= 0) {
+ if (getPoint(p).incidentEdge[i] == numberOfEdges()) {
+ _pts[p].incidentEdge[i] = b;
+ }
+ }
+
+ p = getEdge(a).en;
+ if (p >= 0) {
+ if (getPoint(p).incidentEdge[i] == numberOfEdges()) {
+ _pts[p].incidentEdge[i] = b;
+ }
+ }
+
+ }
+
+ if (getEdge(a).prevS == b)
+ _aretes[a].prevS = a;
+ if (getEdge(a).prevE == b)
+ _aretes[a].prevE = a;
+ if (getEdge(a).nextS == b)
+ _aretes[a].nextS = a;
+ if (getEdge(a).nextE == b)
+ _aretes[a].nextE = a;
+ if (getEdge(b).prevS == a)
+ _aretes[a].prevS = b;
+ if (getEdge(b).prevE == a)
+ _aretes[a].prevE = b;
+ if (getEdge(b).nextS == a)
+ _aretes[a].nextS = b;
+ if (getEdge(b).nextE == a)
+ _aretes[a].nextE = b;
+
+ dg_arete swap = getEdge(a);
+ _aretes[a] = getEdge(b);
+ _aretes[b] = swap;
+ if (_has_edges_data)
+ {
+ edge_data swae = eData[a];
+ eData[a] = eData[b];
+ eData[b] = swae;
+ }
+ if (_has_sweep_src_data)
+ {
+ sweep_src_data swae = swsData[a];
+ swsData[a] = swsData[b];
+ swsData[b] = swae;
+ }
+ if (_has_sweep_dest_data)
+ {
+ sweep_dest_data swae = swdData[a];
+ swdData[a] = swdData[b];
+ swdData[b] = swae;
+ }
+ if (_has_raster_data)
+ {
+ raster_data swae = swrData[a];
+ swrData[a] = swrData[b];
+ swrData[b] = swae;
+ }
+ if (_has_back_data)
+ {
+ back_data swae = ebData[a];
+ ebData[a] = ebData[b];
+ ebData[b] = swae;
+ }
+ if (_has_voronoi_data)
+ {
+ voronoi_edge swav = voreData[a];
+ voreData[a] = voreData[b];
+ voreData[b] = swav;
+ }
+}
+void
+Shape::SwapEdges (int a, int b, int c)
+{
+ if (a == b || b == c || a == c)
+ return;
+ SwapEdges (a, b);
+ SwapEdges (b, c);
+}
+
+void
+Shape::SortEdges ()
+{
+ if (_need_edges_sorting == false) {
+ return;
+ }
+ _need_edges_sorting = false;
+
+ edge_list *list = (edge_list *) g_malloc(numberOfEdges() * sizeof (edge_list));
+ for (int p = 0; p < numberOfPoints(); p++)
+ {
+ int const d = getPoint(p).totalDegree();
+ if (d > 1)
+ {
+ int cb;
+ cb = getPoint(p).incidentEdge[FIRST];
+ int nb = 0;
+ while (cb >= 0)
+ {
+ int n = nb++;
+ list[n].no = cb;
+ if (getEdge(cb).st == p)
+ {
+ list[n].x = getEdge(cb).dx;
+ list[n].starting = true;
+ }
+ else
+ {
+ list[n].x = -getEdge(cb).dx;
+ list[n].starting = false;
+ }
+ cb = NextAt (p, cb);
+ }
+ SortEdgesList (list, 0, nb - 1);
+ _pts[p].incidentEdge[FIRST] = list[0].no;
+ _pts[p].incidentEdge[LAST] = list[nb - 1].no;
+ for (int i = 0; i < nb; i++)
+ {
+ if (list[i].starting)
+ {
+ if (i > 0)
+ {
+ _aretes[list[i].no].prevS = list[i - 1].no;
+ }
+ else
+ {
+ _aretes[list[i].no].prevS = -1;
+ }
+ if (i < nb - 1)
+ {
+ _aretes[list[i].no].nextS = list[i + 1].no;
+ }
+ else
+ {
+ _aretes[list[i].no].nextS = -1;
+ }
+ }
+ else
+ {
+ if (i > 0)
+ {
+ _aretes[list[i].no].prevE = list[i - 1].no;
+ }
+ else
+ {
+ _aretes[list[i].no].prevE = -1;
+ }
+ if (i < nb - 1)
+ {
+ _aretes[list[i].no].nextE = list[i + 1].no;
+ }
+ else
+ {
+ _aretes[list[i].no].nextE = -1;
+ }
+ }
+ }
+ }
+ }
+ g_free(list);
+}
+
+int
+Shape::CmpToVert (Geom::Point ax, Geom::Point bx,bool as,bool bs)
+{
+ int tstAX = 0;
+ int tstAY = 0;
+ int tstBX = 0;
+ int tstBY = 0;
+ if (ax[0] > 0)
+ tstAX = 1;
+ if (ax[0] < 0)
+ tstAX = -1;
+ if (ax[1] > 0)
+ tstAY = 1;
+ if (ax[1] < 0)
+ tstAY = -1;
+ if (bx[0] > 0)
+ tstBX = 1;
+ if (bx[0] < 0)
+ tstBX = -1;
+ if (bx[1] > 0)
+ tstBY = 1;
+ if (bx[1] < 0)
+ tstBY = -1;
+
+ int quadA = 0, quadB = 0;
+ if (tstAX < 0)
+ {
+ if (tstAY < 0)
+ {
+ quadA = 7;
+ }
+ else if (tstAY == 0)
+ {
+ quadA = 6;
+ }
+ else if (tstAY > 0)
+ {
+ quadA = 5;
+ }
+ }
+ else if (tstAX == 0)
+ {
+ if (tstAY < 0)
+ {
+ quadA = 0;
+ }
+ else if (tstAY == 0)
+ {
+ quadA = -1;
+ }
+ else if (tstAY > 0)
+ {
+ quadA = 4;
+ }
+ }
+ else if (tstAX > 0)
+ {
+ if (tstAY < 0)
+ {
+ quadA = 1;
+ }
+ else if (tstAY == 0)
+ {
+ quadA = 2;
+ }
+ else if (tstAY > 0)
+ {
+ quadA = 3;
+ }
+ }
+ if (tstBX < 0)
+ {
+ if (tstBY < 0)
+ {
+ quadB = 7;
+ }
+ else if (tstBY == 0)
+ {
+ quadB = 6;
+ }
+ else if (tstBY > 0)
+ {
+ quadB = 5;
+ }
+ }
+ else if (tstBX == 0)
+ {
+ if (tstBY < 0)
+ {
+ quadB = 0;
+ }
+ else if (tstBY == 0)
+ {
+ quadB = -1;
+ }
+ else if (tstBY > 0)
+ {
+ quadB = 4;
+ }
+ }
+ else if (tstBX > 0)
+ {
+ if (tstBY < 0)
+ {
+ quadB = 1;
+ }
+ else if (tstBY == 0)
+ {
+ quadB = 2;
+ }
+ else if (tstBY > 0)
+ {
+ quadB = 3;
+ }
+ }
+ if (quadA < quadB)
+ return 1;
+ if (quadA > quadB)
+ return -1;
+
+ Geom::Point av, bv;
+ av = ax;
+ bv = bx;
+ double si = cross(av, bv);
+ int tstSi = 0;
+ if (si > 0.000001) tstSi = 1;
+ if (si < -0.000001) tstSi = -1;
+ if ( tstSi == 0 ) {
+ if ( as && !bs ) return -1;
+ if ( !as && bs ) return 1;
+ }
+ return tstSi;
+}
+
+void
+Shape::SortEdgesList (edge_list * list, int s, int e)
+{
+ if (s >= e)
+ return;
+ if (e == s + 1) {
+ int cmpval=CmpToVert (list[e].x, list[s].x,list[e].starting,list[s].starting);
+ if ( cmpval > 0 ) { // priorite aux sortants
+ edge_list swap = list[s];
+ list[s] = list[e];
+ list[e] = swap;
+ }
+ return;
+ }
+
+ int ppos = (s + e) / 2;
+ int plast = ppos;
+ Geom::Point pvalx = list[ppos].x;
+ bool pvals = list[ppos].starting;
+
+ int le = s, ri = e;
+ while (le < ppos || ri > plast)
+ {
+ if (le < ppos)
+ {
+ do
+ {
+ int test = CmpToVert (pvalx, list[le].x,pvals,list[le].starting);
+ if (test == 0)
+ {
+ // on colle les valeurs egales au pivot ensemble
+ if (le < ppos - 1)
+ {
+ edge_list swap = list[le];
+ list[le] = list[ppos - 1];
+ list[ppos - 1] = list[ppos];
+ list[ppos] = swap;
+ ppos--;
+ continue; // sans changer le
+ }
+ else if (le == ppos - 1)
+ {
+ ppos--;
+ break;
+ }
+ else
+ {
+ // oupsie
+ break;
+ }
+ }
+ if (test > 0)
+ {
+ break;
+ }
+ le++;
+ }
+ while (le < ppos);
+ }
+ if (ri > plast)
+ {
+ do
+ {
+ int test = CmpToVert (pvalx, list[ri].x,pvals,list[ri].starting);
+ if (test == 0)
+ {
+ // on colle les valeurs egales au pivot ensemble
+ if (ri > plast + 1)
+ {
+ edge_list swap = list[ri];
+ list[ri] = list[plast + 1];
+ list[plast + 1] = list[plast];
+ list[plast] = swap;
+ plast++;
+ continue; // sans changer ri
+ }
+ else if (ri == plast + 1)
+ {
+ plast++;
+ break;
+ }
+ else
+ {
+ // oupsie
+ break;
+ }
+ }
+ if (test < 0)
+ {
+ break;
+ }
+ ri--;
+ }
+ while (ri > plast);
+ }
+
+ if (le < ppos)
+ {
+ if (ri > plast)
+ {
+ edge_list swap = list[le];
+ list[le] = list[ri];
+ list[ri] = swap;
+ le++;
+ ri--;
+ }
+ else if (le < ppos - 1)
+ {
+ edge_list swap = list[ppos - 1];
+ list[ppos - 1] = list[plast];
+ list[plast] = list[le];
+ list[le] = swap;
+ ppos--;
+ plast--;
+ }
+ else if (le == ppos - 1)
+ {
+ edge_list swap = list[plast];
+ list[plast] = list[le];
+ list[le] = swap;
+ ppos--;
+ plast--;
+ }
+ else
+ {
+ break;
+ }
+ }
+ else
+ {
+ if (ri > plast + 1)
+ {
+ edge_list swap = list[plast + 1];
+ list[plast + 1] = list[ppos];
+ list[ppos] = list[ri];
+ list[ri] = swap;
+ ppos++;
+ plast++;
+ }
+ else if (ri == plast + 1)
+ {
+ edge_list swap = list[ppos];
+ list[ppos] = list[ri];
+ list[ri] = swap;
+ ppos++;
+ plast++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ SortEdgesList (list, s, ppos - 1);
+ SortEdgesList (list, plast + 1, e);
+
+}
+
+
+
+/*
+ *
+ */
+void
+Shape::ConnectStart (int p, int b)
+{
+ if (getEdge(b).st >= 0)
+ DisconnectStart (b);
+
+ _aretes[b].st = p;
+ _pts[p].dO++;
+ _aretes[b].nextS = -1;
+ _aretes[b].prevS = getPoint(p).incidentEdge[LAST];
+ if (getPoint(p).incidentEdge[LAST] >= 0)
+ {
+ if (getEdge(getPoint(p).incidentEdge[LAST]).st == p)
+ {
+ _aretes[getPoint(p).incidentEdge[LAST]].nextS = b;
+ }
+ else if (getEdge(getPoint(p).incidentEdge[LAST]).en == p)
+ {
+ _aretes[getPoint(p).incidentEdge[LAST]].nextE = b;
+ }
+ }
+ _pts[p].incidentEdge[LAST] = b;
+ if (getPoint(p).incidentEdge[FIRST] < 0)
+ _pts[p].incidentEdge[FIRST] = b;
+}
+
+void
+Shape::ConnectEnd (int p, int b)
+{
+ if (getEdge(b).en >= 0)
+ DisconnectEnd (b);
+ _aretes[b].en = p;
+ _pts[p].dI++;
+ _aretes[b].nextE = -1;
+ _aretes[b].prevE = getPoint(p).incidentEdge[LAST];
+ if (getPoint(p).incidentEdge[LAST] >= 0)
+ {
+ if (getEdge(getPoint(p).incidentEdge[LAST]).st == p)
+ {
+ _aretes[getPoint(p).incidentEdge[LAST]].nextS = b;
+ }
+ else if (getEdge(getPoint(p).incidentEdge[LAST]).en == p)
+ {
+ _aretes[getPoint(p).incidentEdge[LAST]].nextE = b;
+ }
+ }
+ _pts[p].incidentEdge[LAST] = b;
+ if (getPoint(p).incidentEdge[FIRST] < 0)
+ _pts[p].incidentEdge[FIRST] = b;
+}
+
+void
+Shape::DisconnectStart (int b)
+{
+ if (getEdge(b).st < 0)
+ return;
+ _pts[getEdge(b).st].dO--;
+ if (getEdge(b).prevS >= 0)
+ {
+ if (getEdge(getEdge(b).prevS).st == getEdge(b).st)
+ {
+ _aretes[getEdge(b).prevS].nextS = getEdge(b).nextS;
+ }
+ else if (getEdge(getEdge(b).prevS).en == getEdge(b).st)
+ {
+ _aretes[getEdge(b).prevS].nextE = getEdge(b).nextS;
+ }
+ }
+ if (getEdge(b).nextS >= 0)
+ {
+ if (getEdge(getEdge(b).nextS).st == getEdge(b).st)
+ {
+ _aretes[getEdge(b).nextS].prevS = getEdge(b).prevS;
+ }
+ else if (getEdge(getEdge(b).nextS).en == getEdge(b).st)
+ {
+ _aretes[getEdge(b).nextS].prevE = getEdge(b).prevS;
+ }
+ }
+ if (getPoint(getEdge(b).st).incidentEdge[FIRST] == b)
+ _pts[getEdge(b).st].incidentEdge[FIRST] = getEdge(b).nextS;
+ if (getPoint(getEdge(b).st).incidentEdge[LAST] == b)
+ _pts[getEdge(b).st].incidentEdge[LAST] = getEdge(b).prevS;
+ _aretes[b].st = -1;
+}
+
+void
+Shape::DisconnectEnd (int b)
+{
+ if (getEdge(b).en < 0)
+ return;
+ _pts[getEdge(b).en].dI--;
+ if (getEdge(b).prevE >= 0)
+ {
+ if (getEdge(getEdge(b).prevE).st == getEdge(b).en)
+ {
+ _aretes[getEdge(b).prevE].nextS = getEdge(b).nextE;
+ }
+ else if (getEdge(getEdge(b).prevE).en == getEdge(b).en)
+ {
+ _aretes[getEdge(b).prevE].nextE = getEdge(b).nextE;
+ }
+ }
+ if (getEdge(b).nextE >= 0)
+ {
+ if (getEdge(getEdge(b).nextE).st == getEdge(b).en)
+ {
+ _aretes[getEdge(b).nextE].prevS = getEdge(b).prevE;
+ }
+ else if (getEdge(getEdge(b).nextE).en == getEdge(b).en)
+ {
+ _aretes[getEdge(b).nextE].prevE = getEdge(b).prevE;
+ }
+ }
+ if (getPoint(getEdge(b).en).incidentEdge[FIRST] == b)
+ _pts[getEdge(b).en].incidentEdge[FIRST] = getEdge(b).nextE;
+ if (getPoint(getEdge(b).en).incidentEdge[LAST] == b)
+ _pts[getEdge(b).en].incidentEdge[LAST] = getEdge(b).prevE;
+ _aretes[b].en = -1;
+}
+
+
+void
+Shape::Inverse (int b)
+{
+ int swap;
+ swap = getEdge(b).st;
+ _aretes[b].st = getEdge(b).en;
+ _aretes[b].en = swap;
+ swap = getEdge(b).prevE;
+ _aretes[b].prevE = getEdge(b).prevS;
+ _aretes[b].prevS = swap;
+ swap = getEdge(b).nextE;
+ _aretes[b].nextE = getEdge(b).nextS;
+ _aretes[b].nextS = swap;
+ _aretes[b].dx = -getEdge(b).dx;
+ if (getEdge(b).st >= 0)
+ {
+ _pts[getEdge(b).st].dO++;
+ _pts[getEdge(b).st].dI--;
+ }
+ if (getEdge(b).en >= 0)
+ {
+ _pts[getEdge(b).en].dO--;
+ _pts[getEdge(b).en].dI++;
+ }
+ if (_has_edges_data)
+ eData[b].weight = -eData[b].weight;
+ if (_has_sweep_dest_data)
+ {
+ int swap = swdData[b].leW;
+ swdData[b].leW = swdData[b].riW;
+ swdData[b].riW = swap;
+ }
+ if (_has_back_data)
+ {
+ double swat = ebData[b].tSt;
+ ebData[b].tSt = ebData[b].tEn;
+ ebData[b].tEn = swat;
+ }
+ if (_has_voronoi_data)
+ {
+ int swai = voreData[b].leF;
+ voreData[b].leF = voreData[b].riF;
+ voreData[b].riF = swai;
+ }
+}
+void
+Shape::CalcBBox (bool strict_degree)
+{
+ if (_bbox_up_to_date)
+ return;
+ if (hasPoints() == false)
+ {
+ leftX = rightX = topY = bottomY = 0;
+ _bbox_up_to_date = true;
+ return;
+ }
+ leftX = rightX = getPoint(0).x[0];
+ topY = bottomY = getPoint(0).x[1];
+ bool not_set=true;
+ for (int i = 0; i < numberOfPoints(); i++)
+ {
+ if ( strict_degree == false || getPoint(i).dI > 0 || getPoint(i).dO > 0 ) {
+ if ( not_set ) {
+ leftX = rightX = getPoint(i).x[0];
+ topY = bottomY = getPoint(i).x[1];
+ not_set=false;
+ } else {
+ if ( getPoint(i).x[0] < leftX) leftX = getPoint(i).x[0];
+ if ( getPoint(i).x[0] > rightX) rightX = getPoint(i).x[0];
+ if ( getPoint(i).x[1] < topY) topY = getPoint(i).x[1];
+ if ( getPoint(i).x[1] > bottomY) bottomY = getPoint(i).x[1];
+ }
+ }
+ }
+
+ _bbox_up_to_date = true;
+}
+
+// winding of a point with respect to the Shape
+// 0= outside
+// 1= inside (or -1, that usually the same)
+// other=depends on your fill rule
+// if the polygon is uncrossed, it's all the same, usually
+int
+Shape::PtWinding (const Geom::Point px) const
+{
+ int lr = 0, ll = 0, rr = 0;
+
+ for (int i = 0; i < numberOfEdges(); i++)
+ {
+ Geom::Point const adir = getEdge(i).dx;
+
+ Geom::Point const ast = getPoint(getEdge(i).st).x;
+ Geom::Point const aen = getPoint(getEdge(i).en).x;
+
+ //int const nWeight = eData[i].weight;
+ int const nWeight = 1;
+
+ if (ast[0] < aen[0]) {
+ if (ast[0] > px[0]) continue;
+ if (aen[0] < px[0]) continue;
+ } else {
+ if (ast[0] < px[0]) continue;
+ if (aen[0] > px[0]) continue;
+ }
+ if (ast[0] == px[0]) {
+ if (ast[1] >= px[1]) continue;
+ if (aen[0] == px[0]) continue;
+ if (aen[0] < px[0]) ll += nWeight; else rr -= nWeight;
+ continue;
+ }
+ if (aen[0] == px[0]) {
+ if (aen[1] >= px[1]) continue;
+ if (ast[0] == px[0]) continue;
+ if (ast[0] < px[0]) ll -= nWeight; else rr += nWeight;
+ continue;
+ }
+
+ if (ast[1] < aen[1]) {
+ if (ast[1] >= px[1]) continue;
+ } else {
+ if (aen[1] >= px[1]) continue;
+ }
+
+ Geom::Point const diff = px - ast;
+ double const cote = cross(adir, diff);
+ if (cote == 0) continue;
+ if (cote < 0) {
+ if (ast[0] > px[0]) lr += nWeight;
+ } else {
+ if (ast[0] < px[0]) lr -= nWeight;
+ }
+ }
+ return lr + (ll + rr) / 2;
+}
+
+
+void Shape::initialisePointData()
+{
+ if (_point_data_initialised)
+ return;
+ int const N = numberOfPoints();
+
+ for (int i = 0; i < N; i++) {
+ pData[i].pending = 0;
+ pData[i].edgeOnLeft = -1;
+ pData[i].nextLinkedPoint = -1;
+ pData[i].rx[0] = Round(getPoint(i).x[0]);
+ pData[i].rx[1] = Round(getPoint(i).x[1]);
+ }
+
+ _point_data_initialised = true;
+}
+
+void Shape::initialiseEdgeData()
+{
+ int const N = numberOfEdges();
+
+ for (int i = 0; i < N; i++) {
+ eData[i].rdx = pData[getEdge(i).en].rx - pData[getEdge(i).st].rx;
+ eData[i].length = dot(eData[i].rdx, eData[i].rdx);
+ eData[i].ilength = 1 / eData[i].length;
+ eData[i].sqlength = sqrt(eData[i].length);
+ eData[i].isqlength = 1 / eData[i].sqlength;
+ eData[i].siEd = eData[i].rdx[1] * eData[i].isqlength;
+ eData[i].coEd = eData[i].rdx[0] * eData[i].isqlength;
+
+ if (eData[i].siEd < 0) {
+ eData[i].siEd = -eData[i].siEd;
+ eData[i].coEd = -eData[i].coEd;
+ }
+
+ swsData[i].misc = nullptr;
+ swsData[i].firstLinkedPoint = -1;
+ swsData[i].stPt = swsData[i].enPt = -1;
+ swsData[i].leftRnd = swsData[i].rightRnd = -1;
+ swsData[i].nextSh = nullptr;
+ swsData[i].nextBo = -1;
+ swsData[i].curPoint = -1;
+ swsData[i].doneTo = -1;
+ }
+}
+
+
+void Shape::clearIncidenceData()
+{
+ g_free(iData);
+ iData = nullptr;
+ nbInc = maxInc = 0;
+}
+
+
+
+/**
+ * A directed graph is Eulerian iff every vertex has equal indegree and outdegree.
+ * http://mathworld.wolfram.com/EulerianGraph.html
+ *
+ * \param s Directed shape.
+ * \return true if s is Eulerian.
+ */
+
+bool directedEulerian(Shape const *s)
+{
+ for (int i = 0; i < s->numberOfPoints(); i++) {
+ if (s->getPoint(i).dI != s->getPoint(i).dO) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+/**
+ * \param s Shape.
+ * \param p Point.
+ * \return Minimum distance from p to any of the points or edges of s.
+ */
+
+double distance(Shape const *s, Geom::Point const &p)
+{
+ if ( s->hasPoints() == false) {
+ return 0.0;
+ }
+
+ /* Find the minimum distance from p to one of the points on s.
+ ** Computing the dot product of the difference vector gives
+ ** us the distance squared; we can leave the square root
+ ** until the end.
+ */
+ double bdot = Geom::dot(p - s->getPoint(0).x, p - s->getPoint(0).x);
+
+ for (int i = 0; i < s->numberOfPoints(); i++) {
+ Geom::Point const offset( p - s->getPoint(i).x );
+ double ndot = Geom::dot(offset, offset);
+ if ( ndot < bdot ) {
+ bdot = ndot;
+ }
+ }
+
+ for (int i = 0; i < s->numberOfEdges(); i++) {
+ if ( s->getEdge(i).st >= 0 && s->getEdge(i).en >= 0 ) {
+ /* The edge has start and end points */
+ Geom::Point const st(s->getPoint(s->getEdge(i).st).x); // edge start
+ Geom::Point const en(s->getPoint(s->getEdge(i).en).x); // edge end
+
+ Geom::Point const d(p - st); // vector between p and edge start
+ Geom::Point const e(en - st); // vector of the edge
+ double const el = Geom::dot(e, e); // edge length
+
+ /* Update bdot if appropriate */
+ if ( el > 0.001 ) {
+ double const npr = Geom::dot(d, e);
+ if ( npr > 0 && npr < el ) {
+ double const nl = fabs( Geom::cross(d, e) );
+ double ndot = nl * nl / el;
+ if ( ndot < bdot ) {
+ bdot = ndot;
+ }
+ }
+ }
+ }
+ }
+
+ return sqrt(bdot);
+}
+
+
+
+/**
+ * Returns true iff the L2 distance from \a thePt to this shape is <= \a max_l2.
+ * Distance = the min of distance to its points and distance to its edges.
+ * Points without edges are considered, which is maybe unwanted...
+ *
+ * This is largely similar to distance().
+ *
+ * \param s Shape.
+ * \param p Point.
+ * \param max_l2 L2 distance.
+ */
+
+bool distanceLessThanOrEqual(Shape const *s, Geom::Point const &p, double const max_l2)
+{
+ if ( s->hasPoints() == false ) {
+ return false;
+ }
+
+ /* TODO: Consider using bbox to return early, perhaps conditional on nbPt or nbAr. */
+
+ /* TODO: Efficiency: In one test case (scribbling with the freehand tool to create a small number of long
+ ** path elements), changing from a Distance method to a DistanceLE method reduced this
+ ** function's CPU time from about 21% of total inkscape CPU time to 14-15% of total inkscape
+ ** CPU time, due to allowing early termination. I don't know how much the L1 test helps, it
+ ** may well be a case of premature optimization. Consider testing dot(offset, offset)
+ ** instead.
+ */
+
+ double const max_l1 = max_l2 * M_SQRT2;
+ for (int i = 0; i < s->numberOfPoints(); i++) {
+ Geom::Point const offset( p - s->getPoint(i).x );
+ double const l1 = Geom::L1(offset);
+ if ( (l1 <= max_l2) || ((l1 <= max_l1) && (Geom::L2(offset) <= max_l2)) ) {
+ return true;
+ }
+ }
+
+ for (int i = 0; i < s->numberOfEdges(); i++) {
+ if ( s->getEdge(i).st >= 0 && s->getEdge(i).en >= 0 ) {
+ Geom::Point const st(s->getPoint(s->getEdge(i).st).x);
+ Geom::Point const en(s->getPoint(s->getEdge(i).en).x);
+ Geom::Point const d(p - st);
+ Geom::Point const e(en - st);
+ double const el = Geom::L2(e);
+ if ( el > 0.001 ) {
+ Geom::Point const e_unit(e / el);
+ double const npr = Geom::dot(d, e_unit);
+ if ( npr > 0 && npr < el ) {
+ double const nl = fabs(Geom::cross(d, e_unit));
+ if ( nl <= max_l2 ) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+//};
+
diff --git a/src/livarot/Shape.h b/src/livarot/Shape.h
new file mode 100644
index 0000000..2764b90
--- /dev/null
+++ b/src/livarot/Shape.h
@@ -0,0 +1,577 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifndef my_shape
+#define my_shape
+
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <vector>
+#include <2geom/point.h>
+
+#include "livarot/LivarotDefs.h"
+#include "object/object-set.h"
+
+class Path;
+class FloatLigne;
+
+class SweepTree;
+class SweepTreeList;
+class SweepEventQueue;
+
+enum {
+ tweak_mode_grow,
+ tweak_mode_push,
+ tweak_mode_repel,
+ tweak_mode_roughen
+};
+
+/*
+ * the Shape class (was the Digraph class, as the header says) stores digraphs (no kidding!) of which
+ * a very interesting kind are polygons.
+ * the main use of this class is the ConvertToShape() (or Booleen(), quite the same) function, which
+ * removes all problems a polygon can present: duplicate points or edges, self-intersection. you end up with a
+ * full-fledged polygon
+ */
+
+// possible values for the "type" field in the Shape class:
+enum
+{
+ shape_graph = 0, // it's just a graph; a bunch of edges, maybe intersections
+ shape_polygon = 1, // a polygon: intersection-free, edges oriented so that the inside is on their left
+ shape_polypatch = 2 // a graph without intersection; each face is a polygon (not yet used)
+};
+
+class IntLigne;
+class BitLigne;
+class AlphaLigne;
+
+class Shape
+{
+public:
+
+ struct back_data
+ {
+ int pathID, pieceID;
+ double tSt, tEn;
+ };
+
+ struct voronoi_point
+ { // info for points treated as points of a voronoi diagram (obtained by MakeShape())
+ double value; // distance to source
+ int winding; // winding relatively to source
+ };
+
+ struct voronoi_edge
+ { // info for edges, treated as approximation of edges of the voronoi diagram
+ int leF, riF; // left and right site
+ double leStX, leStY, riStX, riStY; // on the left side: (leStX,leStY) is the smallest vector from the source to st
+ // etc...
+ double leEnX, leEnY, riEnX, riEnY;
+ };
+
+ struct quick_raster_data
+ {
+ double x; // x-position on the sweepline
+ int bord; // index of the edge
+ int ind; // index of qrsData elem for edge (ie inverse of the bord)
+ int next,prev; // dbl linkage
+ };
+
+ enum sTreeChangeType
+ {
+ EDGE_INSERTED = 0,
+ EDGE_REMOVED = 1,
+ INTERSECTION = 2
+ };
+
+ struct sTreeChange
+ {
+ sTreeChangeType type; // type of modification to the sweepline:
+ int ptNo; // point at which the modification takes place
+
+ Shape *src; // left edge (or unique edge if not an intersection) involved in the event
+ int bord;
+ Shape *osrc; // right edge (if intersection)
+ int obord;
+ Shape *lSrc; // edge directly on the left in the sweepline at the moment of the event
+ int lBrd;
+ Shape *rSrc; // edge directly on the right
+ int rBrd;
+ };
+
+ struct incidenceData
+ {
+ int nextInc; // next incidence in the linked list
+ int pt; // point incident to the edge (there is one list per edge)
+ double theta; // coordinate of the incidence on the edge
+ };
+
+ Shape();
+ virtual ~Shape();
+
+ void MakeBackData(bool nVal);
+ void MakeVoronoiData(bool nVal);
+
+ void Affiche();
+
+ // insertion/deletion/movement of elements in the graph
+ void Copy(Shape *a);
+ // -reset the graph, and ensure there's room for n points and m edges
+ void Reset(int n = 0, int m = 0);
+ // -points:
+ int AddPoint(const Geom::Point x); // as the function name says
+ // returns the index at which the point has been added in the array
+ void SubPoint(int p); // removes the point at index p
+ // nota: this function relocates the last point to the index p
+ // so don't trust point indices if you use SubPoint
+ void SwapPoints(int a, int b); // swaps 2 points at indices a and b
+ void SwapPoints(int a, int b, int c); // swaps 3 points: c <- a <- b <- c
+ void SortPoints(); // sorts the points if needed (checks the need_points_sorting flag)
+
+ // -edges:
+ // add an edge between points of indices st and en
+ int AddEdge(int st, int en);
+ // return the edge index in the array
+
+ // add an edge between points of indices st and en
+ int AddEdge(int st, int en, int leF, int riF);
+ // return the edge index in the array
+
+ // version for the voronoi (with faces IDs)
+ void SubEdge(int e); // removes the edge at index e (same remarks as for SubPoint)
+ void SwapEdges(int a, int b); // swaps 2 edges
+ void SwapEdges(int a, int b, int c); // swaps 3 edges
+ void SortEdges(); // sort the edges if needed (checks the need_edges_sorting falg)
+
+ // primitives for topological manipulations
+
+ // endpoint of edge at index b that is different from the point p
+ inline int Other(int p, int b) const
+ {
+ if (getEdge(b).st == p) {
+ return getEdge(b).en;
+ }
+ return getEdge(b).st;
+ }
+
+ // next edge (after edge b) in the double-linked list at point p
+ inline int NextAt(int p, int b) const
+ {
+ if (p == getEdge(b).st) {
+ return getEdge(b).nextS;
+ }
+ else if (p == getEdge(b).en) {
+ return getEdge(b).nextE;
+ }
+
+ return -1;
+ }
+
+ // previous edge
+ inline int PrevAt(int p, int b) const
+ {
+ if (p == getEdge(b).st) {
+ return getEdge(b).prevS;
+ }
+ else if (p == getEdge(b).en) {
+ return getEdge(b).prevE;
+ }
+
+ return -1;
+ }
+
+ // same as NextAt, but the list is considered circular
+ inline int CycleNextAt(int p, int b) const
+ {
+ if (p == getEdge(b).st) {
+ if (getEdge(b).nextS < 0) {
+ return getPoint(p).incidentEdge[FIRST];
+ }
+ return getEdge(b).nextS;
+ } else if (p == getEdge(b).en) {
+ if (getEdge(b).nextE < 0) {
+ return getPoint(p).incidentEdge[FIRST];
+ }
+
+ return getEdge(b).nextE;
+ }
+
+ return -1;
+ }
+
+ // same as PrevAt, but the list is considered circular
+ inline int CyclePrevAt(int p, int b) const
+ {
+ if (p == getEdge(b).st) {
+ if (getEdge(b).prevS < 0) {
+ return getPoint(p).incidentEdge[LAST];
+ }
+ return getEdge(b).prevS;
+ } else if (p == getEdge(b).en) {
+ if (getEdge(b).prevE < 0) {
+ return getPoint(p).incidentEdge[LAST];
+ }
+ return getEdge(b).prevE;
+ }
+
+ return -1;
+ }
+
+ void ConnectStart(int p, int b); // set the point p as the start of edge b
+ void ConnectEnd(int p, int b); // set the point p as the end of edge b
+ void DisconnectStart(int b); // disconnect edge b from its start point
+ void DisconnectEnd(int b); // disconnect edge b from its end point
+
+ // reverses edge b (start <-> end)
+ void Inverse(int b);
+ // calc bounding box and sets leftX,rightX,topY and bottomY to their values
+ void CalcBBox(bool strict_degree = false);
+
+ // debug function: plots the graph (mac only)
+ void Plot(double ix, double iy, double ir, double mx, double my, bool doPoint,
+ bool edgesNo, bool pointNo, bool doDir, char *fileName);
+
+ // transforms a polygon in a "forme" structure, ie a set of contours, which can be holes (see ShapeUtils.h)
+ // return NULL in case it's not possible
+ void ConvertToForme(Path *dest);
+
+ // version to use when conversion was done with ConvertWithBackData(): will attempt to merge segment belonging to
+ // the same curve
+ // nota: apparently the function doesn't like very small segments of arc
+ void ConvertToForme(Path *dest, int nbP, Path **orig, bool splitWhenForced = false);
+ // version trying to recover the nesting of subpaths (ie: holes)
+ void ConvertToFormeNested(Path *dest, int nbP, Path **orig, int wildPath, int &nbNest,
+ int *&nesting, int *&contStart, bool splitWhenForced = false);
+
+ // sweeping a digraph to produce a intersection-free polygon
+ // return 0 if everything is ok and a return code otherwise (see LivarotDefs.h)
+ // the input is the Shape "a"
+ // directed=true <=> non-zero fill rule
+ int ConvertToShape(Shape *a, FillRule directed = fill_nonZero, bool invert = false);
+ // directed=false <=> even-odd fill rule
+ // invert=true: make as if you inverted all edges in the source
+ int Reoriente(Shape *a); // subcase of ConvertToShape: the input a is already intersection-free
+ // all that's missing are the correct directions of the edges
+ // Reoriented is equivalent to ConvertToShape(a,false,false) , but faster sicne
+ // it doesn't computes interections nor adjacencies
+ void ForceToPolygon(); // force the Shape to believe it's a polygon (eulerian+intersection-free+no
+ // duplicate edges+no duplicate points)
+ // be careful when using this function
+
+ // the coordinate rounding function
+ inline static double Round(double x)
+ {
+ return ldexp(rint(ldexp(x, 9)), -9);
+ }
+
+ // 2 miscannellous variations on it, to scale to and back the rounding grid
+ inline static double HalfRound(double x)
+ {
+ return ldexp(x, -9);
+ }
+
+ inline static double IHalfRound(double x)
+ {
+ return ldexp(x, 9);
+ }
+
+ // boolean operations on polygons (requests intersection-free poylygons)
+ // boolean operation types are defined in LivarotDefs.h
+ // same return code as ConvertToShape
+ int Booleen(Shape *a, Shape *b, BooleanOp mod, int cutPathID = -1);
+
+ // create a graph that is an offseted version of the graph "of"
+ // the offset is dec, with joins between edges of type "join" (see LivarotDefs.h)
+ // the result is NOT a polygon; you need a subsequent call to ConvertToShape to get a real polygon
+ int MakeOffset(Shape *of, double dec, JoinType join, double miter, bool do_profile=false, double cx = 0, double cy = 0, double radius = 0, Geom::Affine *i2doc = nullptr);
+
+ int MakeTweak (int mode, Shape *a, double dec, JoinType join, double miter, bool do_profile, Geom::Point c, Geom::Point vector, double radius, Geom::Affine *i2doc);
+
+ int PtWinding(const Geom::Point px) const; // plus rapide
+ int Winding(const Geom::Point px) const;
+
+ // rasterization
+ void BeginRaster(float &pos, int &curPt);
+ void EndRaster();
+ void BeginQuickRaster(float &pos, int &curPt);
+ void EndQuickRaster();
+
+ void Scan(float &pos, int &curP, float to, float step);
+ void QuickScan(float &pos, int &curP, float to, bool doSort, float step);
+ void DirectScan(float &pos, int &curP, float to, float step);
+ void DirectQuickScan(float &pos, int &curP, float to, bool doSort, float step);
+
+ void Scan(float &pos, int &curP, float to, FloatLigne *line, bool exact, float step);
+ void Scan(float &pos, int &curP, float to, FillRule directed, BitLigne *line, bool exact, float step);
+ void Scan(float &pos, int &curP, float to, AlphaLigne *line, bool exact, float step);
+
+ void QuickScan(float &pos, int &curP, float to, FloatLigne* line, float step);
+ void QuickScan(float &pos, int &curP, float to, FillRule directed, BitLigne* line, float step);
+ void QuickScan(float &pos, int &curP, float to, AlphaLigne* line, float step);
+
+ void Transform(Geom::Affine const &tr)
+ {for(auto & _pt : _pts) _pt.x*=tr;}
+
+ std::vector<back_data> ebData;
+ std::vector<voronoi_point> vorpData;
+ std::vector<voronoi_edge> voreData;
+
+ int nbQRas;
+ int firstQRas;
+ int lastQRas;
+ quick_raster_data *qrsData;
+
+ std::vector<sTreeChange> chgts;
+ int nbInc;
+ int maxInc;
+
+ incidenceData *iData;
+ // these ones are allocated at the beginning of each sweep and freed at the end of the sweep
+ SweepTreeList *sTree;
+ SweepEventQueue *sEvts;
+
+ // bounding box stuff
+ double leftX, topY, rightX, bottomY;
+
+ // topological information: who links who?
+ struct dg_point
+ {
+ Geom::Point x; // position
+ int dI, dO; // indegree and outdegree
+ int incidentEdge[2]; // first and last incident edge
+ int oldDegree;
+
+ int totalDegree() const { return dI + dO; }
+ };
+
+ struct dg_arete
+ {
+ Geom::Point dx; // edge vector
+ int st, en; // start and end points of the edge
+ int nextS, prevS; // next and previous edge in the double-linked list at the start point
+ int nextE, prevE; // next and previous edge in the double-linked list at the end point
+ };
+
+ // lists of the nodes and edges
+ int maxPt; // [FIXME: remove this]
+ int maxAr; // [FIXME: remove this]
+
+ // flags
+ int type;
+
+ inline int numberOfPoints() const { return _pts.size(); }
+ inline bool hasPoints() const { return (_pts.empty() == false); }
+ inline int numberOfEdges() const { return _aretes.size(); }
+ inline bool hasEdges() const { return (_aretes.empty() == false); }
+
+ inline void needPointsSorting() { _need_points_sorting = true; }
+ inline void needEdgesSorting() { _need_edges_sorting = true; }
+
+ inline bool hasBackData() const { return _has_back_data; }
+
+ inline dg_point const &getPoint(int n) const { return _pts[n]; }
+ inline dg_arete const &getEdge(int n) const { return _aretes[n]; }
+
+private:
+
+ friend class SweepTree;
+ friend class SweepEvent;
+ friend class SweepEventQueue;
+
+ // temporary data for the various algorithms
+ struct edge_data
+ {
+ int weight; // weight of the edge (to handle multiple edges)
+ Geom::Point rdx; // rounded edge vector
+ double length, sqlength, ilength, isqlength; // length^2, length, 1/length^2, 1/length
+ double siEd, coEd; // siEd=abs(rdy/length) and coEd=rdx/length
+ edge_data() : weight(0), length(0.0), sqlength(0.0), ilength(0.0), isqlength(0.0), siEd(0.0), coEd(0.0) {}
+ // used to determine the "most horizontal" edge between 2 edges
+ };
+
+ struct sweep_src_data
+ {
+ void *misc; // pointer to the SweepTree* in the sweepline
+ int firstLinkedPoint; // not used
+ int stPt, enPt; // start- end end- points for this edge in the resulting polygon
+ int ind; // for the GetAdjacencies function: index in the sliceSegs array (for quick deletions)
+ int leftRnd, rightRnd; // leftmost and rightmost points (in the result polygon) that are incident to
+ // the edge, for the current sweep position
+ // not set if the edge doesn't start/end or intersect at the current sweep position
+ Shape *nextSh; // nextSh and nextBo identify the next edge in the list
+ int nextBo; // they are used to maintain a linked list of edge that start/end or intersect at
+ // the current sweep position
+ int curPoint, doneTo;
+ double curT;
+ };
+
+ struct sweep_dest_data
+ {
+ void *misc; // used to check if an edge has already been seen during the depth-first search
+ int suivParc, precParc; // previous and current next edge in the depth-first search
+ int leW, riW; // left and right winding numbers for this edge
+ int ind; // order of the edges during the depth-first search
+ };
+
+ struct raster_data
+ {
+ SweepTree *misc; // pointer to the associated SweepTree* in the sweepline
+ double lastX, lastY, curX, curY; // curX;curY is the current intersection of the edge with the sweepline
+ // lastX;lastY is the intersection with the previous sweepline
+ bool sens; // true if the edge goes down, false otherwise
+ double calcX; // horizontal position of the intersection of the edge with the
+ // previous sweepline
+ double dxdy, dydx; // horizontal change per unit vertical move of the intersection with the sweepline
+ int guess;
+ };
+
+ struct point_data
+ {
+ int oldInd, newInd; // back and forth indices used when sorting the points, to know where they have
+ // been relocated in the array
+ int pending; // number of intersection attached to this edge, and also used when sorting arrays
+ int edgeOnLeft; // not used (should help speeding up winding calculations)
+ int nextLinkedPoint; // not used
+ Shape *askForWindingS;
+ int askForWindingB;
+ Geom::Point rx; // rounded coordinates of the point
+ };
+
+
+ struct edge_list
+ { // temporary array of edges for easier sorting
+ int no;
+ bool starting;
+ Geom::Point x;
+ };
+
+ void initialisePointData();
+ void initialiseEdgeData();
+ void clearIncidenceData();
+
+ void _countUpDown(int P, int *numberUp, int *numberDown, int *upEdge, int *downEdge) const;
+ void _countUpDownTotalDegree2(int P, int *numberUp, int *numberDown, int *upEdge, int *downEdge) const;
+ void _updateIntersection(int e, int p);
+
+ // activation/deactivation of the temporary data arrays
+ void MakePointData(bool nVal);
+ void MakeEdgeData(bool nVal);
+ void MakeSweepSrcData(bool nVal);
+ void MakeSweepDestData(bool nVal);
+ void MakeRasterData(bool nVal);
+ void MakeQuickRasterData(bool nVal);
+
+ void SortPoints(int s, int e);
+ void SortPointsByOldInd(int s, int e);
+
+ // fonctions annexes pour ConvertToShape et Booleen
+ void ResetSweep(); // allocates sweep structures
+ void CleanupSweep(); // deallocates them
+
+ // edge sorting function
+ void SortEdgesList(edge_list *edges, int s, int e);
+
+ void TesteIntersection(SweepTree *t, Side s, bool onlyDiff); // test if there is an intersection
+ bool TesteIntersection(SweepTree *iL, SweepTree *iR, Geom::Point &atx, double &atL, double &atR, bool onlyDiff);
+ bool TesteIntersection(Shape *iL, Shape *iR, int ilb, int irb,
+ Geom::Point &atx, double &atL, double &atR,
+ bool onlyDiff);
+ bool TesteAdjacency(Shape *iL, int ilb, const Geom::Point atx, int nPt,
+ bool push);
+ int PushIncidence(Shape *a, int cb, int pt, double theta);
+ int CreateIncidence(Shape *a, int cb, int pt);
+ void AssemblePoints(Shape *a);
+ int AssemblePoints(int st, int en);
+ void AssembleAretes(FillRule directed = fill_nonZero);
+ void AddChgt(int lastPointNo, int lastChgtPt, Shape *&shapeHead,
+ int &edgeHead, sTreeChangeType type, Shape *lS, int lB, Shape *rS,
+ int rB);
+ void CheckAdjacencies(int lastPointNo, int lastChgtPt, Shape *shapeHead, int edgeHead);
+ void CheckEdges(int lastPointNo, int lastChgtPt, Shape *a, Shape *b, BooleanOp mod);
+ void Avance(int lastPointNo, int lastChgtPt, Shape *iS, int iB, Shape *a, Shape *b, BooleanOp mod);
+ void DoEdgeTo(Shape *iS, int iB, int iTo, bool direct, bool sens);
+ void GetWindings(Shape *a, Shape *b = nullptr, BooleanOp mod = bool_op_union, bool brutal = false);
+
+ void Validate();
+
+ int Winding(int nPt) const;
+ void SortPointsRounded();
+ void SortPointsRounded(int s, int e);
+
+ void CreateEdge(int no, float to, float step);
+ void AvanceEdge(int no, float to, bool exact, float step);
+ void DestroyEdge(int no, float to, FloatLigne *line);
+ void AvanceEdge(int no, float to, FloatLigne *line, bool exact, float step);
+ void DestroyEdge(int no, BitLigne *line);
+ void AvanceEdge(int no, float to, BitLigne *line, bool exact, float step);
+ void DestroyEdge(int no, AlphaLigne *line);
+ void AvanceEdge(int no, float to, AlphaLigne *line, bool exact, float step);
+
+ void AddContour(Path * dest, int nbP, Path **orig, int startBord,
+ int curBord, bool splitWhenForced);
+ int ReFormeLineTo(int bord, int curBord, Path *dest, Path *orig);
+ int ReFormeArcTo(int bord, int curBord, Path *dest, Path *orig);
+ int ReFormeCubicTo(int bord, int curBord, Path *dest, Path *orig);
+ int ReFormeBezierTo(int bord, int curBord, Path *dest, Path *orig);
+ void ReFormeBezierChunk(const Geom::Point px, const Geom::Point nx,
+ Path *dest, int inBezier, int nbInterm,
+ Path *from, int p, double ts, double te);
+
+ int QuickRasterChgEdge(int oBord, int nbord, double x);
+ int QuickRasterAddEdge(int bord, double x, int guess);
+ void QuickRasterSubEdge(int bord);
+ void QuickRasterSwapEdge(int a, int b);
+ void QuickRasterSort();
+
+ bool _need_points_sorting; ///< points have been added or removed: we need to sort the points again
+ bool _need_edges_sorting; ///< edges have been added: maybe they are not ordered clockwise
+ ///< nota: if you remove an edge, the clockwise order still holds
+ bool _has_points_data; ///< the pData array is allocated
+ bool _point_data_initialised;///< the pData array is up to date
+ bool _has_edges_data; ///< the eData array is allocated
+ bool _has_sweep_src_data; ///< the swsData array is allocated
+ bool _has_sweep_dest_data; ///< the swdData array is allocated
+ bool _has_raster_data; ///< the swrData array is allocated
+ bool _has_quick_raster_data;///< the swrData array is allocated
+ bool _has_back_data; //< the ebData array is allocated
+ bool _has_voronoi_data;
+ bool _bbox_up_to_date; ///< the leftX/rightX/topY/bottomY are up to date
+
+ std::vector<dg_point> _pts;
+ std::vector<dg_arete> _aretes;
+
+ // the arrays of temporary data
+ // these ones are dynamically kept at a length of maxPt or maxAr
+ std::vector<edge_data> eData;
+ std::vector<sweep_src_data> swsData;
+ std::vector<sweep_dest_data> swdData;
+ std::vector<raster_data> swrData;
+ std::vector<point_data> pData;
+
+ static int CmpQRs(const quick_raster_data &p1, const quick_raster_data &p2) {
+ if ( fabs(p1.x - p2.x) < 0.00001 ) {
+ return 0;
+ }
+
+ return ( ( p1.x < p2.x ) ? -1 : 1 );
+ };
+
+ // edge direction comparison function
+ static int CmpToVert(const Geom::Point ax, const Geom::Point bx, bool as, bool bs);
+};
+
+bool directedEulerian(Shape const *s);
+double distance(Shape const *s, Geom::Point const &p);
+bool distanceLessThanOrEqual(Shape const *s, Geom::Point const &p, double const max_l2);
+
+#endif
diff --git a/src/livarot/ShapeDraw.cpp b/src/livarot/ShapeDraw.cpp
new file mode 100644
index 0000000..79d070b
--- /dev/null
+++ b/src/livarot/ShapeDraw.cpp
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+/*
+ * ShapeDraw.cpp
+ * nlivarot
+ *
+ * Created by fred on Mon Jun 16 2003.
+ *
+ */
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include "Shape.h"
+//#include <ApplicationServices/ApplicationServices.h>
+
+// debug routine for vizualizing the polygons
+void
+Shape::Plot (double ix, double iy, double ir, double mx, double my, bool doPoint,
+ bool edgesNo, bool pointsNo, bool doDir,char* fileName)
+{
+ FILE* outFile=fopen(fileName,"w+");
+// fprintf(outFile,"\n\n\n");
+ fprintf(outFile,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
+ fprintf(outFile,"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\"\n");
+ fprintf(outFile,"\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
+ fprintf(outFile,"<svg:svg\n");
+ fprintf(outFile," id=\"svg1\"\n");
+ fprintf(outFile," inkscape:version=\"0.38cvs\"\n");
+ fprintf(outFile," xmlns:svg=\"http://www.w3.org/2000/svg\"\n");
+ fprintf(outFile," xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"\n");
+ fprintf(outFile," xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"\n");
+ fprintf(outFile," xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
+ fprintf(outFile," width=\"210mm\"\n");
+ fprintf(outFile," height=\"297mm\"\n");
+ fprintf(outFile," sodipodi:docbase=\"/Volumes/Sancho/inkscapecvs\"\n");
+ fprintf(outFile," sodipodi:docname=\"/Volumes/Sancho/inkscapecvs/modele.svg\">\n");
+ fprintf(outFile," <svg:defs\n");
+ fprintf(outFile," id=\"defs3\" />\n");
+ fprintf(outFile," <sodipodi:namedview\n");
+ fprintf(outFile," id=\"base\"\n");
+ fprintf(outFile," pagecolor=\"#ffffff\"\n");
+ fprintf(outFile," bordercolor=\"#666666\"\n");
+ fprintf(outFile," borderopacity=\"1.0\"\n");
+ fprintf(outFile," inkscape:pageopacity=\"0.0\"\n");
+ fprintf(outFile," inkscape:pageshadow=\"2\"\n");
+ fprintf(outFile," inkscape:zoom=\"0.43415836\"\n");
+ fprintf(outFile," inkscape:cx=\"305.25952637\"\n");
+ fprintf(outFile," inkscape:cy=\"417.84947271\"\n");
+ fprintf(outFile," inkscape:window-width=\"640\"\n");
+ fprintf(outFile," inkscape:window-height=\"496\"\n");
+ fprintf(outFile," inkscape:window-x=\"20\"\n");
+ fprintf(outFile," inkscape:window-y=\"42\" />\n");
+
+ if ( doPoint ) {
+ for (int i=0;i<numberOfPoints();i++) {
+ double ph=(getPoint(i).x[0]-ix)*ir+mx;
+ double pv=(getPoint(i).x[1]-iy)*ir+my;
+ fprintf(outFile," <svg:circle cx=\"%f\" cy=\"%f\" r=\"5\" fill=\"none\" stroke=\"red\" stroke-width=\"0.25\" />\n",ph,pv); // localizing ok
+ }
+ }
+ if ( pointsNo ) {
+ for (int i=0;i<numberOfPoints();i++) {
+ double ph=(getPoint(i).x[0]-ix)*ir+mx;
+ double pv=(getPoint(i).x[1]-iy)*ir+my;
+ fprintf(outFile," <svg:text x=\"%f\" y=\"%f\" font-family=\"Monaco\" font-size=\"5\" fill=\"blue\" >\n",ph-2,pv+1); // localizing ok
+ fprintf(outFile,"%i\n",i);
+ fprintf(outFile," </text>\n");
+ }
+ }
+ {
+ for (int i=0;i<numberOfEdges();i++) {
+ int stP=getEdge(i).st;
+ int enP=getEdge(i).en;
+ if ( stP < 0 || enP < 0 ) continue;
+ double sh=(getPoint(stP).x[0]-ix)*ir+mx;
+ double sv=(getPoint(stP).x[1]-iy)*ir+my;
+ double eh=(getPoint(enP).x[0]-ix)*ir+mx;
+ double ev=(getPoint(enP).x[1]-iy)*ir+my;
+ if ( doDir ) {
+ double endh=(9*eh+1*sh)/10;
+ double endv=(9*ev+1*sv)/10;
+ fprintf(outFile," <svg:line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" stroke=\"black\" stroke-width=\"0.5\" />\n",sh,sv,endh,endv); // localizing ok
+ } else {
+ fprintf(outFile," <svg:line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" stroke=\"black\" stroke-width=\"0.5\" />\n",sh,sv,eh,ev); // localizing ok
+ }
+ }
+ }
+ if ( edgesNo ) {
+ for (int i=0;i<numberOfEdges();i++) {
+ int stP=getEdge(i).st;
+ int enP=getEdge(i).en;
+ if ( stP < 0 || enP < 0 ) continue;
+ double sh=(getPoint(stP).x[0]-ix)*ir+mx;
+ double sv=(getPoint(stP).x[1]-iy)*ir+my;
+ double eh=(getPoint(enP).x[0]-ix)*ir+mx;
+ double ev=(getPoint(enP).x[1]-iy)*ir+my;
+ fprintf(outFile," <svg:text x=\"%f\" y=\"%f\" font-family=\"Monaco\" font-size=\"5\" fill=\"blue\" >\n",(sh+eh)/2+2,(sv+ev)/2); // localizing ok
+ fprintf(outFile,"%i\n",i);
+ fprintf(outFile," </text>\n");
+ }
+ }
+
+ fprintf(outFile,"</svg>\n");
+ fclose(outFile);
+
+}
diff --git a/src/livarot/ShapeMisc.cpp b/src/livarot/ShapeMisc.cpp
new file mode 100644
index 0000000..b76ea58
--- /dev/null
+++ b/src/livarot/ShapeMisc.cpp
@@ -0,0 +1,1457 @@
+// 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;
+ }
+ {
+ 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 % numberOfPoints()).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
diff --git a/src/livarot/ShapeRaster.cpp b/src/livarot/ShapeRaster.cpp
new file mode 100644
index 0000000..7aa300e
--- /dev/null
+++ b/src/livarot/ShapeRaster.cpp
@@ -0,0 +1,2014 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+/*
+ * ShapeRaster.cpp
+ * nlivarot
+ *
+ * Created by fred on Sat Jul 19 2003.
+ *
+ */
+
+#include "Shape.h"
+
+#include "livarot/float-line.h"
+#include "AlphaLigne.h"
+#include "BitLigne.h"
+
+#include "livarot/sweep-event-queue.h"
+#include "livarot/sweep-tree-list.h"
+#include "livarot/sweep-tree.h"
+
+/*
+ * polygon rasterization: the sweepline algorithm in all its glory
+ * nothing unusual in this implementation, so nothing special to say
+ * the *Quick*() functions are not useful. forget about them
+ */
+
+void Shape::BeginRaster(float &pos, int &curPt)
+{
+ if ( numberOfPoints() <= 1 || numberOfEdges() <= 1 ) {
+ curPt = 0;
+ pos = 0;
+ return;
+ }
+
+ MakeRasterData(true);
+ MakePointData(true);
+ MakeEdgeData(true);
+
+ if (sTree == nullptr) {
+ sTree = new SweepTreeList(numberOfEdges());
+ }
+ if (sEvts == nullptr) {
+ sEvts = new SweepEventQueue(numberOfEdges());
+ }
+
+ SortPoints();
+
+ curPt = 0;
+ pos = getPoint(0).x[1] - 1.0;
+
+ for (int i = 0; i < numberOfPoints(); i++) {
+ pData[i].pending = 0;
+ pData[i].edgeOnLeft = -1;
+ pData[i].nextLinkedPoint = -1;
+ pData[i].rx[0] = /*Round(*/getPoint(i).x[0]/*)*/;
+ pData[i].rx[1] = /*Round(*/getPoint(i).x[1]/*)*/;
+ }
+
+ for (int i = 0;i < numberOfEdges(); i++) {
+ swrData[i].misc = nullptr;
+ eData[i].rdx=pData[getEdge(i).en].rx - pData[getEdge(i).st].rx;
+ }
+}
+
+
+void Shape::EndRaster()
+{
+ delete sTree;
+ sTree = nullptr;
+ delete sEvts;
+ sEvts = nullptr;
+
+ MakePointData(false);
+ MakeEdgeData(false);
+ MakeRasterData(false);
+}
+
+
+void Shape::BeginQuickRaster(float &pos, int &curPt)
+{
+ if ( numberOfPoints() <= 1 || numberOfEdges() <= 1 ) {
+ curPt = 0;
+ pos = 0;
+ return;
+ }
+
+ MakeRasterData(true);
+ MakeQuickRasterData(true);
+ nbQRas = 0;
+ firstQRas = lastQRas = -1;
+ MakePointData(true);
+ MakeEdgeData(true);
+
+ curPt = 0;
+ pos = getPoint(0).x[1] - 1.0;
+
+ initialisePointData();
+
+ for (int i=0;i<numberOfEdges();i++) {
+ swrData[i].misc = nullptr;
+ qrsData[i].ind = -1;
+ eData[i].rdx = pData[getEdge(i).en].rx - pData[getEdge(i).st].rx;
+ }
+
+ SortPoints();
+// SortPointsRounded();
+}
+
+
+void Shape::EndQuickRaster()
+{
+ MakePointData(false);
+ MakeEdgeData(false);
+ MakeRasterData(false);
+ MakeQuickRasterData(false);
+}
+
+
+// 2 versions of the Scan() series to move the scanline to a given position withou actually computing coverages
+void Shape::Scan(float &pos, int &curP, float to, float step)
+{
+ if ( numberOfEdges() <= 1 ) {
+ return;
+ }
+
+ if ( pos == to ) {
+ return;
+ }
+
+ enum Direction {
+ DOWNWARDS,
+ UPWARDS
+ };
+
+ Direction const d = (pos < to) ? DOWNWARDS : UPWARDS;
+
+ // points of the polygon are sorted top-down, so we take them in order, starting with the one at index curP,
+ // until we reach the wanted position to.
+ // don't forget to update curP and pos when we're done
+ int curPt = curP;
+ while ( ( d == DOWNWARDS && curPt < numberOfPoints() && getPoint(curPt).x[1] <= to) ||
+ ( d == UPWARDS && curPt > 0 && getPoint(curPt - 1).x[1] >= to) )
+ {
+ int nPt = (d == DOWNWARDS) ? curPt++ : --curPt;
+
+ // treat a new point: remove and add edges incident to it
+ int nbUp;
+ int nbDn;
+ int upNo;
+ int dnNo;
+ _countUpDown(nPt, &nbUp, &nbDn, &upNo, &dnNo);
+
+ if ( d == DOWNWARDS ) {
+ if ( nbDn <= 0 ) {
+ upNo = -1;
+ }
+ if ( upNo >= 0 && swrData[upNo].misc == nullptr ) {
+ upNo = -1;
+ }
+ } else {
+ if ( nbUp <= 0 ) {
+ dnNo = -1;
+ }
+ if ( dnNo >= 0 && swrData[dnNo].misc == nullptr ) {
+ dnNo = -1;
+ }
+ }
+
+ if ( ( d == DOWNWARDS && nbUp > 0 ) || ( d == UPWARDS && nbDn > 0 ) ) {
+ // first remove edges coming from above or below, as appropriate
+ int cb = getPoint(nPt).incidentEdge[FIRST];
+ while ( cb >= 0 && cb < numberOfEdges() ) {
+
+ Shape::dg_arete const &e = getEdge(cb);
+ if ( (d == DOWNWARDS && nPt == std::max(e.st, e.en)) ||
+ (d == UPWARDS && nPt == std::min(e.st, e.en)) )
+ {
+ if ( ( d == DOWNWARDS && cb != upNo ) || ( d == UPWARDS && cb != dnNo ) ) {
+ // we salvage the edge upNo to plug the edges we'll be addingat its place
+ // but the other edge don't have this chance
+ SweepTree *node = swrData[cb].misc;
+ if ( node ) {
+ swrData[cb].misc = nullptr;
+ node->Remove(*sTree, *sEvts, true);
+ }
+ }
+ }
+ cb = NextAt(nPt, cb);
+ }
+ }
+
+ // if there is one edge going down and one edge coming from above, we don't Insert() the new edge,
+ // but replace the upNo edge by the new one (faster)
+ SweepTree* insertionNode = nullptr;
+ if ( dnNo >= 0 ) {
+ if ( upNo >= 0 ) {
+ int rmNo=(d == DOWNWARDS) ? upNo:dnNo;
+ int neNo=(d == DOWNWARDS) ? dnNo:upNo;
+ SweepTree* node = swrData[rmNo].misc;
+ swrData[rmNo].misc = nullptr;
+
+ int const P = (d == DOWNWARDS) ? nPt : Other(nPt, neNo);
+ node->ConvertTo(this, neNo, 1, P);
+
+ swrData[neNo].misc = node;
+ insertionNode = node;
+ CreateEdge(neNo, to, step);
+ } else {
+ // always DOWNWARDS
+ SweepTree* node = sTree->add(this, dnNo, 1, nPt, this);
+ swrData[dnNo].misc = node;
+ node->Insert(*sTree, *sEvts, this, nPt, true);
+ //if (d == UPWARDS) {
+ // node->startPoint = Other(nPt, dnNo);
+ //}
+ insertionNode = node;
+ CreateEdge(dnNo,to,step);
+ }
+ } else {
+ if ( upNo >= 0 ) {
+ // always UPWARDS
+ SweepTree* node = sTree->add(this, upNo, 1, nPt, this);
+ swrData[upNo].misc = node;
+ node->Insert(*sTree, *sEvts, this, nPt, true);
+ //if (d == UPWARDS) {
+ node->startPoint = Other(nPt, upNo);
+ //}
+ insertionNode = node;
+ CreateEdge(upNo,to,step);
+ }
+ }
+
+ // add the remaining edges
+ if ( ( d == DOWNWARDS && nbDn > 1 ) || ( d == UPWARDS && nbUp > 1 ) ) {
+ // si nbDn == 1 , alors dnNo a deja ete traite
+ int cb = getPoint(nPt).incidentEdge[FIRST];
+ while ( cb >= 0 && cb < numberOfEdges() ) {
+ Shape::dg_arete const &e = getEdge(cb);
+ if ( nPt == std::min(e.st, e.en) ) {
+ if ( cb != dnNo && cb != upNo ) {
+ SweepTree *node = sTree->add(this, cb, 1, nPt, this);
+ swrData[cb].misc = node;
+ node->InsertAt(*sTree, *sEvts, this, insertionNode, nPt, true);
+ if (d == UPWARDS) {
+ node->startPoint = Other(nPt, cb);
+ }
+ CreateEdge(cb, to, step);
+ }
+ }
+ cb = NextAt(nPt,cb);
+ }
+ }
+ }
+
+ curP = curPt;
+ if ( curPt > 0 ) {
+ pos = getPoint(curPt - 1).x[1];
+ } else {
+ pos = to;
+ }
+
+ // the final touch: edges intersecting the sweepline must be update so that their intersection with
+ // said sweepline is correct.
+ pos = to;
+ if ( sTree->racine ) {
+ SweepTree* curS = static_cast<SweepTree*>(sTree->racine->Leftmost());
+ while ( curS ) {
+ int cb = curS->bord;
+ AvanceEdge(cb, to, true, step);
+ curS = static_cast<SweepTree*>(curS->elem[RIGHT]);
+ }
+ }
+}
+
+
+
+void Shape::QuickScan(float &pos,int &curP, float to, bool /*doSort*/, float step)
+{
+ if ( numberOfEdges() <= 1 ) {
+ return;
+ }
+
+ if ( pos == to ) {
+ return;
+ }
+
+ enum Direction {
+ DOWNWARDS,
+ UPWARDS
+ };
+
+ Direction const d = (pos < to) ? DOWNWARDS : UPWARDS;
+
+ int curPt = curP;
+ while ( (d == DOWNWARDS && curPt < numberOfPoints() && getPoint(curPt ).x[1] <= to) ||
+ (d == UPWARDS && curPt > 0 && getPoint(curPt - 1).x[1] >= to) )
+ {
+ int nPt = (d == DOWNWARDS) ? curPt++ : --curPt;
+
+ int nbUp;
+ int nbDn;
+ int upNo;
+ int dnNo;
+ _countUpDown(nPt, &nbUp, &nbDn, &upNo, &dnNo);
+
+ if ( nbDn <= 0 ) {
+ upNo = -1;
+ }
+ if ( upNo >= 0 && swrData[upNo].misc == nullptr ) {
+ upNo = -1;
+ }
+
+ if ( nbUp > 0 ) {
+ int cb = getPoint(nPt).incidentEdge[FIRST];
+ while ( cb >= 0 && cb < numberOfEdges() ) {
+ Shape::dg_arete const &e = getEdge(cb);
+ if ( (d == DOWNWARDS && nPt == std::max(e.st, e.en)) ||
+ (d == UPWARDS && nPt == std::min(e.st, e.en)) )
+ {
+ if ( cb != upNo ) {
+ QuickRasterSubEdge(cb);
+ }
+ }
+ cb = NextAt(nPt,cb);
+ }
+ }
+
+ // traitement du "upNo devient dnNo"
+ int ins_guess = -1;
+ if ( dnNo >= 0 ) {
+ if ( upNo >= 0 ) {
+ ins_guess = QuickRasterChgEdge(upNo, dnNo, getPoint(nPt).x[0]);
+ } else {
+ ins_guess = QuickRasterAddEdge(dnNo, getPoint(nPt).x[0], ins_guess);
+ }
+ CreateEdge(dnNo, to, step);
+ }
+
+ if ( nbDn > 1 ) { // si nbDn == 1 , alors dnNo a deja ete traite
+ int cb = getPoint(nPt).incidentEdge[FIRST];
+ while ( cb >= 0 && cb < numberOfEdges() ) {
+ Shape::dg_arete const &e = getEdge(cb);
+ if ( (d == DOWNWARDS && nPt == std::min(e.st, e.en)) ||
+ (d == UPWARDS && nPt == std::max(e.st, e.en)) )
+ {
+ if ( cb != dnNo ) {
+ ins_guess = QuickRasterAddEdge(cb, getPoint(nPt).x[0], ins_guess);
+ CreateEdge(cb, to, step);
+ }
+ }
+ cb = NextAt(nPt,cb);
+ }
+ }
+
+
+ curP = curPt;
+ if ( curPt > 0 ) {
+ pos = getPoint(curPt-1).x[1];
+ } else {
+ pos = to;
+ }
+ }
+
+ pos = to;
+
+ for (int i=0; i < nbQRas; i++) {
+ int cb = qrsData[i].bord;
+ AvanceEdge(cb, to, true, step);
+ qrsData[i].x=swrData[cb].curX;
+ }
+
+ QuickRasterSort();
+}
+
+
+
+int Shape::QuickRasterChgEdge(int oBord, int nBord, double x)
+{
+ if ( oBord == nBord ) {
+ return -1;
+ }
+
+ int no = qrsData[oBord].ind;
+ if ( no >= 0 ) {
+ qrsData[no].bord = nBord;
+ qrsData[no].x = x;
+ qrsData[oBord].ind = -1;
+ qrsData[nBord].ind = no;
+ }
+
+ return no;
+}
+
+
+
+int Shape::QuickRasterAddEdge(int bord, double x, int guess)
+{
+ int no = nbQRas++;
+ qrsData[no].bord = bord;
+ qrsData[no].x = x;
+ qrsData[bord].ind = no;
+ qrsData[no].prev = -1;
+ qrsData[no].next = -1;
+
+ if ( no < 0 || no >= nbQRas ) {
+ return -1;
+ }
+
+ if ( firstQRas < 0 ) {
+ firstQRas = lastQRas = no;
+ qrsData[no].prev = -1;
+ qrsData[no].next = -1;
+ return no;
+ }
+
+ if ( guess < 0 || guess >= nbQRas ) {
+
+ int c = firstQRas;
+ while ( c >= 0 && c < nbQRas && CmpQRs(qrsData[c],qrsData[no]) < 0 ) {
+ c = qrsData[c].next;
+ }
+
+ if ( c < 0 || c >= nbQRas ) {
+ qrsData[no].prev = lastQRas;
+ qrsData[lastQRas].next = no;
+ lastQRas = no;
+ } else {
+ qrsData[no].prev = qrsData[c].prev;
+ if ( qrsData[no].prev >= 0 ) {
+ qrsData[qrsData[no].prev].next=no;
+ } else {
+ firstQRas = no;
+ }
+
+ qrsData[no].next = c;
+ qrsData[c].prev = no;
+ }
+
+ } else {
+ int c = guess;
+ int stTst = CmpQRs(qrsData[c],qrsData[no]);
+ if ( stTst == 0 ) {
+
+ qrsData[no].prev = qrsData[c].prev;
+ if ( qrsData[no].prev >= 0 ) {
+ qrsData[qrsData[no].prev].next = no;
+ } else {
+ firstQRas = no;
+ }
+
+ qrsData[no].next = c;
+ qrsData[c].prev = no;
+
+ } else if ( stTst > 0 ) {
+
+ while ( c >= 0 && c < nbQRas && CmpQRs(qrsData[c],qrsData[no]) > 0 ) {
+ c = qrsData[c].prev;
+ }
+
+ if ( c < 0 || c >= nbQRas ) {
+ qrsData[no].next = firstQRas;
+ qrsData[firstQRas].prev = no; // firstQRas != -1
+ firstQRas = no;
+ } else {
+ qrsData[no].next = qrsData[c].next;
+ if ( qrsData[no].next >= 0 ) {
+ qrsData[qrsData[no].next].prev = no;
+ } else {
+ lastQRas = no;
+ }
+ qrsData[no].prev = c;
+ qrsData[c].next = no;
+ }
+
+ } else {
+
+ while ( c >= 0 && c < nbQRas && CmpQRs(qrsData[c],qrsData[no]) < 0 ) {
+ c = qrsData[c].next;
+ }
+
+ if ( c < 0 || c >= nbQRas ) {
+ qrsData[no].prev = lastQRas;
+ qrsData[lastQRas].next = no;
+ lastQRas = no;
+ } else {
+ qrsData[no].prev = qrsData[c].prev;
+ if ( qrsData[no].prev >= 0 ) {
+ qrsData[qrsData[no].prev].next = no;
+ } else {
+ firstQRas = no;
+ }
+
+ qrsData[no].next = c;
+ qrsData[c].prev = no;
+ }
+ }
+ }
+
+ return no;
+}
+
+
+
+void Shape::QuickRasterSubEdge(int bord)
+{
+ int no = qrsData[bord].ind;
+ if ( no < 0 || no >= nbQRas ) {
+ return; // euuhHHH
+ }
+
+ if ( qrsData[no].prev >= 0 ) {
+ qrsData[qrsData[no].prev].next=qrsData[no].next;
+ }
+
+ if ( qrsData[no].next >= 0 ) {
+ qrsData[qrsData[no].next].prev = qrsData[no].prev;
+ }
+
+ if ( no == firstQRas ) {
+ firstQRas = qrsData[no].next;
+ }
+
+ if ( no == lastQRas ) {
+ lastQRas = qrsData[no].prev;
+ }
+
+ qrsData[no].prev = qrsData[no].next = -1;
+
+ int savInd = qrsData[no].ind;
+ qrsData[no] = qrsData[--nbQRas];
+ qrsData[no].ind = savInd;
+ qrsData[qrsData[no].bord].ind = no;
+ qrsData[bord].ind = -1;
+
+ if ( nbQRas > 0 ) {
+ if ( firstQRas == nbQRas ) {
+ firstQRas = no;
+ }
+ if ( lastQRas == nbQRas ) {
+ lastQRas = no;
+ }
+ if ( qrsData[no].prev >= 0 ) {
+ qrsData[qrsData[no].prev].next = no;
+ }
+ if ( qrsData[no].next >= 0 ) {
+ qrsData[qrsData[no].next].prev = no;
+ }
+ }
+}
+
+
+
+void Shape::QuickRasterSwapEdge(int a, int b)
+{
+ if ( a == b ) {
+ return;
+ }
+
+ int na = qrsData[a].ind;
+ int nb = qrsData[b].ind;
+ if ( na < 0 || na >= nbQRas || nb < 0 || nb >= nbQRas ) {
+ return; // errrm
+ }
+
+ qrsData[na].bord = b;
+ qrsData[nb].bord = a;
+ qrsData[a].ind = nb;
+ qrsData[b].ind = na;
+
+ double swd = qrsData[na].x;
+ qrsData[na].x = qrsData[nb].x;
+ qrsData[nb].x = swd;
+}
+
+
+void Shape::QuickRasterSort()
+{
+ if ( nbQRas <= 1 ) {
+ return;
+ }
+
+ int cb = qrsData[firstQRas].bord;
+
+ while ( cb >= 0 ) {
+ int bI = qrsData[cb].ind;
+ int nI = qrsData[bI].next;
+
+ if ( nI < 0 ) {
+ break;
+ }
+
+ int ncb = qrsData[nI].bord;
+ if ( CmpQRs(qrsData[nI], qrsData[bI]) < 0 ) {
+ QuickRasterSwapEdge(cb, ncb);
+ int pI = qrsData[bI].prev; // ca reste bI, puisqu'on a juste echange les contenus
+ if ( pI < 0 ) {
+ cb = ncb; // en fait inutile; mais bon...
+ } else {
+ int pcb = qrsData[pI].bord;
+ cb = pcb;
+ }
+ } else {
+ cb = ncb;
+ }
+ }
+}
+
+
+// direct scan to a given position. goes through the edge list to keep only the ones intersecting the target sweepline
+// good for initial setup of scanline algo, bad for incremental changes
+void Shape::DirectScan(float &pos, int &curP, float to, float step)
+{
+ if ( numberOfEdges() <= 1 ) {
+ return;
+ }
+
+ if ( pos == to ) {
+ return;
+ }
+
+ if ( pos < to ) {
+ // we're moving downwards
+ // points of the polygon are sorted top-down, so we take them in order, starting with the one at index curP,
+ // until we reach the wanted position to.
+ // don't forget to update curP and pos when we're done
+ int curPt = curP;
+ while ( curPt < numberOfPoints() && getPoint(curPt).x[1] <= to ) {
+ curPt++;
+ }
+
+ for (int i=0;i<numberOfEdges();i++) {
+ if ( swrData[i].misc ) {
+ SweepTree* node = swrData[i].misc;
+ swrData[i].misc = nullptr;
+ node->Remove(*sTree, *sEvts, true);
+ }
+ }
+
+ for (int i=0; i < numberOfEdges(); i++) {
+ Shape::dg_arete const &e = getEdge(i);
+ if ( ( e.st < curPt && e.en >= curPt ) || ( e.en < curPt && e.st >= curPt )) {
+ // crosses sweepline
+ int nPt = (e.st < curPt) ? e.st : e.en;
+ SweepTree* node = sTree->add(this, i, 1, nPt, this);
+ swrData[i].misc = node;
+ node->Insert(*sTree, *sEvts, this, nPt, true);
+ CreateEdge(i, to, step);
+ }
+ }
+
+ curP = curPt;
+ if ( curPt > 0 ) {
+ pos = getPoint(curPt - 1).x[1];
+ } else {
+ pos = to;
+ }
+
+ } else {
+
+ // same thing, but going up. so the sweepSens is inverted for the Find() function
+ int curPt=curP;
+ while ( curPt > 0 && getPoint(curPt-1).x[1] >= to ) {
+ curPt--;
+ }
+
+ for (int i = 0; i < numberOfEdges(); i++) {
+ if ( swrData[i].misc ) {
+ SweepTree* node = swrData[i].misc;
+ swrData[i].misc = nullptr;
+ node->Remove(*sTree, *sEvts, true);
+ }
+ }
+
+ for (int i=0;i<numberOfEdges();i++) {
+ Shape::dg_arete const &e = getEdge(i);
+ if ( ( e.st > curPt - 1 && e.en <= curPt - 1 ) || ( e.en > curPt - 1 && e.st <= curPt - 1 )) {
+ // crosses sweepline
+ int nPt = (e.st > curPt) ? e.st : e.en;
+ SweepTree* node = sTree->add(this, i, 1, nPt, this);
+ swrData[i].misc = node;
+ node->Insert(*sTree, *sEvts, this, nPt, false);
+ node->startPoint = Other(nPt, i);
+ CreateEdge(i, to, step);
+ }
+ }
+
+ curP = curPt;
+ if ( curPt > 0 ) {
+ pos = getPoint(curPt - 1).x[1];
+ } else {
+ pos = to;
+ }
+ }
+
+ // the final touch: edges intersecting the sweepline must be update so that their intersection with
+ // said sweepline is correct.
+ pos = to;
+ if ( sTree->racine ) {
+ SweepTree* curS=static_cast<SweepTree*>(sTree->racine->Leftmost());
+ while ( curS ) {
+ int cb = curS->bord;
+ AvanceEdge(cb, to, true, step);
+ curS = static_cast<SweepTree*>(curS->elem[RIGHT]);
+ }
+ }
+}
+
+
+
+void Shape::DirectQuickScan(float &pos, int &curP, float to, bool /*doSort*/, float step)
+{
+ if ( numberOfEdges() <= 1 ) {
+ return;
+ }
+
+ if ( pos == to ) {
+ return;
+ }
+
+ if ( pos < to ) {
+ // we're moving downwards
+ // points of the polygon are sorted top-down, so we take them in order, starting with the one at index curP,
+ // until we reach the wanted position to.
+ // don't forget to update curP and pos when we're done
+ int curPt=curP;
+ while ( curPt < numberOfPoints() && getPoint(curPt).x[1] <= to ) {
+ curPt++;
+ }
+
+ for (int i = 0; i < numberOfEdges(); i++) {
+ if ( qrsData[i].ind < 0 ) {
+ QuickRasterSubEdge(i);
+ }
+ }
+
+ for (int i = 0; i < numberOfEdges(); i++) {
+ Shape::dg_arete const &e = getEdge(i);
+ if ( ( e.st < curPt && e.en >= curPt ) || ( e.en < curPt && e.st >= curPt )) {
+ // crosses sweepline
+ int nPt = (e.st < e.en) ? e.st : e.en;
+ QuickRasterAddEdge(i, getPoint(nPt).x[0], -1);
+ CreateEdge(i, to, step);
+ }
+ }
+
+ curP = curPt;
+ if ( curPt > 0 ) {
+ pos=getPoint(curPt-1).x[1];
+ } else {
+ pos = to;
+ }
+
+ } else {
+
+ // same thing, but going up. so the sweepSens is inverted for the Find() function
+ int curPt=curP;
+ while ( curPt > 0 && getPoint(curPt-1).x[1] >= to ) {
+ curPt--;
+ }
+
+ for (int i = 0; i < numberOfEdges(); i++) {
+ if ( qrsData[i].ind < 0 ) {
+ QuickRasterSubEdge(i);
+ }
+ }
+
+ for (int i=0;i<numberOfEdges();i++) {
+ Shape::dg_arete const &e = getEdge(i);
+ if ( ( e.st < curPt-1 && e.en >= curPt-1 ) || ( e.en < curPt-1 && e.st >= curPt-1 )) {
+ // crosses sweepline
+ int nPt = (e.st > e.en) ? e.st : e.en;
+ QuickRasterAddEdge(i, getPoint(nPt).x[0], -1);
+ CreateEdge(i, to, step);
+ }
+ }
+
+ curP = curPt;
+ if ( curPt > 0 ) {
+ pos = getPoint(curPt-1).x[1];
+ } else {
+ pos = to;
+ }
+
+ }
+
+ pos = to;
+ for (int i = 0; i < nbQRas; i++) {
+ int cb = qrsData[i].bord;
+ AvanceEdge(cb, to, true, step);
+ qrsData[i].x = swrData[cb].curX;
+ }
+
+ QuickRasterSort();
+}
+
+
+// scan and compute coverage, FloatLigne version coverage of the line is bult in 2 parts: first a
+// set of rectangles of height the height of the line (here: "step") one rectangle for each portion
+// of the sweepline that is in the polygon at the beginning of the scan. then a set ot trapezoids
+// are added or removed to these rectangles, one trapezoid for each edge destroyed or edge crossing
+// the entire line. think of it as a refinement of the coverage by rectangles
+
+void Shape::Scan(float &pos, int &curP, float to, FloatLigne *line, bool exact, float step)
+{
+ if ( numberOfEdges() <= 1 ) {
+ return;
+ }
+
+ if ( pos >= to ) {
+ return;
+ }
+
+ // first step: the rectangles since we read the sweepline left to right, we know the
+ // boundaries of the rectangles are appended in a list, hence the AppendBord(). we salvage
+ // the guess value for the trapezoids the edges will induce
+
+ if ( sTree->racine ) {
+ SweepTree *curS = static_cast<SweepTree*>(sTree->racine->Leftmost());
+ while ( curS ) {
+
+ int lastGuess = -1;
+ int cb = curS->bord;
+
+ if ( swrData[cb].sens == false && curS->elem[LEFT] ) {
+
+ int lb = (static_cast<SweepTree*>(curS->elem[LEFT]))->bord;
+
+ lastGuess = line->AppendBord(swrData[lb].curX,
+ to - swrData[lb].curY,
+ swrData[cb].curX,
+ to - swrData[cb].curY,0.0);
+
+ swrData[lb].guess = lastGuess - 1;
+ swrData[cb].guess = lastGuess;
+ } else {
+ int lb = curS->bord;
+ swrData[lb].guess = -1;
+ }
+
+ curS=static_cast <SweepTree*> (curS->elem[RIGHT]);
+ }
+ }
+
+ int curPt = curP;
+ while ( curPt < numberOfPoints() && getPoint(curPt).x[1] <= to ) {
+
+ int nPt = curPt++;
+
+ // same thing as the usual Scan(), just with a hardcoded "indegree+outdegree=2" case, since
+ // it's the most common one
+
+ int nbUp;
+ int nbDn;
+ int upNo;
+ int dnNo;
+ if ( getPoint(nPt).totalDegree() == 2 ) {
+ _countUpDownTotalDegree2(nPt, &nbUp, &nbDn, &upNo, &dnNo);
+ } else {
+ _countUpDown(nPt, &nbUp, &nbDn, &upNo, &dnNo);
+ }
+
+ if ( nbDn <= 0 ) {
+ upNo = -1;
+ }
+ if ( upNo >= 0 && swrData[upNo].misc == nullptr ) {
+ upNo = -1;
+ }
+
+ if ( nbUp > 1 || ( nbUp == 1 && upNo < 0 ) ) {
+ int cb = getPoint(nPt).incidentEdge[FIRST];
+ while ( cb >= 0 && cb < numberOfEdges() ) {
+ Shape::dg_arete const &e = getEdge(cb);
+ if ( nPt == std::max(e.st, e.en) ) {
+ if ( cb != upNo ) {
+ SweepTree* node = swrData[cb].misc;
+ if ( node ) {
+ _updateIntersection(cb, nPt);
+ // create trapezoid for the chunk of edge intersecting with the line
+ DestroyEdge(cb, to, line);
+ node->Remove(*sTree, *sEvts, true);
+ }
+ }
+ }
+
+ cb = NextAt(nPt,cb);
+ }
+ }
+
+ // traitement du "upNo devient dnNo"
+ SweepTree *insertionNode = nullptr;
+ if ( dnNo >= 0 ) {
+ if ( upNo >= 0 ) {
+ SweepTree* node = swrData[upNo].misc;
+ _updateIntersection(upNo, nPt);
+ DestroyEdge(upNo, to, line);
+
+ node->ConvertTo(this, dnNo, 1, nPt);
+
+ swrData[dnNo].misc = node;
+ insertionNode = node;
+ CreateEdge(dnNo, to, step);
+ swrData[dnNo].guess = swrData[upNo].guess;
+ } else {
+ SweepTree *node = sTree->add(this, dnNo, 1, nPt, this);
+ swrData[dnNo].misc = node;
+ node->Insert(*sTree, *sEvts, this, nPt, true);
+ insertionNode = node;
+ CreateEdge(dnNo, to, step);
+ }
+ }
+
+ if ( nbDn > 1 ) { // si nbDn == 1 , alors dnNo a deja ete traite
+ int cb = getPoint(nPt).incidentEdge[FIRST];
+ while ( cb >= 0 && cb < numberOfEdges() ) {
+ Shape::dg_arete const &e = getEdge(cb);
+ if ( nPt == std::min(e.st, e.en) ) {
+ if ( cb != dnNo ) {
+ SweepTree *node = sTree->add(this, cb, 1, nPt, this);
+ swrData[cb].misc = node;
+ node->InsertAt(*sTree, *sEvts, this, insertionNode, nPt, true);
+ CreateEdge(cb, to, step);
+ }
+ }
+ cb = NextAt(nPt,cb);
+ }
+ }
+ }
+
+ curP = curPt;
+ if ( curPt > 0 ) {
+ pos = getPoint(curPt - 1).x[1];
+ } else {
+ pos = to;
+ }
+
+ // update intersections with the sweepline, and add trapezoids for edges crossing the line
+ pos = to;
+ if ( sTree->racine ) {
+ SweepTree* curS = static_cast<SweepTree*>(sTree->racine->Leftmost());
+ while ( curS ) {
+ int cb = curS->bord;
+ AvanceEdge(cb, to, line, exact, step);
+ curS = static_cast<SweepTree*>(curS->elem[RIGHT]);
+ }
+ }
+}
+
+
+
+
+void Shape::Scan(float &pos, int &curP, float to, FillRule directed, BitLigne *line, bool exact, float step)
+{
+ if ( numberOfEdges() <= 1 ) {
+ return;
+ }
+
+ if ( pos >= to ) {
+ return;
+ }
+
+ if ( sTree->racine ) {
+ int curW = 0;
+ float lastX = 0;
+ SweepTree* curS = static_cast<SweepTree*>(sTree->racine->Leftmost());
+
+ if ( directed == fill_oddEven ) {
+
+ while ( curS ) {
+ int cb = curS->bord;
+ curW++;
+ curW &= 0x00000001;
+ if ( curW == 0 ) {
+ line->AddBord(lastX,swrData[cb].curX,true);
+ } else {
+ lastX = swrData[cb].curX;
+ }
+ curS = static_cast<SweepTree*>(curS->elem[RIGHT]);
+ }
+
+ } else if ( directed == fill_positive ) {
+
+ // doesn't behave correctly; no way i know to do this without a ConvertToShape()
+ while ( curS ) {
+ int cb = curS->bord;
+ int oW = curW;
+ if ( swrData[cb].sens ) {
+ curW++;
+ } else {
+ curW--;
+ }
+
+ if ( curW <= 0 && oW > 0) {
+ line->AddBord(lastX, swrData[cb].curX, true);
+ } else if ( curW > 0 && oW <= 0 ) {
+ lastX = swrData[cb].curX;
+ }
+
+ curS = static_cast<SweepTree*>(curS->elem[RIGHT]);
+ }
+
+ } else if ( directed == fill_nonZero ) {
+
+ while ( curS ) {
+ int cb = curS->bord;
+ int oW = curW;
+ if ( swrData[cb].sens ) {
+ curW++;
+ } else {
+ curW--;
+ }
+
+ if ( curW == 0 && oW != 0) {
+ line->AddBord(lastX,swrData[cb].curX,true);
+ } else if ( curW != 0 && oW == 0 ) {
+ lastX=swrData[cb].curX;
+ }
+ curS = static_cast<SweepTree*>(curS->elem[RIGHT]);
+ }
+ }
+
+ }
+
+ int curPt = curP;
+ while ( curPt < numberOfPoints() && getPoint(curPt).x[1] <= to ) {
+ int nPt = curPt++;
+
+ int cb;
+ int nbUp;
+ int nbDn;
+ int upNo;
+ int dnNo;
+
+ if ( getPoint(nPt).totalDegree() == 2 ) {
+ _countUpDownTotalDegree2(nPt, &nbUp, &nbDn, &upNo, &dnNo);
+ } else {
+ _countUpDown(nPt, &nbUp, &nbDn, &upNo, &dnNo);
+ }
+
+ if ( nbDn <= 0 ) {
+ upNo = -1;
+ }
+ if ( upNo >= 0 && swrData[upNo].misc == nullptr ) {
+ upNo = -1;
+ }
+
+ if ( nbUp > 1 || ( nbUp == 1 && upNo < 0 ) ) {
+ int cb = getPoint(nPt).incidentEdge[FIRST];
+ while ( cb >= 0 && cb < numberOfEdges() ) {
+ Shape::dg_arete const &e = getEdge(cb);
+ if ( nPt == std::max(e.st, e.en) ) {
+ if ( cb != upNo ) {
+ SweepTree* node=swrData[cb].misc;
+ if ( node ) {
+ _updateIntersection(cb, nPt);
+ DestroyEdge(cb, line);
+ node->Remove(*sTree,*sEvts,true);
+ }
+ }
+ }
+ cb = NextAt(nPt,cb);
+ }
+ }
+
+ // traitement du "upNo devient dnNo"
+ SweepTree* insertionNode = nullptr;
+ if ( dnNo >= 0 ) {
+ if ( upNo >= 0 ) {
+ SweepTree* node = swrData[upNo].misc;
+ _updateIntersection(upNo, nPt);
+ DestroyEdge(upNo, line);
+
+ node->ConvertTo(this, dnNo, 1, nPt);
+
+ swrData[dnNo].misc = node;
+ insertionNode = node;
+ CreateEdge(dnNo, to, step);
+
+ } else {
+
+ SweepTree* node = sTree->add(this,dnNo,1,nPt,this);
+ swrData[dnNo].misc = node;
+ node->Insert(*sTree, *sEvts, this, nPt, true);
+ insertionNode = node;
+ CreateEdge(dnNo, to, step);
+ }
+ }
+
+ if ( nbDn > 1 ) { // si nbDn == 1 , alors dnNo a deja ete traite
+ cb = getPoint(nPt).incidentEdge[FIRST];
+ while ( cb >= 0 && cb < numberOfEdges() ) {
+ Shape::dg_arete const &e = getEdge(cb);
+ if ( nPt == std::min(e.st, e.en) ) {
+ if ( cb != dnNo ) {
+ SweepTree* node = sTree->add(this, cb, 1, nPt, this);
+ swrData[cb].misc = node;
+ node->InsertAt(*sTree, *sEvts, this, insertionNode, nPt, true);
+ CreateEdge(cb, to, step);
+ }
+ }
+ cb = NextAt(nPt, cb);
+ }
+ }
+ }
+
+ curP = curPt;
+ if ( curPt > 0 ) {
+ pos = getPoint(curPt - 1).x[1];
+ } else {
+ pos = to;
+ }
+
+ pos = to;
+ if ( sTree->racine ) {
+ SweepTree* curS = static_cast<SweepTree*>(sTree->racine->Leftmost());
+ while ( curS ) {
+ int cb = curS->bord;
+ AvanceEdge(cb, to, line, exact, step);
+ curS = static_cast<SweepTree*>(curS->elem[RIGHT]);
+ }
+ }
+}
+
+
+void Shape::Scan(float &pos, int &curP, float to, AlphaLigne *line, bool exact, float step)
+{
+ if ( numberOfEdges() <= 1 ) {
+ return;
+ }
+
+ if ( pos >= to ) {
+ return;
+ }
+
+ int curPt = curP;
+ while ( curPt < numberOfPoints() && getPoint(curPt).x[1] <= to ) {
+ int nPt = curPt++;
+
+ int nbUp;
+ int nbDn;
+ int upNo;
+ int dnNo;
+ if ( getPoint(nPt).totalDegree() == 2 ) {
+ _countUpDownTotalDegree2(nPt, &nbUp, &nbDn, &upNo, &dnNo);
+ } else {
+ _countUpDown(nPt, &nbUp, &nbDn, &upNo, &dnNo);
+ }
+
+ if ( nbDn <= 0 ) {
+ upNo=-1;
+ }
+ if ( upNo >= 0 && swrData[upNo].misc == nullptr ) {
+ upNo=-1;
+ }
+
+ if ( nbUp > 1 || ( nbUp == 1 && upNo < 0 ) ) {
+ int cb = getPoint(nPt).incidentEdge[FIRST];
+ while ( cb >= 0 && cb < numberOfEdges() ) {
+ Shape::dg_arete const &e = getEdge(cb);
+ if ( nPt == std::max(e.st, e.en) ) {
+ if ( cb != upNo ) {
+ SweepTree* node = swrData[cb].misc;
+ if ( node ) {
+ _updateIntersection(cb, nPt);
+ DestroyEdge(cb, line);
+ node->Remove(*sTree, *sEvts, true);
+ }
+ }
+ }
+
+ cb = NextAt(nPt,cb);
+ }
+ }
+
+ // traitement du "upNo devient dnNo"
+ SweepTree* insertionNode = nullptr;
+ if ( dnNo >= 0 ) {
+ if ( upNo >= 0 ) {
+ SweepTree* node = swrData[upNo].misc;
+ _updateIntersection(upNo, nPt);
+ DestroyEdge(upNo, line);
+
+ node->ConvertTo(this, dnNo, 1, nPt);
+
+ swrData[dnNo].misc = node;
+ insertionNode = node;
+ CreateEdge(dnNo, to, step);
+ swrData[dnNo].guess = swrData[upNo].guess;
+ } else {
+ SweepTree* node = sTree->add(this, dnNo, 1, nPt, this);
+ swrData[dnNo].misc = node;
+ node->Insert(*sTree, *sEvts, this, nPt, true);
+ insertionNode = node;
+ CreateEdge(dnNo, to, step);
+ }
+ }
+
+ if ( nbDn > 1 ) { // si nbDn == 1 , alors dnNo a deja ete traite
+ int cb = getPoint(nPt).incidentEdge[FIRST];
+ while ( cb >= 0 && cb < numberOfEdges() ) {
+ Shape::dg_arete const &e = getEdge(cb);
+ if ( nPt == std::min(e.st, e.en) ) {
+ if ( cb != dnNo ) {
+ SweepTree* node = sTree->add(this, cb, 1, nPt, this);
+ swrData[cb].misc = node;
+ node->InsertAt(*sTree, *sEvts, this, insertionNode, nPt, true);
+ CreateEdge(cb, to, step);
+ }
+ }
+ cb = NextAt(nPt,cb);
+ }
+ }
+ }
+
+ curP = curPt;
+ if ( curPt > 0 ) {
+ pos = getPoint(curPt - 1).x[1];
+ } else {
+ pos = to;
+ }
+
+ pos = to;
+ if ( sTree->racine ) {
+ SweepTree* curS = static_cast<SweepTree*>(sTree->racine->Leftmost());
+ while ( curS ) {
+ int cb = curS->bord;
+ AvanceEdge(cb, to, line, exact, step);
+ curS = static_cast<SweepTree*>(curS->elem[RIGHT]);
+ }
+ }
+}
+
+
+
+void Shape::QuickScan(float &pos, int &curP, float to, FloatLigne* line, float step)
+{
+ if ( numberOfEdges() <= 1 ) {
+ return;
+ }
+
+ if ( pos >= to ) {
+ return;
+ }
+
+ if ( nbQRas > 1 ) {
+ int curW = 0;
+ // float lastX = 0;
+ // float lastY = 0;
+ int lastGuess = -1;
+ int lastB = -1;
+
+ for (int i = firstQRas; i >= 0 && i < nbQRas; i = qrsData[i].next) {
+ int cb = qrsData[i].bord;
+ int oW = curW;
+ if ( swrData[cb].sens ) {
+ curW++;
+ } else {
+ curW--;
+ }
+
+ if ( curW % 2 == 0 && oW % 2 != 0) {
+
+ lastGuess = line->AppendBord(swrData[lastB].curX,
+ to - swrData[lastB].curY,
+ swrData[cb].curX,
+ to - swrData[cb].curY,
+ 0.0);
+
+ swrData[cb].guess = lastGuess;
+ if ( lastB >= 0 ) {
+ swrData[lastB].guess = lastGuess - 1;
+ }
+
+ } else if ( curW%2 != 0 && oW%2 == 0 ) {
+
+ // lastX = swrData[cb].curX;
+ // lastY = swrData[cb].curY;
+ lastB = cb;
+ swrData[cb].guess = -1;
+
+ } else {
+ swrData[cb].guess = -1;
+ }
+ }
+ }
+
+ int curPt = curP;
+ while ( curPt < numberOfPoints() && getPoint(curPt).x[1] <= to ) {
+ int nPt = curPt++;
+
+ int nbUp;
+ int nbDn;
+ int upNo;
+ int dnNo;
+ if ( getPoint(nPt).totalDegree() == 2 ) {
+ _countUpDownTotalDegree2(nPt, &nbUp, &nbDn, &upNo, &dnNo);
+ } else {
+ _countUpDown(nPt, &nbUp, &nbDn, &upNo, &dnNo);
+ }
+
+ if ( nbDn <= 0 ) {
+ upNo = -1;
+ }
+ if ( upNo >= 0 && swrData[upNo].misc == nullptr ) {
+ upNo = -1;
+ }
+
+ if ( nbUp > 1 || ( nbUp == 1 && upNo < 0 ) ) {
+ int cb = getPoint(nPt).incidentEdge[FIRST];
+ while ( cb >= 0 && cb < numberOfEdges() ) {
+ Shape::dg_arete const &e = getEdge(cb);
+ if ( nPt == std::max(e.st, e.en) ) {
+ if ( cb != upNo ) {
+ QuickRasterSubEdge(cb);
+ _updateIntersection(cb, nPt);
+ DestroyEdge(cb, to, line);
+ }
+ }
+ cb = NextAt(nPt, cb);
+ }
+ }
+
+ // traitement du "upNo devient dnNo"
+ int ins_guess=-1;
+ if ( dnNo >= 0 ) {
+ if ( upNo >= 0 ) {
+ ins_guess = QuickRasterChgEdge(upNo ,dnNo, getPoint(nPt).x[0]);
+ _updateIntersection(upNo, nPt);
+ DestroyEdge(upNo, to, line);
+
+ CreateEdge(dnNo, to, step);
+ swrData[dnNo].guess = swrData[upNo].guess;
+ } else {
+ ins_guess = QuickRasterAddEdge(dnNo, getPoint(nPt).x[0], ins_guess);
+ CreateEdge(dnNo, to, step);
+ }
+ }
+
+ if ( nbDn > 1 ) { // si nbDn == 1 , alors dnNo a deja ete traite
+ int cb = getPoint(nPt).incidentEdge[FIRST];
+ while ( cb >= 0 && cb < numberOfEdges() ) {
+ Shape::dg_arete const &e = getEdge(cb);
+ if ( nPt == std::min(e.st, e.en) ) {
+ if ( cb != dnNo ) {
+ ins_guess = QuickRasterAddEdge(cb, getPoint(nPt).x[0], ins_guess);
+ CreateEdge(cb, to, step);
+ }
+ }
+ cb = NextAt(nPt, cb);
+ }
+ }
+ }
+
+ curP = curPt;
+ if ( curPt > 0 ) {
+ pos = getPoint(curPt-1).x[1];
+ } else {
+ pos=to;
+ }
+
+ pos = to;
+ for (int i=0; i < nbQRas; i++) {
+ int cb = qrsData[i].bord;
+ AvanceEdge(cb, to, line, true, step);
+ qrsData[i].x = swrData[cb].curX;
+ }
+
+ QuickRasterSort();
+}
+
+
+
+
+void Shape::QuickScan(float &pos, int &curP, float to, FillRule directed, BitLigne* line, float step)
+{
+ if ( numberOfEdges() <= 1 ) {
+ return;
+ }
+
+ if ( pos >= to ) {
+ return;
+ }
+
+ if ( nbQRas > 1 ) {
+ int curW = 0;
+ float lastX = 0;
+
+ if ( directed == fill_oddEven ) {
+
+ for (int i = firstQRas; i >= 0 && i < nbQRas; i = qrsData[i].next) {
+ int cb = qrsData[i].bord;
+ curW++;
+ curW &= 1;
+ if ( curW == 0 ) {
+ line->AddBord(lastX, swrData[cb].curX, true);
+ } else {
+ lastX = swrData[cb].curX;
+ }
+ }
+
+ } else if ( directed == fill_positive ) {
+ // doesn't behave correctly; no way i know to do this without a ConvertToShape()
+ for (int i = firstQRas; i >= 0 && i < nbQRas; i = qrsData[i].next) {
+ int cb = qrsData[i].bord;
+ int oW = curW;
+ if ( swrData[cb].sens ) {
+ curW++;
+ } else {
+ curW--;
+ }
+
+ if ( curW <= 0 && oW > 0) {
+ line->AddBord(lastX, swrData[cb].curX, true);
+ } else if ( curW > 0 && oW <= 0 ) {
+ lastX = swrData[cb].curX;
+ }
+ }
+
+ } else if ( directed == fill_nonZero ) {
+ for (int i = firstQRas; i >= 0 && i < nbQRas; i = qrsData[i].next) {
+ int cb = qrsData[i].bord;
+ int oW = curW;
+ if ( swrData[cb].sens ) {
+ curW++;
+ } else {
+ curW--;
+ }
+
+ if ( curW == 0 && oW != 0) {
+ line->AddBord(lastX, swrData[cb].curX, true);
+ } else if ( curW != 0 && oW == 0 ) {
+ lastX = swrData[cb].curX;
+ }
+ }
+ }
+ }
+
+ int curPt = curP;
+ while ( curPt < numberOfPoints() && getPoint(curPt).x[1] <= to ) {
+ int nPt = curPt++;
+
+ int nbUp;
+ int nbDn;
+ int upNo;
+ int dnNo;
+ if ( getPoint(nPt).totalDegree() == 2 ) {
+ _countUpDownTotalDegree2(nPt, &nbUp, &nbDn, &upNo, &dnNo);
+ } else {
+ _countUpDown(nPt, &nbUp, &nbDn, &upNo, &dnNo);
+ }
+
+ if ( nbDn <= 0 ) {
+ upNo = -1;
+ }
+
+ if ( upNo >= 0 && swrData[upNo].misc == nullptr ) {
+ upNo = -1;
+ }
+
+ if ( nbUp > 1 || ( nbUp == 1 && upNo < 0 ) ) {
+ int cb = getPoint(nPt).incidentEdge[FIRST];
+ while ( cb >= 0 && cb < numberOfEdges() ) {
+ Shape::dg_arete const &e = getEdge(cb);
+ if ( nPt == std::max(e.st, e.en) ) {
+ if ( cb != upNo ) {
+ QuickRasterSubEdge(cb);
+ _updateIntersection(cb, nPt);
+ DestroyEdge(cb, line);
+ }
+ }
+ cb = NextAt(nPt, cb);
+ }
+ }
+
+ // traitement du "upNo devient dnNo"
+ int ins_guess = -1;
+ if ( dnNo >= 0 ) {
+ if ( upNo >= 0 ) {
+ ins_guess = QuickRasterChgEdge(upNo, dnNo, getPoint(nPt).x[0]);
+ _updateIntersection(upNo, nPt);
+ DestroyEdge(upNo, line);
+
+ CreateEdge(dnNo, to, step);
+ } else {
+ ins_guess = QuickRasterAddEdge(dnNo, getPoint(nPt).x[0], ins_guess);
+ CreateEdge(dnNo, to, step);
+ }
+ }
+
+ if ( nbDn > 1 ) { // si nbDn == 1 , alors dnNo a deja ete traite
+ int cb = getPoint(nPt).incidentEdge[FIRST];
+ while ( cb >= 0 && cb < numberOfEdges() ) {
+ Shape::dg_arete const &e = getEdge(cb);
+ if ( nPt == std::min(e.st, e.en) ) {
+ if ( cb != dnNo ) {
+ ins_guess = QuickRasterAddEdge(cb, getPoint(nPt).x[0], ins_guess);
+ CreateEdge(cb, to, step);
+ }
+ }
+ cb = NextAt(nPt,cb);
+ }
+ }
+ }
+
+ curP = curPt;
+ if ( curPt > 0 ) {
+ pos=getPoint(curPt - 1).x[1];
+ } else {
+ pos = to;
+ }
+
+ pos = to;
+ for (int i = 0; i < nbQRas; i++) {
+ int cb = qrsData[i].bord;
+ AvanceEdge(cb, to, line, true, step);
+ qrsData[i].x = swrData[cb].curX;
+ }
+
+ QuickRasterSort();
+}
+
+
+
+void Shape::QuickScan(float &pos, int &curP, float to, AlphaLigne* line, float step)
+{
+ if ( numberOfEdges() <= 1 ) {
+ return;
+ }
+ if ( pos >= to ) {
+ return;
+ }
+
+ int curPt = curP;
+ while ( curPt < numberOfPoints() && getPoint(curPt).x[1] <= to ) {
+ int nPt = curPt++;
+
+ int nbUp;
+ int nbDn;
+ int upNo;
+ int dnNo;
+ if ( getPoint(nPt).totalDegree() == 2 ) {
+ _countUpDownTotalDegree2(nPt, &nbUp, &nbDn, &upNo, &dnNo);
+ } else {
+ _countUpDown(nPt, &nbUp, &nbDn, &upNo, &dnNo);
+ }
+
+ if ( nbDn <= 0 ) {
+ upNo = -1;
+ }
+ if ( upNo >= 0 && swrData[upNo].misc == nullptr ) {
+ upNo = -1;
+ }
+
+ if ( nbUp > 1 || ( nbUp == 1 && upNo < 0 ) ) {
+ int cb = getPoint(nPt).incidentEdge[FIRST];
+ while ( cb >= 0 && cb < numberOfEdges() ) {
+ Shape::dg_arete const &e = getEdge(cb);
+ if ( nPt == std::max(e.st, e.en) ) {
+ if ( cb != upNo ) {
+ QuickRasterSubEdge(cb);
+ _updateIntersection(cb, nPt);
+ DestroyEdge(cb, line);
+ }
+ }
+ cb = NextAt(nPt,cb);
+ }
+ }
+
+ // traitement du "upNo devient dnNo"
+ int ins_guess = -1;
+ if ( dnNo >= 0 ) {
+ if ( upNo >= 0 ) {
+ ins_guess = QuickRasterChgEdge(upNo, dnNo, getPoint(nPt).x[0]);
+ _updateIntersection(upNo, nPt);
+ DestroyEdge(upNo, line);
+
+ CreateEdge(dnNo, to, step);
+ swrData[dnNo].guess = swrData[upNo].guess;
+ } else {
+ ins_guess = QuickRasterAddEdge(dnNo, getPoint(nPt).x[0], ins_guess);
+ CreateEdge(dnNo, to, step);
+ }
+ }
+
+ if ( nbDn > 1 ) { // si nbDn == 1 , alors dnNo a deja ete traite
+ int cb = getPoint(nPt).incidentEdge[FIRST];
+ while ( cb >= 0 && cb < numberOfEdges() ) {
+ Shape::dg_arete const &e = getEdge(cb);
+ if ( nPt == std::min(e.st, e.en) ) {
+ if ( cb != dnNo ) {
+ ins_guess = QuickRasterAddEdge(cb,getPoint(nPt).x[0], ins_guess);
+ CreateEdge(cb, to, step);
+ }
+ }
+ cb = NextAt(nPt,cb);
+ }
+ }
+ }
+
+ curP = curPt;
+ if ( curPt > 0 ) {
+ pos = getPoint(curPt-1).x[1];
+ } else {
+ pos = to;
+ }
+
+ pos = to;
+ for (int i = 0; i < nbQRas; i++) {
+ int cb = qrsData[i].bord;
+ AvanceEdge(cb, to, line, true, step);
+ qrsData[i].x = swrData[cb].curX;
+ }
+
+ QuickRasterSort();
+}
+
+
+/*
+ * operations de bases pour la rasterization
+ *
+ */
+void Shape::CreateEdge(int no, float to, float step)
+{
+ int cPt;
+ Geom::Point dir;
+ if ( getEdge(no).st < getEdge(no).en ) {
+ cPt = getEdge(no).st;
+ swrData[no].sens = true;
+ dir = getEdge(no).dx;
+ } else {
+ cPt = getEdge(no).en;
+ swrData[no].sens = false;
+ dir = -getEdge(no).dx;
+ }
+
+ swrData[no].lastX = swrData[no].curX = getPoint(cPt).x[0];
+ swrData[no].lastY = swrData[no].curY = getPoint(cPt).x[1];
+
+ if ( fabs(dir[1]) < 0.000001 ) {
+ swrData[no].dxdy = 0;
+ } else {
+ swrData[no].dxdy = dir[0]/dir[1];
+ }
+
+ if ( fabs(dir[0]) < 0.000001 ) {
+ swrData[no].dydx = 0;
+ } else {
+ swrData[no].dydx = dir[1]/dir[0];
+ }
+
+ swrData[no].calcX = swrData[no].curX + (to - step - swrData[no].curY) * swrData[no].dxdy;
+ swrData[no].guess = -1;
+}
+
+
+void Shape::AvanceEdge(int no, float to, bool exact, float step)
+{
+ if ( exact ) {
+ Geom::Point dir;
+ Geom::Point stp;
+ if ( swrData[no].sens ) {
+ stp = getPoint(getEdge(no).st).x;
+ dir = getEdge(no).dx;
+ } else {
+ stp = getPoint(getEdge(no).en).x;
+ dir = -getEdge(no).dx;
+ }
+
+ if ( fabs(dir[1]) < 0.000001 ) {
+ swrData[no].calcX = stp[0] + dir[0];
+ } else {
+ swrData[no].calcX = stp[0] + ((to - stp[1]) * dir[0]) / dir[1];
+ }
+ } else {
+ swrData[no].calcX += step * swrData[no].dxdy;
+ }
+
+ swrData[no].lastX = swrData[no].curX;
+ swrData[no].lastY = swrData[no].curY;
+ swrData[no].curX = swrData[no].calcX;
+ swrData[no].curY = to;
+}
+
+/*
+ * specialisation par type de structure utilise
+ */
+
+void Shape::DestroyEdge(int no, float to, FloatLigne* line)
+{
+ if ( swrData[no].sens ) {
+
+ if ( swrData[no].curX < swrData[no].lastX ) {
+
+ swrData[no].guess = line->AddBordR(swrData[no].curX,
+ to - swrData[no].curY,
+ swrData[no].lastX,
+ to - swrData[no].lastY,
+ -swrData[no].dydx,
+ swrData[no].guess);
+
+ } else if ( swrData[no].curX > swrData[no].lastX ) {
+
+ swrData[no].guess = line->AddBord(swrData[no].lastX,
+ -(to - swrData[no].lastY),
+ swrData[no].curX,
+ -(to - swrData[no].curY),
+ swrData[no].dydx,
+ swrData[no].guess);
+ }
+
+ } else {
+
+ if ( swrData[no].curX < swrData[no].lastX ) {
+
+ swrData[no].guess = line->AddBordR(swrData[no].curX,
+ -(to - swrData[no].curY),
+ swrData[no].lastX,
+ -(to - swrData[no].lastY),
+ swrData[no].dydx,
+ swrData[no].guess);
+
+ } else if ( swrData[no].curX > swrData[no].lastX ) {
+
+ swrData[no].guess = line->AddBord(swrData[no].lastX,
+ to - swrData[no].lastY,
+ swrData[no].curX,
+ to - swrData[no].curY,
+ -swrData[no].dydx,
+ swrData[no].guess);
+ }
+ }
+}
+
+
+
+void Shape::AvanceEdge(int no, float to, FloatLigne *line, bool exact, float step)
+{
+ AvanceEdge(no,to,exact,step);
+
+ if ( swrData[no].sens ) {
+
+ if ( swrData[no].curX < swrData[no].lastX ) {
+
+ swrData[no].guess = line->AddBordR(swrData[no].curX,
+ to - swrData[no].curY,
+ swrData[no].lastX,
+ to - swrData[no].lastY,
+ -swrData[no].dydx,
+ swrData[no].guess);
+
+ } else if ( swrData[no].curX > swrData[no].lastX ) {
+
+ swrData[no].guess = line->AddBord(swrData[no].lastX,
+ -(to - swrData[no].lastY),
+ swrData[no].curX,
+ -(to - swrData[no].curY),
+ swrData[no].dydx,
+ swrData[no].guess);
+ }
+
+ } else {
+
+ if ( swrData[no].curX < swrData[no].lastX ) {
+
+ swrData[no].guess = line->AddBordR(swrData[no].curX,
+ -(to - swrData[no].curY),
+ swrData[no].lastX,
+ -(to - swrData[no].lastY),
+ swrData[no].dydx,
+ swrData[no].guess);
+
+ } else if ( swrData[no].curX > swrData[no].lastX ) {
+
+ swrData[no].guess = line->AddBord(swrData[no].lastX,
+ to - swrData[no].lastY,
+ swrData[no].curX,
+ to - swrData[no].curY,
+ -swrData[no].dydx,
+ swrData[no].guess);
+ }
+ }
+}
+
+
+void Shape::DestroyEdge(int no, BitLigne *line)
+{
+ if ( swrData[no].sens ) {
+
+ if ( swrData[no].curX < swrData[no].lastX ) {
+
+ line->AddBord(swrData[no].curX, swrData[no].lastX, false);
+
+ } else if ( swrData[no].curX > swrData[no].lastX ) {
+
+ line->AddBord(swrData[no].lastX,swrData[no].curX,false);
+ }
+
+ } else {
+
+ if ( swrData[no].curX < swrData[no].lastX ) {
+
+ line->AddBord(swrData[no].curX, swrData[no].lastX, false);
+
+ } else if ( swrData[no].curX > swrData[no].lastX ) {
+
+ line->AddBord(swrData[no].lastX, swrData[no].curX, false);
+
+ }
+ }
+}
+
+
+void Shape::AvanceEdge(int no, float to, BitLigne *line, bool exact, float step)
+{
+ AvanceEdge(no, to, exact, step);
+
+ if ( swrData[no].sens ) {
+
+ if ( swrData[no].curX < swrData[no].lastX ) {
+
+ line->AddBord(swrData[no].curX, swrData[no].lastX, false);
+
+ } else if ( swrData[no].curX > swrData[no].lastX ) {
+
+ line->AddBord(swrData[no].lastX, swrData[no].curX, false);
+ }
+
+ } else {
+
+ if ( swrData[no].curX < swrData[no].lastX ) {
+
+ line->AddBord(swrData[no].curX, swrData[no].lastX, false);
+
+ } else if ( swrData[no].curX > swrData[no].lastX ) {
+
+ line->AddBord(swrData[no].lastX, swrData[no].curX, false);
+ }
+ }
+}
+
+
+void Shape::DestroyEdge(int no, AlphaLigne* line)
+{
+ if ( swrData[no].sens ) {
+
+ if ( swrData[no].curX <= swrData[no].lastX ) {
+
+ line->AddBord(swrData[no].curX,
+ 0,
+ swrData[no].lastX,
+ swrData[no].curY - swrData[no].lastY,
+ -swrData[no].dydx);
+
+ } else if ( swrData[no].curX > swrData[no].lastX ) {
+
+ line->AddBord(swrData[no].lastX,
+ 0,
+ swrData[no].curX,
+ swrData[no].curY - swrData[no].lastY,
+ swrData[no].dydx);
+ }
+
+ } else {
+
+ if ( swrData[no].curX <= swrData[no].lastX ) {
+
+ line->AddBord(swrData[no].curX,
+ 0,
+ swrData[no].lastX,
+ swrData[no].lastY - swrData[no].curY,
+ swrData[no].dydx);
+
+ } else if ( swrData[no].curX > swrData[no].lastX ) {
+
+ line->AddBord(swrData[no].lastX,
+ 0,
+ swrData[no].curX,
+ swrData[no].lastY - swrData[no].curY,
+ -swrData[no].dydx);
+ }
+ }
+}
+
+
+void Shape::AvanceEdge(int no, float to, AlphaLigne *line, bool exact, float step)
+{
+ AvanceEdge(no,to,exact,step);
+
+ if ( swrData[no].sens ) {
+
+ if ( swrData[no].curX <= swrData[no].lastX ) {
+
+ line->AddBord(swrData[no].curX,
+ 0,
+ swrData[no].lastX,
+ swrData[no].curY - swrData[no].lastY,
+ -swrData[no].dydx);
+
+ } else if ( swrData[no].curX > swrData[no].lastX ) {
+
+ line->AddBord(swrData[no].lastX,
+ 0,
+ swrData[no].curX,
+ swrData[no].curY - swrData[no].lastY,
+ swrData[no].dydx);
+ }
+
+ } else {
+
+ if ( swrData[no].curX <= swrData[no].lastX ) {
+
+ line->AddBord(swrData[no].curX,
+ 0,
+ swrData[no].lastX,
+ swrData[no].lastY - swrData[no].curY,
+ swrData[no].dydx);
+
+ } else if ( swrData[no].curX > swrData[no].lastX ) {
+
+ line->AddBord(swrData[no].lastX,
+ 0,
+ swrData[no].curX,
+ swrData[no].lastY - swrData[no].curY,
+ -swrData[no].dydx);
+ }
+ }
+}
+
+/**
+ * \param P point index.
+ * \param numberUp Filled in with the number of edges coming into P from above.
+ * \param numberDown Filled in with the number of edges coming exiting P to go below.
+ * \param upEdge One of the numberUp edges, or -1.
+ * \param downEdge One of the numberDown edges, or -1.
+ */
+
+void Shape::_countUpDown(int P, int *numberUp, int *numberDown, int *upEdge, int *downEdge) const
+{
+ *numberUp = 0;
+ *numberDown = 0;
+ *upEdge = -1;
+ *downEdge = -1;
+
+ int i = getPoint(P).incidentEdge[FIRST];
+
+ while ( i >= 0 && i < numberOfEdges() ) {
+ Shape::dg_arete const &e = getEdge(i);
+ if ( P == std::max(e.st, e.en) ) {
+ *upEdge = i;
+ (*numberUp)++;
+ }
+ if ( P == std::min(e.st, e.en) ) {
+ *downEdge = i;
+ (*numberDown)++;
+ }
+ i = NextAt(P, i);
+ }
+
+}
+
+
+
+/**
+ * Version of Shape::_countUpDown optimised for the case when getPoint(P).totalDegree() == 2.
+ */
+
+void Shape::_countUpDownTotalDegree2(int P,
+ int *numberUp, int *numberDown, int *upEdge, int *downEdge) const
+{
+ *numberUp = 0;
+ *numberDown = 0;
+ *upEdge = -1;
+ *downEdge = -1;
+
+ for (int j : getPoint(P).incidentEdge) {
+ Shape::dg_arete const &e = getEdge(j);
+ if ( P == std::max(e.st, e.en) ) {
+ *upEdge = j;
+ (*numberUp)++;
+ }
+ if ( P == std::min(e.st, e.en) ) {
+ *downEdge = j;
+ (*numberDown)++;
+ }
+ }
+}
+
+
+void Shape::_updateIntersection(int e, int p)
+{
+ swrData[e].lastX = swrData[e].curX;
+ swrData[e].lastY = swrData[e].curY;
+ swrData[e].curX = getPoint(p).x[0];
+ swrData[e].curY = getPoint(p).x[1];
+ swrData[e].misc = nullptr;
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/livarot/ShapeSweep.cpp b/src/livarot/ShapeSweep.cpp
new file mode 100644
index 0000000..3b5ed72
--- /dev/null
+++ b/src/livarot/ShapeSweep.cpp
@@ -0,0 +1,3319 @@
+// 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++)
+ {
+ swdData[i].leW = swdData[i].leW % 2;
+ if (swdData[i].leW < 0)
+ swdData[i].leW = -swdData[i].leW;
+ swdData[i].riW = swdData[i].riW;
+ 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;
+ }
+}
diff --git a/src/livarot/float-line.cpp b/src/livarot/float-line.cpp
new file mode 100644
index 0000000..9a19729
--- /dev/null
+++ b/src/livarot/float-line.cpp
@@ -0,0 +1,916 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * Implementation of coverage with floating-point values.
+ *//*
+ * Authors:
+ * see git history
+ * Fred
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifdef faster_flatten
+# include <cmath> // std::abs(float)
+#endif
+#include <cstdio>
+#include "livarot/float-line.h"
+#include "livarot/int-line.h"
+#include <cstdio>
+
+FloatLigne::FloatLigne()
+{
+ s_first = s_last = -1;
+}
+
+
+FloatLigne::~FloatLigne()
+= default;
+
+/// Reset the line to empty (boundaries and runs).
+void FloatLigne::Reset()
+{
+ bords.clear();
+ runs.clear();
+ s_first = s_last = -1;
+}
+
+/**
+ * Add a coverage portion.
+ *
+ * \param guess Position from where we should try to insert the first
+ * boundary, or -1 if we don't have a clue.
+ */
+int FloatLigne::AddBord(float spos, float sval, float epos, float eval, int guess)
+{
+// if ( showCopy ) printf("b= %f %f -> %f %f \n",spos,sval,epos,eval);
+ if ( spos >= epos ) {
+ return -1;
+ }
+
+ float pente = (eval - sval) / (epos - spos);
+
+#ifdef faster_flatten
+ if ( std::abs(epos - spos) < 0.001 || std::abs(pente) > 1000 ) {
+ return -1;
+ epos = spos;
+ pente = 0;
+ }
+#endif
+
+ if ( guess >= int(bords.size()) ) {
+ guess = -1;
+ }
+
+ // add the left boundary
+ float_ligne_bord b;
+ int n = bords.size();
+ b.pos = spos;
+ b.val = sval;
+ b.start = true;
+ b.other = n + 1;
+ b.pente = pente;
+ b.s_prev = b.s_next = -1;
+ bords.push_back(b);
+
+ // insert it in the doubly-linked list
+ InsertBord(n, spos, guess);
+
+ // add the right boundary
+ n = bords.size();
+ b.pos = epos;
+ b.val = eval;
+ b.start = false;
+ b.other = n-1;
+ b.pente = pente;
+ b.s_prev = b.s_next = -1;
+ bords.push_back(b);
+
+ // insert it in the doubly-linked list, knowing that boundary at index n-1 is not too far before me
+ InsertBord(n, epos, n - 1);
+
+ return n;
+}
+
+/**
+ * Add a coverage portion.
+ *
+ * \param guess Position from where we should try to insert the first
+ * boundary, or -1 if we don't have a clue.
+ */
+int FloatLigne::AddBord(float spos, float sval, float epos, float eval, float pente, int guess)
+{
+// if ( showCopy ) printf("b= %f %f -> %f %f \n",spos,sval,epos,eval);
+ if ( spos >= epos ) {
+ return -1;
+ }
+
+#ifdef faster_flatten
+ if ( std::abs(epos - spos) < 0.001 || std::abs(pente) > 1000 ) {
+ return -1;
+ epos = spos;
+ pente = 0;
+ }
+#endif
+
+ if ( guess >= int(bords.size()) ) {
+ guess=-1;
+ }
+
+ float_ligne_bord b;
+ int n = bords.size();
+ b.pos = spos;
+ b.val = sval;
+ b.start = true;
+ b.other = n + 1;
+ b.pente = pente;
+ b.s_prev = b.s_next = -1;
+ bords.push_back(b);
+
+ n = bords.size();
+ b.pos = epos;
+ b.val = eval;
+ b.start = false;
+ b.other = n - 1;
+ b.pente = pente;
+ b.s_prev = b.s_next = -1;
+ bords.push_back(b);
+
+ InsertBord(n - 1, spos, guess);
+ InsertBord(n, epos, n - 1);
+/* if ( bords[n-1].s_next < 0 ) {
+ bords[n].s_next=-1;
+ s_last=n;
+
+ bords[n].s_prev=n-1;
+ bords[n-1].s_next=n;
+ } else if ( bords[bords[n-1].s_next].pos >= epos ) {
+ bords[n].s_next=bords[n-1].s_next;
+ bords[bords[n].s_next].s_prev=n;
+
+ bords[n].s_prev=n-1;
+ bords[n-1].s_next=n;
+ } else {
+ int c=bords[bords[n-1].s_next].s_next;
+ while ( c >= 0 && bords[c].pos < epos ) c=bords[c].s_next;
+ if ( c < 0 ) {
+ bords[n].s_prev=s_last;
+ bords[s_last].s_next=n;
+ s_last=n;
+ } else {
+ bords[n].s_prev=bords[c].s_prev;
+ bords[bords[n].s_prev].s_next=n;
+
+ bords[n].s_next=c;
+ bords[c].s_prev=n;
+ }
+
+ }*/
+ return n;
+}
+
+/**
+ * Add a coverage portion.
+ *
+ * \param guess Position from where we should try to insert the last
+ * boundary, or -1 if we don't have a clue.
+ */
+int FloatLigne::AddBordR(float spos, float sval, float epos, float eval, float pente, int guess)
+{
+// if ( showCopy ) printf("br= %f %f -> %f %f \n",spos,sval,epos,eval);
+// return AddBord(spos,sval,epos,eval,pente,guess);
+ if ( spos >= epos ){
+ return -1;
+ }
+
+#ifdef faster_flatten
+ if ( std::abs(epos - spos) < 0.001 || std::abs(pente) > 1000 ) {
+ return -1;
+ epos = spos;
+ pente = 0;
+ }
+#endif
+
+ if ( guess >= int(bords.size()) ) {
+ guess=-1;
+ }
+
+ float_ligne_bord b;
+ int n = bords.size();
+ b.pos = spos;
+ b.val = sval;
+ b.start = true;
+ b.other = n + 1;
+ b.pente = pente;
+ b.s_prev = b.s_next = -1;
+ bords.push_back(b);
+
+ n = bords.size();
+ b.pos = epos;
+ b.val = eval;
+ b.start = false;
+ b.other = n - 1;
+ b.pente = pente;
+ b.s_prev = b.s_next = -1;
+ bords.push_back(b);
+
+ InsertBord(n, epos, guess);
+ InsertBord(n - 1, spos, n);
+
+/* if ( bords[n].s_prev < 0 ) {
+ bords[n-1].s_prev=-1;
+ s_first=n-1;
+
+ bords[n-1].s_next=n;
+ bords[n].s_prev=n-1;
+ } else if ( bords[bords[n].s_prev].pos <= spos ) {
+ bords[n-1].s_prev=bords[n].s_prev;
+ bords[bords[n-1].s_prev].s_next=n-1;
+
+ bords[n-1].s_next=n;
+ bords[n].s_prev=n-1;
+ } else {
+ int c=bords[bords[n].s_prev].s_prev;
+ while ( c >= 0 && bords[c].pos > spos ) c=bords[c].s_prev;
+ if ( c < 0 ) {
+ bords[n-1].s_next=s_first;
+ bords[s_first].s_prev=n-1;
+ s_first=n-1;
+ } else {
+ bords[n-1].s_next=bords[c].s_next;
+ bords[bords[n-1].s_next].s_prev=n-1;
+
+ bords[n-1].s_prev=c;
+ bords[c].s_next=n-1;
+ }
+
+ }*/
+ return n - 1;
+}
+
+/**
+ * Add a coverage portion by appending boundaries at the end of the list.
+ *
+ * This works because we know they are on the right.
+ */
+int FloatLigne::AppendBord(float spos, float sval, float epos, float eval, float pente)
+{
+// if ( showCopy ) printf("b= %f %f -> %f %f \n",spos,sval,epos,eval);
+// return AddBord(spos,sval,epos,eval,pente,s_last);
+ if ( spos >= epos ) {
+ return -1;
+ }
+
+#ifdef faster_flatten
+ if ( std::abs(epos - spos) < 0.001 || std::abs(pente) > 1000 ) {
+ return -1;
+ epos = spos;
+ pente = 0;
+ }
+#endif
+
+ int n = bords.size();
+ float_ligne_bord b;
+ b.pos = spos;
+ b.val = sval;
+ b.start = true;
+ b.other = n + 1;
+ b.pente = pente;
+ b.s_prev = s_last;
+ b.s_next = n + 1;
+ bords.push_back(b);
+
+ if ( s_last >= 0 ) {
+ bords[s_last].s_next = n;
+ }
+
+ if ( s_first < 0 ) {
+ s_first = n;
+ }
+
+ n = bords.size();
+ b.pos = epos;
+ b.val = eval;
+ b.start = false;
+ b.other = n - 1;
+ b.pente = pente;
+ b.s_prev = n - 1;
+ b.s_next = -1;
+ bords.push_back(b);
+
+ s_last = n;
+
+ return n;
+}
+
+
+
+// insertion in a boubly-linked list. nothing interesting here
+void FloatLigne::InsertBord(int no, float /*p*/, int guess)
+{
+// TODO check if ignoring p is bad
+ if ( no < 0 || no >= int(bords.size()) ) {
+ return;
+ }
+
+ if ( s_first < 0 ) {
+ s_first = s_last = no;
+ bords[no].s_prev = -1;
+ bords[no].s_next = -1;
+ return;
+ }
+
+ if ( guess < 0 || guess >= int(bords.size()) ) {
+ int c = s_first;
+ while ( c >= 0 && c < int(bords.size()) && CmpBord(bords[c], bords[no]) < 0 ) {
+ c = bords[c].s_next;
+ }
+
+ if ( c < 0 || c >= int(bords.size()) ) {
+ bords[no].s_prev = s_last;
+ bords[s_last].s_next = no;
+ s_last = no;
+ } else {
+ bords[no].s_prev = bords[c].s_prev;
+ if ( bords[no].s_prev >= 0 ) {
+ bords[bords[no].s_prev].s_next = no;
+ } else {
+ s_first = no;
+ }
+ bords[no].s_next = c;
+ bords[c].s_prev = no;
+ }
+ } else {
+ int c = guess;
+ int stTst = CmpBord(bords[c], bords[no]);
+
+ if ( stTst == 0 ) {
+
+ bords[no].s_prev = bords[c].s_prev;
+ if ( bords[no].s_prev >= 0 ) {
+ bords[bords[no].s_prev].s_next = no;
+ } else {
+ s_first = no;
+ }
+ bords[no].s_next = c;
+ bords[c].s_prev = no;
+
+ } else if ( stTst > 0 ) {
+
+ while ( c >= 0 && c < int(bords.size()) && CmpBord(bords[c], bords[no]) > 0 ) {
+ c = bords[c].s_prev;
+ }
+
+ if ( c < 0 || c >= int(bords.size()) ) {
+ bords[no].s_next = s_first;
+ bords[s_first].s_prev =no; // s_first != -1
+ s_first = no;
+ } else {
+ bords[no].s_next = bords[c].s_next;
+ if ( bords[no].s_next >= 0 ) {
+ bords[bords[no].s_next].s_prev = no;
+ } else {
+ s_last = no;
+ }
+ bords[no].s_prev = c;
+ bords[c].s_next = no;
+ }
+
+ } else {
+
+ while ( c >= 0 && c < int(bords.size()) && CmpBord(bords[c],bords[no]) < 0 ) {
+ c = bords[c].s_next;
+ }
+
+ if ( c < 0 || c >= int(bords.size()) ) {
+ bords[no].s_prev = s_last;
+ bords[s_last].s_next = no;
+ s_last = no;
+ } else {
+ bords[no].s_prev = bords[c].s_prev;
+ if ( bords[no].s_prev >= 0 ) {
+ bords[bords[no].s_prev].s_next = no;
+ } else {
+ s_first = no;
+ }
+ bords[no].s_next = c;
+ bords[c].s_prev = no;
+ }
+ }
+ }
+}
+
+/**
+ * Computes the sum of the coverages of the runs currently being scanned,
+ * of which there are "pending".
+ */
+float FloatLigne::RemainingValAt(float at, int pending)
+{
+ float sum = 0;
+/* int no=firstAc;
+ while ( no >= 0 && no < bords.size() ) {
+ int nn=bords[no].other;
+ sum+=bords[nn].val+(at-bords[nn].pos)*bords[nn].pente;
+// sum+=((at-bords[nn].pos)*bords[no].val+(bords[no].pos-at)*bords[nn].val)/(bords[no].pos-bords[nn].pos);
+// sum+=ValAt(at,bords[nn].pos,bords[no].pos,bords[nn].val,bords[no].val);
+ no=bords[no].next;
+ }*/
+ // for each portion being scanned, compute coverage at position "at" and sum.
+ // we could simply compute the sum of portion coverages as a "f(x)=ux+y" and evaluate it at "x=at",
+ // but there are numerical problems with this approach, and it produces ugly lines of incorrectly
+ // computed alpha values, so i reverted to this "safe but slow" version
+
+ for (int i=0; i < pending; i++) {
+ int const nn = bords[i].pend_ind;
+ sum += bords[nn].val + (at - bords[nn].pos) * bords[nn].pente;
+ }
+
+ return sum;
+}
+
+
+/**
+ * Extract a set of non-overlapping runs from the boundaries.
+ *
+ * We scan the boundaries left to right, maintaining a set of coverage
+ * portions currently being scanned. For each such portion, the function
+ * will add the index of its first boundary in an array; but instead of
+ * allocating another array, it uses a field in float_ligne_bord: pend_ind.
+ * The outcome is that an array of float_ligne_run is produced.
+ */
+void FloatLigne::Flatten()
+{
+ if ( int(bords.size()) <= 1 ) {
+ Reset();
+ return;
+ }
+
+ runs.clear();
+
+// qsort(bords,bords.size(),sizeof(float_ligne_bord),FloatLigne::CmpBord);
+// SortBords(0,bords.size()-1);
+
+ float totPente = 0;
+ float totStart = 0;
+ float totX = bords[0].pos;
+
+ bool startExists = false;
+ float lastStart = 0;
+ float lastVal = 0;
+ int pending = 0;
+
+// for (int i=0;i<bords.size();) {
+ // read the list from left to right, adding a run for each boundary crossed, minus runs with alpha=0
+ for (int i=/*0*/s_first; i>=0 && i < int(bords.size()) ;) {
+
+ float cur = bords[i].pos; // position of the current boundary (there may be several boundaries at this position)
+ float leftV = 0; // deltas in coverage value at this position
+ float rightV = 0;
+ float leftP = 0; // deltas in coverage increase per unit length at this position
+ float rightP = 0;
+
+ // more precisely, leftV is the sum of decreases of coverage value,
+ // while rightV is the sum of increases, so that leftV+rightV is the delta.
+ // idem for leftP and rightP
+
+ // start by scanning all boundaries that end a portion at this position
+ while ( i >= 0 && i < int(bords.size()) && bords[i].pos == cur && bords[i].start == false ) {
+ leftV += bords[i].val;
+ leftP += bords[i].pente;
+
+#ifndef faster_flatten
+ // we need to remove the boundary that started this coverage portion for the pending list
+ if ( bords[i].other >= 0 && bords[i].other < int(bords.size()) ) {
+ // so we use the pend_inv "array"
+ int const k = bords[bords[i].other].pend_inv;
+ if ( k >= 0 && k < pending ) {
+ // and update the pend_ind array and its inverse pend_inv
+ bords[k].pend_ind = bords[pending - 1].pend_ind;
+ bords[bords[k].pend_ind].pend_inv = k;
+ }
+ }
+#endif
+
+ // one less portion pending
+ pending--;
+ // and we move to the next boundary in the doubly linked list
+ i=bords[i].s_next;
+ //i++;
+ }
+
+ // then scan all boundaries that start a portion at this position
+ while ( i >= 0 && i < int(bords.size()) && bords[i].pos == cur && bords[i].start ) {
+ rightV += bords[i].val;
+ rightP += bords[i].pente;
+#ifndef faster_flatten
+ bords[pending].pend_ind=i;
+ bords[i].pend_inv=pending;
+#endif
+ pending++;
+ i = bords[i].s_next;
+ //i++;
+ }
+
+ // coverage value at end of the run will be "start coverage"+"delta per unit length"*"length"
+ totStart = totStart + totPente * (cur - totX);
+
+ if ( startExists ) {
+ // add that run
+ AddRun(lastStart, cur, lastVal, totStart, totPente);
+ }
+ // update "delta coverage per unit length"
+ totPente += rightP - leftP;
+ // not really needed here
+ totStart += rightV - leftV;
+ // update position
+ totX = cur;
+ if ( pending > 0 ) {
+ startExists = true;
+
+#ifndef faster_flatten
+ // to avoid accumulation of numerical errors, we compute an accurate coverage for this position "cur"
+ totStart = RemainingValAt(cur, pending);
+#endif
+ lastVal = totStart;
+ lastStart = cur;
+ } else {
+ startExists = false;
+ totStart = 0;
+ totPente = 0;
+ }
+ }
+}
+
+
+/// Debug dump of the instance.
+void FloatLigne::Affiche()
+{
+ printf("%lu : \n", (long unsigned int) bords.size());
+ for (auto & bord : bords) {
+ printf("(%f %f %f %i) ",bord.pos,bord.val,bord.pente,(bord.start?1:0)); // localization ok
+ }
+
+ printf("\n");
+ printf("%lu : \n", (long unsigned int) runs.size());
+
+ for (auto & run : runs) {
+ printf("(%f %f -> %f %f / %f)",
+ run.st, run.vst, run.en, run.ven, run.pente); // localization ok
+ }
+
+ printf("\n");
+}
+
+
+int FloatLigne::AddRun(float st, float en, float vst, float ven)
+{
+ return AddRun(st, en, vst, ven, (ven - vst) / (en - st));
+}
+
+
+int FloatLigne::AddRun(float st, float en, float vst, float ven, float pente)
+{
+ if ( st >= en ) {
+ return -1;
+ }
+
+ int const n = runs.size();
+ float_ligne_run r;
+ r.st = st;
+ r.en = en;
+ r.vst = vst;
+ r.ven = ven;
+ r.pente = pente;
+ runs.push_back(r);
+
+ return n;
+}
+
+void FloatLigne::Copy(FloatLigne *a)
+{
+ if ( a->runs.empty() ) {
+ Reset();
+ return;
+ }
+
+ bords.clear();
+ runs = a->runs;
+}
+
+void FloatLigne::Copy(IntLigne *a)
+{
+ if ( a->nbRun ) {
+ Reset();
+ return;
+ }
+
+ bords.clear();
+ runs.resize(a->nbRun);
+
+ for (int i = 0; i < int(runs.size()); i++) {
+ runs[i].st = a->runs[i].st;
+ runs[i].en = a->runs[i].en;
+ runs[i].vst = a->runs[i].vst;
+ runs[i].ven = a->runs[i].ven;
+ }
+}
+
+/// Cuts the parts having less than tresh coverage.
+void FloatLigne::Min(FloatLigne *a, float tresh, bool addIt)
+{
+ Reset();
+ if ( a->runs.empty() ) {
+ return;
+ }
+
+ bool startExists = false;
+ float lastStart=0;
+ float lastEnd = 0;
+
+ for (auto runA : a->runs) {
+ if ( runA.vst <= tresh ) {
+ if ( runA.ven <= tresh ) {
+ if ( startExists ) {
+ if ( lastEnd >= runA.st - 0.00001 ) {
+ lastEnd = runA.en;
+ } else {
+ if ( addIt ) {
+ AddRun(lastStart, lastEnd, tresh, tresh);
+ }
+ lastStart = runA.st;
+ lastEnd = runA.en;
+ }
+ } else {
+ lastStart = runA.st;
+ lastEnd = runA.en;
+ }
+ startExists = true;
+ } else {
+ float cutPos = (runA.st * (tresh - runA.ven) + runA.en * (runA.vst - tresh)) / (runA.vst - runA.ven);
+ if ( startExists ) {
+ if ( lastEnd >= runA.st - 0.00001 ) {
+ if ( addIt ) {
+ AddRun(lastStart, cutPos, tresh, tresh);
+ }
+ AddRun(cutPos,runA.en, tresh, runA.ven);
+ } else {
+ if ( addIt ) {
+ AddRun(lastStart, lastEnd, tresh, tresh);
+ }
+ if ( addIt ) {
+ AddRun(runA.st, cutPos, tresh, tresh);
+ }
+ AddRun(cutPos, runA.en, tresh, runA.ven);
+ }
+ } else {
+ if ( addIt ) {
+ AddRun(runA.st, cutPos, tresh, tresh);
+ }
+ AddRun(cutPos, runA.en, tresh, runA.ven);
+ }
+ startExists = false;
+ }
+
+ } else {
+
+ if ( runA.ven <= tresh ) {
+ float cutPos = (runA.st * (runA.ven - tresh) + runA.en * (tresh - runA.vst)) / (runA.ven - runA.vst);
+ if ( startExists ) {
+ if ( addIt ) {
+ AddRun(lastStart, lastEnd, tresh, tresh);
+ }
+ }
+ AddRun(runA.st, cutPos, runA.vst, tresh);
+ startExists = true;
+ lastStart = cutPos;
+ lastEnd = runA.en;
+ } else {
+ if ( startExists ) {
+ if ( addIt ) {
+ AddRun(lastStart, lastEnd, tresh, tresh);
+ }
+ }
+ startExists = false;
+ AddRun(runA.st, runA.en, runA.vst, runA.ven);
+ }
+ }
+ }
+
+ if ( startExists ) {
+ if ( addIt ) {
+ AddRun(lastStart, lastEnd, tresh, tresh);
+ }
+ }
+}
+
+/**
+ * Cuts the coverage a in 2 parts.
+ *
+ * over will receive the parts where coverage > tresh, while the present
+ * FloatLigne will receive the parts where coverage <= tresh.
+ */
+void FloatLigne::Split(FloatLigne *a, float tresh, FloatLigne *over)
+{
+ Reset();
+ if ( a->runs.empty() ) {
+ return;
+ }
+
+ for (auto runA : a->runs) {
+ if ( runA.vst >= tresh ) {
+ if ( runA.ven >= tresh ) {
+ if ( over ) {
+ over->AddRun(runA.st, runA.en, runA.vst, runA.ven);
+ }
+ } else {
+ float cutPos = (runA.st * (tresh - runA.ven) + runA.en * (runA.vst - tresh)) / (runA.vst - runA.ven);
+ if ( over ) {
+ over->AddRun(runA.st, cutPos, runA.vst, tresh);
+ }
+ AddRun(cutPos, runA.en, tresh, runA.ven);
+ }
+ } else {
+ if ( runA.ven >= tresh ) {
+ float cutPos = (runA.st * (runA.ven - tresh) + runA.en * (tresh-runA.vst)) / (runA.ven - runA.vst);
+ AddRun(runA.st, cutPos, runA.vst, tresh);
+ if ( over ) {
+ over->AddRun(cutPos, runA.en, tresh, runA.ven);
+ }
+ } else {
+ AddRun(runA.st, runA.en, runA.vst, runA.ven);
+ }
+ }
+ }
+}
+
+/**
+ * Clips the coverage runs to tresh.
+ *
+ * If addIt is false, it only leaves the parts that are not entirely under
+ * tresh. If addIt is true, it's the coverage clamped to tresh.
+ */
+void FloatLigne::Max(FloatLigne *a, float tresh, bool addIt)
+{
+ Reset();
+ if ( a->runs.empty() <= 0 ) {
+ return;
+ }
+
+ bool startExists = false;
+ float lastStart = 0;
+ float lastEnd = 0;
+ for (auto runA : a->runs) {
+ if ( runA.vst >= tresh ) {
+ if ( runA.ven >= tresh ) {
+ if ( startExists ) {
+ if ( lastEnd >= runA.st-0.00001 ) {
+ lastEnd = runA.en;
+ } else {
+ if ( addIt ) {
+ AddRun(lastStart,lastEnd,tresh,tresh);
+ }
+ lastStart = runA.st;
+ lastEnd = runA.en;
+ }
+ } else {
+ lastStart = runA.st;
+ lastEnd = runA.en;
+ }
+ startExists = true;
+ } else {
+ float cutPos = (runA.st * (tresh - runA.ven) + runA.en * (runA.vst - tresh)) / (runA.vst - runA.ven);
+ if ( startExists ) {
+ if ( lastEnd >= runA.st-0.00001 ) {
+ if ( addIt ) {
+ AddRun(lastStart, cutPos, tresh, tresh);
+ }
+ AddRun(cutPos, runA.en, tresh, runA.ven);
+ } else {
+ if ( addIt ) {
+ AddRun(lastStart, lastEnd, tresh, tresh);
+ }
+ if ( addIt ) {
+ AddRun(runA.st, cutPos, tresh, tresh);
+ }
+ AddRun(cutPos, runA.en, tresh, runA.ven);
+ }
+ } else {
+ if ( addIt ) {
+ AddRun(runA.st, cutPos, tresh, tresh);
+ }
+ AddRun(cutPos, runA.en, tresh, runA.ven);
+ }
+ startExists = false;
+ }
+
+ } else {
+
+ if ( runA.ven >= tresh ) {
+ float cutPos = (runA.st * (runA.ven - tresh) + runA.en * (tresh - runA.vst)) / (runA.ven - runA.vst);
+ if ( startExists ) {
+ if ( addIt ) {
+ AddRun(lastStart,lastEnd,tresh,tresh);
+ }
+ }
+ AddRun(runA.st, cutPos, runA.vst, tresh);
+ startExists = true;
+ lastStart = cutPos;
+ lastEnd = runA.en;
+ } else {
+ if ( startExists ) {
+ if ( addIt ) {
+ AddRun(lastStart,lastEnd,tresh,tresh);
+ }
+ }
+ startExists = false;
+ AddRun(runA.st, runA.en, runA.vst, runA.ven);
+ }
+ }
+ }
+
+ if ( startExists ) {
+ if ( addIt ) {
+ AddRun(lastStart, lastEnd, tresh, tresh);
+ }
+ }
+}
+
+/// Extract the parts where coverage > tresh.
+void FloatLigne::Over(FloatLigne *a, float tresh)
+{
+ Reset();
+ if ( a->runs.empty() ) {
+ return;
+ }
+
+ bool startExists = false;
+ float lastStart = 0;
+ float lastEnd = 0;
+
+ for (auto runA : a->runs) {
+ if ( runA.vst >= tresh ) {
+ if ( runA.ven >= tresh ) {
+ if ( startExists ) {
+ if ( lastEnd >= runA.st - 0.00001 ) {
+ lastEnd = runA.en;
+ } else {
+ AddRun(lastStart, lastEnd, tresh, tresh);
+ lastStart = runA.st;
+ lastEnd = runA.en;
+ }
+ } else {
+ lastStart = runA.st;
+ lastEnd = runA.en;
+ }
+ startExists = true;
+
+ } else {
+
+ float cutPos = (runA.st * (tresh - runA.ven) + runA.en * (runA.vst - tresh)) / (runA.vst - runA.ven);
+ if ( startExists ) {
+ if ( lastEnd >= runA.st - 0.00001 ) {
+ AddRun(lastStart, cutPos, tresh, tresh);
+ } else {
+ AddRun(lastStart, lastEnd, tresh, tresh);
+ AddRun(runA.st, cutPos, tresh, tresh);
+ }
+ } else {
+ AddRun(runA.st, cutPos, tresh, tresh);
+ }
+ startExists = false;
+ }
+
+ } else {
+ if ( runA.ven >= tresh ) {
+ float cutPos = (runA.st * (runA.ven - tresh) + runA.en * (tresh - runA.vst)) / (runA.ven - runA.vst);
+ if ( startExists ) {
+ AddRun(lastStart, lastEnd, tresh, tresh);
+ }
+ startExists = true;
+ lastStart = cutPos;
+ lastEnd = runA.en;
+ } else {
+ if ( startExists ) {
+ AddRun(lastStart, lastEnd, tresh, tresh);
+ }
+ startExists = false;
+ }
+ }
+ }
+
+ if ( startExists ) {
+ AddRun(lastStart, lastEnd, tresh, tresh);
+ }
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/livarot/float-line.h b/src/livarot/float-line.h
new file mode 100644
index 0000000..8d7faf1
--- /dev/null
+++ b/src/livarot/float-line.h
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2010 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#ifndef INKSCAPE_LIVAROT_FLOAT_LINE_H
+#define INKSCAPE_LIVAROT_FLOAT_LINE_H
+
+/** \file
+ * Coverage with floating-point boundaries.
+ */
+
+#include <vector>
+#include "livarot/LivarotDefs.h"
+
+class IntLigne;
+class BitLigne;
+
+/// A coverage portion ("run") with floating point boundaries.
+struct float_ligne_run {
+ float st;
+ float en;
+ float vst;
+ float ven;
+ float pente; ///< (ven-vst)/(en-st)
+};
+
+/**
+ * A floating-point boundary.
+ *
+ * Each float_ligne_bord is a boundary of some coverage.
+ * The Flatten() function will extract non-overlapping runs and produce an
+ * array of float_ligne_run. The float_ligne_bord are stored in an array, but
+ * linked like a doubly-linked list.
+ *
+ * The idea behind that is that a given edge produces one float_ligne_bord at
+ * the beginning of Scan() and possibly another in AvanceEdge() and
+ * DestroyEdge(); but that second float_ligne_bord will not be far away in
+ * the list from the first, so it's faster to salvage the index of the first
+ * float_ligne_bord and try to insert the second from that salvaged position.
+ */
+struct float_ligne_bord {
+ float pos; ///< position of the boundary
+ bool start; ///< is the beginning of the coverage portion?
+ float val; ///< amount of coverage (ie vst if start==true, and ven if start==false)
+ float pente; ///< (ven-vst)/(en-st)
+ int other; ///< index, in the array of float_ligne_bord, of the other boundary associated to this one
+ int s_prev; ///< index of the previous bord in the doubly-linked list
+ int s_next; ///< index of the next bord in the doubly-linked list
+ int pend_ind; ///< bords[i].pend_ind is the index of the float_ligne_bord that is the start of the
+ ///< coverage portion being scanned (in the Flatten() )
+ int pend_inv; ///< inverse of pend_ind, for faster handling of insertion/removal in the "pending" array
+};
+
+/**
+ * Coverage with floating-point boundaries.
+ *
+ * The goal is to salvage exact coverage info in the sweepline performed by
+ * Scan() or QuickScan(), then clean up a bit, convert floating point bounds
+ * to integer bounds, because pixel have integer bounds, and then raster runs
+ * of the type:
+ * \verbatim
+ position on the (pixel) line: st en
+ | |
+ coverage value (0=empty, 1=full) vst -> ven \endverbatim
+ */
+class FloatLigne {
+public:
+ std::vector<float_ligne_bord> bords; ///< vector of coverage boundaries
+ std::vector<float_ligne_run> runs; ///< vector of runs
+
+ /// first boundary in the doubly-linked list
+ int s_first;
+ /// last boundary in the doubly-linked list
+ int s_last;
+
+ FloatLigne();
+ virtual ~FloatLigne();
+
+ void Reset();
+
+ int AddBord(float spos, float sval, float epos, float eval, int guess = -1);
+ int AddBord(float spos, float sval, float epos, float eval, float pente, int guess = -1);
+ int AddBordR(float spos, float sval, float epos, float eval, float pente, int guess = -1);
+ int AppendBord(float spos, float sval, float epos, float eval, float pente);
+
+ void Flatten();
+
+ void Affiche();
+
+ void Max(FloatLigne *a, float tresh, bool addIt);
+
+ void Min(FloatLigne *a, float tresh, bool addIt);
+
+ void Split(FloatLigne *a, float tresh, FloatLigne *over);
+
+ void Over(FloatLigne *a, float tresh);
+
+ void Copy(IntLigne *a);
+ void Copy(FloatLigne *a);
+
+ float RemainingValAt(float at, int pending);
+
+ static int CmpBord(float_ligne_bord const &d1, float_ligne_bord const &d2) {
+ if ( d1.pos == d2.pos ) {
+ if ( d1.start && !(d2.start) ) {
+ return 1;
+ }
+ if ( !(d1.start) && d2.start ) {
+ return -1;
+ }
+ return 0;
+ }
+
+ return (( d1.pos < d2.pos ) ? -1 : 1);
+ };
+
+ int AddRun(float st, float en, float vst, float ven, float pente);
+
+private:
+ void InsertBord(int no, float p, int guess);
+ int AddRun(float st, float en, float vst, float ven);
+
+ inline float ValAt(float at, float ps, float pe, float vs, float ve) {
+ return ((at - ps) * ve + (pe - at) * vs) / (pe - ps);
+ };
+};
+
+#endif
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/livarot/int-line.cpp b/src/livarot/int-line.cpp
new file mode 100644
index 0000000..ff87475
--- /dev/null
+++ b/src/livarot/int-line.cpp
@@ -0,0 +1,1071 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * Implementation of coverage with integer boundaries.
+ *//*
+ * Authors:
+ * see git history
+ * Fred
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <glib.h>
+#include <cmath>
+#include <cstring>
+#include <string>
+#include <cstdlib>
+#include <cstdio>
+#include "livarot/int-line.h"
+#include "livarot/float-line.h"
+#include "livarot/BitLigne.h"
+
+IntLigne::IntLigne()
+{
+ nbBord = maxBord = 0;
+ bords = nullptr;
+
+ nbRun = maxRun = 0;
+ runs = nullptr;
+
+ firstAc = lastAc = -1;
+}
+
+
+IntLigne::~IntLigne()
+{
+ if ( maxBord > 0 ) {
+ g_free(bords);
+ nbBord = maxBord = 0;
+ bords = nullptr;
+ }
+ if ( maxRun > 0 ) {
+ g_free(runs);
+ nbRun = maxRun = 0;
+ runs = nullptr;
+ }
+}
+
+void IntLigne::Reset()
+{
+ nbBord = 0;
+ nbRun = 0;
+ firstAc = lastAc = -1;
+}
+
+int IntLigne::AddBord(int spos, float sval, int epos, float eval)
+{
+ if ( nbBord + 1 >= maxBord ) {
+ maxBord = 2 * nbBord + 2;
+ bords = (int_ligne_bord *) g_realloc(bords, maxBord * sizeof(int_ligne_bord));
+
+ }
+
+ int n = nbBord++;
+ bords[n].pos = spos;
+ bords[n].val = sval;
+ bords[n].start = true;
+ bords[n].other = n+1;
+ bords[n].prev = bords[n].next = -1;
+
+ n = nbBord++;
+ bords[n].pos = epos;
+ bords[n].val = eval;
+ bords[n].start = false;
+ bords[n].other = n-1;
+ bords[n].prev = bords[n].next = -1;
+
+ return n - 1;
+}
+
+
+float IntLigne::RemainingValAt(int at)
+{
+ int no = firstAc;
+ float sum = 0;
+ while ( no >= 0 ) {
+ int nn = bords[no].other;
+ sum += ValAt(at, bords[nn].pos, bords[no].pos, bords[nn].val, bords[no].val);
+ no = bords[no].next;
+ }
+ return sum;
+}
+
+void IntLigne::Flatten()
+{
+ if ( nbBord <= 1 ) {
+ Reset();
+ return;
+ }
+
+ nbRun = 0;
+ firstAc = lastAc = -1;
+
+ for (int i = 0; i < nbBord; i++) {
+ bords[i].prev = i;
+ }
+
+ qsort(bords, nbBord, sizeof(int_ligne_bord), IntLigne::CmpBord);
+ for (int i = 0; i < nbBord; i++) {
+ bords[bords[i].prev].next = i;
+ }
+
+ for (int i = 0; i < nbBord; i++) {
+ bords[i].other = bords[bords[i].other].next;
+ }
+
+ int lastStart = 0;
+ float lastVal = 0;
+ bool startExists = false;
+
+ for (int i = 0; i < nbBord; ) {
+ int cur = bords[i].pos;
+ float leftV = 0;
+ float rightV = 0;
+ float midV = 0;
+ while ( i < nbBord && bords[i].pos == cur && bords[i].start == false ) {
+ Dequeue(i);
+ leftV += bords[i].val;
+ i++;
+ }
+ midV = RemainingValAt(cur);
+ while ( i < nbBord && bords[i].pos == cur && bords[i].start ) {
+ rightV += bords[i].val;
+ Enqueue(bords[i].other);
+ i++;
+ }
+
+ if ( startExists ) {
+ AddRun(lastStart, cur, lastVal, leftV + midV);
+ }
+ if ( firstAc >= 0 ) {
+ startExists = true;
+ lastVal = midV + rightV;
+ lastStart = cur;
+ } else {
+ startExists = false;
+ }
+ }
+}
+
+
+void IntLigne::Affiche()
+{
+ printf("%i : \n", nbRun);
+ for (int i = 0; i < nbRun;i++) {
+ printf("(%i %f -> %i %f) ", runs[i].st, runs[i].vst, runs[i].en, runs[i].ven); // localization ok
+ }
+ printf("\n");
+}
+
+int IntLigne::AddRun(int st, int en, float vst, float ven)
+{
+ if ( st >= en ) {
+ return -1;
+ }
+
+ if ( nbRun >= maxRun ) {
+ maxRun = 2 * nbRun + 1;
+ runs = (int_ligne_run *) g_realloc(runs, maxRun * sizeof(int_ligne_run));
+ }
+
+ int n = nbRun++;
+ runs[n].st = st;
+ runs[n].en = en;
+ runs[n].vst = vst;
+ runs[n].ven = ven;
+ return n;
+}
+
+void IntLigne::Booleen(IntLigne *a, IntLigne *b, BooleanOp mod)
+{
+ Reset();
+ if ( a->nbRun <= 0 && b->nbRun <= 0 ) {
+ return;
+ }
+
+ if ( a->nbRun <= 0 ) {
+ if ( mod == bool_op_union || mod == bool_op_symdiff ) {
+ Copy(b);
+ }
+ return;
+ }
+
+ if ( b->nbRun <= 0 ) {
+ if ( mod == bool_op_union || mod == bool_op_diff || mod == bool_op_symdiff ) {
+ Copy(a);
+ }
+ return;
+ }
+
+ int curA = 0;
+ int curB = 0;
+ int curPos = (a->runs[0].st < b->runs[0].st) ? a->runs[0].st : b->runs[0].st;
+ int nextPos = curPos;
+ float valA = 0;
+ float valB = 0;
+ if ( curPos == a->runs[0].st ) {
+ valA = a->runs[0].vst;
+ }
+ if ( curPos == b->runs[0].st ) {
+ valB = b->runs[0].vst;
+ }
+
+ while ( curA < a->nbRun && curB < b->nbRun ) {
+ int_ligne_run runA = a->runs[curA];
+ int_ligne_run runB = b->runs[curB];
+ const bool inA = ( curPos >= runA.st && curPos < runA.en );
+ const bool inB = ( curPos >= runB.st && curPos < runB.en );
+
+ bool startA = false;
+ bool startB = false;
+ bool endA = false;
+ bool endB = false;
+
+ if ( curPos < runA.st ) {
+ if ( curPos < runB.st ) {
+ startA = runA.st <= runB.st;
+ startB = runA.st >= runB.st;
+ nextPos = startA ? runA.st : runB.st;
+ } else if ( curPos >= runB.st ) {
+ startA = runA.st <= runB.en;
+ endB = runA.st >= runB.en;
+ nextPos = startA ? runA.st : runB.en;
+ }
+ } else if ( curPos == runA.st ) {
+ if ( curPos < runB.st ) {
+ endA = runA.en <= runB.st;
+ startB = runA.en >= runB.st;
+ nextPos = startB ? runB.en : runA.st;
+ } else if ( curPos == runB.st ) {
+ endA = runA.en <= runB.en;
+ endB = runA.en >= runB.en;
+ nextPos = endA? runA.en : runB.en;
+ } else {
+ endA = runA.en <= runB.en;
+ endB = runA.en >= runB.en;
+ nextPos = endA ? runA.en : runB.en;
+ }
+ } else {
+ if ( curPos < runB.st ) {
+ endA = runA.en <= runB.st;
+ startB = runA.en >= runB.st;
+ nextPos = startB ? runB.st : runA.en;
+ } else if ( curPos == runB.st ) {
+ endA = runA.en <= runB.en;
+ endB = runA.en >= runB.en;
+ nextPos = endA ? runA.en : runB.en;
+ } else {
+ endA = runA.en <= runB.en;
+ endB = runA.en >= runB.en;
+ nextPos = endA ? runA.en : runB.en;
+ }
+ }
+
+ float oValA = valA;
+ float oValB = valB;
+ valA = inA ? ValAt(nextPos, runA.st, runA.en, runA.vst, runA.ven) : 0;
+ valB = inB ? ValAt(nextPos, runB.st, runB.en, runB.vst, runB.ven) : 0;
+
+ if ( mod == bool_op_union ) {
+
+ if ( inA || inB ) {
+ AddRun(curPos, nextPos, oValA + oValB, valA + valB);
+ }
+
+ } else if ( mod == bool_op_inters ) {
+
+ if ( inA && inB ) {
+ AddRun(curPos, nextPos, oValA * oValB, valA * valB);
+ }
+
+ } else if ( mod == bool_op_diff ) {
+
+ if ( inA ) {
+ AddRun(curPos, nextPos, oValA - oValB, valA - valB);
+ }
+
+ } else if ( mod == bool_op_symdiff ) {
+ if ( inA && !(inB) ) {
+ AddRun(curPos, nextPos, oValA - oValB, valA - valB);
+ }
+ if ( !(inA) && inB ) {
+ AddRun(curPos, nextPos, oValB - oValA, valB - valA);
+ }
+ }
+
+ curPos = nextPos;
+ if ( startA ) {
+ // inA=true; these are never used
+ valA = runA.vst;
+ }
+ if ( startB ) {
+ //inB=true;
+ valB = runB.vst;
+ }
+ if ( endA ) {
+ //inA=false;
+ valA = 0;
+ curA++;
+ if ( curA < a->nbRun && a->runs[curA].st == curPos ) {
+ valA = a->runs[curA].vst;
+ }
+ }
+ if ( endB ) {
+ //inB=false;
+ valB = 0;
+ curB++;
+ if ( curB < b->nbRun && b->runs[curB].st == curPos ) {
+ valB = b->runs[curB].vst;
+ }
+ }
+ }
+
+ while ( curA < a->nbRun ) {
+ int_ligne_run runA = a->runs[curA];
+ const bool inA = ( curPos >= runA.st && curPos < runA.en );
+ const bool inB = false;
+
+ bool startA = false;
+ bool endA = false;
+ if ( curPos < runA.st ) {
+ nextPos = runA.st;
+ startA = true;
+ } else if ( curPos >= runA.st ) {
+ nextPos = runA.en;
+ endA = true;
+ }
+
+ float oValA = valA;
+ float oValB = valB;
+ valA = inA ? ValAt(nextPos,runA.st, runA.en, runA.vst, runA.ven) : 0;
+ valB = 0;
+
+ if ( mod == bool_op_union ) {
+ if ( inA || inB ) {
+ AddRun(curPos, nextPos, oValA + oValB, valA + valB);
+ }
+ } else if ( mod == bool_op_inters ) {
+ if ( inA && inB ) {
+ AddRun(curPos, nextPos, oValA * oValB, valA * valB);
+ }
+ } else if ( mod == bool_op_diff ) {
+ if ( inA ) {
+ AddRun(curPos, nextPos, oValA - oValB, valA - valB);
+ }
+ } else if ( mod == bool_op_symdiff ) {
+ if ( inA && !(inB) ) {
+ AddRun(curPos, nextPos, oValA - oValB, valA - valB);
+ }
+ if ( !(inA) && inB ) {
+ AddRun(curPos,nextPos,oValB-oValA,valB-valA);
+ }
+ }
+
+ curPos = nextPos;
+ if ( startA ) {
+ //inA=true;
+ valA = runA.vst;
+ }
+ if ( endA ) {
+ //inA=false;
+ valA = 0;
+ curA++;
+ if ( curA < a->nbRun && a->runs[curA].st == curPos ) {
+ valA = a->runs[curA].vst;
+ }
+ }
+ }
+
+ while ( curB < b->nbRun ) {
+ int_ligne_run runB = b->runs[curB];
+ const bool inB = ( curPos >= runB.st && curPos < runB.en );
+ const bool inA = false;
+
+ bool startB = false;
+ bool endB = false;
+ if ( curPos < runB.st ) {
+ nextPos = runB.st;
+ startB = true;
+ } else if ( curPos >= runB.st ) {
+ nextPos = runB.en;
+ endB = true;
+ }
+
+ float oValA = valA;
+ float oValB = valB;
+ valB = inB ? ValAt(nextPos, runB.st, runB.en, runB.vst, runB.ven) : 0;
+ valA = 0;
+
+ if ( mod == bool_op_union ) {
+ if ( inA || inB ) {
+ AddRun(curPos, nextPos, oValA + oValB,valA + valB);
+ }
+ } else if ( mod == bool_op_inters ) {
+ if ( inA && inB ) {
+ AddRun(curPos, nextPos, oValA * oValB, valA * valB);
+ }
+ } else if ( mod == bool_op_diff ) {
+ if ( inA ) {
+ AddRun(curPos, nextPos, oValA - oValB, valA - valB);
+ }
+ } else if ( mod == bool_op_symdiff ) {
+ if ( inA && !(inB) ) {
+ AddRun(curPos, nextPos, oValA - oValB,valA - valB);
+ }
+ if ( !(inA) && inB ) {
+ AddRun(curPos, nextPos, oValB - oValA, valB - valA);
+ }
+ }
+
+ curPos = nextPos;
+ if ( startB ) {
+ //inB=true;
+ valB = runB.vst;
+ }
+ if ( endB ) {
+ //inB=false;
+ valB = 0;
+ curB++;
+ if ( curB < b->nbRun && b->runs[curB].st == curPos ) {
+ valB = b->runs[curB].vst;
+ }
+ }
+ }
+}
+
+/**
+ * Transform a line of bits into pixel coverage values.
+ *
+ * This is where you go from supersampled data to alpha values.
+ * \see IntLigne::Copy(int nbSub,BitLigne* *a).
+ */
+void IntLigne::Copy(BitLigne* a)
+{
+ if ( a->curMax <= a->curMin ) {
+ Reset();
+ return;
+ }
+
+ if ( a->curMin < a->st ) {
+ a->curMin = a->st;
+ }
+
+ if ( a->curMax < a->st ) {
+ Reset();
+ return;
+ }
+
+ if ( a->curMin > a->en ) {
+ Reset();
+ return;
+ }
+
+ if ( a->curMax > a->en ) {
+ a->curMax=a->en;
+ }
+
+ nbBord = 0;
+ nbRun = 0;
+
+ int lastVal = 0;
+ int lastStart = 0;
+ bool startExists = false;
+
+ int masks[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
+
+ uint32_t c_full = a->fullB[(a->curMin-a->st) >> 3];
+ uint32_t c_part = a->partB[(a->curMin-a->st) >> 3];
+ c_full <<= 4 * ((a->curMin - a->st) & 0x00000007);
+ c_part <<= 4 * ((a->curMin - a->st) & 0x00000007);
+ for (int i = a->curMin; i <= a->curMax; i++) {
+ int nbBit = masks[c_full >> 28] + masks[c_part >> 28];
+
+ if ( nbBit > 0 ) {
+ if ( startExists ) {
+ if ( lastVal == nbBit ) {
+ // on continue le run
+ } else {
+ AddRun(lastStart, i, ((float) lastVal) / 4, ((float) lastVal) / 4);
+ lastStart = i;
+ lastVal = nbBit;
+ }
+ } else {
+ lastStart = i;
+ lastVal = nbBit;
+ startExists = true;
+ }
+ } else {
+ if ( startExists ) {
+ AddRun(lastStart, i, ((float) lastVal) / 4, ((float) lastVal) / 4);
+ }
+ startExists = false;
+ }
+ int chg = (i + 1 - a->st) & 0x00000007;
+ if ( chg == 0 ) {
+ c_full = a->fullB[(i + 1 - a->st) >> 3];
+ c_part = a->partB[(i + 1 - a->st) >> 3];
+ } else {
+ c_full <<= 4;
+ c_part <<= 4;
+ }
+ }
+ if ( startExists ) {
+ AddRun(lastStart, a->curMax + 1, ((float) lastVal) / 4, ((float) lastVal) / 4);
+ }
+}
+
+/**
+ * Transform a line of bits into pixel coverage values.
+ *
+ * Alpha values are computed from supersampled data, so we have to scan the
+ * BitLigne left to right, summing the bits in each pixel. The alpha value
+ * is then "number of bits"/(nbSub*nbSub)". Full bits and partial bits are
+ * treated as equals because the method produces ugly results otherwise.
+ *
+ * \param nbSub Number of BitLigne in the array "a".
+ */
+void IntLigne::Copy(int nbSub, BitLigne **as)
+{
+ if ( nbSub <= 0 ) {
+ Reset();
+ return;
+ }
+
+ if ( nbSub == 1 ) {
+ Copy(as[0]);
+ return;
+ }
+
+ // compute the min-max of the pixels to be rasterized from the min-max of the inpur bitlignes
+ int curMin = as[0]->curMin;
+ int curMax = as[0]->curMax;
+ for (int i = 1; i < nbSub; i++) {
+ if ( as[i]->curMin < curMin ) {
+ curMin = as[i]->curMin;
+ }
+ if ( as[i]->curMax > curMax ) {
+ curMax = as[i]->curMax;
+ }
+ }
+
+ if ( curMin < as[0]->st ) {
+ curMin = as[0]->st;
+ }
+
+ if ( curMax > as[0]->en ) {
+ curMax = as[0]->en;
+ }
+
+ if ( curMax <= curMin ) {
+ Reset();
+ return;
+ }
+
+ nbBord = 0;
+ nbRun = 0;
+
+ int lastVal = 0;
+ int lastStart = 0;
+ bool startExists = false;
+ float spA;
+ int masks[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
+
+ int theSt = as[0]->st;
+ if ( nbSub == 4 ) {
+ // special case for 4*4 supersampling, to avoid a few loops
+ uint32_t c_full[4];
+ c_full[0] = as[0]->fullB[(curMin - theSt) >> 3] | as[0]->partB[(curMin - theSt) >> 3];
+ c_full[0] <<= 4 * ((curMin - theSt) & 7);
+ c_full[1] = as[1]->fullB[(curMin - theSt) >> 3] | as[1]->partB[(curMin - theSt) >> 3];
+ c_full[1] <<= 4 * ((curMin - theSt) & 7);
+ c_full[2] = as[2]->fullB[(curMin - theSt) >> 3] | as[2]->partB[(curMin - theSt) >> 3];
+ c_full[2] <<= 4* ((curMin - theSt) & 7);
+ c_full[3] = as[3]->fullB[(curMin - theSt) >> 3] | as[3]->partB[(curMin - theSt) >> 3];
+ c_full[3] <<= 4* ((curMin - theSt) & 7);
+
+ spA = 1.0 / (4 * 4);
+ for (int i = curMin; i <= curMax; i++) {
+ int nbBit = 0;
+
+ if ( c_full[0] == 0 && c_full[1] == 0 && c_full[2] == 0 && c_full[3] == 0 ) {
+
+ if ( startExists ) {
+ AddRun(lastStart, i, ((float) lastVal) * spA, ((float) lastVal) * spA);
+ }
+ startExists = false;
+ i = theSt + (((i - theSt) & (~7) ) + 7);
+
+ } else if ( c_full[0] == 0xFFFFFFFF && c_full[1] == 0xFFFFFFFF &&
+ c_full[2] == 0xFFFFFFFF && c_full[3] == 0xFFFFFFFF ) {
+
+ if ( startExists ) {
+ if ( lastVal == 4*4) {
+ } else {
+ AddRun(lastStart, i, ((float) lastVal) * spA, ((float) lastVal) * spA);
+ lastStart = i;
+ }
+ } else {
+ lastStart = i;
+ }
+ lastVal = 4*4;
+ startExists = true;
+ i = theSt + (((i - theSt) & (~7) ) + 7);
+
+ } else {
+ nbBit += masks[c_full[0] >> 28];
+ nbBit += masks[c_full[1] >> 28];
+ nbBit += masks[c_full[2] >> 28];
+ nbBit += masks[c_full[3] >> 28];
+
+ if ( nbBit > 0 ) {
+ if ( startExists ) {
+ if ( lastVal == nbBit ) {
+ // on continue le run
+ } else {
+ AddRun(lastStart, i, ((float) lastVal) * spA, ((float) lastVal) * spA);
+ lastStart = i;
+ lastVal = nbBit;
+ }
+ } else {
+ lastStart = i;
+ lastVal = nbBit;
+ startExists = true;
+ }
+ } else {
+ if ( startExists ) {
+ AddRun(lastStart, i, ((float) lastVal) * spA, ((float) lastVal) * spA);
+ }
+ startExists = false;
+ }
+ }
+ int chg = (i + 1 - theSt) & 7;
+ if ( chg == 0 ) {
+ if ( i < curMax ) {
+ c_full[0] = as[0]->fullB[(i + 1 - theSt) >> 3] | as[0]->partB[(i + 1 - theSt) >> 3];
+ c_full[1] = as[1]->fullB[(i + 1 - theSt) >> 3] | as[1]->partB[(i + 1 - theSt) >> 3];
+ c_full[2] = as[2]->fullB[(i + 1 - theSt) >> 3] | as[2]->partB[(i + 1 - theSt) >> 3];
+ c_full[3] = as[3]->fullB[(i + 1 - theSt) >> 3] | as[3]->partB[(i + 1 - theSt) >> 3];
+ } else {
+ // end of line. byebye
+ }
+ } else {
+ c_full[0] <<= 4;
+ c_full[1] <<= 4;
+ c_full[2] <<= 4;
+ c_full[3] <<= 4;
+ }
+ }
+
+ } else {
+
+ uint32_t c_full[16]; // we take nbSub < 16, since 16*16 supersampling makes a 1/256 precision in alpha values
+ // and that's the max of what 32bit argb can represent
+ // in fact, we'll treat it as 4*nbSub supersampling, so that's a half truth and a full lazyness from me
+ // uint32_t c_part[16];
+ // start by putting the bits of the nbSub BitLignes in as[] in their respective c_full
+
+ for (int i = 0; i < nbSub; i++) {
+ // fullB and partB treated equally
+ c_full[i] = as[i]->fullB[(curMin - theSt) >> 3] | as[i]->partB[(curMin - theSt) >> 3];
+ c_full[i] <<= 4 * ((curMin - theSt) & 7);
+ /* c_part[i]=as[i]->partB[(curMin-theSt)>>3];
+ c_part[i]<<=4*((curMin-theSt)&7);*/
+ }
+
+ spA = 1.0 / (4 * nbSub); // contribution to the alpha value of a single bit of the supersampled data
+ for (int i = curMin; i <= curMax;i++) {
+ int nbBit = 0;
+ // int nbPartBit=0;
+ // a little acceleration: if the lines only contain full or empty bits, we can flush
+ // what's remaining in the c_full at best we flush an entire c_full, ie 32 bits, or 32/4=8 pixels
+ bool allEmpty = true;
+ bool allFull = true;
+ for (int j = 0; j < nbSub; j++) {
+ if ( c_full[j] != 0 /*|| c_part[j] != 0*/ ) {
+ allEmpty=false;
+ break;
+ }
+ }
+
+ if ( allEmpty ) {
+ // the remaining bits in c_full[] are empty: flush
+ if ( startExists ) {
+ AddRun(lastStart, i, ((float) lastVal) * spA, ((float) lastVal) * spA);
+ }
+ startExists = false;
+ i = theSt + (((i - theSt) & (~7) ) + 7);
+ } else {
+ for (int j = 0; j < nbSub; j++) {
+ if ( c_full[j] != 0xFFFFFFFF ) {
+ allFull=false;
+ break;
+ }
+ }
+
+ if ( allFull ) {
+ // the remaining bits in c_full[] are empty: flush
+ if ( startExists ) {
+ if ( lastVal == 4 * nbSub) {
+ } else {
+ AddRun(lastStart, i, ((float) lastVal) * spA,((float) lastVal) * spA);
+ lastStart = i;
+ }
+ } else {
+ lastStart = i;
+ }
+ lastVal = 4 * nbSub;
+ startExists = true;
+ i = theSt + (((i - theSt) & (~7) ) + 7);
+ } else {
+ // alpha values will be between 0 and 1, so we have more work to do
+ // compute how many bit this pixel holds
+ for (int j = 0; j < nbSub; j++) {
+ nbBit += masks[c_full[j] >> 28];
+// nbPartBit+=masks[c_part[j]>>28];
+ }
+ // and add a single-pixel run if needed, or extend the current run if the alpha value hasn't changed
+ if ( nbBit > 0 ) {
+ if ( startExists ) {
+ if ( lastVal == nbBit ) {
+ // alpha value hasn't changed: we continue
+ } else {
+ // alpha value did change: put the run that was being done,...
+ AddRun(lastStart, i, ((float) lastVal) * spA, ((float) lastVal) * spA);
+ // ... and start a new one
+ lastStart = i;
+ lastVal = nbBit;
+ }
+ } else {
+ // alpha value was 0, so we "create" a new run with alpha nbBit
+ lastStart = i;
+ lastVal = nbBit;
+ startExists = true;
+ }
+ } else {
+ if ( startExists ) {
+ AddRun(lastStart, i, ((float) lastVal) * spA,((float) lastVal) * spA);
+ }
+ startExists = false;
+ }
+ }
+ }
+ // move to the right: shift bits in the c_full[], and if we shifted everything, load the next c_full[]
+ int chg = (i + 1 - theSt) & 7;
+ if ( chg == 0 ) {
+ if ( i < curMax ) {
+ for (int j = 0; j < nbSub; j++) {
+ c_full[j] = as[j]->fullB[(i + 1 - theSt) >> 3] | as[j]->partB[(i + 1 - theSt) >> 3];
+ // c_part[j]=as[j]->partB[(i+1-theSt)>>3];
+ }
+ } else {
+ // end of line. byebye
+ }
+ } else {
+ for (int j = 0; j < nbSub; j++) {
+ c_full[j]<<=4;
+ // c_part[j]<<=4;
+ }
+ }
+ }
+ }
+
+ if ( startExists ) {
+ AddRun(lastStart, curMax + 1, ((float) lastVal) * spA,((float) lastVal) * spA);
+ }
+}
+
+/// Copy another IntLigne
+void IntLigne::Copy(IntLigne *a)
+{
+ if ( a->nbRun <= 0 ) {
+ Reset();
+ return;
+ }
+
+ nbBord = 0;
+ nbRun = a->nbRun;
+ if ( nbRun > maxRun ) {
+ maxRun = nbRun;
+ runs = (int_ligne_run*) g_realloc(runs, maxRun * sizeof(int_ligne_run));
+ }
+ memcpy(runs, a->runs, nbRun * sizeof(int_ligne_run));
+}
+
+
+/**
+ * Copy a FloatLigne's runs.
+ *
+ * Compute non-overlapping runs with integer boundaries from a set of runs
+ * with floating-point boundaries. This involves replacing floating-point
+ * boundaries that are not integer by single-pixel runs, so this function
+ * contains plenty of rounding and float->integer conversion (read:
+ * time-consuming).
+ *
+ * \todo
+ * Optimization Questions: Why is this called so often compared with the
+ * other Copy() routines? How does AddRun() look for optimization potential?
+ */
+void IntLigne::Copy(FloatLigne* a)
+{
+ if ( a->runs.empty() ) {
+ Reset();
+ return;
+ }
+
+ /* if ( showCopy ) {
+ printf("\nfloatligne:\n");
+ a->Affiche();
+ }*/
+
+ nbBord = 0;
+ nbRun = 0;
+ firstAc = lastAc = -1;
+ bool pixExists = false;
+ int curPos = (int) floor(a->runs[0].st) - 1;
+ float lastSurf = 0;
+ float tolerance = 0.00001;
+
+ // we take each run of the FloatLigne in sequence and make single-pixel runs of its boundaries as needed
+ // since the float_ligne_runs are non-overlapping, when a single-pixel run intersects with another runs,
+ // it must intersect with the single-pixel run created for the end of that run. so instead of creating a new
+ // int_ligne_run, we just add the coverage to that run.
+ for (auto & run : a->runs) {
+ float_ligne_run runA = run;
+ float curStF = floor(runA.st);
+ float curEnF = floor(runA.en);
+ int curSt = (int) curStF;
+ int curEn = (int) curEnF;
+
+ // stEx: start boundary is not integer -> create single-pixel run for it
+ // enEx: end boundary is not integer -> create single-pixel run for it
+ // miEx: the runs minus the eventual single-pixel runs is not empty
+ bool stEx = true;
+ bool miEx = true;
+ bool enEx = true;
+ int miSt = curSt;
+ float miStF = curStF;
+ float msv;
+ float mev;
+ if ( runA.en - curEnF < tolerance ) {
+ enEx = false;
+ }
+
+ // msv and mev are the start and end value of the middle section of the run, that is the run minus the
+ // single-pixel runs creaed for its boundaries
+ if ( runA.st-curStF < tolerance /*miSt == runA.st*/ ) {
+ stEx = false;
+ msv = runA.vst;
+ } else {
+ miSt += 1;
+ miStF += 1.0;
+ if ( enEx == false && miSt == curEn ) {
+ msv = runA.ven;
+ } else {
+ // msv=a->ValAt(miSt,runA.st,runA.en,runA.vst,runA.ven);
+ msv = runA.vst + (miStF-runA.st) * runA.pente;
+ }
+ }
+
+ if ( miSt >= curEn ) {
+ miEx = false;
+ }
+ if ( stEx == false && miEx == false /*curEn == runA.st*/ ) {
+ mev = runA.vst;
+ } else if ( enEx == false /*curEn == runA.en*/ ) {
+ mev = runA.ven;
+ } else {
+ // mev=a->ValAt(curEn,runA.st,runA.en,runA.vst,runA.ven);
+ mev = runA.vst + (curEnF-runA.st) * runA.pente;
+ }
+
+ // check the different cases
+ if ( stEx && enEx ) {
+ // stEx && enEx
+ if ( curEn > curSt ) {
+ if ( pixExists ) {
+ if ( curPos < curSt ) {
+ AddRun(curPos,curPos+1,lastSurf,lastSurf);
+ lastSurf=0.5*(msv+run.vst)*(miStF-run.st);
+ AddRun(curSt,curSt+1,lastSurf,lastSurf);
+ } else {
+ lastSurf+=0.5*(msv+run.vst)*(miStF-run.st);
+ AddRun(curSt,curSt+1,lastSurf,lastSurf);
+ }
+ pixExists=false;
+ } else {
+ lastSurf=0.5*(msv+run.vst)*(miStF-run.st);
+ AddRun(curSt,curSt+1,lastSurf,lastSurf);
+ }
+ } else if ( pixExists ) {
+ if ( curPos < curSt ) {
+ AddRun(curPos,curPos+1,lastSurf,lastSurf);
+ lastSurf=0.5*(run.ven+run.vst)*(run.en-run.st);
+ curPos=curSt;
+ } else {
+ lastSurf += 0.5 * (run.ven+run.vst)*(run.en-run.st);
+ }
+ } else {
+ lastSurf=0.5*(run.ven+run.vst)*(run.en-run.st);
+ curPos=curSt;
+ pixExists=true;
+ }
+ } else if ( pixExists ) {
+ if ( curPos < curSt ) {
+ AddRun(curPos,curPos+1,lastSurf,lastSurf);
+ lastSurf = 0.5 * (msv+run.vst) * (miStF-run.st);
+ AddRun(curSt,curSt+1,lastSurf,lastSurf);
+ } else {
+ lastSurf += 0.5 * (msv+run.vst) * (miStF-run.st);
+ AddRun(curSt,curSt+1,lastSurf,lastSurf);
+ }
+ pixExists=false;
+ } else {
+ lastSurf = 0.5 * (msv+run.vst) * (miStF-run.st);
+ AddRun(curSt,curSt+1,lastSurf,lastSurf);
+ }
+ if ( miEx ) {
+ if ( pixExists && curPos < miSt ) {
+ AddRun(curPos,curPos+1,lastSurf,lastSurf);
+ }
+ pixExists=false;
+ AddRun(miSt,curEn,msv,mev);
+ }
+ if ( enEx ) {
+ if ( curEn > curSt ) {
+ lastSurf=0.5*(mev+run.ven)*(run.en-curEnF);
+ pixExists=true;
+ curPos=curEn;
+ } else if ( ! stEx ) {
+ if ( pixExists ) {
+ AddRun(curPos,curPos+1,lastSurf,lastSurf);
+ }
+ lastSurf=0.5*(mev+run.ven)*(run.en-curEnF);
+ pixExists=true;
+ curPos=curEn;
+ }
+ }
+ }
+ if ( pixExists ) {
+ AddRun(curPos,curPos+1,lastSurf,lastSurf);
+ }
+ /* if ( showCopy ) {
+ printf("-> intligne:\n");
+ Affiche();
+ }*/
+}
+
+
+void IntLigne::Enqueue(int no)
+{
+ if ( firstAc < 0 ) {
+ firstAc = lastAc = no;
+ bords[no].prev = bords[no].next = -1;
+ } else {
+ bords[no].next = -1;
+ bords[no].prev = lastAc;
+ bords[lastAc].next = no;
+ lastAc = no;
+ }
+}
+
+
+void IntLigne::Dequeue(int no)
+{
+ if ( no == firstAc ) {
+ if ( no == lastAc ) {
+ firstAc = lastAc = -1;
+ } else {
+ firstAc = bords[no].next;
+ }
+ } else if ( no == lastAc ) {
+ lastAc = bords[no].prev;
+ } else {
+ }
+ if ( bords[no].prev >= 0 ) {
+ bords[bords[no].prev].next = bords[no].next;
+ }
+ if ( bords[no].next >= 0 ) {
+ bords[bords[no].next].prev = bords[no].prev;
+ }
+
+ bords[no].prev = bords[no].next = -1;
+}
+
+/**
+ * Rasterization.
+ *
+ * The parameters have the same meaning as in the AlphaLigne class.
+ */
+void IntLigne::Raster(raster_info &dest, void *color, RasterInRunFunc worker)
+{
+ if ( nbRun <= 0 ) {
+ return;
+ }
+
+ int min = runs[0].st;
+ int max = runs[nbRun-1].en;
+ if ( dest.endPix <= min || dest.startPix >= max ) {
+ return;
+ }
+
+ int curRun = -1;
+ for (curRun = 0; curRun < nbRun; curRun++) {
+ if ( runs[curRun].en > dest.startPix ) {
+ break;
+ }
+ }
+
+ if ( curRun >= nbRun ) {
+ return;
+ }
+
+ if ( runs[curRun].st < dest.startPix ) {
+ int nst = runs[curRun].st;
+ int nen = runs[curRun].en;
+ float vst = runs[curRun].vst;
+ float ven = runs[curRun].ven;
+ float nvst = (vst * (nen - dest.startPix) + ven * (dest.startPix - nst)) / ((float) (nen - nst));
+ if ( runs[curRun].en <= dest.endPix ) {
+ (worker)(dest, color, dest.startPix, nvst, runs[curRun].en, runs[curRun].ven);
+ } else {
+ float nven = (vst * (nen - dest.endPix) + ven * (dest.endPix - nst)) / ((float)(nen - nst));
+ (worker)(dest, color, dest.startPix, nvst, dest.endPix, nven);
+ return;
+ }
+ curRun++;
+ }
+
+ for (; (curRun < nbRun && runs[curRun].en <= dest.endPix); curRun++) {
+ (worker)(dest, color, runs[curRun].st, runs[curRun].vst, runs[curRun].en, runs[curRun].ven);
+//Buffer::RasterRun(*dest,color,runs[curRun].st,runs[curRun].vst,runs[curRun].en,runs[curRun].ven);
+ }
+
+ if ( curRun >= nbRun ) {
+ return;
+ }
+
+ if ( runs[curRun].st < dest.endPix && runs[curRun].en > dest.endPix ) {
+ int const nst = runs[curRun].st;
+ int const nen = runs[curRun].en;
+ float const vst = runs[curRun].vst;
+ float const ven = runs[curRun].ven;
+ float const nven = (vst * (nen - dest.endPix) + ven * (dest.endPix - nst)) / ((float)(nen - nst));
+
+ (worker)(dest,color,runs[curRun].st,runs[curRun].vst,dest.endPix,nven);
+ curRun++;
+ }
+}
+
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/livarot/int-line.h b/src/livarot/int-line.h
new file mode 100644
index 0000000..576cfcf
--- /dev/null
+++ b/src/livarot/int-line.h
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#ifndef INKSCAPE_LIVAROT_INT_LINE_H
+#define INKSCAPE_LIVAROT_INT_LINE_H
+
+#include "livarot/LivarotDefs.h"
+#include "object/object-set.h" // For BooleanOp
+
+/** \file
+ * Coverage with integer boundaries.
+ */
+
+class BitLigne;
+class FloatLigne;
+
+/// A run with integer boundaries.
+struct int_ligne_run {
+ int st;
+ int en;
+ float vst;
+ float ven;
+};
+
+/// Integer boundary.
+struct int_ligne_bord {
+ int pos;
+ bool start;
+ float val;
+ int other;
+ int prev;
+ int next;
+};
+
+/**
+ * Coverage with integer boundaries.
+ *
+ * This is what we want for actual rasterization. It contains the same
+ * stuff as FloatLigne, but technically only the Copy() functions are used.
+ */
+class IntLigne {
+public:
+
+ int nbBord;
+ int maxBord;
+ int_ligne_bord* bords;
+
+ int nbRun;
+ int maxRun;
+ int_ligne_run* runs;
+
+ int firstAc;
+ int lastAc;
+
+ IntLigne();
+ virtual ~IntLigne();
+
+ void Reset();
+ int AddBord(int spos, float sval, int epos, float eval);
+
+ void Flatten();
+
+ void Affiche();
+
+ int AddRun(int st, int en, float vst, float ven);
+
+ void Booleen(IntLigne* a, IntLigne* b, BooleanOp mod);
+
+ void Copy(IntLigne* a);
+ void Copy(FloatLigne* a);
+ void Copy(BitLigne* a);
+ void Copy(int nbSub,BitLigne **a);
+
+ void Enqueue(int no);
+ void Dequeue(int no);
+ float RemainingValAt(int at);
+
+ static int CmpBord(void const *p1, void const *p2) {
+ int_ligne_bord const *d1 = reinterpret_cast<int_ligne_bord const *>(p1);
+ int_ligne_bord const *d2 = reinterpret_cast<int_ligne_bord const *>(p2);
+
+ if ( d1->pos == d2->pos ) {
+ if ( d1->start && !(d2->start) ) {
+ return 1;
+ }
+ if ( !(d1->start) && d2->start ) {
+ return -1;
+ }
+ return 0;
+ }
+ return (( d1->pos < d2->pos ) ? -1 : 1);
+ };
+
+ inline float ValAt(int at, int ps, int pe, float vs, float ve) {
+ return ((at - ps) * ve + (pe - at) * vs) / (pe - ps);
+ };
+
+ void Raster(raster_info &dest, void *color, RasterInRunFunc worker);
+};
+
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/livarot/path-description.cpp b/src/livarot/path-description.cpp
new file mode 100644
index 0000000..ecf50ca
--- /dev/null
+++ b/src/livarot/path-description.cpp
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2011 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#include "livarot/path-description.h"
+#include <2geom/affine.h>
+
+PathDescr *PathDescrMoveTo::clone() const
+{
+ return new PathDescrMoveTo(*this);
+}
+
+void PathDescrMoveTo::dumpSVG(Inkscape::SVGOStringStream& s, Geom::Point const &/*last*/) const
+{
+ s << "M " << p[Geom::X] << " " << p[Geom::Y] << " ";
+}
+
+void PathDescrMoveTo::transform(Geom::Affine const& t)
+{
+ p = p * t;
+}
+
+void PathDescrMoveTo::dump(std::ostream &s) const
+{
+ /* localizing ok */
+ s << " m " << p[Geom::X] << " " << p[Geom::Y];
+}
+
+void PathDescrLineTo::dumpSVG(Inkscape::SVGOStringStream& s, Geom::Point const &/*last*/) const
+{
+ s << "L " << p[Geom::X] << " " << p[Geom::Y] << " ";
+}
+
+PathDescr *PathDescrLineTo::clone() const
+{
+ return new PathDescrLineTo(*this);
+}
+
+void PathDescrLineTo::transform(Geom::Affine const& t)
+{
+ p = p * t;
+}
+
+void PathDescrLineTo::dump(std::ostream &s) const
+{
+ /* localizing ok */
+ s << " l " << p[Geom::X] << " " << p[Geom::Y];
+}
+
+PathDescr *PathDescrBezierTo::clone() const
+{
+ return new PathDescrBezierTo(*this);
+}
+
+void PathDescrBezierTo::transform(Geom::Affine const& t)
+{
+ p = p * t;
+}
+
+void PathDescrBezierTo::dump(std::ostream &s) const
+{
+ /* localizing ok */
+ s << " b " << p[Geom::X] << " " << p[Geom::Y] << " " << nb;
+}
+
+PathDescr *PathDescrIntermBezierTo::clone() const
+{
+ return new PathDescrIntermBezierTo(*this);
+}
+
+void PathDescrIntermBezierTo::transform(Geom::Affine const& t)
+{
+ p = p * t;
+}
+
+void PathDescrIntermBezierTo::dump(std::ostream &s) const
+{
+ /* localizing ok */
+ s << " i " << p[Geom::X] << " " << p[Geom::Y];
+}
+
+void PathDescrCubicTo::dumpSVG(Inkscape::SVGOStringStream& s, Geom::Point const &last) const
+{
+ s << "C "
+ << last[Geom::X] + start[0] / 3 << " "
+ << last[Geom::Y] + start[1] / 3 << " "
+ << p[Geom::X] - end[0] / 3 << " "
+ << p[Geom::Y] - end[1] / 3 << " "
+ << p[Geom::X] << " "
+ << p[Geom::Y] << " ";
+}
+
+PathDescr *PathDescrCubicTo::clone() const
+{
+ return new PathDescrCubicTo(*this);
+}
+
+void PathDescrCubicTo::dump(std::ostream &s) const
+{
+ /* localizing ok */
+ s << " c "
+ << p[Geom::X] << " " << p[Geom::Y] << " "
+ << start[Geom::X] << " " << start[Geom::Y] << " "
+ << end[Geom::X] << " " << end[Geom::Y] << " ";
+}
+
+void PathDescrCubicTo::transform(Geom::Affine const& t)
+{
+ Geom::Affine tr = t;
+ tr[4] = tr[5] = 0;
+ start = start * tr;
+ end = end * tr;
+
+ p = p * t;
+}
+
+void PathDescrArcTo::dumpSVG(Inkscape::SVGOStringStream& s, Geom::Point const &/*last*/) const
+{
+ s << "A "
+ << rx << " "
+ << ry << " "
+ << angle << " "
+ << (large ? "1" : "0") << " "
+ << (clockwise ? "0" : "1") << " "
+ << p[Geom::X] << " "
+ << p[Geom::Y] << " ";
+}
+
+PathDescr *PathDescrArcTo::clone() const
+{
+ return new PathDescrArcTo(*this);
+}
+
+void PathDescrArcTo::transform(Geom::Affine const& t)
+{
+ p = p * t;
+}
+
+void PathDescrArcTo::dump(std::ostream &s) const
+{
+ /* localizing ok */
+ s << " a "
+ << p[Geom::X] << " " << p[Geom::Y] << " "
+ << rx << " " << ry << " "
+ << angle << " "
+ << (clockwise ? 1 : 0) << " "
+ << (large ? 1 : 0);
+}
+
+PathDescr *PathDescrForced::clone() const
+{
+ return new PathDescrForced(*this);
+}
+
+void PathDescrClose::dumpSVG(Inkscape::SVGOStringStream& s, Geom::Point const &/*last*/) const
+{
+ s << "z ";
+}
+
+PathDescr *PathDescrClose::clone() const
+{
+ return new PathDescrClose(*this);
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/livarot/path-description.h b/src/livarot/path-description.h
new file mode 100644
index 0000000..dd957ec
--- /dev/null
+++ b/src/livarot/path-description.h
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#ifndef SEEN_INKSCAPE_LIVAROT_PATH_DESCRIPTION_H
+#define SEEN_INKSCAPE_LIVAROT_PATH_DESCRIPTION_H
+
+#include <2geom/point.h>
+#include "svg/stringstream.h"
+
+// path description commands
+/* FIXME: these should be unnecessary once the refactoring of the path
+** description stuff is finished.
+*/
+enum
+{
+ descr_moveto = 0, // a moveto
+ descr_lineto = 1, // a (guess what) lineto
+ descr_cubicto = 2,
+ descr_bezierto = 3, // "beginning" of a quadratic bezier spline, will contain its endpoint (i know, it's bad...)
+ descr_arcto = 4,
+ descr_close = 5,
+ descr_interm_bezier = 6, // control point of the bezier spline
+ descr_forced = 7,
+
+ descr_type_mask = 15 // the command no will be stored in a "flags" field, potentially with other info, so we need
+ // a mask to AND the field and extract the command
+};
+
+struct PathDescr
+{
+ PathDescr() : flags(0), associated(-1), tSt(0), tEn(1) {}
+ PathDescr(int f) : flags(f), associated(-1), tSt(0), tEn(1) {}
+ virtual ~PathDescr() = default;
+
+ int getType() const { return flags & descr_type_mask; }
+ void setType(int t) {
+ flags &= ~descr_type_mask;
+ flags |= t;
+ }
+
+ virtual void dumpSVG(Inkscape::SVGOStringStream &/*s*/, Geom::Point const &/*last*/) const {}
+ virtual PathDescr *clone() const = 0;
+ virtual void transform(Geom::Affine const &/*t*/) {}
+ virtual void dump(std::ostream &/*s*/) const {}
+
+ int flags; // most notably contains the path command no
+ int associated; // index in the polyline of the point that ends the path portion of this command
+ double tSt;
+ double tEn;
+};
+
+struct PathDescrMoveTo : public PathDescr
+{
+ PathDescrMoveTo(Geom::Point const &pp)
+ : PathDescr(descr_moveto), p(pp) {}
+
+ void dumpSVG(Inkscape::SVGOStringStream &s, Geom::Point const &last) const override;
+ PathDescr *clone() const override;
+ void transform(Geom::Affine const &t) override;
+ void dump(std::ostream &s) const override;
+
+ Geom::Point p;
+};
+
+struct PathDescrLineTo : public PathDescr
+{
+ PathDescrLineTo(Geom::Point const &pp)
+ : PathDescr(descr_lineto), p(pp) {}
+
+ void dumpSVG(Inkscape::SVGOStringStream &s, Geom::Point const &last) const override;
+ PathDescr *clone() const override;
+ void transform(Geom::Affine const &t) override;
+ void dump(std::ostream &s) const override;
+
+ Geom::Point p;
+};
+
+// quadratic bezier curves: a set of control points, and an endpoint
+struct PathDescrBezierTo : public PathDescr
+{
+ PathDescrBezierTo(Geom::Point const &pp, int n)
+ : PathDescr(descr_bezierto), p(pp), nb(n) {}
+
+ PathDescr *clone() const override;
+ void transform(Geom::Affine const &t) override;
+ void dump(std::ostream &s) const override;
+
+ Geom::Point p; // the endpoint's coordinates
+ int nb; // number of control points, stored in the next path description commands
+};
+
+/* FIXME: I don't think this should be necessary */
+struct PathDescrIntermBezierTo : public PathDescr
+{
+ PathDescrIntermBezierTo()
+ : PathDescr(descr_interm_bezier) , p(0, 0) {}
+ PathDescrIntermBezierTo(Geom::Point const &pp)
+ : PathDescr(descr_interm_bezier), p(pp) {}
+
+ PathDescr *clone() const override;
+ void transform(Geom::Affine const &t) override;
+ void dump(std::ostream &s) const override;
+
+ Geom::Point p; // control point coordinates
+};
+
+// cubic spline curve: 2 tangents and one endpoint
+struct PathDescrCubicTo : public PathDescr
+{
+ PathDescrCubicTo(Geom::Point const &pp, Geom::Point const &s, Geom::Point const& e)
+ : PathDescr(descr_cubicto), p(pp), start(s), end(e) {}
+
+ void dumpSVG(Inkscape::SVGOStringStream &s, Geom::Point const &last) const override;
+ PathDescr *clone() const override;
+ void transform(Geom::Affine const &t) override;
+ void dump(std::ostream &s) const override;
+
+ Geom::Point p;
+ Geom::Point start;
+ Geom::Point end;
+};
+
+// arc: endpoint, 2 radii and one angle, plus 2 booleans to choose the arc (svg style)
+struct PathDescrArcTo : public PathDescr
+{
+ PathDescrArcTo(Geom::Point const &pp, double x, double y, double a, bool l, bool c)
+ : PathDescr(descr_arcto), p(pp), rx(x), ry(y), angle(a), large(l), clockwise(c) {}
+
+ void dumpSVG(Inkscape::SVGOStringStream &s, Geom::Point const &last) const override;
+ PathDescr *clone() const override;
+ void transform(Geom::Affine const &t) override;
+ void dump(std::ostream &s) const override;
+
+ Geom::Point p;
+ double rx;
+ double ry;
+ double angle;
+ bool large;
+ bool clockwise;
+};
+
+struct PathDescrForced : public PathDescr
+{
+ PathDescrForced() : PathDescr(descr_forced), p(0, 0) {}
+
+ PathDescr *clone() const override;
+
+ /* FIXME: not sure whether _forced should have a point associated with it;
+ ** Path::ConvertForcedToMoveTo suggests that maybe it should.
+ */
+ Geom::Point p;
+};
+
+struct PathDescrClose : public PathDescr
+{
+ PathDescrClose() : PathDescr(descr_close) {}
+
+ void dumpSVG(Inkscape::SVGOStringStream &s, Geom::Point const &last) const override;
+ PathDescr *clone() const override;
+
+ /* FIXME: not sure whether _forced should have a point associated with it;
+ ** Path::ConvertForcedToMoveTo suggests that maybe it should.
+ */
+ Geom::Point p;
+};
+
+#endif
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/livarot/sweep-event-queue.h b/src/livarot/sweep-event-queue.h
new file mode 100644
index 0000000..759727a
--- /dev/null
+++ b/src/livarot/sweep-event-queue.h
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * A container of intersection events.
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2010 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#ifndef SEEN_LIVAROT_SWEEP_EVENT_QUEUE_H
+#define SEEN_LIVAROT_SWEEP_EVENT_QUEUE_H
+
+#include <2geom/forward.h>
+class SweepEvent;
+class SweepTree;
+
+
+/**
+ * The structure to hold the intersections events encountered during the sweep. It's an array of
+ * SweepEvent (not allocated with "new SweepEvent[n]" but with a malloc). There's a list of
+ * indices because it's a binary heap: inds[i] tell that events[inds[i]] has position i in the
+ * heap. Each SweepEvent has a field to store its index in the heap, too.
+ */
+class SweepEventQueue
+{
+public:
+ SweepEventQueue(int s);
+ virtual ~SweepEventQueue();
+
+ int size() const { return nbEvt; }
+
+ /// Look for the topmost intersection in the heap
+ bool peek(SweepTree * &iLeft, SweepTree * &iRight, Geom::Point &oPt, double &itl, double &itr);
+ /// Extract the topmost intersection from the heap
+ bool extract(SweepTree * &iLeft, SweepTree * &iRight, Geom::Point &oPt, double &itl, double &itr);
+ /// Add one intersection in the binary heap
+ SweepEvent *add(SweepTree *iLeft, SweepTree *iRight, Geom::Point &iPt, double itl, double itr);
+
+ void remove(SweepEvent *e);
+ void relocate(SweepEvent *e, int to);
+
+private:
+ int nbEvt; ///< Number of events currently in the heap.
+ int maxEvt; ///< Allocated size of the heap.
+ int *inds; ///< Indices.
+ SweepEvent *events; ///< Sweep events.
+};
+
+#endif /* !SEEN_LIVAROT_SWEEP_EVENT_QUEUE_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/livarot/sweep-event.cpp b/src/livarot/sweep-event.cpp
new file mode 100644
index 0000000..bb9e4e7
--- /dev/null
+++ b/src/livarot/sweep-event.cpp
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#include <glib.h>
+#include "livarot/sweep-event-queue.h"
+#include "livarot/sweep-tree.h"
+#include "livarot/sweep-event.h"
+#include "livarot/Shape.h"
+
+SweepEventQueue::SweepEventQueue(int s) : nbEvt(0), maxEvt(s)
+{
+ /* FIXME: use new[] for this, but this causes problems when delete[]
+ ** calls the SweepEvent destructors.
+ */
+ events = (SweepEvent *) g_malloc(maxEvt * sizeof(SweepEvent));
+ inds = new int[maxEvt];
+}
+
+SweepEventQueue::~SweepEventQueue()
+{
+ g_free(events);
+ delete []inds;
+}
+
+SweepEvent *SweepEventQueue::add(SweepTree *iLeft, SweepTree *iRight, Geom::Point &px, double itl, double itr)
+{
+ if (nbEvt > maxEvt) {
+ return nullptr;
+ }
+
+ int const n = nbEvt++;
+ events[n].MakeNew (iLeft, iRight, px, itl, itr);
+
+ SweepTree *t[2] = { iLeft, iRight };
+ for (auto & i : t) {
+ Shape *s = i->src;
+ Shape::dg_arete const &e = s->getEdge(i->bord);
+ int const n = std::max(e.st, e.en);
+ s->pData[n].pending++;;
+ }
+
+ events[n].ind = n;
+ inds[n] = n;
+
+ int curInd = n;
+ while (curInd > 0) {
+ int const half = (curInd - 1) / 2;
+ int const no = inds[half];
+ if (px[1] < events[no].posx[1]
+ || (px[1] == events[no].posx[1] && px[0] < events[no].posx[0]))
+ {
+ events[n].ind = half;
+ events[no].ind = curInd;
+ inds[half] = n;
+ inds[curInd] = no;
+ } else {
+ break;
+ }
+
+ curInd = half;
+ }
+
+ return events + n;
+}
+
+
+
+bool SweepEventQueue::peek(SweepTree * &iLeft, SweepTree * &iRight, Geom::Point &px, double &itl, double &itr)
+{
+ if (nbEvt <= 0) {
+ return false;
+ }
+
+ SweepEvent const &e = events[inds[0]];
+
+ iLeft = e.sweep[LEFT];
+ iRight = e.sweep[RIGHT];
+ px = e.posx;
+ itl = e.tl;
+ itr = e.tr;
+
+ return true;
+}
+
+bool SweepEventQueue::extract(SweepTree * &iLeft, SweepTree * &iRight, Geom::Point &px, double &itl, double &itr)
+{
+ if (nbEvt <= 0) {
+ return false;
+ }
+
+ SweepEvent &e = events[inds[0]];
+
+ iLeft = e.sweep[LEFT];
+ iRight = e.sweep[RIGHT];
+ px = e.posx;
+ itl = e.tl;
+ itr = e.tr;
+ remove(&e);
+
+ return true;
+}
+
+
+void SweepEventQueue::remove(SweepEvent *e)
+{
+ if (nbEvt <= 1) {
+ e->MakeDelete ();
+ nbEvt = 0;
+ return;
+ }
+
+ int const n = e->ind;
+ int to = inds[n];
+ e->MakeDelete();
+ relocate(&events[--nbEvt], to);
+
+ int const moveInd = nbEvt;
+ if (moveInd == n) {
+ return;
+ }
+
+ to = inds[moveInd];
+
+ events[to].ind = n;
+ inds[n] = to;
+
+ int curInd = n;
+ Geom::Point const px = events[to].posx;
+ bool didClimb = false;
+ while (curInd > 0) {
+ int const half = (curInd - 1) / 2;
+ int const no = inds[half];
+ if (px[1] < events[no].posx[1]
+ || (px[1] == events[no].posx[1] && px[0] < events[no].posx[0]))
+ {
+ events[to].ind = half;
+ events[no].ind = curInd;
+ inds[half] = to;
+ inds[curInd] = no;
+ didClimb = true;
+ } else {
+ break;
+ }
+ curInd = half;
+ }
+
+ if (didClimb) {
+ return;
+ }
+
+ while (2 * curInd + 1 < nbEvt) {
+ int const child1 = 2 * curInd + 1;
+ int const child2 = child1 + 1;
+ int const no1 = inds[child1];
+ int const no2 = inds[child2];
+ if (child2 < nbEvt) {
+ if (px[1] > events[no1].posx[1]
+ || (px[1] == events[no1].posx[1]
+ && px[0] > events[no1].posx[0]))
+ {
+ if (events[no2].posx[1] > events[no1].posx[1]
+ || (events[no2].posx[1] == events[no1].posx[1]
+ && events[no2].posx[0] > events[no1].posx[0]))
+ {
+ events[to].ind = child1;
+ events[no1].ind = curInd;
+ inds[child1] = to;
+ inds[curInd] = no1;
+ curInd = child1;
+ } else {
+ events[to].ind = child2;
+ events[no2].ind = curInd;
+ inds[child2] = to;
+ inds[curInd] = no2;
+ curInd = child2;
+ }
+ } else {
+ if (px[1] > events[no2].posx[1]
+ || (px[1] == events[no2].posx[1]
+ && px[0] > events[no2].posx[0]))
+ {
+ events[to].ind = child2;
+ events[no2].ind = curInd;
+ inds[child2] = to;
+ inds[curInd] = no2;
+ curInd = child2;
+ } else {
+ break;
+ }
+ }
+ } else {
+ if (px[1] > events[no1].posx[1]
+ || (px[1] == events[no1].posx[1]
+ && px[0] > events[no1].posx[0]))
+ {
+ events[to].ind = child1;
+ events[no1].ind = curInd;
+ inds[child1] = to;
+ inds[curInd] = no1;
+ }
+
+ break;
+ }
+ }
+}
+
+
+
+
+void SweepEventQueue::relocate(SweepEvent *e, int to)
+{
+ if (inds[e->ind] == to) {
+ return; // j'y suis deja
+ }
+
+ events[to] = *e;
+
+ e->sweep[LEFT]->evt[RIGHT] = events + to;
+ e->sweep[RIGHT]->evt[LEFT] = events + to;
+ inds[e->ind] = to;
+}
+
+
+/*
+ * a simple binary heap
+ * it only contains intersection events
+ * the regular benley-ottman stuffs the segment ends in it too, but that not needed here since theses points
+ * are already sorted. and the binary heap is much faster with only intersections...
+ * the code sample on which this code is based comes from purists.org
+ */
+SweepEvent::SweepEvent()
+{
+ MakeNew (nullptr, nullptr, Geom::Point(0, 0), 0, 0);
+}
+
+SweepEvent::~SweepEvent()
+{
+ MakeDelete();
+}
+
+void SweepEvent::MakeNew(SweepTree *iLeft, SweepTree *iRight, Geom::Point const &px, double itl, double itr)
+{
+ ind = -1;
+ posx = px;
+ tl = itl;
+ tr = itr;
+ sweep[LEFT] = iLeft;
+ sweep[RIGHT] = iRight;
+ sweep[LEFT]->evt[RIGHT] = this;
+ sweep[RIGHT]->evt[LEFT] = this;
+}
+
+void SweepEvent::MakeDelete()
+{
+ for (int i = 0; i < 2; i++) {
+ if (sweep[i]) {
+ Shape *s = sweep[i]->src;
+ Shape::dg_arete const &e = s->getEdge(sweep[i]->bord);
+ int const n = std::max(e.st, e.en);
+ s->pData[n].pending--;
+ }
+
+ sweep[i]->evt[1 - i] = nullptr;
+ sweep[i] = nullptr;
+ }
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/livarot/sweep-event.h b/src/livarot/sweep-event.h
new file mode 100644
index 0000000..492c4e8
--- /dev/null
+++ b/src/livarot/sweep-event.h
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#ifndef INKSCAPE_LIVAROT_SWEEP_EVENT_H
+#define INKSCAPE_LIVAROT_SWEEP_EVENT_H
+/** \file
+ * Intersection events.
+ */
+
+#include <2geom/point.h>
+class SweepTree;
+
+
+/** One intersection event. */
+class SweepEvent
+{
+public:
+ SweepTree *sweep[2]; ///< Sweep element associated with the left and right edge of the intersection.
+
+ Geom::Point posx; ///< Coordinates of the intersection.
+ double tl, tr; ///< Coordinates of the intersection on the left edge (tl) and on the right edge (tr).
+
+ int ind; ///< Index in the binary heap.
+
+ SweepEvent(); // not used.
+ virtual ~SweepEvent(); // not used.
+
+ /// Initialize a SweepEvent structure.
+ void MakeNew (SweepTree * iLeft, SweepTree * iRight, Geom::Point const &iPt,
+ double itl, double itr);
+
+ /// Void a SweepEvent structure.
+ void MakeDelete ();
+};
+
+
+#endif /* !INKSCAPE_LIVAROT_SWEEP_EVENT_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/livarot/sweep-tree-list.cpp b/src/livarot/sweep-tree-list.cpp
new file mode 100644
index 0000000..97640fd
--- /dev/null
+++ b/src/livarot/sweep-tree-list.cpp
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#include <glib.h>
+#include "livarot/sweep-tree.h"
+#include "livarot/sweep-tree-list.h"
+
+
+SweepTreeList::SweepTreeList(int s) :
+ nbTree(0),
+ maxTree(s),
+ trees((SweepTree *) g_malloc(s * sizeof(SweepTree))),
+ racine(nullptr)
+{
+ /* FIXME: Use new[] for trees initializer above, but watch out for bad things happening when
+ * SweepTree::~SweepTree is called.
+ */
+}
+
+
+SweepTreeList::~SweepTreeList()
+{
+ g_free(trees);
+ trees = nullptr;
+}
+
+
+SweepTree *SweepTreeList::add(Shape *iSrc, int iBord, int iWeight, int iStartPoint, Shape */*iDst*/)
+{
+ if (nbTree >= maxTree) {
+ return nullptr;
+ }
+
+ int const n = nbTree++;
+ trees[n].MakeNew(iSrc, iBord, iWeight, iStartPoint);
+
+ return trees + n;
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/livarot/sweep-tree-list.h b/src/livarot/sweep-tree-list.h
new file mode 100644
index 0000000..84939c4
--- /dev/null
+++ b/src/livarot/sweep-tree-list.h
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2010 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+/** @file
+ * @brief SweepTreeList definition
+ */
+
+#ifndef INKSCAPE_LIVAROT_SWEEP_TREE_LIST_H
+#define INKSCAPE_LIVAROT_SWEEP_TREE_LIST_H
+
+class Shape;
+class SweepTree;
+
+/**
+ * The sweepline: a set of edges intersecting the current sweepline
+ * stored as an AVL tree.
+ */
+class SweepTreeList {
+public:
+ int nbTree; ///< Number of nodes in the tree.
+ int const maxTree; ///< Max number of nodes in the tree.
+ SweepTree *trees; ///< The array of nodes.
+ SweepTree *racine; ///< Root of the tree.
+
+ SweepTreeList(int s);
+ virtual ~SweepTreeList();
+
+ SweepTree *add(Shape *iSrc, int iBord, int iWeight, int iStartPoint, Shape *iDst);
+};
+
+
+#endif /* !INKSCAPE_LIVAROT_SWEEP_TREE_LIST_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/livarot/sweep-tree.cpp b/src/livarot/sweep-tree.cpp
new file mode 100644
index 0000000..dc350c4
--- /dev/null
+++ b/src/livarot/sweep-tree.cpp
@@ -0,0 +1,567 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#include "livarot/sweep-event-queue.h"
+#include "livarot/sweep-tree-list.h"
+#include "livarot/sweep-tree.h"
+#include "livarot/sweep-event.h"
+#include "livarot/Shape.h"
+
+
+/*
+ * the AVL tree holding the edges intersecting the sweepline
+ * that structure is very sensitive to anything
+ * you have edges stored in nodes, the nodes are sorted in increasing x-order of intersection
+ * with the sweepline, you have the 2 potential intersections of the edge in the node with its
+ * neighbours, plus the fact that it's stored in an array that's realloc'd
+ */
+
+SweepTree::SweepTree()
+{
+ src = nullptr;
+ bord = -1;
+ startPoint = -1;
+ evt[LEFT] = evt[RIGHT] = nullptr;
+ sens = true;
+ //invDirLength=1;
+}
+
+SweepTree::~SweepTree()
+{
+ MakeDelete();
+}
+
+void
+SweepTree::MakeNew(Shape *iSrc, int iBord, int iWeight, int iStartPoint)
+{
+ AVLTree::MakeNew();
+ ConvertTo(iSrc, iBord, iWeight, iStartPoint);
+}
+
+void
+SweepTree::ConvertTo(Shape *iSrc, int iBord, int iWeight, int iStartPoint)
+{
+ src = iSrc;
+ bord = iBord;
+ evt[LEFT] = evt[RIGHT] = nullptr;
+ startPoint = iStartPoint;
+ if (src->getEdge(bord).st < src->getEdge(bord).en) {
+ if (iWeight >= 0)
+ sens = true;
+ else
+ sens = false;
+ } else {
+ if (iWeight >= 0)
+ sens = false;
+ else
+ sens = true;
+ }
+ //invDirLength=src->eData[bord].isqlength;
+ //invDirLength=1/sqrt(src->getEdge(bord).dx*src->getEdge(bord).dx+src->getEdge(bord).dy*src->getEdge(bord).dy);
+}
+
+
+void SweepTree::MakeDelete()
+{
+ for (int i = 0; i < 2; i++) {
+ if (evt[i]) {
+ evt[i]->sweep[1 - i] = nullptr;
+ }
+ evt[i] = nullptr;
+ }
+
+ AVLTree::MakeDelete();
+}
+
+
+// find the position at which node "newOne" should be inserted in the subtree rooted here
+// we want to order with respect to the order of intersections with the sweepline, currently
+// lying at y=px[1].
+// px is the upper endpoint of newOne
+int
+SweepTree::Find(Geom::Point const &px, SweepTree *newOne, SweepTree *&insertL,
+ SweepTree *&insertR, bool sweepSens)
+{
+ // get the edge associated with this node: one point+one direction
+ // since we're dealing with line, the direction (bNorm) is taken downwards
+ Geom::Point bOrig, bNorm;
+ bOrig = src->pData[src->getEdge(bord).st].rx;
+ bNorm = src->eData[bord].rdx;
+ if (src->getEdge(bord).st > src->getEdge(bord).en) {
+ bNorm = -bNorm;
+ }
+ // rotate to get the normal to the edge
+ bNorm=bNorm.ccw();
+
+ Geom::Point diff;
+ diff = px - bOrig;
+
+ // compute (px-orig)^dir to know on which side of this edge the point px lies
+ double y = 0;
+ //if ( startPoint == newOne->startPoint ) {
+ // y=0;
+ //} else {
+ y = dot(bNorm, diff);
+ //}
+ //y*=invDirLength;
+ if (fabs(y) < 0.000001) {
+ // that damn point px lies on me, so i need to consider to direction of the edge in
+ // newOne to know if it goes toward my left side or my right side
+ // sweepSens is needed (actually only used by the Scan() functions) because if the sweepline goes upward,
+ // signs change
+ // prendre en compte les directions
+ Geom::Point nNorm;
+ nNorm = newOne->src->eData[newOne->bord].rdx;
+ if (newOne->src->getEdge(newOne->bord).st >
+ newOne->src->getEdge(newOne->bord).en)
+ {
+ nNorm = -nNorm;
+ }
+ nNorm=nNorm.ccw();
+
+ if (sweepSens) {
+ y = cross(bNorm, nNorm);
+ } else {
+ y = cross(nNorm, bNorm);
+ }
+ if (y == 0) {
+ y = dot(bNorm, nNorm);
+ if (y == 0) {
+ insertL = this;
+ insertR = static_cast<SweepTree *>(elem[RIGHT]);
+ return found_exact;
+ }
+ }
+ }
+ if (y < 0) {
+ if (child[LEFT]) {
+ return (static_cast<SweepTree *>(child[LEFT]))->Find(px, newOne,
+ insertL, insertR,
+ sweepSens);
+ } else {
+ insertR = this;
+ insertL = static_cast<SweepTree *>(elem[LEFT]);
+ if (insertL) {
+ return found_between;
+ } else {
+ return found_on_left;
+ }
+ }
+ } else {
+ if (child[RIGHT]) {
+ return (static_cast<SweepTree *>(child[RIGHT]))->Find(px, newOne,
+ insertL, insertR,
+ sweepSens);
+ } else {
+ insertL = this;
+ insertR = static_cast<SweepTree *>(elem[RIGHT]);
+ if (insertR) {
+ return found_between;
+ } else {
+ return found_on_right;
+ }
+ }
+ }
+ return not_found;
+}
+
+// only find a point's position
+int
+SweepTree::Find(Geom::Point const &px, SweepTree * &insertL,
+ SweepTree * &insertR)
+{
+ Geom::Point bOrig, bNorm;
+ bOrig = src->pData[src->getEdge(bord).st].rx;
+ bNorm = src->eData[bord].rdx;
+ if (src->getEdge(bord).st > src->getEdge(bord).en)
+ {
+ bNorm = -bNorm;
+ }
+ bNorm=bNorm.ccw();
+
+ Geom::Point diff;
+ diff = px - bOrig;
+
+ double y = 0;
+ y = dot(bNorm, diff);
+ if (y == 0)
+ {
+ insertL = this;
+ insertR = static_cast<SweepTree *>(elem[RIGHT]);
+ return found_exact;
+ }
+ if (y < 0)
+ {
+ if (child[LEFT])
+ {
+ return (static_cast<SweepTree *>(child[LEFT]))->Find(px, insertL,
+ insertR);
+ }
+ else
+ {
+ insertR = this;
+ insertL = static_cast<SweepTree *>(elem[LEFT]);
+ if (insertL)
+ {
+ return found_between;
+ }
+ else
+ {
+ return found_on_left;
+ }
+ }
+ }
+ else
+ {
+ if (child[RIGHT])
+ {
+ return (static_cast<SweepTree *>(child[RIGHT]))->Find(px, insertL,
+ insertR);
+ }
+ else
+ {
+ insertL = this;
+ insertR = static_cast<SweepTree *>(elem[RIGHT]);
+ if (insertR)
+ {
+ return found_between;
+ }
+ else
+ {
+ return found_on_right;
+ }
+ }
+ }
+ return not_found;
+}
+
+void
+SweepTree::RemoveEvents(SweepEventQueue & queue)
+{
+ RemoveEvent(queue, LEFT);
+ RemoveEvent(queue, RIGHT);
+}
+
+void SweepTree::RemoveEvent(SweepEventQueue &queue, Side s)
+{
+ if (evt[s]) {
+ queue.remove(evt[s]);
+ evt[s] = nullptr;
+ }
+}
+
+int
+SweepTree::Remove(SweepTreeList &list, SweepEventQueue &queue,
+ bool rebalance)
+{
+ RemoveEvents(queue);
+ AVLTree *tempR = static_cast<AVLTree *>(list.racine);
+ int err = AVLTree::Remove(tempR, rebalance);
+ list.racine = static_cast<SweepTree *>(tempR);
+ MakeDelete();
+ if (list.nbTree <= 1)
+ {
+ list.nbTree = 0;
+ list.racine = nullptr;
+ }
+ else
+ {
+ if (list.racine == list.trees + (list.nbTree - 1))
+ list.racine = this;
+ list.trees[--list.nbTree].Relocate(this);
+ }
+ return err;
+}
+
+int
+SweepTree::Insert(SweepTreeList &list, SweepEventQueue &queue,
+ Shape *iDst, int iAtPoint, bool rebalance, bool sweepSens)
+{
+ if (list.racine == nullptr)
+ {
+ list.racine = this;
+ return avl_no_err;
+ }
+ SweepTree *insertL = nullptr;
+ SweepTree *insertR = nullptr;
+ int insertion =
+ list.racine->Find(iDst->getPoint(iAtPoint).x, this,
+ insertL, insertR, sweepSens);
+
+ if (insertion == found_exact) {
+ if (insertR) {
+ insertR->RemoveEvent(queue, LEFT);
+ }
+ if (insertL) {
+ insertL->RemoveEvent(queue, RIGHT);
+ }
+
+ } else if (insertion == found_between) {
+ insertR->RemoveEvent(queue, LEFT);
+ insertL->RemoveEvent(queue, RIGHT);
+ }
+
+ AVLTree *tempR = static_cast<AVLTree *>(list.racine);
+ int err =
+ AVLTree::Insert(tempR, insertion, static_cast<AVLTree *>(insertL),
+ static_cast<AVLTree *>(insertR), rebalance);
+ list.racine = static_cast<SweepTree *>(tempR);
+ return err;
+}
+
+// insertAt() is a speedup on the regular sweepline: if the polygon contains a point of high degree, you
+// get a set of edge that are to be added in the same position. thus you insert one edge with a regular insert(),
+// and then insert all the other in a doubly-linked list fashion. this avoids the Find() call, but is O(d^2) worst-case
+// where d is the number of edge to add in this fashion. hopefully d remains small
+
+int
+SweepTree::InsertAt(SweepTreeList &list, SweepEventQueue &queue,
+ Shape */*iDst*/, SweepTree *insNode, int fromPt,
+ bool rebalance, bool sweepSens)
+{
+ if (list.racine == nullptr)
+ {
+ list.racine = this;
+ return avl_no_err;
+ }
+
+ Geom::Point fromP;
+ fromP = src->pData[fromPt].rx;
+ Geom::Point nNorm;
+ nNorm = src->getEdge(bord).dx;
+ if (src->getEdge(bord).st > src->getEdge(bord).en)
+ {
+ nNorm = -nNorm;
+ }
+ if (sweepSens == false)
+ {
+ nNorm = -nNorm;
+ }
+
+ Geom::Point bNorm;
+ bNorm = insNode->src->getEdge(insNode->bord).dx;
+ if (insNode->src->getEdge(insNode->bord).st >
+ insNode->src->getEdge(insNode->bord).en)
+ {
+ bNorm = -bNorm;
+ }
+
+ SweepTree *insertL = nullptr;
+ SweepTree *insertR = nullptr;
+ double ang = cross(bNorm, nNorm);
+ if (ang == 0)
+ {
+ insertL = insNode;
+ insertR = static_cast<SweepTree *>(insNode->elem[RIGHT]);
+ }
+ else if (ang > 0)
+ {
+ insertL = insNode;
+ insertR = static_cast<SweepTree *>(insNode->elem[RIGHT]);
+
+ while (insertL)
+ {
+ if (insertL->src == src)
+ {
+ if (insertL->src->getEdge(insertL->bord).st != fromPt
+ && insertL->src->getEdge(insertL->bord).en != fromPt)
+ {
+ break;
+ }
+ }
+ else
+ {
+ int ils = insertL->src->getEdge(insertL->bord).st;
+ int ile = insertL->src->getEdge(insertL->bord).en;
+ if ((insertL->src->pData[ils].rx[0] != fromP[0]
+ || insertL->src->pData[ils].rx[1] != fromP[1])
+ && (insertL->src->pData[ile].rx[0] != fromP[0]
+ || insertL->src->pData[ile].rx[1] != fromP[1]))
+ {
+ break;
+ }
+ }
+ bNorm = insertL->src->getEdge(insertL->bord).dx;
+ if (insertL->src->getEdge(insertL->bord).st >
+ insertL->src->getEdge(insertL->bord).en)
+ {
+ bNorm = -bNorm;
+ }
+ ang = cross(bNorm, nNorm);
+ if (ang <= 0)
+ {
+ break;
+ }
+ insertR = insertL;
+ insertL = static_cast<SweepTree *>(insertR->elem[LEFT]);
+ }
+ }
+ else if (ang < 0)
+ {
+ insertL = insNode;
+ insertR = static_cast<SweepTree *>(insNode->elem[RIGHT]);
+
+ while (insertR)
+ {
+ if (insertR->src == src)
+ {
+ if (insertR->src->getEdge(insertR->bord).st != fromPt
+ && insertR->src->getEdge(insertR->bord).en != fromPt)
+ {
+ break;
+ }
+ }
+ else
+ {
+ int ils = insertR->src->getEdge(insertR->bord).st;
+ int ile = insertR->src->getEdge(insertR->bord).en;
+ if ((insertR->src->pData[ils].rx[0] != fromP[0]
+ || insertR->src->pData[ils].rx[1] != fromP[1])
+ && (insertR->src->pData[ile].rx[0] != fromP[0]
+ || insertR->src->pData[ile].rx[1] != fromP[1]))
+ {
+ break;
+ }
+ }
+ bNorm = insertR->src->getEdge(insertR->bord).dx;
+ if (insertR->src->getEdge(insertR->bord).st >
+ insertR->src->getEdge(insertR->bord).en)
+ {
+ bNorm = -bNorm;
+ }
+ ang = cross(bNorm, nNorm);
+ if (ang > 0)
+ {
+ break;
+ }
+ insertL = insertR;
+ insertR = static_cast<SweepTree *>(insertL->elem[RIGHT]);
+ }
+ }
+
+ int insertion = found_between;
+
+ if (insertL == nullptr) {
+ insertion = found_on_left;
+ }
+ if (insertR == nullptr) {
+ insertion = found_on_right;
+ }
+
+ if (insertion == found_exact) {
+ /* FIXME: surely this can never be called? */
+ if (insertR) {
+ insertR->RemoveEvent(queue, LEFT);
+ }
+ if (insertL) {
+ insertL->RemoveEvent(queue, RIGHT);
+ }
+ } else if (insertion == found_between) {
+ insertR->RemoveEvent(queue, LEFT);
+ insertL->RemoveEvent(queue, RIGHT);
+ }
+
+ AVLTree *tempR = static_cast<AVLTree *>(list.racine);
+ int err =
+ AVLTree::Insert(tempR, insertion, static_cast<AVLTree *>(insertL),
+ static_cast<AVLTree *>(insertR), rebalance);
+ list.racine = static_cast<SweepTree *>(tempR);
+ return err;
+}
+
+void
+SweepTree::Relocate(SweepTree * to)
+{
+ if (this == to)
+ return;
+ AVLTree::Relocate(to);
+ to->src = src;
+ to->bord = bord;
+ to->sens = sens;
+ to->evt[LEFT] = evt[LEFT];
+ to->evt[RIGHT] = evt[RIGHT];
+ to->startPoint = startPoint;
+ if (unsigned(bord) < src->swsData.size())
+ src->swsData[bord].misc = to;
+ if (unsigned(bord) < src->swrData.size())
+ src->swrData[bord].misc = to;
+ if (evt[LEFT])
+ evt[LEFT]->sweep[RIGHT] = to;
+ if (evt[RIGHT])
+ evt[RIGHT]->sweep[LEFT] = to;
+}
+
+// TODO check if ignoring these parameters is bad
+void
+SweepTree::SwapWithRight(SweepTreeList &/*list*/, SweepEventQueue &/*queue*/)
+{
+ SweepTree *tL = this;
+ SweepTree *tR = static_cast<SweepTree *>(elem[RIGHT]);
+
+ tL->src->swsData[tL->bord].misc = tR;
+ tR->src->swsData[tR->bord].misc = tL;
+
+ {
+ Shape *swap = tL->src;
+ tL->src = tR->src;
+ tR->src = swap;
+ }
+ {
+ int swap = tL->bord;
+ tL->bord = tR->bord;
+ tR->bord = swap;
+ }
+ {
+ int swap = tL->startPoint;
+ tL->startPoint = tR->startPoint;
+ tR->startPoint = swap;
+ }
+ //{double swap=tL->invDirLength;tL->invDirLength=tR->invDirLength;tR->invDirLength=swap;}
+ {
+ bool swap = tL->sens;
+ tL->sens = tR->sens;
+ tR->sens = swap;
+ }
+}
+
+void
+SweepTree::Avance(Shape */*dstPts*/, int /*curPoint*/, Shape */*a*/, Shape */*b*/)
+{
+ return;
+/* if ( curPoint != startPoint ) {
+ int nb=-1;
+ if ( sens ) {
+// nb=dstPts->AddEdge(startPoint,curPoint);
+ } else {
+// nb=dstPts->AddEdge(curPoint,startPoint);
+ }
+ if ( nb >= 0 ) {
+ dstPts->swsData[nb].misc=(void*)((src==b)?1:0);
+ int wp=waitingPoint;
+ dstPts->eData[nb].firstLinkedPoint=waitingPoint;
+ waitingPoint=-1;
+ while ( wp >= 0 ) {
+ dstPts->pData[wp].edgeOnLeft=nb;
+ wp=dstPts->pData[wp].nextLinkedPoint;
+ }
+ }
+ startPoint=curPoint;
+ }*/
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/livarot/sweep-tree.h b/src/livarot/sweep-tree.h
new file mode 100644
index 0000000..13e1f76
--- /dev/null
+++ b/src/livarot/sweep-tree.h
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+#ifndef INKSCAPE_LIVAROT_SWEEP_TREE_H
+#define INKSCAPE_LIVAROT_SWEEP_TREE_H
+
+#include "livarot/AVL.h"
+#include <2geom/point.h>
+
+class Shape;
+class SweepEvent;
+class SweepEventQueue;
+class SweepTreeList;
+
+
+/**
+ * One node in the AVL tree of edges.
+ * Note that these nodes will be stored in a dynamically allocated array, hence the Relocate() function.
+ */
+class SweepTree:public AVLTree
+{
+public:
+ SweepEvent *evt[2]; ///< Intersection with the edge on the left and right (if any).
+
+ Shape *src; /**< Shape from which the edge comes. (When doing boolean operation on polygons,
+ * edges can come from 2 different polygons.)
+ */
+ int bord; ///< Edge index in the Shape.
+ bool sens; ///< true= top->bottom; false= bottom->top.
+ int startPoint; ///< point index in the result Shape associated with the upper end of the edge
+
+ SweepTree();
+ ~SweepTree() override;
+
+ // Inits a brand new node.
+ void MakeNew(Shape *iSrc, int iBord, int iWeight, int iStartPoint);
+ // changes the edge associated with this node
+ // goal: reuse the node when an edge follows another, which is the most common case
+ void ConvertTo(Shape *iSrc, int iBord, int iWeight, int iStartPoint);
+
+ // Delete the contents of node.
+ void MakeDelete();
+
+ // utilites
+
+ // the find function that was missing in the AVLTrree class
+ // the return values are defined in LivarotDefs.h
+ int Find(Geom::Point const &iPt, SweepTree *newOne, SweepTree *&insertL,
+ SweepTree *&insertR, bool sweepSens = true);
+ int Find(Geom::Point const &iPt, SweepTree *&insertL, SweepTree *&insertR);
+
+ /// Remove sweepevents attached to this node.
+ void RemoveEvents(SweepEventQueue &queue);
+
+ void RemoveEvent(SweepEventQueue &queue, Side s);
+
+ // overrides of the AVLTree functions, to account for the sorting in the tree
+ // and some other stuff
+ int Remove(SweepTreeList &list, SweepEventQueue &queue, bool rebalance = true);
+ int Insert(SweepTreeList &list, SweepEventQueue &queue, Shape *iDst,
+ int iAtPoint, bool rebalance = true, bool sweepSens = true);
+ int InsertAt(SweepTreeList &list, SweepEventQueue &queue, Shape *iDst,
+ SweepTree *insNode, int fromPt, bool rebalance = true, bool sweepSens = true);
+
+ /// Swap nodes, or more exactly, swap the edges in them.
+ void SwapWithRight(SweepTreeList &list, SweepEventQueue &queue);
+
+ void Avance(Shape *dst, int nPt, Shape *a, Shape *b);
+
+ void Relocate(SweepTree *to);
+};
+
+
+#endif /* !INKSCAPE_LIVAROT_SWEEP_TREE_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :