Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
sweep-tree.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
/*
5 * Authors: see git history
6 *
7 * Copyright (C) 2018 Authors
8 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
9 */
12#include "livarot/sweep-tree.h"
13#include "livarot/sweep-event.h"
14#include "livarot/Shape.h"
15
16
17/*
18 * the AVL tree holding the edges intersecting the sweepline
19 * that structure is very sensitive to anything
20 * you have edges stored in nodes, the nodes are sorted in increasing x-order of intersection
21 * with the sweepline, you have the 2 potential intersections of the edge in the node with its
22 * neighbours, plus the fact that it's stored in an array that's realloc'd
23 */
24
26{
27 src = nullptr;
28 bord = -1;
29 evt[LEFT] = evt[RIGHT] = nullptr;
30}
31
36
37void
38SweepTree::MakeNew(Shape *iSrc, int iBord, int iWeight, int iStartPoint)
39{
41 ConvertTo(iSrc, iBord, iWeight, iStartPoint);
42}
43
44void
45SweepTree::ConvertTo(Shape *iSrc, int iBord, int iWeight, int iStartPoint)
46{
47 src = iSrc;
48 bord = iBord;
49 evt[LEFT] = evt[RIGHT] = nullptr;
50}
51
52
54{
55 for (int i = 0; i < 2; i++) {
56 if (evt[i]) {
57 evt[i]->sweep[1 - i] = nullptr;
58 }
59 evt[i] = nullptr;
60 }
61
63}
64
65
66// find the position at which node "newOne" should be inserted in the subtree rooted here
67// we want to order with respect to the order of intersections with the sweepline, currently
68// lying at y=px[1].
69// px is the upper endpoint of newOne
70int
71SweepTree::Find(Geom::Point const &px, SweepTree *newOne, SweepTree *&insertL,
72 SweepTree *&insertR, bool sweepSens)
73{
74 // get the edge associated with this node: one point+one direction
75 // since we're dealing with line, the direction (bNorm) is taken downwards
76 Geom::Point bOrig, bNorm;
77 bOrig = src->pData[src->getEdge(bord).st].rx;
78 bNorm = src->eData[bord].rdx;
79 if (src->getEdge(bord).st > src->getEdge(bord).en) {
80 bNorm = -bNorm;
81 }
82 // rotate to get the normal to the edge
83 bNorm=bNorm.ccw();
84
85 Geom::Point diff;
86 diff = px - bOrig;
87
88 // compute (px-orig)^dir to know on which side of this edge the point px lies
89 double y = 0;
90 //if ( startPoint == newOne->startPoint ) {
91 // y=0;
92 //} else {
93 y = dot(bNorm, diff);
94 //}
95 //y*=invDirLength;
96 if (fabs(y) < 0.000001) {
97 // that damn point px lies on me, so i need to consider to direction of the edge in
98 // newOne to know if it goes toward my left side or my right side
99 // sweepSens is needed (actually only used by the Scan() functions) because if the sweepline goes upward,
100 // signs change
101 // prendre en compte les directions
102 Geom::Point nNorm;
103 nNorm = newOne->src->eData[newOne->bord].rdx;
104 if (newOne->src->getEdge(newOne->bord).st >
105 newOne->src->getEdge(newOne->bord).en)
106 {
107 nNorm = -nNorm;
108 }
109 nNorm=nNorm.ccw();
110
111 if (sweepSens) {
112 y = cross(bNorm, nNorm);
113 } else {
114 y = cross(nNorm, bNorm);
115 }
116 if (y == 0) {
117 y = dot(bNorm, nNorm);
118 if (y == 0) {
119 insertL = this;
120 insertR = static_cast<SweepTree *>(elem[RIGHT]);
121 return found_exact;
122 }
123 }
124 }
125 if (y < 0) {
126 if (child[LEFT]) {
127 return (static_cast<SweepTree *>(child[LEFT]))->Find(px, newOne,
128 insertL, insertR,
129 sweepSens);
130 } else {
131 insertR = this;
132 insertL = static_cast<SweepTree *>(elem[LEFT]);
133 if (insertL) {
134 return found_between;
135 } else {
136 return found_on_left;
137 }
138 }
139 } else {
140 if (child[RIGHT]) {
141 return (static_cast<SweepTree *>(child[RIGHT]))->Find(px, newOne,
142 insertL, insertR,
143 sweepSens);
144 } else {
145 insertL = this;
146 insertR = static_cast<SweepTree *>(elem[RIGHT]);
147 if (insertR) {
148 return found_between;
149 } else {
150 return found_on_right;
151 }
152 }
153 }
154 return not_found;
155}
156
157// only find a point's position
158int
160 SweepTree * &insertR)
161{
162 Geom::Point bOrig, bNorm;
163 // The start point of the original edge vector
164 bOrig = src->pData[src->getEdge(bord).st].rx;
165 // The edge vector
166 bNorm = src->eData[bord].rdx;
167 // Flip the edge vector if it's bottom to top or horizontal and right to left
168 if (src->getEdge(bord).st > src->getEdge(bord).en)
169 {
170 bNorm = -bNorm;
171 }
172 // rotate the edge vector counter clockwise by 90 degrees
173 bNorm=bNorm.ccw();
174
175 // draw a vector from the start point to the actual point
176 Geom::Point diff;
177 diff = px - bOrig;
178
179 double y = 0;
180 y = dot(bNorm, diff); // take the dot product
181 // ANALOGY FROM DIAGRAM (See doc in header file):
182 // this case is the same as if we are at node (15) and want to add (15). Usually, you can't
183 // add same stuff in a binary search tree but here it's fine to do so (I guess)
184 // In that case, we have found an exact match and the point belongs between 15 and the one
185 // on it's right (16).
186 if (y == 0) // point lies on the edge (or at least the line of the edge)
187 {
188 insertL = this;
189 insertR = static_cast<SweepTree *>(elem[RIGHT]);
190 return found_exact;
191 }
192
193 // ANALOGY FROM DIAGRAM (See doc in header file):
194 // This is the same as inserting 3 while standing at 10, or inserting 3 while at 5 or inserting
195 // 13 while at 15 or inserting 1 while at 2 or inserting 11 while at 12.
196 if (y < 0) // lies to the left of the edge
197 {
198 if (child[LEFT]) // is there child on left? This is true at 10, 5, 15 but not at the nodes such as 2, 6, 12, 16
199 {
200 // if there is a child on left, let the child do the searching
201 return (static_cast<SweepTree *>(child[LEFT]))->Find(px, insertL, // if yes, let that child do the finding now
202 insertR);
203 }
204 else // no child on the left? Means either a leaf node or node has no child on left.
205 {
206 // well we are sure that there is no child on the left, which means this new node goes
207 // to the left of this node, but that doesn't really mean there no left element in the
208 // linked list, there sure can be. For example, if you're inserting 11 while standing
209 // at 12, there is no left child, but in the linked list, there is 10 to the left.
210 insertR = this;
211 insertL = static_cast<SweepTree *>(elem[LEFT]);
212 if (insertL)
213 {
214 return found_between;
215 }
216 else // however, if you're at 2 and inserting 1, there is no left child, but there is also nothing on the left in the linked list either
217 {
218 return found_on_left;
219 }
220 }
221 } // lies to the right of the edge
222 // ANALOGY FROM DIAGRAM (See doc in header file):
223 // This is the same as inserting 14 while standing at 10, 7 while standing at 5, 18 while
224 // standing at 15, 7 while standing at 6, you get the point
225 else
226 {
227 if (child[RIGHT]) // is there a child to the right? If you're at 10 or 5 or 15 there is child on right so you let the child decide where
228 { // new node goes but not if you're at leaf nodes such as 2, 6, 12, 16 or any other node that doesn't have a right child
229 return (static_cast<SweepTree *>(child[RIGHT]))->Find(px, insertL, // let that child do the finding now
230 insertR);
231 }
232 else
233 {
234 // okay so no right child, but stil you can have an element to the right in the linked
235 // list. For example you are at 6 and want to insert 7, no child on the right so we are
236 // sure the new node goes to the right of 6 but there is still 10 to the right in the
237 // double-linked list.
238 insertL = this;
239 insertR = static_cast<SweepTree *>(elem[RIGHT]);
240 if (insertR)
241 {
242 return found_between;
243 }
244 else
245 {
246 return found_on_right;
247 }
248 }
249 }
250 return not_found;
251}
252
253void
255{
256 RemoveEvent(queue, LEFT);
257 RemoveEvent(queue, RIGHT);
258}
259
261{
262 if (evt[s]) {
263 queue.remove(evt[s]);
264 evt[s] = nullptr;
265 }
266}
267
268int
270 bool rebalance)
271{
272 RemoveEvents(queue);
273 AVLTree *tempR = static_cast<AVLTree *>(list.racine);
274 int err = AVLTree::Remove(tempR, rebalance);
275 list.racine = static_cast<SweepTree *>(tempR);
276 MakeDelete();
277 if (list.nbTree <= 1)
278 {
279 list.nbTree = 0;
280 list.racine = nullptr;
281 }
282 else
283 {
284 if (list.racine == list.trees + (list.nbTree - 1))
285 list.racine = this;
286 list.trees[--list.nbTree].Relocate(this);
287 }
288 return err;
289}
290
291int
293 Shape *iDst, int iAtPoint, bool rebalance, bool sweepSens)
294{
295 // if the root node doesn't exist, make this one the root node
296 if (list.racine == nullptr)
297 {
298 list.racine = this;
299 return avl_no_err;
300 }
301 SweepTree *insertL = nullptr;
302 SweepTree *insertR = nullptr;
303 // use the Find call to figure out the exact position where this needs to go
304 int insertion =
305 list.racine->Find(iDst->getPoint(iAtPoint).x, this,
306 insertL, insertR, sweepSens);
307
308 // if the insertion type is found_exact or found_between this new node is getting in between
309 // two existing nodes, which demands that any intersection event that was recorded between
310 // the two must be destroyed now *cuz they are no longer together* :-(
311 if (insertion == found_exact) { // not sure if these if statements are really needed.
312 if (insertR) {
313 insertR->RemoveEvent(queue, LEFT);
314 }
315 if (insertL) {
316 insertL->RemoveEvent(queue, RIGHT);
317 }
318
319 } else if (insertion == found_between) {
320 insertR->RemoveEvent(queue, LEFT);
321 insertL->RemoveEvent(queue, RIGHT);
322 }
323 // let the parent class do the adding now
324 AVLTree *tempR = static_cast<AVLTree *>(list.racine);
325 int err =
326 AVLTree::Insert(tempR, insertion, static_cast<AVLTree *>(insertL),
327 static_cast<AVLTree *>(insertR), rebalance);
328 list.racine = static_cast<SweepTree *>(tempR);
329 return err;
330}
331
332// insertAt() is a speedup on the regular sweepline: if the polygon contains a point of high degree, you
333// get a set of edge that are to be added in the same position. thus you insert one edge with a regular insert(),
334// and then insert all the other in a doubly-linked list fashion. this avoids the Find() call, but is O(d^2) worst-case
335// where d is the number of edge to add in this fashion. hopefully d remains small
336
337int
339 Shape */*iDst*/, SweepTree *insNode, int fromPt,
340 bool rebalance, bool sweepSens)
341{
342 // if root node not set, set it
343 if (list.racine == nullptr)
344 {
345 list.racine = this;
346 return avl_no_err;
347 }
348
349 // the common point between edges
350 Geom::Point fromP;
351 fromP = src->pData[fromPt].rx;
352 // get the edge vector
353 Geom::Point nNorm;
354 nNorm = src->getEdge(bord).dx;
355 // make sure the edge vector is top to bottom or if horizontal
356 if (src->getEdge(bord).st > src->getEdge(bord).en)
357 {
358 nNorm = -nNorm;
359 }
360 if (sweepSens == false) // why tho?
361 {
362 nNorm = -nNorm;
363 }
364
365 Geom::Point bNorm; // the existing edge (kinda the reference node u can say) that we wanna add this one near to
366 bNorm = insNode->src->getEdge(insNode->bord).dx;
367 if (insNode->src->getEdge(insNode->bord).st >
368 insNode->src->getEdge(insNode->bord).en)
369 {
370 bNorm = -bNorm;
371 }
372
373 SweepTree *insertL = nullptr;
374 SweepTree *insertR = nullptr;
375 double ang = cross(bNorm, nNorm); // you can use the diagram in the header documentation to make sense of cross product's direction.
376 if (ang == 0) // node on top of this one, so we just add right here
377 {
378 insertL = insNode;
379 insertR = static_cast<SweepTree *>(insNode->elem[RIGHT]);
380 }
381 else if (ang > 0) // edge is to the left
382 {
383 // initialize such that we are adding this edge between insNode (reference) and whatever is
384 // to it's right, this position will change as we go left now
385 insertL = insNode;
386 insertR = static_cast<SweepTree *>(insNode->elem[RIGHT]);
387
388 // start moving to the left
389 while (insertL)
390 {
391 if (insertL->src == src)
392 {
393 if (insertL->src->getEdge(insertL->bord).st != fromPt
394 && insertL->src->getEdge(insertL->bord).en != fromPt)
395 {
396 break; // if the edge on the left has no endpoint that's fromPt, means we have gone too far, so break
397 // we only case about inserting this at the right position relative to the
398 // existing edges that are connected to fromPt
399 }
400 }
401 else
402 { // TODO: has to do with case when an edge can come from another shape
403 int ils = insertL->src->getEdge(insertL->bord).st;
404 int ile = insertL->src->getEdge(insertL->bord).en;
405 if ((insertL->src->pData[ils].rx[0] != fromP[0]
406 || insertL->src->pData[ils].rx[1] != fromP[1])
407 && (insertL->src->pData[ile].rx[0] != fromP[0]
408 || insertL->src->pData[ile].rx[1] != fromP[1]))
409 {
410 break;
411 }
412 }
413 // bNorm is the new edge (the new reference to which we will compare the new edge (to
414 // add))
415 bNorm = insertL->src->getEdge(insertL->bord).dx;
416 if (insertL->src->getEdge(insertL->bord).st >
417 insertL->src->getEdge(insertL->bord).en)
418 {
419 bNorm = -bNorm;
420 }
421 ang = cross(bNorm, nNorm);
422 if (ang <= 0) // the new edge should go to the right of this one, so break as insertL and insertR are perfect as they are
423 {
424 break;
425 }
426 insertR = insertL; // otherwise go left
427 insertL = static_cast<SweepTree *>(insertR->elem[LEFT]);
428 }
429 }
430 else if (ang < 0) // the new edge goes to the right
431 {
432 // initialize such that we are adding this edge between insNode (reference) and whatever is
433 // to it's right, this position will change as we go left now
434 insertL = insNode;
435 insertR = static_cast<SweepTree *>(insNode->elem[RIGHT]);
436
437 // start moving to the right now
438 while (insertR)
439 {
440 if (insertR->src == src)
441 {
442 if (insertR->src->getEdge(insertR->bord).st != fromPt
443 && insertR->src->getEdge(insertR->bord).en != fromPt)
444 {
445 break; // is the right edge not really attached to fromPt at all? so break
446 }
447 }
448 else
449 {
450 int ils = insertR->src->getEdge(insertR->bord).st;
451 int ile = insertR->src->getEdge(insertR->bord).en;
452 if ((insertR->src->pData[ils].rx[0] != fromP[0]
453 || insertR->src->pData[ils].rx[1] != fromP[1])
454 && (insertR->src->pData[ile].rx[0] != fromP[0]
455 || insertR->src->pData[ile].rx[1] != fromP[1]))
456 {
457 break;
458 }
459 }
460 // the new reference vector that we wanna compare to
461 bNorm = insertR->src->getEdge(insertR->bord).dx;
462 if (insertR->src->getEdge(insertR->bord).st >
463 insertR->src->getEdge(insertR->bord).en)
464 {
465 bNorm = -bNorm;
466 }
467 ang = cross(bNorm, nNorm);
468 if (ang > 0) // oh the edge goes to the left? so we just break since insertL and insertR are perfect then
469 {
470 break;
471 }
472 insertL = insertR; // go further to the right
473 insertR = static_cast<SweepTree *>(insertL->elem[RIGHT]);
474 }
475 }
476
477 int insertion = found_between; // by default set to found_between
478
479 if (insertL == nullptr) { // if nothing to left, it's found_on_left
480 insertion = found_on_left;
481 }
482 if (insertR == nullptr) { // if nothing on right, it's found_on_right
483 insertion = found_on_right;
484 }
485
486 if (insertion == found_exact) {
487 /* FIXME: surely this can never be called? */ // yea never called it looks like :P
488 if (insertR) {
489 insertR->RemoveEvent(queue, LEFT);
490 }
491 if (insertL) {
492 insertL->RemoveEvent(queue, RIGHT);
493 }
494 } else if (insertion == found_between) { // if found_between we do clear any events associated to the two nodes who are now no longer gonna be adjacent
495 insertR->RemoveEvent(queue, LEFT);
496 insertL->RemoveEvent(queue, RIGHT);
497 }
498
499 // let the parent do the actual insertion stuff in the tree now
500 AVLTree *tempR = static_cast<AVLTree *>(list.racine);
501 int err =
502 AVLTree::Insert(tempR, insertion, static_cast<AVLTree *>(insertL),
503 static_cast<AVLTree *>(insertR), rebalance);
504 list.racine = static_cast<SweepTree *>(tempR);
505 return err;
506}
507
508void
510{
511 if (this == to)
512 return;
514 to->src = src;
515 to->bord = bord;
516 to->evt[LEFT] = evt[LEFT];
517 to->evt[RIGHT] = evt[RIGHT];
518 if (unsigned(bord) < src->swsData.size())
519 src->swsData[bord].misc = to;
520 if (unsigned(bord) < src->swrData.size())
521 src->swrData[bord].misc = to;
522 if (evt[LEFT])
523 evt[LEFT]->sweep[RIGHT] = to;
524 if (evt[RIGHT])
525 evt[RIGHT]->sweep[LEFT] = to;
526}
527
528// TODO check if ignoring these parameters is bad
529void
531{
532 SweepTree *tL = this;
533 SweepTree *tR = static_cast<SweepTree *>(elem[RIGHT]);
534
535 tL->src->swsData[tL->bord].misc = tR;
536 tR->src->swsData[tR->bord].misc = tL;
537
538 std::swap(tL->src, tR->src);
539 std::swap(tL->bord, tR->bord);
540}
541
542/*
543 Local Variables:
544 mode:c++
545 c-file-style:"stroustrup"
546 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
547 indent-tabs-mode:nil
548 fill-column:99
549 End:
550*/
551// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
@ found_on_left
Definition LivarotDefs.h:45
@ found_on_right
Definition LivarotDefs.h:46
@ not_found
Definition LivarotDefs.h:43
@ found_between
Definition LivarotDefs.h:47
@ found_exact
Definition LivarotDefs.h:44
Side
Definition LivarotDefs.h:86
@ LEFT
Definition LivarotDefs.h:87
@ RIGHT
Definition LivarotDefs.h:88
@ avl_no_err
Definition LivarotDefs.h:22
TODO: insert short description here.
Definition AVL.h:32
AVLTree * elem[2]
Definition AVL.h:35
void MakeNew()
Definition AVL.cpp:30
void Relocate(AVLTree *to)
Definition AVL.cpp:913
int Insert(AVLTree *&racine, int insertType, AVLTree *insertL, AVLTree *insertR, bool rebalance)
Definition AVL.cpp:808
void MakeDelete()
Definition AVL.cpp:42
AVLTree * child[2]
Definition AVL.h:42
int Remove(AVLTree *&racine, bool rebalance=true)
Definition AVL.cpp:661
Two-dimensional point that doubles as a vector.
Definition point.h:66
constexpr Point ccw() const
Return a point like this point but rotated -90 degrees.
Definition point.h:130
A class to store/manipulate directed graphs.
Definition Shape.h:65
std::vector< sweep_src_data > swsData
Definition Shape.h:1082
std::vector< raster_data > swrData
Definition Shape.h:1084
std::vector< edge_data > eData
Definition Shape.h:1081
dg_arete const & getEdge(int n) const
Get an edge.
Definition Shape.h:548
dg_point const & getPoint(int n) const
Get a point.
Definition Shape.h:537
std::vector< point_data > pData
Definition Shape.h:1085
The structure to hold the intersections events encountered during the sweep.
void remove(SweepEvent *e)
Remove event from the event queue.
SweepTree * sweep[2]
Definition sweep-event.h:27
The sweepline tree to store a linear sequence of edges that intersect with the sweepline in the exact...
SweepTree * racine
SweepTree * trees
One node in the AVL tree of edges.
Definition sweep-tree.h:42
void ConvertTo(Shape *iSrc, int iBord, int iWeight, int iStartPoint)
Reuse this node by just changing the variables.
void SwapWithRight(SweepTreeList &list, SweepEventQueue &queue)
Swap nodes, or more exactly, swap the edges in them.
int InsertAt(SweepTreeList &list, SweepEventQueue &queue, Shape *iDst, SweepTree *insNode, int fromPt, bool rebalance=true, bool sweepSens=true)
Insert this node near an existing node.
void Relocate(SweepTree *to)
TODO: Probably has to do with some AVL relocation.
void RemoveEvent(SweepEventQueue &queue, Side s)
Remove event on the side s if it exists from event queue.
int Insert(SweepTreeList &list, SweepEventQueue &queue, Shape *iDst, int iAtPoint, bool rebalance=true, bool sweepSens=true)
Insert this node at it's appropriate position in the sweepline tree.
void MakeDelete()
Delete this node.
int Remove(SweepTreeList &list, SweepEventQueue &queue, bool rebalance=true)
Shape * src
Definition sweep-tree.h:46
SweepEvent * evt[2]
Definition sweep-tree.h:44
~SweepTree() override
void RemoveEvents(SweepEventQueue &queue)
Remove sweepevents attached to this node.
int Find(Geom::Point const &iPt, SweepTree *newOne, SweepTree *&insertL, SweepTree *&insertR, bool sweepSens=true)
Find where the new edge needs to go in the sweepline tree.
double ang(Point n1, Point n2)
Definition sanitize.cpp:89
Geom::Point dx
Definition Shape.h:463
Geom::Point x
Definition Shape.h:449
A container of intersection events.
TODO: insert short description here.
TODO: insert short description here.
TODO: insert short description here.
void dot(Cairo::RefPtr< Cairo::Context > &cr, double x, double y)