Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
ShapeSweep.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
/*
5 * Authors:
6 * see git history
7 * Fred
8 *
9 * Copyright (C) 2018 Authors
10 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11 */
12
13#include <cstdio>
14#include <cstdlib>
15#include <cstring>
16#include <glib.h>
17#include <2geom/affine.h>
18#include "Shape.h"
21#include "livarot/sweep-tree.h"
22
23//int doDebug=0;
24
25/*
26 * El Intersector.
27 * algorithm: 1) benley ottman to get intersections of all the polygon's edges
28 * 2) rounding of the points of the polygon, Hooby's algorithm
29 * 3) DFS with clockwise choice of the edge to compute the windings
30 * 4) choose edges according to winding numbers and fill rule
31 * some additional nastyness: step 2 needs a seed winding number for the upper-left point of each
32 * connex subgraph of the graph. computing these brutally is O(n^3): baaaad. so during the sweeping in 1)
33 * we keep for each point the edge of the resulting graph (not the original) that lies just on its left;
34 * when the time comes for the point to get its winding number computed, that edge must have been treated,
35 * because its upper end lies above the aforementioned point, meaning we know the winding number of the point.
36 * only, there is a catch: since we're sweeping the polygon, the edge we want to link the point to has not yet been
37 * added (that would be too easy...). so the points are put on a linked list on the original shape's edge, and the list
38 * is flushed when the edge is added.
39 * rounding: to do the rounding, we need to find which edges cross the surrounding of the rounded points (at
40 * each sweepline position). grunt method tries all combination of "rounded points in the sweepline"x"edges crossing
41 * the sweepline". That's bad (and that's what polyboolean does, if i am not mistaken). so for each point
42 * rounded in a given sweepline, keep immediate left and right edges at the time the point is treated.
43 * when edges/points crossing are searched, walk the edge list (in the sweepline at the end of the batch) starting
44 * from the rounded points' left and right from that time. may sound strange, but it works because edges that
45 * end or start in the batch have at least one end in the batch.
46 * all these are the cause of the numerous linked lists of points and edges maintained in the sweeping
47 */
48
49void
51{
52 MakePointData (true);
53 MakeEdgeData (true);
54 MakeSweepSrcData (true);
55}
56
57void
59{
60 MakePointData (false);
61 MakeEdgeData (false);
62 MakeSweepSrcData (false);
63}
64
65void
70
71int
73{
74 Reset (0, 0);
75 if (a->numberOfPoints() <= 1 || a->numberOfEdges() <= 1)
76 return 0;
77 if (directedEulerian(a) == false)
78 return shape_input_err;
79
80 _pts = a->_pts;
81 if (numberOfPoints() > maxPt)
82 {
84 if (_has_points_data) {
85 pData.resize(maxPt);
87 _bbox_up_to_date = false;
88 }
89 }
90
91 _aretes = a->_aretes;
92 if (numberOfEdges() > maxAr)
93 {
96 eData.resize(maxAr);
98 swsData.resize(maxAr);
100 swdData.resize(maxAr);
102 swrData.resize(maxAr);
103 }
104
105 MakePointData (true);
106 MakeEdgeData (true);
107 MakeSweepDestData (true);
108
110
111 for (int i = 0; i < numberOfPoints(); i++) {
112 _pts[i].x = pData[i].rx;
113 _pts[i].oldDegree = getPoint(i).totalDegree();
114 }
115
116 for (int i = 0; i < a->numberOfEdges(); i++)
117 {
118 eData[i].rdx = pData[getEdge(i).en].rx - pData[getEdge(i).st].rx;
119 eData[i].weight = 1;
120 _aretes[i].dx = eData[i].rdx;
121 }
122
124
125 _need_edges_sorting = true;
126 GetWindings (true);
127
128// Plot(341,56,8,400,400,true,true,false,true);
129 for (int i = 0; i < numberOfEdges(); i++)
130 {
131 swdData[i].leW %= 2;
132 swdData[i].riW %= 2;
133 if (swdData[i].leW < 0)
134 swdData[i].leW = -swdData[i].leW;
135 if (swdData[i].riW < 0)
136 swdData[i].riW = -swdData[i].riW;
137 if (swdData[i].leW > 0 && swdData[i].riW <= 0)
138 {
139 eData[i].weight = 1;
140 }
141 else if (swdData[i].leW <= 0 && swdData[i].riW > 0)
142 {
143 Inverse (i);
144 eData[i].weight = 1;
145 }
146 else
147 {
148 eData[i].weight = 0;
149 SubEdge (i);
150 i--;
151 }
152 }
153
154 MakePointData (false);
155 MakeEdgeData (false);
156 MakeSweepDestData (false);
157
158 if (directedEulerian(this) == false)
159 {
160// printf( "pas euclidian2");
161 _pts.clear();
162 _aretes.clear();
163 return shape_euler_err;
164 }
165
167 return 0;
168}
169
170int
172{
173 // reset any existing stuff in this shape
174 Reset (0, 0);
175
176 // nothing to do with 0/1 points/edges
177 if (a->numberOfPoints() <= 1 || a->numberOfEdges() <= 1) {
178 return 0;
179 }
180
181 // if shape is not eulerian, we can't proceedA, why though you might ask?
182 // I think because if it's not eulerian, winding numbers won't be consistent and thus
183 // nothing else will work
184 if ( directed != fill_justDont && directedEulerian(a) == false ) {
185 g_warning ("Shape error in ConvertToShape: directedEulerian(a) == false\n");
186 return shape_input_err;
187 }
188
189 a->ResetSweep();
190
191 // allocating the sweepline data structures
192 if (sTree == nullptr) {
194 }
195 if (sEvts == nullptr) {
197 }
198
199 // make room for stuff and set flags
200 MakePointData(true);
201 MakeEdgeData(true);
202 MakeSweepSrcData(true);
203 MakeSweepDestData(true);
205
206 // initialize pData and eData arrays, stores rounded points, rounded edge vectors and their lengths
209
210 // sort points of a, top to bottom so we can sweepline
212
213 // clear the chgts array
214 chgts.clear();
215
216 double lastChange = a->pData[0].rx[1] - 1.0;
217 int lastChgtPt = 0;
218 int edgeHead = -1;
219 Shape *shapeHead = nullptr;
220
222
223 // index of the current point in shape a
224 int curAPt = 0;
225
226 // as long as there is a point we haven't seen yet or events that we haven't popped yet
227 while (curAPt < a->numberOfPoints() || sEvts->size() > 0) {
228 Geom::Point ptX;
229 double ptL, ptR;
230 SweepTree *intersL = nullptr;
231 SweepTree *intersR = nullptr;
232 int nPt = -1;
233 Shape *ptSh = nullptr;
234 bool isIntersection = false;
235
236 // this block gives us the earliest most point (sweeping top to bottom and left to right)
237 // whether it comes from the intersection event priority queue sEvts or sweepline list sTree.
238 // isIntersection tell us if it's coming from an intersection or from the endpoints list of shape a
239 // ptX contains the actual point itself
240 // ptSh contains the shape from which the point comes (if it does) (shape a always)
241 // nPt is the index of the point if it's coming from a shape
242
243 // is there an intersection event to pop?
244 if (sEvts->peek(intersL, intersR, ptX, ptL, ptR))
245 {
246 // is that intersection event before the current point in shape a? If yes, we pop and process the intersection event otherwise
247 // we process the point in shape a, whichever comes first (sweeping top to bottom) the one with smaller y or smaller x (if same y)
248 if (a->pData[curAPt].pending > 0
249 || (a->pData[curAPt].rx[1] > ptX[1]
250 || (a->pData[curAPt].rx[1] == ptX[1]
251 && a->pData[curAPt].rx[0] > ptX[0])))
252 {
253 // if yes, let's process the intersection point
254 /* FIXME: could just be pop? */
255 sEvts->extract(intersL, intersR, ptX, ptL, ptR);
256 isIntersection = true;
257 }
258 else // otherwise, we process the current point in shape a
259 {
260 nPt = curAPt++; // nPt stores the index of this point in shape a that we are going to process
261 ptSh = a;
262 ptX = ptSh->pData[nPt].rx; // get the rounded version of the current point in ptX
263 isIntersection = false; // not an intersection
264 }
265 }
266 else
267 {
268 nPt = curAPt++;
269 ptSh = a;
270 ptX = ptSh->pData[nPt].rx;
271 isIntersection = false;
272 }
273
274 if (isIntersection == false) // if this is not intersection event and the point has total degree of 0, we have nothing to do
275 {
276 if (ptSh->getPoint(nPt).dI == 0 && ptSh->getPoint(nPt).dO == 0)
277 continue;
278 }
279
280 // wherever the point comes from, we add the rounded version to "this" shape's list of points
281 Geom::Point rPtX;
282 rPtX[0]= Round (ptX[0]);
283 rPtX[1]= Round (ptX[1]);
284 int lastPointNo = AddPoint (rPtX); // lastPointNo is the index of this last rounded point we added
285 pData[lastPointNo].rx = rPtX;
286
287 // this whole block deals with the reconstruction procedure only do this if the y level
288 // changed, we don't need to do this as long as the y level has not changed, note that
289 // sweepline in this algorithm moves top to bottom but also left to right when there are
290 // multiple points at same y better to think of it as the sweepline being slightly slanted such
291 // that it'll hit the left points earlier than the right ones In fact, it's better to visualize
292 // as if we are iterating through set of points top to bottom and left ot right instead of a
293 // sweepline.
294 if (rPtX[1] > lastChange)
295 {
296 // the important thing this function does is that it sorts points and merges any duplicate points
297 // so edges with different points (which were at the same coordinates) would be forced to point
298 // to the same exact point. (useful for cases when multiple edges intersect at the same point)
299 // Sort all points before lastPointNo
300 int lastI = AssemblePoints (lastChgtPt, lastPointNo); // lastI is one more than the index of the last point that
301 // was added by AssemblePoints after sorting and merging
302
303 // after AssemblePoints is done sorting, the newInd variable holds the new index of that point
304
305 // update the leftRnd and rightRnd indexes to newInd while traversing the linked list of
306 // edges and shapes. leftRnd and rightRnd are the left most and right most points of an edge
307 // in the resultant polygon that intersect the sweepline. In all non-horizontal edges, both
308 // would be identical, only when the edge is horizontal, the two will be different since the
309 // sweepline will intersect it at multiple endpoints.
310 // To define it more strictly, you can think of leftRnd and rightRnd as being the left most
311 // and right most point in the final resulted shape ("this" shape) at the y level lastChange.
312 Shape *curSh = shapeHead;
313 int curBo = edgeHead;
314 while (curSh)
315 {
316 curSh->swsData[curBo].leftRnd =
317 pData[curSh->swsData[curBo].leftRnd].newInd;
318 curSh->swsData[curBo].rightRnd =
319 pData[curSh->swsData[curBo].rightRnd].newInd;
320
321 Shape *neSh = curSh->swsData[curBo].nextSh;
322 curBo = curSh->swsData[curBo].nextBo;
323 curSh = neSh;
324 }
325
326 for (auto & chgt : chgts)
327 {
328 chgt.ptNo = pData[chgt.ptNo].newInd; // this updates the ptNo index to the new index, so identical points really merge
329 if (chgt.type == 0)
330 {
331 if (chgt.src->getEdge(chgt.bord).st <
332 chgt.src->getEdge(chgt.bord).en)
333 {
334 chgt.src->swsData[chgt.bord].stPt = // <-- No idea where stPt and enPt are really used
335 chgt.ptNo;
336 }
337 else
338 {
339 chgt.src->swsData[chgt.bord].enPt =
340 chgt.ptNo;
341 }
342 }
343 else if (chgt.type == 1)
344 {
345 if (chgt.src->getEdge(chgt.bord).st >
346 chgt.src->getEdge(chgt.bord).en)
347 {
348 chgt.src->swsData[chgt.bord].stPt =
349 chgt.ptNo;
350 }
351 else
352 {
353 chgt.src->swsData[chgt.bord].enPt =
354 chgt.ptNo;
355 }
356 }
357 }
358
359 // finds if points at the y level "lastChange" lie on top of the edge and if yes, modifies
360 // leftRnd and rightRnd of those edges accordingly
361 CheckAdjacencies (lastI, lastChgtPt, shapeHead, edgeHead);
362
363 // reconstruct the edges
364 CheckEdges (lastI, lastChgtPt, a, nullptr, bool_op_union);
365
366 // for each one of the points in the range lastChgtPt..(lastI-1) and if there are already
367 // points associated to the edge pData[i].askForWindingB, push the current one (i) in the linked
368 // list
369 for (int i = lastChgtPt; i < lastI; i++) {
370 if (pData[i].askForWindingS) {
371 Shape *windS = pData[i].askForWindingS;
372 int windB = pData[i].askForWindingB;
373 pData[i].nextLinkedPoint = windS->swsData[windB].firstLinkedPoint;
374 windS->swsData[windB].firstLinkedPoint = i;
375 }
376 }
377
378 // note that AssemblePoints doesn't even touch lastPointNo, it iterates on all the points before
379 // lastPointNo, so while that range might have shrinked because of duplicate points, lastPointNo
380 // remains where it was, so we check if some points got merged, if yes, lastPointNo is kinda far away
381 // from those points (there are garbage points in between due to merging), so we drag it back
382 if (lastI < lastPointNo) {
383 _pts[lastI] = getPoint(lastPointNo);
384 pData[lastI] = pData[lastPointNo];
385 }
386 // set lastPointNo to lastI (the new index for lastPointNo)
387 lastPointNo = lastI;
388 _pts.resize(lastI + 1); // resize and delete everything beyond lastPointNo, we add 1 cuz lastI is an index so u
389 // add one to convert it to size
390
391 lastChgtPt = lastPointNo; // set lastChgtPt
392 lastChange = rPtX[1]; // set lastChange
393 chgts.clear(); // this chgts array gets cleared up whenever the y of the sweepline changes
394 edgeHead = -1;
395 shapeHead = nullptr;
396 }
397
398
399 // are we processing an intersection point?
400 if (isIntersection)
401 {
402 // printf("(%i %i [%i %i]) ",intersL->bord,intersR->bord,intersL->startPoint,intersR->startPoint);
403 // remove any intersections that have interesL as a RIGHT edge
404 intersL->RemoveEvent (*sEvts, LEFT);
405 // remove any intersections that have intersR as a LEFT edge
406 intersR->RemoveEvent (*sEvts, RIGHT);
407
408 // add the intersection point to the chgts array
409 AddChgt (lastPointNo, lastChgtPt, shapeHead, edgeHead, INTERSECTION,
410 intersL->src, intersL->bord, intersR->src, intersR->bord);
411
412 // swap the edges intersL and intersR with each other (because they swap their intersection point with the sweepline as
413 // you pass the intersection point)
414 intersL->SwapWithRight (*sTree, *sEvts);
415
416 // test intersection of the now left edge with the one on its left
417 TesteIntersection (intersL, LEFT, false);
418 // test intersection of the now right edge with the one on its right
419 TesteIntersection (intersR, RIGHT, false);
420 }
421 else
422 { // we are doing with a point in shape a (not an intersection point)
423 int cb;
424
425 // this whole block:
426 // - Counts how many edges start at the current point (nbDn)
427 // - Counts how many edges end at the current point (nbUp)
428 // - Notes the last edge that starts here (dnNo)
429 // - Notes the last edge that ends here (upNo)
430 int nbUp = 0, nbDn = 0;
431 int upNo = -1, dnNo = -1;
432 cb = ptSh->getPoint(nPt).incidentEdge[FIRST];
433 while (cb >= 0 && cb < ptSh->numberOfEdges())
434 {
435 if ((ptSh->getEdge(cb).st < ptSh->getEdge(cb).en
436 && nPt == ptSh->getEdge(cb).en)
437 || (ptSh->getEdge(cb).st > ptSh->getEdge(cb).en
438 && nPt == ptSh->getEdge(cb).st))
439 {
440 upNo = cb;
441 nbUp++;
442 }
443 if ((ptSh->getEdge(cb).st > ptSh->getEdge(cb).en
444 && nPt == ptSh->getEdge(cb).en)
445 || (ptSh->getEdge(cb).st < ptSh->getEdge(cb).en
446 && nPt == ptSh->getEdge(cb).st))
447 {
448 dnNo = cb;
449 nbDn++;
450 }
451 cb = ptSh->NextAt (nPt, cb);
452 }
453
454 if (nbDn <= 0)
455 {
456 upNo = -1;
457 }
458 if (upNo >= 0 && ptSh->swsData[upNo].misc == nullptr)
459 {
460 upNo = -1;
461 }
462
463 bool doWinding = true;
464
465 // these blocks of code do the job of adding/removing edges
466 // however, there are some optimizations which leads to their weird if blocks
467
468 // Is there any edge that ends here?
469 if (nbUp > 0)
470 {
471 cb = ptSh->getPoint(nPt).incidentEdge[FIRST];
472 // for all edges that connect to this point
473 while (cb >= 0 && cb < ptSh->numberOfEdges())
474 { // if the edge ends here
475 if ((ptSh->getEdge(cb).st < ptSh->getEdge(cb).en
476 && nPt == ptSh->getEdge(cb).en)
477 || (ptSh->getEdge(cb).st > ptSh->getEdge(cb).en
478 && nPt == ptSh->getEdge(cb).st))
479 {
480 if (cb != upNo) // if this is not the last edge that ended at this point
481 {
482 SweepTree *node = ptSh->swsData[cb].misc; // get the sweepline node for this edge
483 if (node == nullptr)
484 {
485 }
486 else
487 {
488 AddChgt (lastPointNo, lastChgtPt, shapeHead, // add the corresponding remove event in chgts
489 edgeHead, EDGE_REMOVED, node->src, node->bord,
490 nullptr, -1);
491 ptSh->swsData[cb].misc = nullptr;
492
493 int onLeftB = -1, onRightB = -1;
494 Shape *onLeftS = nullptr;
495 Shape *onRightS = nullptr;
496 if (node->elem[LEFT])
497 {
498 onLeftB =
499 (static_cast <
500 SweepTree * >(node->elem[LEFT]))->bord;
501 onLeftS =
502 (static_cast <
503 SweepTree * >(node->elem[LEFT]))->src;
504 }
505 if (node->elem[RIGHT])
506 {
507 onRightB =
508 (static_cast <
509 SweepTree * >(node->elem[RIGHT]))->bord;
510 onRightS =
511 (static_cast <
512 SweepTree * >(node->elem[RIGHT]))->src;
513 }
514
515 node->Remove (*sTree, *sEvts, true); // remove this edge
516 // if there are edges on the left and right and they do not start or end at the current point
517 // test if they interesect with each other (since now this edge just go removed and they are side to side)
518 if (onLeftS && onRightS)
519 {
520 SweepTree *onLeft = onLeftS->swsData[onLeftB].misc;
521 if (onLeftS == ptSh
522 && (onLeftS->getEdge(onLeftB).en == nPt
523 || onLeftS->getEdge(onLeftB).st ==
524 nPt))
525 {
526 }
527 else
528 {
529 if (onRightS == ptSh
530 && (onRightS->getEdge(onRightB).en ==
531 nPt
532 || onRightS->getEdge(onRightB).
533 st == nPt))
534 {
535 }
536 else
537 {
538 TesteIntersection (onLeft, RIGHT, false);
539 }
540 }
541 }
542 }
543 }
544 }
545 cb = ptSh->NextAt (nPt, cb);
546 }
547 }
548
549 // traitement du "upNo devient dnNo"
550 SweepTree *insertionNode = nullptr;
551 if (dnNo >= 0) // if there is a last edge that started here
552 {
553 if (upNo >= 0) // and there is a last edge that ended here
554 {
555 SweepTree *node = (SweepTree *) ptSh->swsData[upNo].misc;
556
557 AddChgt (lastPointNo, lastChgtPt, shapeHead, edgeHead, EDGE_REMOVED, // add edge removal event to the list
558 node->src, node->bord, nullptr, -1);
559
560 ptSh->swsData[upNo].misc = nullptr;
561
562 node->RemoveEvents (*sEvts); // remove any events associated with this node
563 node->ConvertTo (ptSh, dnNo, 1, lastPointNo); // convert this node the last edge that got added at this point
564 ptSh->swsData[dnNo].misc = node; // store the sweepline edge tree node at misc for later use
565 TesteIntersection (node, RIGHT, false); // test the interesction of this node with the one on its right
566 TesteIntersection (node, LEFT, false); // test the interesection of this node with the one on its left
567 insertionNode = node; // a variable to keep the pointer to this node for later use
568
569 ptSh->swsData[dnNo].curPoint = lastPointNo; // mark the curPoint in swsData for later use in reconstruction
570 AddChgt (lastPointNo, lastChgtPt, shapeHead, edgeHead, EDGE_INSERTED,
571 node->src, node->bord, nullptr, -1); // add the edge insertion event to chgts
572 }
573 else // if there is a last edge that started at this point but not any that ended
574 {
575 SweepTree *node = sTree->add(ptSh, dnNo, 1, lastPointNo, this); // add this edge
576 ptSh->swsData[dnNo].misc = node; // store in misc too
577 node->Insert (*sTree, *sEvts, this, lastPointNo, true); // insert the node at its right location in the sweepline tree
578 if (doWinding)
579 {
580 SweepTree *myLeft =
581 static_cast < SweepTree * >(node->elem[LEFT]);
582 if (myLeft)
583 {
584 pData[lastPointNo].askForWindingS = myLeft->src;
585 pData[lastPointNo].askForWindingB = myLeft->bord;
586 }
587 else
588 {
589 pData[lastPointNo].askForWindingB = -1;
590 }
591 doWinding = false;
592 }
593 TesteIntersection (node, RIGHT, false); // test intersection of this newly inserted node with the one on its right
594 TesteIntersection (node, LEFT, false); // test intersection of this newly inserted node with the one on its left
595 insertionNode = node; // store insertionNode for later use
596
597 ptSh->swsData[dnNo].curPoint = lastPointNo; // mark the curPoint appropriately
598 AddChgt (lastPointNo, lastChgtPt, shapeHead, edgeHead, EDGE_INSERTED, // add the edge inserted event
599 node->src, node->bord, nullptr, -1);
600 }
601 }
602
603 if (nbDn > 1) // if there are more than 1 edges that start at this point
604 { // si nbDn == 1 , alors dnNo a deja ete traite
605 cb = ptSh->getPoint(nPt).incidentEdge[FIRST];
606 while (cb >= 0 && cb < ptSh->numberOfEdges()) // for all edges that connect to this point
607 {
608 if ((ptSh->getEdge(cb).st > ptSh->getEdge(cb).en
609 && nPt == ptSh->getEdge(cb).en)
610 || (ptSh->getEdge(cb).st < ptSh->getEdge(cb).en
611 && nPt == ptSh->getEdge(cb).st))
612 { // if the edge starts here
613 if (cb != dnNo)
614 {
615 SweepTree *node = sTree->add(ptSh, cb, 1, lastPointNo, this); // add the node to the tree
616 ptSh->swsData[cb].misc = node;
617 node->InsertAt (*sTree, *sEvts, this, insertionNode, // insert it at appropriate position
618 nPt, true);
619 if (doWinding)
620 {
621 SweepTree *myLeft =
622 static_cast < SweepTree * >(node->elem[LEFT]);
623 if (myLeft)
624 {
625 pData[lastPointNo].askForWindingS =
626 myLeft->src;
627 pData[lastPointNo].askForWindingB =
628 myLeft->bord;
629 }
630 else
631 {
632 pData[lastPointNo].askForWindingB = -1;
633 }
634 doWinding = false;
635 }
636 TesteIntersection (node, RIGHT, false); // test intersection of this edge with one on its left
637 TesteIntersection (node, LEFT, false); // test intersection of this edge with one on its right
638
639 ptSh->swsData[cb].curPoint = lastPointNo; // store curPoint in sws
640 AddChgt (lastPointNo, lastChgtPt, shapeHead, // add appropriate edge insertion event in chgts
641 edgeHead, EDGE_INSERTED, node->src, node->bord, nullptr,
642 -1);
643 }
644 }
645 cb = ptSh->NextAt (nPt, cb);
646 }
647 }
648 }
649 }
650 // a code block totally identical to the one found above in loop. Has to be run one last time
651 // so written outside..
652 {
653 int lastI = AssemblePoints (lastChgtPt, numberOfPoints());
654
655
656 Shape *curSh = shapeHead;
657 int curBo = edgeHead;
658 while (curSh)
659 {
660 curSh->swsData[curBo].leftRnd =
661 pData[curSh->swsData[curBo].leftRnd].newInd;
662 curSh->swsData[curBo].rightRnd =
663 pData[curSh->swsData[curBo].rightRnd].newInd;
664
665 Shape *neSh = curSh->swsData[curBo].nextSh;
666 curBo = curSh->swsData[curBo].nextBo;
667 curSh = neSh;
668 }
669
670 for (auto & chgt : chgts)
671 {
672 chgt.ptNo = pData[chgt.ptNo].newInd;
673 if (chgt.type == 0)
674 {
675 if (chgt.src->getEdge(chgt.bord).st <
676 chgt.src->getEdge(chgt.bord).en)
677 {
678 chgt.src->swsData[chgt.bord].stPt = chgt.ptNo;
679 }
680 else
681 {
682 chgt.src->swsData[chgt.bord].enPt = chgt.ptNo;
683 }
684 }
685 else if (chgt.type == 1)
686 {
687 if (chgt.src->getEdge(chgt.bord).st >
688 chgt.src->getEdge(chgt.bord).en)
689 {
690 chgt.src->swsData[chgt.bord].stPt = chgt.ptNo;
691 }
692 else
693 {
694 chgt.src->swsData[chgt.bord].enPt = chgt.ptNo;
695 }
696 }
697 }
698
699 CheckAdjacencies (lastI, lastChgtPt, shapeHead, edgeHead);
700
701 CheckEdges (lastI, lastChgtPt, a, nullptr, bool_op_union);
702
703 for (int i = lastChgtPt; i < lastI; i++)
704 {
705 if (pData[i].askForWindingS)
706 {
707 Shape *windS = pData[i].askForWindingS;
708 int windB = pData[i].askForWindingB;
709 pData[i].nextLinkedPoint = windS->swsData[windB].firstLinkedPoint;
710 windS->swsData[windB].firstLinkedPoint = i;
711 }
712 }
713
714 _pts.resize(lastI);
715
716 edgeHead = -1;
717 shapeHead = nullptr;
718 }
719
720 chgts.clear();
721
722 // Plot (98.0, 112.0, 8.0, 400.0, 400.0, true, true, true, true);
723 // Plot(200.0,200.0,2.0,400.0,400.0,true,true,true,true);
724
725 // AssemblePoints(a);
726
727 // GetAdjacencies(a);
728
729 // MakeAretes(a);
731
732 // deal with doublon edges (edges on top of each other)
733 // we only keep one edge and set its weight equivalent to the net difference of the edges.
734 // If two edges are exactly identical and in same direction their weights add up, if they
735 // are in the opposite direction, weights are subtracted. The other edges are removed.
736 AssembleAretes (directed);
737
738 // Plot (98.0, 112.0, 8.0, 400.0, 400.0, true, true, true, true);
739
740 // store the degrees at this point in time
741 for (int i = 0; i < numberOfPoints(); i++)
742 {
743 _pts[i].oldDegree = getPoint(i).totalDegree();
744 }
745 // Validate();
746
747 _need_edges_sorting = true;
748 if ( directed == fill_justDont ) {
749 SortEdges(); // sorting edges
750 } else {
751 GetWindings (); // getting winding numbers of the edges
752 }
753 // Plot (98.0, 112.0, 8.0, 400.0, 400.0, true, true, true, true);
754 // if ( doDebug ) {
755 // a->CalcBBox();
756 // a->Plot(a->leftX,a->topY,32.0,0.0,0.0,true,true,true,true,"orig.svg");
757 // Plot(a->leftX,a->topY,32.0,0.0,0.0,true,true,true,true,"winded.svg");
758 // }
759
760 // change edges depending on the fill rule. Decide whether to keep it, invert it or get rid of it
761 if (directed == fill_positive)
762 {
763 if (invert)
764 {
765 for (int i = 0; i < numberOfEdges(); i++)
766 {
767 if (swdData[i].leW < 0 && swdData[i].riW >= 0)
768 {
769 eData[i].weight = 1;
770 }
771 else if (swdData[i].leW >= 0 && swdData[i].riW < 0)
772 {
773 Inverse (i);
774 eData[i].weight = 1;
775 }
776 else
777 {
778 eData[i].weight = 0;
779 SubEdge (i);
780 i--;
781 }
782 }
783 }
784 else
785 {
786 for (int i = 0; i < numberOfEdges(); i++)
787 {
788 if (swdData[i].leW > 0 && swdData[i].riW <= 0)
789 {
790 eData[i].weight = 1;
791 }
792 else if (swdData[i].leW <= 0 && swdData[i].riW > 0)
793 {
794 Inverse (i);
795 eData[i].weight = 1;
796 }
797 else
798 {
799 eData[i].weight = 0;
800 SubEdge (i);
801 i--;
802 }
803 }
804 }
805 }
806 else if (directed == fill_nonZero)
807 {
808 if (invert)
809 {
810 for (int i = 0; i < numberOfEdges(); i++)
811 {
812 if (swdData[i].leW < 0 && swdData[i].riW == 0)
813 {
814 eData[i].weight = 1;
815 }
816 else if (swdData[i].leW > 0 && swdData[i].riW == 0)
817 {
818 eData[i].weight = 1;
819 }
820 else if (swdData[i].leW == 0 && swdData[i].riW < 0)
821 {
822 Inverse (i);
823 eData[i].weight = 1;
824 }
825 else if (swdData[i].leW == 0 && swdData[i].riW > 0)
826 {
827 Inverse (i);
828 eData[i].weight = 1;
829 }
830 else
831 {
832 eData[i].weight = 0;
833 SubEdge (i);
834 i--;
835 }
836 }
837 }
838 else
839 {
840 for (int i = 0; i < numberOfEdges(); i++)
841 {
842 if (swdData[i].leW > 0 && swdData[i].riW == 0)
843 {
844 eData[i].weight = 1;
845 }
846 else if (swdData[i].leW < 0 && swdData[i].riW == 0)
847 {
848 eData[i].weight = 1;
849 }
850 else if (swdData[i].leW == 0 && swdData[i].riW > 0)
851 {
852 Inverse (i);
853 eData[i].weight = 1;
854 }
855 else if (swdData[i].leW == 0 && swdData[i].riW < 0)
856 {
857 Inverse (i);
858 eData[i].weight = 1;
859 }
860 else
861 {
862 eData[i].weight = 0;
863 SubEdge (i);
864 i--;
865 }
866 }
867 }
868 }
869 else if (directed == fill_oddEven)
870 {
871 for (int i = 0; i < numberOfEdges(); i++)
872 {
873 swdData[i].leW %= 2;
874 swdData[i].riW %= 2;
875 if (swdData[i].leW < 0)
876 swdData[i].leW = -swdData[i].leW;
877 if (swdData[i].riW < 0)
878 swdData[i].riW = -swdData[i].riW;
879 if (swdData[i].leW > 0 && swdData[i].riW <= 0)
880 {
881 eData[i].weight = 1;
882 }
883 else if (swdData[i].leW <= 0 && swdData[i].riW > 0)
884 {
885 Inverse (i);
886 eData[i].weight = 1;
887 }
888 else
889 {
890 eData[i].weight = 0;
891 SubEdge (i);
892 i--;
893 }
894 }
895 } else if ( directed == fill_justDont ) {
896 for (int i=0;i<numberOfEdges();i++) {
897 if ( getEdge(i).st < 0 || getEdge(i).en < 0 ) {
898 SubEdge(i);
899 i--;
900 } else {
901 eData[i].weight = 0;
902 }
903 }
904 }
905
906 // Plot(200.0,200.0,2.0,400.0,400.0,true,true,true,true);
907
908 delete sTree;
909 sTree = nullptr;
910 delete sEvts;
911 sEvts = nullptr;
912
913 MakePointData (false);
914 MakeEdgeData (false);
915 MakeSweepSrcData (false);
916 MakeSweepDestData (false);
917 a->CleanupSweep ();
919 return 0;
920}
921
922// technically it's just a ConvertToShape() on 2 polygons at the same time, and different rules
923// for choosing the edges according to their winding numbers.
924// probably one of the biggest function i ever wrote.
925int
926Shape::Booleen (Shape * a, Shape * b, BooleanOp mod,int cutPathID)
927{
928 if (a == b || a == nullptr || b == nullptr)
929 return shape_input_err;
930 Reset (0, 0);
931 if (a->numberOfPoints() <= 1 || a->numberOfEdges() <= 1)
932 return 0;
933 if (b->numberOfPoints() <= 1 || b->numberOfEdges() <= 1)
934 return 0;
935 if ( mod == bool_op_cut ) {
936 } else if ( mod == bool_op_slice ) {
937 } else {
938 if (a->type != shape_polygon)
939 return shape_input_err;
940 if (b->type != shape_polygon)
941 return shape_input_err;
942 }
943
944 a->ResetSweep ();
945 b->ResetSweep ();
946
947 if (sTree == nullptr) {
949 }
950 if (sEvts == nullptr) {
952 }
953
954 MakePointData (true);
955 MakeEdgeData (true);
956 MakeSweepSrcData (true);
957 MakeSweepDestData (true);
958 if (a->hasBackData () && b->hasBackData ())
959 {
960 MakeBackData (true);
961 }
962 else
963 {
964 MakeBackData (false);
965 }
966
969
972
973 a->SortPointsRounded ();
974 b->SortPointsRounded ();
975
976 chgts.clear();
977
978 double lastChange =
979 (a->pData[0].rx[1] <
980 b->pData[0].rx[1]) ? a->pData[0].rx[1] - 1.0 : b->pData[0].rx[1] - 1.0;
981 int lastChgtPt = 0;
982 int edgeHead = -1;
983 Shape *shapeHead = nullptr;
984
986
987 int curAPt = 0;
988 int curBPt = 0;
989
990 while (curAPt < a->numberOfPoints() || curBPt < b->numberOfPoints() || sEvts->size() > 0)
991 {
992/* for (int i=0;i<sEvts.nbEvt;i++) {
993 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
994 }
995 // cout << endl;
996 if ( sTree.racine ) {
997 SweepTree* ct=static_cast <SweepTree*> (sTree.racine->Leftmost());
998 while ( ct ) {
999 printf("%i %i [%i\n",ct->bord,ct->startPoint,(ct->src==a)?1:0);
1000 ct=static_cast <SweepTree*> (ct->elem[RIGHT]);
1001 }
1002 }
1003 printf("\n");*/
1004
1005 Geom::Point ptX;
1006 double ptL, ptR;
1007 SweepTree *intersL = nullptr;
1008 SweepTree *intersR = nullptr;
1009 int nPt = -1;
1010 Shape *ptSh = nullptr;
1011 bool isIntersection = false;
1012
1013 if (sEvts->peek(intersL, intersR, ptX, ptL, ptR))
1014 {
1015 if (curAPt < a->numberOfPoints())
1016 {
1017 if (curBPt < b->numberOfPoints())
1018 {
1019 if (a->pData[curAPt].rx[1] < b->pData[curBPt].rx[1]
1020 || (a->pData[curAPt].rx[1] == b->pData[curBPt].rx[1]
1021 && a->pData[curAPt].rx[0] < b->pData[curBPt].rx[0]))
1022 {
1023 if (a->pData[curAPt].pending > 0
1024 || (a->pData[curAPt].rx[1] > ptX[1]
1025 || (a->pData[curAPt].rx[1] == ptX[1]
1026 && a->pData[curAPt].rx[0] > ptX[0])))
1027 {
1028 /* FIXME: could be pop? */
1029 sEvts->extract(intersL, intersR, ptX, ptL, ptR);
1030 isIntersection = true;
1031 }
1032 else
1033 {
1034 nPt = curAPt++;
1035 ptSh = a;
1036 ptX = ptSh->pData[nPt].rx;
1037 isIntersection = false;
1038 }
1039 }
1040 else
1041 {
1042 if (b->pData[curBPt].pending > 0
1043 || (b->pData[curBPt].rx[1] > ptX[1]
1044 || (b->pData[curBPt].rx[1] == ptX[1]
1045 && b->pData[curBPt].rx[0] > ptX[0])))
1046 {
1047 /* FIXME: could be pop? */
1048 sEvts->extract(intersL, intersR, ptX, ptL, ptR);
1049 isIntersection = true;
1050 }
1051 else
1052 {
1053 nPt = curBPt++;
1054 ptSh = b;
1055 ptX = ptSh->pData[nPt].rx;
1056 isIntersection = false;
1057 }
1058 }
1059 }
1060 else
1061 {
1062 if (a->pData[curAPt].pending > 0
1063 || (a->pData[curAPt].rx[1] > ptX[1]
1064 || (a->pData[curAPt].rx[1] == ptX[1]
1065 && a->pData[curAPt].rx[0] > ptX[0])))
1066 {
1067 /* FIXME: could be pop? */
1068 sEvts->extract(intersL, intersR, ptX, ptL, ptR);
1069 isIntersection = true;
1070 }
1071 else
1072 {
1073 nPt = curAPt++;
1074 ptSh = a;
1075 ptX = ptSh->pData[nPt].rx;
1076 isIntersection = false;
1077 }
1078 }
1079 }
1080 else
1081 {
1082 if (b->pData[curBPt].pending > 0
1083 || (b->pData[curBPt].rx[1] > ptX[1]
1084 || (b->pData[curBPt].rx[1] == ptX[1]
1085 && b->pData[curBPt].rx[0] > ptX[0])))
1086 {
1087 /* FIXME: could be pop? */
1088 sEvts->extract(intersL, intersR, ptX, ptL, ptR);
1089 isIntersection = true;
1090 }
1091 else
1092 {
1093 nPt = curBPt++;
1094 ptSh = b;
1095 ptX = ptSh->pData[nPt].rx;
1096 isIntersection = false;
1097 }
1098 }
1099 }
1100 else
1101 {
1102 if (curAPt < a->numberOfPoints())
1103 {
1104 if (curBPt < b->numberOfPoints())
1105 {
1106 if (a->pData[curAPt].rx[1] < b->pData[curBPt].rx[1]
1107 || (a->pData[curAPt].rx[1] == b->pData[curBPt].rx[1]
1108 && a->pData[curAPt].rx[0] < b->pData[curBPt].rx[0]))
1109 {
1110 nPt = curAPt++;
1111 ptSh = a;
1112 }
1113 else
1114 {
1115 nPt = curBPt++;
1116 ptSh = b;
1117 }
1118 }
1119 else
1120 {
1121 nPt = curAPt++;
1122 ptSh = a;
1123 }
1124 }
1125 else
1126 {
1127 nPt = curBPt++;
1128 ptSh = b;
1129 }
1130 ptX = ptSh->pData[nPt].rx;
1131 isIntersection = false;
1132 }
1133
1134 if (isIntersection == false)
1135 {
1136 if (ptSh->getPoint(nPt).dI == 0 && ptSh->getPoint(nPt).dO == 0)
1137 continue;
1138 }
1139
1140 Geom::Point rPtX;
1141 rPtX[0]= Round (ptX[0]);
1142 rPtX[1]= Round (ptX[1]);
1143 int lastPointNo = AddPoint (rPtX);
1144 pData[lastPointNo].rx = rPtX;
1145
1146 if (rPtX[1] > lastChange)
1147 {
1148 int lastI = AssemblePoints (lastChgtPt, lastPointNo);
1149
1150
1151 Shape *curSh = shapeHead;
1152 int curBo = edgeHead;
1153 while (curSh)
1154 {
1155 curSh->swsData[curBo].leftRnd =
1156 pData[curSh->swsData[curBo].leftRnd].newInd;
1157 curSh->swsData[curBo].rightRnd =
1158 pData[curSh->swsData[curBo].rightRnd].newInd;
1159
1160 Shape *neSh = curSh->swsData[curBo].nextSh;
1161 curBo = curSh->swsData[curBo].nextBo;
1162 curSh = neSh;
1163 }
1164
1165 for (auto & chgt : chgts)
1166 {
1167 chgt.ptNo = pData[chgt.ptNo].newInd;
1168 if (chgt.type == 0)
1169 {
1170 if (chgt.src->getEdge(chgt.bord).st <
1171 chgt.src->getEdge(chgt.bord).en)
1172 {
1173 chgt.src->swsData[chgt.bord].stPt =
1174 chgt.ptNo;
1175 }
1176 else
1177 {
1178 chgt.src->swsData[chgt.bord].enPt =
1179 chgt.ptNo;
1180 }
1181 }
1182 else if (chgt.type == 1)
1183 {
1184 if (chgt.src->getEdge(chgt.bord).st >
1185 chgt.src->getEdge(chgt.bord).en)
1186 {
1187 chgt.src->swsData[chgt.bord].stPt =
1188 chgt.ptNo;
1189 }
1190 else
1191 {
1192 chgt.src->swsData[chgt.bord].enPt =
1193 chgt.ptNo;
1194 }
1195 }
1196 }
1197
1198 CheckAdjacencies (lastI, lastChgtPt, shapeHead, edgeHead);
1199
1200 CheckEdges (lastI, lastChgtPt, a, b, mod);
1201
1202 for (int i = lastChgtPt; i < lastI; i++)
1203 {
1204 if (pData[i].askForWindingS)
1205 {
1206 Shape *windS = pData[i].askForWindingS;
1207 int windB = pData[i].askForWindingB;
1208 pData[i].nextLinkedPoint =
1209 windS->swsData[windB].firstLinkedPoint;
1210 windS->swsData[windB].firstLinkedPoint = i;
1211 }
1212 }
1213
1214 if (lastI < lastPointNo)
1215 {
1216 _pts[lastI] = getPoint(lastPointNo);
1217 pData[lastI] = pData[lastPointNo];
1218 }
1219 lastPointNo = lastI;
1220 _pts.resize(lastI + 1);
1221
1222 lastChgtPt = lastPointNo;
1223 lastChange = rPtX[1];
1224 chgts.clear();
1225 edgeHead = -1;
1226 shapeHead = nullptr;
1227 }
1228
1229
1230 if (isIntersection)
1231 {
1232 // les 2 events de part et d'autre de l'intersection
1233 // (celui de l'intersection a deja ete depile)
1234 intersL->RemoveEvent (*sEvts, LEFT);
1235 intersR->RemoveEvent (*sEvts, RIGHT);
1236
1237 AddChgt (lastPointNo, lastChgtPt, shapeHead, edgeHead, INTERSECTION,
1238 intersL->src, intersL->bord, intersR->src, intersR->bord);
1239
1240 intersL->SwapWithRight (*sTree, *sEvts);
1241
1242 TesteIntersection (intersL, LEFT, true);
1243 TesteIntersection (intersR, RIGHT, true);
1244 }
1245 else
1246 {
1247 int cb;
1248
1249 int nbUp = 0, nbDn = 0;
1250 int upNo = -1, dnNo = -1;
1251 cb = ptSh->getPoint(nPt).incidentEdge[FIRST];
1252 while (cb >= 0 && cb < ptSh->numberOfEdges())
1253 {
1254 if ((ptSh->getEdge(cb).st < ptSh->getEdge(cb).en
1255 && nPt == ptSh->getEdge(cb).en)
1256 || (ptSh->getEdge(cb).st > ptSh->getEdge(cb).en
1257 && nPt == ptSh->getEdge(cb).st))
1258 {
1259 upNo = cb;
1260 nbUp++;
1261 }
1262 if ((ptSh->getEdge(cb).st > ptSh->getEdge(cb).en
1263 && nPt == ptSh->getEdge(cb).en)
1264 || (ptSh->getEdge(cb).st < ptSh->getEdge(cb).en
1265 && nPt == ptSh->getEdge(cb).st))
1266 {
1267 dnNo = cb;
1268 nbDn++;
1269 }
1270 cb = ptSh->NextAt (nPt, cb);
1271 }
1272
1273 if (nbDn <= 0)
1274 {
1275 upNo = -1;
1276 }
1277 if (upNo >= 0 && !ptSh->swsData[upNo].misc)
1278 {
1279 upNo = -1;
1280 }
1281
1282// upNo=-1;
1283
1284 bool doWinding = true;
1285
1286 if (nbUp > 0)
1287 {
1288 cb = ptSh->getPoint(nPt).incidentEdge[FIRST];
1289 while (cb >= 0 && cb < ptSh->numberOfEdges())
1290 {
1291 if ((ptSh->getEdge(cb).st < ptSh->getEdge(cb).en
1292 && nPt == ptSh->getEdge(cb).en)
1293 || (ptSh->getEdge(cb).st > ptSh->getEdge(cb).en
1294 && nPt == ptSh->getEdge(cb).st))
1295 {
1296 if (cb != upNo)
1297 {
1298 SweepTree *node = ptSh->swsData[cb].misc;
1299 if (node == nullptr)
1300 {
1301 }
1302 else
1303 {
1304 AddChgt (lastPointNo, lastChgtPt, shapeHead,
1305 edgeHead, EDGE_REMOVED, node->src, node->bord,
1306 nullptr, -1);
1307 ptSh->swsData[cb].misc = nullptr;
1308
1309 int onLeftB = -1, onRightB = -1;
1310 Shape *onLeftS = nullptr;
1311 Shape *onRightS = nullptr;
1312 if (node->elem[LEFT])
1313 {
1314 onLeftB =
1315 (static_cast <
1316 SweepTree * >(node->elem[LEFT]))->bord;
1317 onLeftS =
1318 (static_cast <
1319 SweepTree * >(node->elem[LEFT]))->src;
1320 }
1321 if (node->elem[RIGHT])
1322 {
1323 onRightB =
1324 (static_cast <
1325 SweepTree * >(node->elem[RIGHT]))->bord;
1326 onRightS =
1327 (static_cast <
1328 SweepTree * >(node->elem[RIGHT]))->src;
1329 }
1330
1331 node->Remove (*sTree, *sEvts, true);
1332 if (onLeftS && onRightS)
1333 {
1334 SweepTree *onLeft = onLeftS->swsData[onLeftB].misc;
1335// SweepTree* onRight=(SweepTree*)onRightS->swsData[onRightB].misc;
1336 if (onLeftS == ptSh
1337 && (onLeftS->getEdge(onLeftB).en == nPt
1338 || onLeftS->getEdge(onLeftB).st ==
1339 nPt))
1340 {
1341 }
1342 else
1343 {
1344 if (onRightS == ptSh
1345 && (onRightS->getEdge(onRightB).en ==
1346 nPt
1347 || onRightS->getEdge(onRightB).
1348 st == nPt))
1349 {
1350 }
1351 else
1352 {
1353 TesteIntersection (onLeft, RIGHT, true);
1354 }
1355 }
1356 }
1357 }
1358 }
1359 }
1360 cb = ptSh->NextAt (nPt, cb);
1361 }
1362 }
1363
1364 // traitement du "upNo devient dnNo"
1365 SweepTree *insertionNode = nullptr;
1366 if (dnNo >= 0)
1367 {
1368 if (upNo >= 0)
1369 {
1370 SweepTree *node = ptSh->swsData[upNo].misc;
1371
1372 AddChgt (lastPointNo, lastChgtPt, shapeHead, edgeHead, EDGE_REMOVED,
1373 node->src, node->bord, nullptr, -1);
1374
1375 ptSh->swsData[upNo].misc = nullptr;
1376
1377 node->RemoveEvents (*sEvts);
1378 node->ConvertTo (ptSh, dnNo, 1, lastPointNo);
1379 ptSh->swsData[dnNo].misc = node;
1380 TesteIntersection (node, RIGHT, true);
1381 TesteIntersection (node, LEFT, true);
1382 insertionNode = node;
1383
1384 ptSh->swsData[dnNo].curPoint = lastPointNo;
1385
1386 AddChgt (lastPointNo, lastChgtPt, shapeHead, edgeHead, EDGE_INSERTED,
1387 node->src, node->bord, nullptr, -1);
1388 }
1389 else
1390 {
1391 SweepTree *node = sTree->add(ptSh, dnNo, 1, lastPointNo, this);
1392 ptSh->swsData[dnNo].misc = node;
1393 node->Insert (*sTree, *sEvts, this, lastPointNo, true);
1394
1395 if (doWinding)
1396 {
1397 SweepTree *myLeft =
1398 static_cast < SweepTree * >(node->elem[LEFT]);
1399 if (myLeft)
1400 {
1401 pData[lastPointNo].askForWindingS = myLeft->src;
1402 pData[lastPointNo].askForWindingB = myLeft->bord;
1403 }
1404 else
1405 {
1406 pData[lastPointNo].askForWindingB = -1;
1407 }
1408 doWinding = false;
1409 }
1410
1411 TesteIntersection (node, RIGHT, true);
1412 TesteIntersection (node, LEFT, true);
1413 insertionNode = node;
1414
1415 ptSh->swsData[dnNo].curPoint = lastPointNo;
1416
1417 AddChgt (lastPointNo, lastChgtPt, shapeHead, edgeHead, EDGE_INSERTED,
1418 node->src, node->bord, nullptr, -1);
1419 }
1420 }
1421
1422 if (nbDn > 1)
1423 { // si nbDn == 1 , alors dnNo a deja ete traite
1424 cb = ptSh->getPoint(nPt).incidentEdge[FIRST];
1425 while (cb >= 0 && cb < ptSh->numberOfEdges())
1426 {
1427 if ((ptSh->getEdge(cb).st > ptSh->getEdge(cb).en
1428 && nPt == ptSh->getEdge(cb).en)
1429 || (ptSh->getEdge(cb).st < ptSh->getEdge(cb).en
1430 && nPt == ptSh->getEdge(cb).st))
1431 {
1432 if (cb != dnNo)
1433 {
1434 SweepTree *node = sTree->add(ptSh, cb, 1, lastPointNo, this);
1435 ptSh->swsData[cb].misc = node;
1436// node->Insert(sTree,*sEvts,this,lastPointNo,true);
1437 node->InsertAt (*sTree, *sEvts, this, insertionNode,
1438 nPt, true);
1439
1440 if (doWinding)
1441 {
1442 SweepTree *myLeft =
1443 static_cast < SweepTree * >(node->elem[LEFT]);
1444 if (myLeft)
1445 {
1446 pData[lastPointNo].askForWindingS =
1447 myLeft->src;
1448 pData[lastPointNo].askForWindingB =
1449 myLeft->bord;
1450 }
1451 else
1452 {
1453 pData[lastPointNo].askForWindingB = -1;
1454 }
1455 doWinding = false;
1456 }
1457
1458 TesteIntersection (node, RIGHT, true);
1459 TesteIntersection (node, LEFT, true);
1460
1461 ptSh->swsData[cb].curPoint = lastPointNo;
1462
1463 AddChgt (lastPointNo, lastChgtPt, shapeHead,
1464 edgeHead, EDGE_INSERTED, node->src, node->bord, nullptr,
1465 -1);
1466 }
1467 }
1468 cb = ptSh->NextAt (nPt, cb);
1469 }
1470 }
1471 }
1472 }
1473 {
1474 int lastI = AssemblePoints (lastChgtPt, numberOfPoints());
1475
1476
1477 Shape *curSh = shapeHead;
1478 int curBo = edgeHead;
1479 while (curSh)
1480 {
1481 curSh->swsData[curBo].leftRnd =
1482 pData[curSh->swsData[curBo].leftRnd].newInd;
1483 curSh->swsData[curBo].rightRnd =
1484 pData[curSh->swsData[curBo].rightRnd].newInd;
1485
1486 Shape *neSh = curSh->swsData[curBo].nextSh;
1487 curBo = curSh->swsData[curBo].nextBo;
1488 curSh = neSh;
1489 }
1490
1491 /* FIXME: this kind of code seems to appear frequently */
1492 for (auto & chgt : chgts)
1493 {
1494 chgt.ptNo = pData[chgt.ptNo].newInd;
1495 if (chgt.type == 0)
1496 {
1497 if (chgt.src->getEdge(chgt.bord).st <
1498 chgt.src->getEdge(chgt.bord).en)
1499 {
1500 chgt.src->swsData[chgt.bord].stPt = chgt.ptNo;
1501 }
1502 else
1503 {
1504 chgt.src->swsData[chgt.bord].enPt = chgt.ptNo;
1505 }
1506 }
1507 else if (chgt.type == 1)
1508 {
1509 if (chgt.src->getEdge(chgt.bord).st >
1510 chgt.src->getEdge(chgt.bord).en)
1511 {
1512 chgt.src->swsData[chgt.bord].stPt = chgt.ptNo;
1513 }
1514 else
1515 {
1516 chgt.src->swsData[chgt.bord].enPt = chgt.ptNo;
1517 }
1518 }
1519 }
1520
1521 CheckAdjacencies (lastI, lastChgtPt, shapeHead, edgeHead);
1522
1523 CheckEdges (lastI, lastChgtPt, a, b, mod);
1524
1525 for (int i = lastChgtPt; i < lastI; i++)
1526 {
1527 if (pData[i].askForWindingS)
1528 {
1529 Shape *windS = pData[i].askForWindingS;
1530 int windB = pData[i].askForWindingB;
1531 pData[i].nextLinkedPoint = windS->swsData[windB].firstLinkedPoint;
1532 windS->swsData[windB].firstLinkedPoint = i;
1533 }
1534 }
1535
1536 _pts.resize(lastI);
1537
1538 edgeHead = -1;
1539 shapeHead = nullptr;
1540 }
1541
1542 chgts.clear();
1544
1545// Plot(190,70,6,400,400,true,false,true,true);
1546
1547 if ( mod == bool_op_cut ) {
1549 // dupliquer les aretes de la coupure
1550 int i=numberOfEdges()-1;
1551 for (;i>=0;i--) {
1552 if ( ebData[i].pathID == cutPathID ) {
1553 // on duplique
1554 int nEd=AddEdge(getEdge(i).en,getEdge(i).st);
1555 ebData[nEd].pathID=cutPathID;
1556 ebData[nEd].pieceID=ebData[i].pieceID;
1557 ebData[nEd].tSt=ebData[i].tEn;
1558 ebData[nEd].tEn=ebData[i].tSt;
1559 eData[nEd].weight=eData[i].weight;
1560 // lui donner les firstlinkedpoitn si besoin
1561 if ( getEdge(i).en >= getEdge(i).st ) {
1562 int cp = swsData[i].firstLinkedPoint;
1563 while (cp >= 0) {
1564 pData[cp].askForWindingB = nEd;
1565 cp = pData[cp].nextLinkedPoint;
1566 }
1567 swsData[nEd].firstLinkedPoint = swsData[i].firstLinkedPoint;
1568 swsData[i].firstLinkedPoint=-1;
1569 }
1570 }
1571 }
1572 } else if ( mod == bool_op_slice ) {
1573 } else {
1574 AssembleAretes ();
1575 }
1576
1577 for (int i = 0; i < numberOfPoints(); i++)
1578 {
1579 _pts[i].oldDegree = getPoint(i).totalDegree();
1580 }
1581
1582 _need_edges_sorting = true;
1583 if ( mod == bool_op_slice ) {
1584 } else {
1585 GetWindings (false);
1586 }
1587// Plot(190,70,6,400,400,true,true,true,true);
1588
1589 if (mod == bool_op_symdiff)
1590 {
1591 for (int i = 0; i < numberOfEdges(); i++)
1592 {
1593 if (swdData[i].leW < 0)
1594 swdData[i].leW = -swdData[i].leW;
1595 if (swdData[i].riW < 0)
1596 swdData[i].riW = -swdData[i].riW;
1597
1598 if (swdData[i].leW > 0 && swdData[i].riW <= 0)
1599 {
1600 eData[i].weight = 1;
1601 }
1602 else if (swdData[i].leW <= 0 && swdData[i].riW > 0)
1603 {
1604 Inverse (i);
1605 eData[i].weight = 1;
1606 }
1607 else
1608 {
1609 eData[i].weight = 0;
1610 SubEdge (i);
1611 i--;
1612 }
1613 }
1614 }
1615 else if (mod == bool_op_union || mod == bool_op_diff)
1616 {
1617 for (int i = 0; i < numberOfEdges(); i++)
1618 {
1619 if (swdData[i].leW > 0 && swdData[i].riW <= 0)
1620 {
1621 eData[i].weight = 1;
1622 }
1623 else if (swdData[i].leW <= 0 && swdData[i].riW > 0)
1624 {
1625 Inverse (i);
1626 eData[i].weight = 1;
1627 }
1628 else
1629 {
1630 eData[i].weight = 0;
1631 SubEdge (i);
1632 i--;
1633 }
1634 }
1635 }
1636 else if (mod == bool_op_inters)
1637 {
1638 for (int i = 0; i < numberOfEdges(); i++)
1639 {
1640 if (swdData[i].leW > 1 && swdData[i].riW <= 1)
1641 {
1642 eData[i].weight = 1;
1643 }
1644 else if (swdData[i].leW <= 1 && swdData[i].riW > 1)
1645 {
1646 Inverse (i);
1647 eData[i].weight = 1;
1648 }
1649 else
1650 {
1651 eData[i].weight = 0;
1652 SubEdge (i);
1653 i--;
1654 }
1655 }
1656 } else if ( mod == bool_op_cut ) {
1657 // inverser les aretes de la coupe au besoin
1658 for (int i=0;i<numberOfEdges();i++) {
1659 if ( getEdge(i).st < 0 || getEdge(i).en < 0 ) {
1660 if ( i < numberOfEdges()-1 ) {
1661 // decaler les askForWinding
1662 int cp = swsData[numberOfEdges()-1].firstLinkedPoint;
1663 while (cp >= 0) {
1664 pData[cp].askForWindingB = i;
1665 cp = pData[cp].nextLinkedPoint;
1666 }
1667 }
1668 SwapEdges(i,numberOfEdges()-1);
1670// SubEdge(i);
1671 i--;
1672 } else if ( ebData[i].pathID == cutPathID ) {
1673 swdData[i].leW=swdData[i].leW%2;
1674 swdData[i].riW=swdData[i].riW%2;
1675 if ( swdData[i].leW < swdData[i].riW ) {
1676 Inverse(i);
1677 }
1678 }
1679 }
1680 } else if ( mod == bool_op_slice ) {
1681 // supprimer les aretes de la coupe
1682 int i=numberOfEdges()-1;
1683 for (;i>=0;i--) {
1684 if ( ebData[i].pathID == cutPathID || getEdge(i).st < 0 || getEdge(i).en < 0 ) {
1685 SubEdge(i);
1686 }
1687 }
1688 }
1689 else
1690 {
1691 for (int i = 0; i < numberOfEdges(); i++)
1692 {
1693 if (swdData[i].leW > 0 && swdData[i].riW <= 0)
1694 {
1695 eData[i].weight = 1;
1696 }
1697 else if (swdData[i].leW <= 0 && swdData[i].riW > 0)
1698 {
1699 Inverse (i);
1700 eData[i].weight = 1;
1701 }
1702 else
1703 {
1704 eData[i].weight = 0;
1705 SubEdge (i);
1706 i--;
1707 }
1708 }
1709 }
1710
1711 delete sTree;
1712 sTree = nullptr;
1713 delete sEvts;
1714 sEvts = nullptr;
1715
1716 if ( mod == bool_op_cut ) {
1717 // on garde le askForWinding
1718 } else {
1719 MakePointData (false);
1720 }
1721 MakeEdgeData (false);
1722 MakeSweepSrcData (false);
1723 MakeSweepDestData (false);
1724 a->CleanupSweep ();
1725 b->CleanupSweep ();
1726
1727 if (directedEulerian(this) == false)
1728 {
1729// printf( "pas euclidian2");
1730 _pts.clear();
1731 _aretes.clear();
1732 return shape_euler_err;
1733 }
1735 return 0;
1736}
1737
1738// frontend to the TesteIntersection() below
1739void Shape::TesteIntersection(SweepTree *t, Side s, bool onlyDiff)
1740{
1741 // get the element that is to the side s of node t
1742 SweepTree *tt = static_cast<SweepTree*>(t->elem[s]);
1743 if (tt == nullptr) {
1744 return;
1745 }
1746
1747 // set left right properly, a is left, b is right
1748 SweepTree *a = (s == LEFT) ? tt : t;
1749 SweepTree *b = (s == LEFT) ? t : tt;
1750
1751 // call the actual intersection checking function and if an intersection
1752 // is detected, add it as an event
1753 Geom::Point atx;
1754 double atl;
1755 double atr;
1756 if (TesteIntersection(a, b, atx, atl, atr, onlyDiff)) {
1757 sEvts->add(a, b, atx, atl, atr);
1758 }
1759}
1760
1761// a crucial piece of code: computing intersections between segments
1762bool
1763Shape::TesteIntersection (SweepTree * iL, SweepTree * iR, Geom::Point &atx, double &atL, double &atR, bool onlyDiff)
1764{
1765 // get the left edge's start and end point
1766 int lSt = iL->src->getEdge(iL->bord).st, lEn = iL->src->getEdge(iL->bord).en;
1767 // get the right edge's start and end point
1768 int rSt = iR->src->getEdge(iR->bord).st, rEn = iR->src->getEdge(iR->bord).en;
1769 // get both edge vectors
1770 Geom::Point ldir, rdir;
1771 ldir = iL->src->eData[iL->bord].rdx;
1772 rdir = iR->src->eData[iR->bord].rdx;
1773 // first, a round of checks to quickly dismiss edge which obviously dont intersect,
1774 // such as having disjoint bounding boxes
1775
1776 // invert the edge vector and swap the endpoints if an edge is bottom to top
1777 // or horizontal and right to left
1778 if (lSt < lEn)
1779 {
1780 }
1781 else
1782 {
1783 std::swap(lSt, lEn);
1784 ldir = -ldir;
1785 }
1786 if (rSt < rEn)
1787 {
1788 }
1789 else
1790 {
1791 std::swap(rSt, rEn);
1792 rdir = -rdir;
1793 }
1794
1795 // these blocks check if the bounding boxes of the two don't overlap, if they
1796 // don't overlap, we can just return false since non-overlapping bounding boxes
1797 // indicate they won't intersect
1798 if (iL->src->pData[lSt].rx[0] < iL->src->pData[lEn].rx[0])
1799 {
1800 if (iR->src->pData[rSt].rx[0] < iR->src->pData[rEn].rx[0])
1801 {
1802 if (iL->src->pData[lSt].rx[0] > iR->src->pData[rEn].rx[0])
1803 return false;
1804 if (iL->src->pData[lEn].rx[0] < iR->src->pData[rSt].rx[0])
1805 return false;
1806 }
1807 else
1808 {
1809 if (iL->src->pData[lSt].rx[0] > iR->src->pData[rSt].rx[0])
1810 return false;
1811 if (iL->src->pData[lEn].rx[0] < iR->src->pData[rEn].rx[0])
1812 return false;
1813 }
1814 }
1815 else
1816 {
1817 if (iR->src->pData[rSt].rx[0] < iR->src->pData[rEn].rx[0])
1818 {
1819 if (iL->src->pData[lEn].rx[0] > iR->src->pData[rEn].rx[0])
1820 return false;
1821 if (iL->src->pData[lSt].rx[0] < iR->src->pData[rSt].rx[0])
1822 return false;
1823 }
1824 else
1825 {
1826 if (iL->src->pData[lEn].rx[0] > iR->src->pData[rSt].rx[0])
1827 return false;
1828 if (iL->src->pData[lSt].rx[0] < iR->src->pData[rEn].rx[0])
1829 return false;
1830 }
1831 }
1832
1833 // see the second image in the header docs of this function to visualize
1834 // this cross product
1835 double ang = cross (ldir, rdir);
1836 // ang*=iL->src->eData[iL->bord].isqlength;
1837 // ang*=iR->src->eData[iR->bord].isqlength;
1838 if (ang <= 0) return false; // edges in opposite directions: <-left ... right ->
1839 // they can't intersect
1840
1841 // d'abord tester les bords qui partent d'un meme point
1842 // if they come from same shape and they share the same start point
1843 if (iL->src == iR->src && lSt == rSt)
1844 {
1845 // if they share the end point too, it's a doublon doesn't count as intersection
1846 if (iL->src == iR->src && lEn == rEn)
1847 return false; // c'est juste un doublon
1848 // if we are here, it means they share the start point only and that counts as an interesection
1849 // intersection point is the starting point and times are all set to -1
1850 atx = iL->src->pData[lSt].rx;
1851 atR = atL = -1;
1852 return true; // l'ordre est mauvais
1853 }
1854 // if they only share the end points, doesn't count as intersection (no idea why)
1855 // in my opinion, both endpoints shouldn't count for intersection
1856 if (iL->src == iR->src && lEn == rEn)
1857 return false; // rien a faire=ils vont terminer au meme endroit
1858
1859 // tester si on est dans une intersection multiple
1860
1861 // I'm not sure what onlyDiff does but it seems it stands for "only different", which means, the intersections
1862 // will only be detected if the two edges are coming from different shapes, if they come from the same shape,
1863 // don't do any checks, we are not interested. but this if statement should be somewhere above in the code
1864 // not here, shouldn't it? Why bother doing the bounding box checks?
1865 if (onlyDiff && iL->src == iR->src)
1866 return false;
1867
1868 // on reprend les vrais points
1869 // get the start end points again, since we might have swapped them above
1870 lSt = iL->src->getEdge(iL->bord).st;
1871 lEn = iL->src->getEdge(iL->bord).en;
1872 rSt = iR->src->getEdge(iR->bord).st;
1873 rEn = iR->src->getEdge(iR->bord).en;
1874
1875 // compute intersection (if there is one)
1876 // Boissonat anr Preparata said in one paper that double precision floats were sufficient for get single precision
1877 // coordinates for the intersection, if the endpoints are single precision. i hope they're right...
1878 // so till here, lSt, lEn, rSt, rEn have been reset, but note that ldir and rdir are the same
1879 {
1880 Geom::Point sDiff, eDiff;
1881 double slDot, elDot;
1882 double srDot, erDot;
1883 // a vector from the start point of right edge to the start point of left edge
1884 sDiff = iL->src->pData[lSt].rx - iR->src->pData[rSt].rx;
1885 // a vector from the start point of right edge to the end point of left edge
1886 eDiff = iL->src->pData[lEn].rx - iR->src->pData[rSt].rx;
1887 srDot = cross(rdir, sDiff);
1888 erDot = cross(rdir, eDiff);
1889 // a vector from the start point of left edge to the start point of right edge
1890 sDiff = iR->src->pData[rSt].rx - iL->src->pData[lSt].rx;
1891 // a vector from the start point of left edge to the end point of right edge
1892 eDiff = iR->src->pData[rEn].rx - iL->src->pData[lSt].rx;
1893 slDot = cross(ldir, sDiff);
1894 elDot = cross(ldir, eDiff);
1895 // these cross products above are shown in the third picture in the header docs of this function.
1896 // The only thing that matters to us at the moment about these cross products is their sign
1897 // not their value, the value comes in later. I've labelled the angle arcs with the name of the
1898 // cross product so that you can immediately visualize the sign that the cross product will take
1899 // take your right hand, index finger to first vector, middle finger to second vector, if thumb
1900 // is into the page, cross is positive, else it's negative.
1901
1902 // basically these cross products give us a sense of where the endpoints of other vector is with
1903 // respect to a particular vector. If both are on the opposite sides, it indicates that an intersection
1904 // will happen. Of course you can have the edge far away and still have endpoints on opposite side,
1905 // they won't intersect but those cases have already been ruled out above
1906
1907 // if both endpoints of left edge are on one side (the same side doesn't matter which one) of the right edge
1908 if ((srDot >= 0 && erDot >= 0) || (srDot <= 0 && erDot <= 0))
1909 {
1910 // you might think okay if both endpoints of left edge are on same side of right edge, then they can't intersect
1911 // right? no, they still can. An endpoint of the left edge can fall on the right edge and in that case
1912 // there is an intersection
1913 // the start point of the left edge is on the right edge
1914 if (srDot == 0)
1915 {
1916 // this condition here is quite weird, due to it, some intersections won't get detected while others might, I
1917 // don't really see why it has been done this way. See the fourth figure in the header docs of this function and
1918 // you'll see both cases. One where the condition lSt < lEn is valid and an intersection is detected and another
1919 // one where it isn't. In fact as I was testing this out, I realized the purpose of CheckAdjacencies. Apparently
1920 // when a point of intersection is missed by this function, the edge still gets split up at the intersection point
1921 // thanks to CheckAdjacencies. You can see this for yourself by taking a 1000 px by 1000 px canvas and the following
1922 // SVG path: M 500,200 L 500,800 L 200,800 L 500,500 L 200,200 Z
1923 // Try Path > Union with and without the CheckAdjacencies call and you'll see the difference in the resultant path.
1924 // So I was trying to find out a case where this if statement lSt < lEn would be true and an intersection would be
1925 // returned, I couldn't succeed at this. You can get lSt < lEn to be true, but something else above in the code will
1926 // cause an early return, most likely the ang <= 0 condition. So in order words, I couldn't get the code below to
1927 // run, ever.
1928 if (lSt < lEn)
1929 {
1930 atx = iL->src->pData[lSt].rx;
1931 atL = 0;
1932 atR = slDot / (slDot - elDot);
1933 return true;
1934 }
1935 else
1936 {
1937 return false;
1938 }
1939 }
1940 else if (erDot == 0)
1941 {
1942 if (lSt > lEn)
1943 {
1944 atx = iL->src->pData[lEn].rx;
1945 atL = 1;
1946 atR = slDot / (slDot - elDot);
1947 return true;
1948 }
1949 else
1950 {
1951 return false;
1952 }
1953 }
1954 // This code doesn't make sense either, I couldn't get it to trigger
1955 // TODO: Try again or just prove that it's impossible to reach here
1956 if (srDot > 0 && erDot > 0)
1957 {
1958 if (rEn < rSt)
1959 {
1960 if (srDot < erDot)
1961 {
1962 if (lSt < lEn)
1963 {
1964 atx = iL->src->pData[lSt].rx;
1965 atL = 0;
1966 atR = slDot / (slDot - elDot);
1967 return true;
1968 }
1969 }
1970 else
1971 {
1972 if (lEn < lSt)
1973 {
1974 atx = iL->src->pData[lEn].rx;
1975 atL = 1;
1976 atR = slDot / (slDot - elDot);
1977 return true;
1978 }
1979 }
1980 }
1981 }
1982 if (srDot < 0 && erDot < 0)
1983 {
1984 if (rEn > rSt)
1985 {
1986 if (srDot > erDot)
1987 {
1988 if (lSt < lEn)
1989 {
1990 atx = iL->src->pData[lSt].rx;
1991 atL = 0;
1992 atR = slDot / (slDot - elDot);
1993 return true;
1994 }
1995 }
1996 else
1997 {
1998 if (lEn < lSt)
1999 {
2000 atx = iL->src->pData[lEn].rx;
2001 atL = 1;
2002 atR = slDot / (slDot - elDot);
2003 return true;
2004 }
2005 }
2006 }
2007 }
2008 return false;
2009 }
2010 // if both endpoints of the right edge are on the same side of left edge
2011 if ((slDot >= 0 && elDot >= 0) || (slDot <= 0 && elDot <= 0))
2012 {
2013 if (slDot == 0)
2014 {
2015 if (rSt < rEn)
2016 {
2017 atx = iR->src->pData[rSt].rx;
2018 atR = 0;
2019 atL = srDot / (srDot - erDot);
2020 return true;
2021 }
2022 else
2023 {
2024 return false;
2025 }
2026 }
2027 else if (elDot == 0)
2028 {
2029 if (rSt > rEn)
2030 {
2031 atx = iR->src->pData[rEn].rx;
2032 atR = 1;
2033 atL = srDot / (srDot - erDot);
2034 return true;
2035 }
2036 else
2037 {
2038 return false;
2039 }
2040 }
2041 if (slDot > 0 && elDot > 0)
2042 {
2043 if (lEn > lSt)
2044 {
2045 if (slDot < elDot)
2046 {
2047 if (rSt < rEn)
2048 {
2049 atx = iR->src->pData[rSt].rx;
2050 atR = 0;
2051 atL = srDot / (srDot - erDot);
2052 return true;
2053 }
2054 }
2055 else
2056 {
2057 if (rEn < rSt)
2058 {
2059 atx = iR->src->pData[rEn].rx;
2060 atR = 1;
2061 atL = srDot / (srDot - erDot);
2062 return true;
2063 }
2064 }
2065 }
2066 }
2067 if (slDot < 0 && elDot < 0)
2068 {
2069 if (lEn < lSt)
2070 {
2071 if (slDot > elDot)
2072 {
2073 if (rSt < rEn)
2074 {
2075 atx = iR->src->pData[rSt].rx;
2076 atR = 0;
2077 atL = srDot / (srDot - erDot);
2078 return true;
2079 }
2080 }
2081 else
2082 {
2083 if (rEn < rSt)
2084 {
2085 atx = iR->src->pData[rEn].rx;
2086 atR = 1;
2087 atL = srDot / (srDot - erDot);
2088 return true;
2089 }
2090 }
2091 }
2092 }
2093 return false;
2094 }
2095
2096 /* double slb=slDot-elDot,srb=srDot-erDot;
2097 if ( slb < 0 ) slb=-slb;
2098 if ( srb < 0 ) srb=-srb;*/
2099 // We use different formulas depending on whose sin is greater
2100
2101 // These formulas would look really weird to you, but let's pick the first one
2102 // and I'll do some maths that you can see in the header docs to elaborate what's
2103 // happening
2104 if (iL->src->eData[iL->bord].siEd > iR->src->eData[iR->bord].siEd)
2105 {
2106 atx =
2107 (slDot * iR->src->pData[rEn].rx -
2108 elDot * iR->src->pData[rSt].rx) / (slDot - elDot);
2109 }
2110 else
2111 {
2112 atx =
2113 (srDot * iL->src->pData[lEn].rx -
2114 erDot * iL->src->pData[lSt].rx) / (srDot - erDot);
2115 }
2116 atL = srDot / (srDot - erDot);
2117 atR = slDot / (slDot - elDot);
2118 return true;
2119 }
2120
2121 return true;
2122}
2123
2124int
2125Shape::PushIncidence (Shape * a, int cb, int pt, double theta)
2126{
2127 if (theta < 0 || theta > 1)
2128 return -1;
2129
2130 if (nbInc >= maxInc)
2131 {
2132 maxInc = 2 * nbInc + 1;
2133 iData =
2134 (incidenceData *) g_realloc(iData, maxInc * sizeof (incidenceData));
2135 }
2136 int n = nbInc++;
2137 iData[n].nextInc = a->swsData[cb].firstLinkedPoint;
2138 iData[n].pt = pt;
2139 iData[n].theta = theta;
2140 a->swsData[cb].firstLinkedPoint = n;
2141 return n;
2142}
2143
2144int
2145Shape::CreateIncidence (Shape * a, int no, int nPt)
2146{
2147 Geom::Point adir, diff;
2148 adir = a->eData[no].rdx;
2149 diff = getPoint(nPt).x - a->pData[a->getEdge(no).st].rx;
2150 double t = dot (diff, adir);
2151 t *= a->eData[no].ilength;
2152 return PushIncidence (a, no, nPt, t);
2153}
2154
2155int
2156Shape::Winding (int nPt) const
2157{
2158 // the array pData has a variable named askForWindingB
2159 // that tells us which edge to go to to find the winding number
2160 // of the pint nPt.
2161 int askTo = pData[nPt].askForWindingB;
2162 if (askTo < 0 || askTo >= numberOfEdges()) // if there is no info there, just return 0
2163 return 0;
2164 // if the edge is top to bottom, return left winding number otherwise the right winding number
2165 // actually, while sweeping, ConvertToShape stores the edge on the immediate left of each point,
2166 // hence, we are seeing the winding to the right of this edge and depending on orientation,
2167 // right is "left" if edge is top to bottom, right is "right" if edge is bottom to top
2168 if (getEdge(askTo).st < getEdge(askTo).en)
2169 {
2170 return swdData[askTo].leW;
2171 }
2172 else
2173 {
2174 return swdData[askTo].riW;
2175 }
2176 return 0;
2177}
2178
2179int
2181{
2182 int lr = 0, ll = 0, rr = 0;
2183
2184 // for each edge
2185 for (int i = 0; i < numberOfEdges(); i++)
2186 {
2187 Geom::Point adir, diff, ast, aen;
2188 adir = eData[i].rdx;
2189
2190 ast = pData[getEdge(i).st].rx; // start point of this edge
2191 aen = pData[getEdge(i).en].rx; // end point of this edge
2192
2193 int nWeight = eData[i].weight; // weight of this edge
2194
2195 // this block checks if the vertical lines crossing the start and end points of the edge
2196 // covers the point px or not. See the first figure in the header documentation to see
2197 // what I mean. The figure shows two contours one inside another. It shows the point px
2198 // and the current edge that the loop is processing is drawn in black color. Two dashed
2199 // vertical lines create a region. This block of code checks if the point px lies within
2200 // that region or not. Because if it doesn't, we really don't care about this edge at all
2201 // then.
2202 if (ast[0] < aen[0])
2203 {
2204 if (ast[0] > px[0])
2205 continue;
2206 if (aen[0] < px[0])
2207 continue;
2208 }
2209 else
2210 {
2211 if (ast[0] < px[0])
2212 continue;
2213 if (aen[0] > px[0])
2214 continue;
2215 }
2216
2217 // the situations in these blocks are explained by the second and third figure in the header documentation
2218 // the fourth figure along with the documentation there describe what ll and rr really do
2219 if (ast[0] == px[0])
2220 {
2221 if (ast[1] >= px[1])
2222 continue;
2223 if (aen[0] == px[0])
2224 continue;
2225 if (aen[0] < px[0])
2226 ll += nWeight;
2227 else
2228 rr -= nWeight;
2229 continue;
2230 }
2231 if (aen[0] == px[0])
2232 {
2233 if (aen[1] >= px[1])
2234 continue;
2235 if (ast[0] == px[0])
2236 continue;
2237 if (ast[0] < px[0])
2238 ll -= nWeight;
2239 else
2240 rr += nWeight;
2241 continue;
2242 }
2243
2244 // if the edge is below the point, it doesn't cut the ray at all
2245 // so we don't care about it
2246 if (ast[1] < aen[1])
2247 {
2248 if (ast[1] >= px[1])
2249 continue;
2250 }
2251 else
2252 {
2253 if (aen[1] >= px[1])
2254 continue;
2255 }
2256
2257 // a vector from the edge start point to our point px whose winding we wanna calculate
2258 diff = px - ast;
2259 double cote = cross(adir, diff); // cross from edge vector to diff vector to figure out the orientation
2260 if (cote == 0)
2261 continue;
2262 if (cote < 0)
2263 {
2264 if (ast[0] > px[0])
2265 lr += nWeight;
2266 }
2267 else
2268 {
2269 if (ast[0] < px[0])
2270 lr -= nWeight;
2271 }
2272 }
2273 return lr + (ll + rr) / 2; // lr comes as it is, ll and rr get divided by two due to the reason I mention in the header file docs
2274}
2275
2276// merging duplicate points and edges
2277int
2279{
2280 if (en > st) {
2281 for (int i = st; i < en; i++) pData[i].oldInd = i;
2282// SortPoints(st,en-1);
2283 SortPointsByOldInd (st, en - 1); // SortPointsByOldInd() is required here, because of the edges we have
2284 // associated with the point for later computation of winding numbers.
2285 // specifically, we need the first point we treated, it's the only one with a valid
2286 // associated edge (man, that was a nice bug).
2287 for (int i = st; i < en; i++) pData[pData[i].oldInd].newInd = i;
2288
2289 int lastI = st;
2290 for (int i = st; i < en; i++) {
2291 pData[i].pending = lastI++;
2292 if (i > st && getPoint(i - 1).x[0] == getPoint(i).x[0] && getPoint(i - 1).x[1] == getPoint(i).x[1]) {
2293 pData[i].pending = pData[i - 1].pending;
2294 if (pData[pData[i].pending].askForWindingS == nullptr) {
2295 pData[pData[i].pending].askForWindingS = pData[i].askForWindingS;
2296 pData[pData[i].pending].askForWindingB = pData[i].askForWindingB;
2297 } else {
2298 if (pData[pData[i].pending].askForWindingS == pData[i].askForWindingS
2299 && pData[pData[i].pending].askForWindingB == pData[i].askForWindingB) {
2300 // meme bord, c bon
2301 } else {
2302 // meme point, mais pas le meme bord: ouille!
2303 // il faut prendre le bord le plus a gauche
2304 // en pratique, n'arrive que si 2 maxima sont dans la meme case -> le mauvais choix prend une arete incidente
2305 // au bon choix
2306// printf("doh");
2307 }
2308 }
2309 lastI--;
2310 } else {
2311 if (i > pData[i].pending) {
2312 _pts[pData[i].pending].x = getPoint(i).x;
2313 pData[pData[i].pending].rx = getPoint(i).x;
2314 pData[pData[i].pending].askForWindingS = pData[i].askForWindingS;
2315 pData[pData[i].pending].askForWindingB = pData[i].askForWindingB;
2316 }
2317 }
2318 }
2319 for (int i = st; i < en; i++) pData[i].newInd = pData[pData[i].newInd].pending;
2320 return lastI;
2321 }
2322 return en;
2323}
2324
2325void
2327{
2328 if (hasPoints())
2329 {
2330 int lastI = AssemblePoints (0, numberOfPoints());
2331
2332 for (int i = 0; i < a->numberOfEdges(); i++)
2333 {
2334 a->swsData[i].stPt = pData[a->swsData[i].stPt].newInd;
2335 a->swsData[i].enPt = pData[a->swsData[i].enPt].newInd;
2336 }
2337 for (int i = 0; i < nbInc; i++)
2338 iData[i].pt = pData[iData[i].pt].newInd;
2339
2340 _pts.resize(lastI);
2341 }
2342}
2343void
2345{
2346 if ( directed == fill_justDont && _has_back_data == false ) {
2347 directed=fill_nonZero;
2348 }
2349
2350 // for each point in points
2351 for (int i = 0; i < numberOfPoints(); i++) {
2352 if (getPoint(i).totalDegree() == 2) { // if simple point with one incoming edge another outgoing edge
2353 int cb, cc;
2354 cb = getPoint(i).incidentEdge[FIRST]; // the first edge connected to the point
2355 cc = getPoint(i).incidentEdge[LAST]; // the last edge connected to the point
2356 bool doublon=false;
2357 if ((getEdge(cb).st == getEdge(cc).st && getEdge(cb).en == getEdge(cc).en)
2358 || (getEdge(cb).st == getEdge(cc).en && getEdge(cb).en == getEdge(cc).en)) doublon=true; // if the start and end edges have same endpoints, it's a doublon edge
2359 if ( directed == fill_justDont ) {
2360 if ( doublon ) {
2361 // depending on pathID, pieceID and tSt reorient cb and cc if needed
2362 if ( ebData[cb].pathID > ebData[cc].pathID ) {
2363 cc = getPoint(i).incidentEdge[FIRST]; // on swappe pour enlever cc
2364 cb = getPoint(i).incidentEdge[LAST];
2365 } else if ( ebData[cb].pathID == ebData[cc].pathID ) {
2366 if ( ebData[cb].pieceID > ebData[cc].pieceID ) {
2367 cc = getPoint(i).incidentEdge[FIRST]; // on swappe pour enlever cc
2368 cb = getPoint(i).incidentEdge[LAST];
2369 } else if ( ebData[cb].pieceID == ebData[cc].pieceID ) {
2370 if ( ebData[cb].tSt > ebData[cc].tSt ) {
2371 cc = getPoint(i).incidentEdge[FIRST]; // on swappe pour enlever cc
2372 cb = getPoint(i).incidentEdge[LAST];
2373 }
2374 }
2375 }
2376 }
2377 if ( doublon ) eData[cc].weight = 0; // make cc's weight zero
2378 } else {
2379 }
2380 if ( doublon ) {
2381 if (getEdge(cb).st == getEdge(cc).st) { // if both edges share same start point
2382 eData[cb].weight += eData[cc].weight; // you get double weight
2383 } else {
2384 eData[cb].weight -= eData[cc].weight; // if one's start is other's end, you subtract weight
2385 }
2386 eData[cc].weight = 0; // remove cc (set weight to zero)
2387
2388 // winding number seed stuff
2389 if (swsData[cc].firstLinkedPoint >= 0) {
2390 int cp = swsData[cc].firstLinkedPoint;
2391 while (cp >= 0) {
2392 pData[cp].askForWindingB = cb;
2393 cp = pData[cp].nextLinkedPoint;
2394 }
2395 if (swsData[cb].firstLinkedPoint < 0) {
2396 swsData[cb].firstLinkedPoint = swsData[cc].firstLinkedPoint;
2397 } else {
2398 int ncp = swsData[cb].firstLinkedPoint;
2399 while (pData[ncp].nextLinkedPoint >= 0) {
2400 ncp = pData[ncp].nextLinkedPoint;
2401 }
2402 pData[ncp].nextLinkedPoint = swsData[cc].firstLinkedPoint;
2403 }
2404 }
2405
2406 // disconnect start and end of cc
2407 DisconnectStart (cc);
2408 DisconnectEnd (cc);
2409
2410 if (numberOfEdges() > 1) {
2411 int cp = swsData[numberOfEdges() - 1].firstLinkedPoint;
2412 while (cp >= 0) {
2413 pData[cp].askForWindingB = cc;
2414 cp = pData[cp].nextLinkedPoint;
2415 }
2416 }
2417 // swap cc with last edge
2418 SwapEdges (cc, numberOfEdges() - 1);
2419 if (cb == numberOfEdges() - 1) {
2420 cb = cc;
2421 }
2422 // pop back the last one (to completely remove it from the array)
2423 _aretes.pop_back();
2424 }
2425 } else {
2426 int cb;
2427 cb = getPoint(i).incidentEdge[FIRST];
2428 while (cb >= 0 && cb < numberOfEdges()) {
2429 int other = Other (i, cb);
2430 int cc;
2431 cc = getPoint(i).incidentEdge[FIRST];
2432 while (cc >= 0 && cc < numberOfEdges()) {
2433 int ncc = NextAt (i, cc);
2434 bool doublon=false;
2435 if (cc != cb && Other (i, cc) == other ) doublon=true;
2436 if ( directed == fill_justDont ) {
2437 if ( doublon ) {
2438 if ( ebData[cb].pathID > ebData[cc].pathID ) {
2439 doublon=false;
2440 } else if ( ebData[cb].pathID == ebData[cc].pathID ) {
2441 if ( ebData[cb].pieceID > ebData[cc].pieceID ) {
2442 doublon=false;
2443 } else if ( ebData[cb].pieceID == ebData[cc].pieceID ) {
2444 if ( ebData[cb].tSt > ebData[cc].tSt ) {
2445 doublon=false;
2446 }
2447 }
2448 }
2449 }
2450 if ( doublon ) eData[cc].weight = 0;
2451 } else {
2452 }
2453 if ( doublon ) {
2454// if (cc != cb && Other (i, cc) == other) {
2455 // doublon
2456 if (getEdge(cb).st == getEdge(cc).st) {
2457 eData[cb].weight += eData[cc].weight;
2458 } else {
2459 eData[cb].weight -= eData[cc].weight;
2460 }
2461 eData[cc].weight = 0;
2462
2463 if (swsData[cc].firstLinkedPoint >= 0) {
2464 int cp = swsData[cc].firstLinkedPoint;
2465 while (cp >= 0) {
2466 pData[cp].askForWindingB = cb;
2467 cp = pData[cp].nextLinkedPoint;
2468 }
2469 if (swsData[cb].firstLinkedPoint < 0) {
2470 swsData[cb].firstLinkedPoint = swsData[cc].firstLinkedPoint;
2471 } else {
2472 int ncp = swsData[cb].firstLinkedPoint;
2473 while (pData[ncp].nextLinkedPoint >= 0) {
2474 ncp = pData[ncp].nextLinkedPoint;
2475 }
2476 pData[ncp].nextLinkedPoint = swsData[cc].firstLinkedPoint;
2477 }
2478 }
2479
2480 DisconnectStart (cc);
2481 DisconnectEnd (cc);
2482 if (numberOfEdges() > 1) {
2483 int cp = swsData[numberOfEdges() - 1].firstLinkedPoint;
2484 while (cp >= 0) {
2485 pData[cp].askForWindingB = cc;
2486 cp = pData[cp].nextLinkedPoint;
2487 }
2488 }
2489 SwapEdges (cc, numberOfEdges() - 1);
2490 if (cb == numberOfEdges() - 1) {
2491 cb = cc;
2492 }
2493 if (ncc == numberOfEdges() - 1) {
2494 ncc = cc;
2495 }
2496 _aretes.pop_back();
2497 }
2498 cc = ncc;
2499 }
2500 cb = NextAt (i, cb);
2501 }
2502 }
2503 }
2504
2505 if ( directed == fill_justDont ) {
2506 for (int i = 0; i < numberOfEdges(); i++) {
2507 if (eData[i].weight == 0) {
2508 // SubEdge(i);
2509 // i--;
2510 } else {
2511 if (eData[i].weight < 0) Inverse (i);
2512 }
2513 }
2514 } else {
2515 for (int i = 0; i < numberOfEdges(); i++) {
2516 if (eData[i].weight == 0) {
2517 // SubEdge(i);
2518 // i--;
2519 } else {
2520 if (eData[i].weight < 0) Inverse (i);
2521 }
2522 }
2523 }
2524 }
2525void
2527{
2528 // preparation du parcours
2529 for (int i = 0; i < numberOfEdges(); i++)
2530 {
2531 swdData[i].misc = 0;
2532 swdData[i].precParc = swdData[i].suivParc = -1;
2533 }
2534
2535 // we make sure that the edges are sorted. What this means is that for each point, all
2536 // the edges that are attached to it are arranged in the linked list according to
2537 // clockwise order (of their spatial orientation)
2538 // chainage
2539 SortEdges ();
2540
2541 int searchInd = 0;
2542
2543 int lastPtUsed = 0;
2544 // okay now let's see what this outer most loop is supposed to do. Look, you can have a directed graph
2545 // with multiple paths that don't touch each other. For example a rectangle inside another rectangle.
2546 // If you just start at the first point and follow the edges moving around, you'd have explored one
2547 // sub-graph but you wouldn't even touch the others. This outer loop ensures that all the points have
2548 // been walked over. We start at the first point and start exploring. When we reach the end of that
2549 // sub-graph, we update lastPtUsed and this outerloop will check if there are still points remaining
2550 // to be explored, if yes, we start with the first point (that we haven't touched yet)
2551 do
2552 {
2553 int startBord = -1;
2554 int outsideW = 0; // the winding number outside (to the top left) of the first point in a sub-graph
2555 {
2556 int fi = 0;
2557 // ignore all points that don't have any edges attached
2558 for (fi = lastPtUsed; fi < numberOfPoints(); fi++)
2559 {
2560 if (getPoint(fi).incidentEdge[FIRST] >= 0 && swdData[getPoint(fi).incidentEdge[FIRST]].misc == 0)
2561 break;
2562 }
2563 lastPtUsed = fi + 1;
2564 if (fi < numberOfPoints())
2565 {
2566 // get the first edge attached to the first point
2567 int bestB = getPoint(fi).incidentEdge[FIRST];
2568 if (bestB >= 0)
2569 {
2570 // let's start with this edge
2571 startBord = bestB;
2572 // is the first point the first in the array? if yes, this ensure it's at the top most and left most position.
2573 // Hence the winding number must be zero (since that region is literally outside everything)
2574 if (fi == 0)
2575 {
2576 outsideW = 0;
2577 }
2578 else
2579 {
2580 // you can either compute the winding number by iterating through all the edges
2581 // basically that would work by seeing how many edges a ray from (0, +infty) would cross
2582 // and in which order
2583 if (brutal)
2584 {
2585 outsideW = Winding (getPoint(fi).x);
2586 }
2587 // or we can get the winding number for that point computed by the sweepline.. this is pretty
2588 // interesting.
2589 else
2590 {
2591 outsideW = Winding (fi);
2592 }
2593 }
2594 // TODO: Look at this piece
2595 if ( getPoint(fi).totalDegree() == 1 ) {
2596 if ( fi == getEdge(startBord).en ) {
2597 if ( eData[startBord].weight == 0 ) {
2598 // on se contente d'inverser
2599 Inverse(startBord);
2600 } else {
2601 // on passe le askForWinding (sinon ca va rester startBord)
2602 pData[getEdge(startBord).st].askForWindingB=pData[getEdge(startBord).en].askForWindingB;
2603 }
2604 }
2605 }
2606 if (getEdge(startBord).en == fi)
2607 outsideW += eData[startBord].weight;
2608 }
2609 }
2610 }
2611 if (startBord >= 0)
2612 {
2613 // now start from this edge
2614 // parcours en profondeur pour mettre les leF et riF a leurs valeurs
2615 swdData[startBord].misc = 1;
2616 // setting the winding numbers for this edge
2617 // one question I had was, would these values for the first edge will always be valid?
2618 // The answer is yes. Due to the fact that edges are sorted clockwise, and that
2619 // we start with the top most (and leftmost if mutliple top most points exist), and that
2620 // there is a piece of code above that adds weight to start edge if the edge ends at the current
2621 // point, I think these will always be correct values.
2622 swdData[startBord].leW = outsideW;
2623 swdData[startBord].riW = outsideW - eData[startBord].weight;
2624// if ( doDebug ) printf("part de %d\n",startBord);
2625 // curBord is the current edge that we are at
2626 int curBord = startBord;
2627 // curDir is the direction, true means we are going in the direction of the edge vector, false means
2628 // we are going in the direction opposite to the edge vector
2629 bool curDir = true;
2630 swdData[curBord].precParc = -1;
2631 swdData[curBord].suivParc = -1;
2632 // the depth first search
2633 do
2634 {
2635 int cPt;
2636 // if curDir is true, we are going along the edge, so get the end point
2637 if (curDir)
2638 cPt = getEdge(curBord).en;
2639 else // if curDir is false, we are going opposite to the edge, so get the start point
2640 cPt = getEdge(curBord).st;
2641
2642 // start finding the next edge to move to
2643 int nb = curBord;
2644// if ( doDebug ) printf("de curBord= %d avec leF= %d et riF= %d -> ",curBord,swdData[curBord].leW,swdData[curBord].riW);
2645 do
2646 {
2647 int nnb = -1;
2648 // see the diagram attached in the header file documentation of this function to see the
2649 // four situations that can come up.
2650 // outsideW here does not mean outside winding, in fact it means inside winding.
2651 // if we are going along the edge, we save the right winding number for later use
2652 if (getEdge(nb).en == cPt)
2653 {
2654 outsideW = swdData[nb].riW;
2655 nnb = CyclePrevAt (cPt, nb); // get the prev edge, since sorting was clockwise, this means get the first counter-clockwise edge
2656 }
2657 // if we are going against the edge, we save it's left winding number for later use
2658 else
2659 {
2660 outsideW = swdData[nb].leW;
2661 nnb = CyclePrevAt (cPt, nb);
2662 }
2663 if (nnb == nb) // if we didn't get any "new" edge
2664 {
2665 // cul-de-sac
2666 nb = -1;
2667 break;
2668 }
2669 nb = nnb;
2670 } // you can break for three reasons from this loop: having no other edge, we got the same one we started on, edge hasn't been visited yet
2671 while (nb >= 0 && nb != curBord && swdData[nb].misc != 0);
2672 // in the beginning, you'd break from the upper loop due to the misc condition and later on, you'll break due to nb != curBord which
2673 // means we need to start backtracking
2674 if (nb < 0 || nb == curBord) // backtracking block
2675 {
2676 // retour en arriere
2677 // so if we are here, we couldn't get any new edge that we haven't seen yet
2678 int oPt;
2679 // we wanna find the previous point (since we are going back)
2680 // if curDir is True, we were going along the edge, so get the start point (going backwards u see)
2681 if (curDir)
2682 oPt = getEdge(curBord).st;
2683 else // if curDir is false, we were going against edge, so get the end point (going backwards)
2684 oPt = getEdge(curBord).en;
2685 curBord = swdData[curBord].precParc; // make current edge the previous one in traversal (back tracking)
2686// if ( doDebug ) printf("retour vers %d\n",curBord);
2687 if (curBord < 0) // if no edge to go back to, break
2688 break;
2689 if (oPt == getEdge(curBord).en) // if this new "current edge" ends at that point, curDir should be true, since we ideally have to go forward
2690 curDir = true;
2691 else // otherwise set it to false, so ideal direction would be against edge
2692 curDir = false;
2693 // I say ideal because this this is how backtracking is, if you have nothing new to go forward to, you go back once and see
2694 // if there is another new edge to go forward to, if not you go back again, and you keep doing this until the point comes where
2695 // you have nothing to go back to and then you break from the loop
2696 }
2697 else // okay we have new edge to compute windings
2698 {
2699 swdData[nb].misc = 1; // we visited this edge, so mark that
2700 swdData[nb].ind = searchInd++; // probably for use later on?
2701 if (cPt == getEdge(nb).st) // this outsideW is the winding stored before, see the diagram in header file
2702 {
2703 swdData[nb].riW = outsideW;
2704 swdData[nb].leW = outsideW + eData[nb].weight;
2705 }
2706 else
2707 {
2708 swdData[nb].leW = outsideW;
2709 swdData[nb].riW = outsideW - eData[nb].weight;
2710 }
2711 // maintaining the stack of traversal
2712 swdData[nb].precParc = curBord;
2713 swdData[curBord].suivParc = nb;
2714 // this edge becomes current edge now
2715 curBord = nb;
2716// if ( doDebug ) printf("suite %d\n",curBord);
2717 // set direction depending on how this edge is oriented
2718 if (cPt == getEdge(nb).en)
2719 curDir = false;
2720 else
2721 curDir = true;
2722 }
2723 }
2724 while (true /*swdData[curBord].precParc >= 0 */ );
2725 // fin du cas non-oriente
2726 }
2727 }
2728 while (lastPtUsed < numberOfPoints());
2729// fflush(stdout);
2730}
2731
2732bool
2733Shape::TesteIntersection (Shape * ils, Shape * irs, int ilb, int irb,
2734 Geom::Point &atx, double &atL, double &atR,
2735 bool /*onlyDiff*/)
2736{
2737 int lSt = ils->getEdge(ilb).st, lEn = ils->getEdge(ilb).en;
2738 int rSt = irs->getEdge(irb).st, rEn = irs->getEdge(irb).en;
2739 if (lSt == rSt || lSt == rEn)
2740 {
2741 return false;
2742 }
2743 if (lEn == rSt || lEn == rEn)
2744 {
2745 return false;
2746 }
2747
2748 Geom::Point ldir, rdir;
2749 ldir = ils->eData[ilb].rdx;
2750 rdir = irs->eData[irb].rdx;
2751
2752 double il = ils->pData[lSt].rx[0], it = ils->pData[lSt].rx[1], ir =
2753 ils->pData[lEn].rx[0], ib = ils->pData[lEn].rx[1];
2754 if (il > ir)
2755 {
2756 std::swap(il, ir);
2757 }
2758 if (it > ib)
2759 {
2760 std::swap(it, ib);
2761 }
2762 double jl = irs->pData[rSt].rx[0], jt = irs->pData[rSt].rx[1], jr =
2763 irs->pData[rEn].rx[0], jb = irs->pData[rEn].rx[1];
2764 if (jl > jr)
2765 {
2766 std::swap(jl, jr);
2767 }
2768 if (jt > jb)
2769 {
2770 std::swap(jt, jb);
2771 }
2772
2773 if (il > jr || it > jb || ir < jl || ib < jt)
2774 return false;
2775
2776 // pre-test
2777 {
2778 Geom::Point sDiff, eDiff;
2779 double slDot, elDot;
2780 double srDot, erDot;
2781 sDiff = ils->pData[lSt].rx - irs->pData[rSt].rx;
2782 eDiff = ils->pData[lEn].rx - irs->pData[rSt].rx;
2783 srDot = cross(rdir, sDiff);
2784 erDot = cross(rdir, eDiff);
2785 if ((srDot >= 0 && erDot >= 0) || (srDot <= 0 && erDot <= 0))
2786 return false;
2787
2788 sDiff = irs->pData[rSt].rx - ils->pData[lSt].rx;
2789 eDiff = irs->pData[rEn].rx - ils->pData[lSt].rx;
2790 slDot = cross(ldir, sDiff);
2791 elDot = cross(ldir, eDiff);
2792 if ((slDot >= 0 && elDot >= 0) || (slDot <= 0 && elDot <= 0))
2793 return false;
2794
2795 double slb = slDot - elDot, srb = srDot - erDot;
2796 if (slb < 0)
2797 slb = -slb;
2798 if (srb < 0)
2799 srb = -srb;
2800 if (slb > srb)
2801 {
2802 atx =
2803 (slDot * irs->pData[rEn].rx - elDot * irs->pData[rSt].rx) / (slDot -
2804 elDot);
2805 }
2806 else
2807 {
2808 atx =
2809 (srDot * ils->pData[lEn].rx - erDot * ils->pData[lSt].rx) / (srDot -
2810 erDot);
2811 }
2812 atL = srDot / (srDot - erDot);
2813 atR = slDot / (slDot - elDot);
2814 return true;
2815 }
2816
2817 // a mettre en double precision pour des resultats exacts
2818 Geom::Point usvs;
2819 usvs = irs->pData[rSt].rx - ils->pData[lSt].rx;
2820
2821 // pas sur de l'ordre des coefs de m
2822 Geom::Affine m(ldir[0], ldir[1],
2823 rdir[0], rdir[1],
2824 0, 0);
2825 double det = m.det();
2826
2827 double tdet = det * ils->eData[ilb].isqlength * irs->eData[irb].isqlength;
2828
2829 if (tdet > -0.0001 && tdet < 0.0001)
2830 { // ces couillons de vecteurs sont colineaires
2831 Geom::Point sDiff, eDiff;
2832 double sDot, eDot;
2833 sDiff = ils->pData[lSt].rx - irs->pData[rSt].rx;
2834 eDiff = ils->pData[lEn].rx - irs->pData[rSt].rx;
2835 sDot = cross(rdir, sDiff);
2836 eDot = cross(rdir, eDiff);
2837
2838 atx =
2839 (sDot * irs->pData[lEn].rx - eDot * irs->pData[lSt].rx) / (sDot -
2840 eDot);
2841 atL = sDot / (sDot - eDot);
2842
2843 sDiff = irs->pData[rSt].rx - ils->pData[lSt].rx;
2844 eDiff = irs->pData[rEn].rx - ils->pData[lSt].rx;
2845 sDot = cross(ldir, sDiff);
2846 eDot = cross(ldir, eDiff);
2847
2848 atR = sDot / (sDot - eDot);
2849
2850 return true;
2851 }
2852
2853 // plus de colinearite ni d'extremites en commun
2854 m[1] = -m[1];
2855 m[2] = -m[2];
2856 {
2857 double swap = m[0];
2858 m[0] = m[3];
2859 m[3] = swap;
2860 }
2861
2862 atL = (m[0]* usvs[0] + m[1] * usvs[1]) / det;
2863 atR = -(m[2] * usvs[0] + m[3] * usvs[1]) / det;
2864 atx = ils->pData[lSt].rx + atL * ldir;
2865
2866
2867 return true;
2868}
2869
2870bool
2871Shape::TesteAdjacency (Shape * a, int no, const Geom::Point atx, int nPt,
2872 bool push)
2873{
2874 if (nPt == a->swsData[no].stPt || nPt == a->swsData[no].enPt)
2875 return false;
2876
2877 Geom::Point adir, diff, ast, aen, diff1, diff2, diff3, diff4;
2878
2879 ast = a->pData[a->getEdge(no).st].rx;
2880 aen = a->pData[a->getEdge(no).en].rx;
2881
2882 adir = a->eData[no].rdx;
2883
2884 double sle = a->eData[no].length;
2885 double ile = a->eData[no].ilength;
2886
2887 diff = atx - ast;
2888
2889 double e = IHalfRound(cross(adir, diff) * a->eData[no].isqlength);
2890 if (-3 < e && e < 3)
2891 {
2892 double rad = HalfRound (0.501); // when using single precision, 0.505 is better (0.5 would be the correct value,
2893 // but it produces lots of bugs)
2894 diff1[0] = diff[0] - rad;
2895 diff1[1] = diff[1] - rad;
2896 diff2[0] = diff[0] + rad;
2897 diff2[1] = diff[1] - rad;
2898 diff3[0] = diff[0] + rad;
2899 diff3[1] = diff[1] + rad;
2900 diff4[0] = diff[0] - rad;
2901 diff4[1] = diff[1] + rad;
2902 double di1, di2;
2903 bool adjacent = false;
2904 di1 = cross(adir, diff1);
2905 di2 = cross(adir, diff3);
2906 if ((di1 < 0 && di2 > 0) || (di1 > 0 && di2 < 0))
2907 {
2908 adjacent = true;
2909 }
2910 else
2911 {
2912 di1 = cross(adir, diff2);
2913 di2 = cross(adir, diff4);
2914 if ((di1 < 0 && di2 > 0) || (di1 > 0 && di2 < 0))
2915 {
2916 adjacent = true;
2917 }
2918 }
2919 if (adjacent)
2920 {
2921 double t = dot (diff, adir);
2922 if (t > 0 && t < sle)
2923 {
2924 if (push)
2925 {
2926 t *= ile;
2927 PushIncidence (a, no, nPt, t);
2928 }
2929 return true;
2930 }
2931 }
2932 }
2933 return false;
2934}
2935
2936void
2937Shape::CheckAdjacencies (int lastPointNo, int lastChgtPt, Shape * /*shapeHead*/,
2938 int /*edgeHead*/)
2939{
2940 // for each event in chgts
2941 for (auto & chgt : chgts)
2942 {
2943 // get the chgt.ptoNo, which is the point at which the event happened
2944 int chLeN = chgt.ptNo;
2945 int chRiN = chgt.ptNo;
2946 if (chgt.src)
2947 {
2948 Shape *lS = chgt.src;
2949 int lB = chgt.bord;
2950 // get the leftRnd and rightRnd of this edge
2951 int lftN = lS->swsData[lB].leftRnd;
2952 int rgtN = lS->swsData[lB].rightRnd;
2953 // expand the range chLeN..chRiN
2954 if (lftN < chLeN)
2955 chLeN = lftN;
2956 if (rgtN > chRiN)
2957 chRiN = rgtN;
2958 // check each point from lastChgtPt to leftN-1 for a possible adjacency with
2959 // the edge, if detected mark it by modifying leftRnd
2960 // Note we do this in reverse order by starting at the point closer to the
2961 // edge first. If an adjacency is not detected, we immediately break since
2962 // if a point is not adjacent, another on its left won't be adjacent either
2963// for (int n=lftN;n<=rgtN;n++) CreateIncidence(lS,lB,n);
2964 for (int n = lftN - 1; n >= lastChgtPt; n--)
2965 {
2966 if (TesteAdjacency (lS, lB, getPoint(n).x, n, false) ==
2967 false)
2968 break;
2969 lS->swsData[lB].leftRnd = n;
2970 }
2971 // check each point from rgtN+1 to lastPointNo (not included) for a possible adjacency with
2972 // the edge, if detected mark it by modifying rightRnd
2973 // If an adjacency is not detected, we immediately break since if a point
2974 // is not adjacent, another one to its right won't be either.
2975 for (int n = rgtN + 1; n < lastPointNo; n++)
2976 {
2977 if (TesteAdjacency (lS, lB, getPoint(n).x, n, false) ==
2978 false)
2979 break;
2980 lS->swsData[lB].rightRnd = n;
2981 }
2982 }
2983 // totally identical to the block above
2984 if (chgt.osrc)
2985 {
2986 Shape *rS = chgt.osrc;
2987 int rB = chgt.obord;
2988 int lftN = rS->swsData[rB].leftRnd;
2989 int rgtN = rS->swsData[rB].rightRnd;
2990 if (lftN < chLeN)
2991 chLeN = lftN;
2992 if (rgtN > chRiN)
2993 chRiN = rgtN;
2994// for (int n=lftN;n<=rgtN;n++) CreateIncidence(rS,rB,n);
2995 for (int n = lftN - 1; n >= lastChgtPt; n--)
2996 {
2997 if (TesteAdjacency (rS, rB, getPoint(n).x, n, false) ==
2998 false)
2999 break;
3000 rS->swsData[rB].leftRnd = n;
3001 }
3002 for (int n = rgtN + 1; n < lastPointNo; n++)
3003 {
3004 if (TesteAdjacency (rS, rB, getPoint(n).x, n, false) ==
3005 false)
3006 break;
3007 rS->swsData[rB].rightRnd = n;
3008 }
3009 }
3010 // very interesting part, deals with edges to the left in the sweepline at the time
3011 // the event took place
3012 if (chgt.lSrc)
3013 {
3014 // is the left edge's leftRnd smaller than lastChgtPt, basically this is a way to check
3015 // if leftRnd got updated in the previous iteration of the main loop of Shape::ConvertToShape
3016 // or not.
3017 if (chgt.lSrc->swsData[chgt.lBrd].leftRnd < lastChgtPt)
3018 {
3019 // get the left edge and its shape
3020 Shape *nSrc = chgt.lSrc;
3021 int nBrd = chgt.lBrd /*,nNo=chgts[cCh].ptNo */ ;
3022 bool hit;
3023
3024 // iterate through the linked list of edges to the left
3025 do
3026 {
3027 hit = false; // adjacency got detected?
3028 // check all points from chRiN to chLeN
3029 // we go right to left
3030 for (int n = chRiN; n >= chLeN; n--)
3031 {
3032 // test if the point is adjacent to the edge
3033 if (TesteAdjacency
3034 (nSrc, nBrd, getPoint(n).x, n, false))
3035 {
3036 // has the leftRnd been updated in previous iteration? if no? set it directly
3037 if (nSrc->swsData[nBrd].leftRnd < lastChgtPt)
3038 {
3039 nSrc->swsData[nBrd].leftRnd = n;
3040 nSrc->swsData[nBrd].rightRnd = n;
3041 }
3042 else // if yes, we do some checking and only update if it expands the span
3043 {
3044 if (n < nSrc->swsData[nBrd].leftRnd)
3045 nSrc->swsData[nBrd].leftRnd = n;
3046 if (n > nSrc->swsData[nBrd].rightRnd)
3047 nSrc->swsData[nBrd].rightRnd = n;
3048 }
3049 hit = true;
3050 }
3051 }
3052 // test all points between lastChgtPt and chLeN - 1
3053 for (int n = chLeN - 1; n >= lastChgtPt; n--)
3054 {
3055 if (TesteAdjacency
3056 (nSrc, nBrd, getPoint(n).x, n, false) == false)
3057 break;
3058 if (nSrc->swsData[nBrd].leftRnd < lastChgtPt)
3059 {
3060 nSrc->swsData[nBrd].leftRnd = n;
3061 nSrc->swsData[nBrd].rightRnd = n;
3062 }
3063 else
3064 {
3065 if (n < nSrc->swsData[nBrd].leftRnd)
3066 nSrc->swsData[nBrd].leftRnd = n;
3067 if (n > nSrc->swsData[nBrd].rightRnd)
3068 nSrc->swsData[nBrd].rightRnd = n;
3069 }
3070 hit = true;
3071 }
3072 // if no adjacency got detected, no point in going further left so break, if yes
3073 // we continue and see if we can repeat the process on the edge to the left
3074 // and so on (basically going left detecting adjacencies)
3075 if (hit)
3076 {
3077 // get the edge on the left, if non exist, break
3078 SweepTree *node = nSrc->swsData[nBrd].misc;
3079 if (node == nullptr)
3080 break;
3081 node = static_cast < SweepTree * >(node->elem[LEFT]);
3082 if (node == nullptr)
3083 break;
3084 nSrc = node->src;
3085 nBrd = node->bord;
3086 // the edge on the left, did its leftRnd update in the previous iteration of the main loop if ConvertToShape?
3087 if (nSrc->swsData[nBrd].leftRnd >= lastChgtPt)
3088 break;
3089 }
3090 }
3091 while (hit);
3092
3093 }
3094 }
3095 // same thing but to the right side?
3096 if (chgt.rSrc)
3097 {
3098 if (chgt.rSrc->swsData[chgt.rBrd].leftRnd < lastChgtPt)
3099 {
3100 Shape *nSrc = chgt.rSrc;
3101 int nBrd = chgt.rBrd /*,nNo=chgts[cCh].ptNo */ ;
3102 bool hit;
3103 do
3104 {
3105 hit = false;
3106 // test points between chLeN and chRiN for adjacency
3107 // but go left to right
3108 for (int n = chLeN; n <= chRiN; n++)
3109 {
3110 if (TesteAdjacency
3111 (nSrc, nBrd, getPoint(n).x, n, false))
3112 {
3113 if (nSrc->swsData[nBrd].leftRnd < lastChgtPt)
3114 {
3115 nSrc->swsData[nBrd].leftRnd = n;
3116 nSrc->swsData[nBrd].rightRnd = n;
3117 }
3118 else
3119 {
3120 if (n < nSrc->swsData[nBrd].leftRnd)
3121 nSrc->swsData[nBrd].leftRnd = n;
3122 if (n > nSrc->swsData[nBrd].rightRnd)
3123 nSrc->swsData[nBrd].rightRnd = n;
3124 }
3125 hit = true;
3126 }
3127 }
3128 // testing points between chRiN and lastPointNo for adjacency
3129 for (int n = chRiN + 1; n < lastPointNo; n++)
3130 {
3131 if (TesteAdjacency
3132 (nSrc, nBrd, getPoint(n).x, n, false) == false)
3133 break;
3134 if (nSrc->swsData[nBrd].leftRnd < lastChgtPt)
3135 {
3136 nSrc->swsData[nBrd].leftRnd = n;
3137 nSrc->swsData[nBrd].rightRnd = n;
3138 }
3139 else
3140 {
3141 if (n < nSrc->swsData[nBrd].leftRnd)
3142 nSrc->swsData[nBrd].leftRnd = n;
3143 if (n > nSrc->swsData[nBrd].rightRnd)
3144 nSrc->swsData[nBrd].rightRnd = n;
3145 }
3146 hit = true;
3147 }
3148 if (hit)
3149 {
3150 SweepTree *node = nSrc->swsData[nBrd].misc;
3151 if (node == nullptr)
3152 break;
3153 node = static_cast < SweepTree * >(node->elem[RIGHT]);
3154 if (node == nullptr)
3155 break;
3156 nSrc = node->src;
3157 nBrd = node->bord;
3158 if (nSrc->swsData[nBrd].leftRnd >= lastChgtPt)
3159 break;
3160 }
3161 }
3162 while (hit);
3163 }
3164 }
3165 }
3166}
3167
3168
3169void Shape::AddChgt(int lastPointNo, int lastChgtPt, Shape * &shapeHead,
3170 int &edgeHead, sTreeChangeType type, Shape * lS, int lB, Shape * rS,
3171 int rB)
3172{
3173 // fill in the event details and push it
3174 sTreeChange c;
3175 c.ptNo = lastPointNo;
3176 c.type = type;
3177 c.src = lS;
3178 c.bord = lB;
3179 c.osrc = rS;
3180 c.obord = rB;
3181 chgts.push_back(c);
3182 // index of the newly added event
3183 const int nCh = chgts.size() - 1;
3184
3185 /* FIXME: this looks like a cut and paste job */
3186
3187 if (lS) {
3188 // if there is an edge to the left, mark it in lSrc
3189 SweepTree *lE = lS->swsData[lB].misc;
3190 if (lE && lE->elem[LEFT]) {
3191 SweepTree *llE = static_cast < SweepTree * >(lE->elem[LEFT]);
3192 chgts[nCh].lSrc = llE->src;
3193 chgts[nCh].lBrd = llE->bord;
3194 } else {
3195 chgts[nCh].lSrc = nullptr;
3196 chgts[nCh].lBrd = -1;
3197 }
3198
3199 // lastChgtPt is same as lastPointNo if lastPointNo is the leftmost point in that y level and it's the
3200 // left most point on the y level of lastPointNo otherwise
3201 // leftRnd will be smaller than lastChgtPt if the last time the edge participated in any event (edge addition/intersection)
3202 // was before lastChgtPt.
3203 if (lS->swsData[lB].leftRnd < lastChgtPt) {
3204 lS->swsData[lB].leftRnd = lastPointNo; // set leftRnd to the current point
3205 lS->swsData[lB].nextSh = shapeHead; // add this edge in the linked list
3206 lS->swsData[lB].nextBo = edgeHead;
3207 edgeHead = lB;
3208 shapeHead = lS;
3209 } else { // If an event occured to the edge after lastChgtPt (which is possible, for example, a horizontal edge
3210 // that intersects with other edges.
3211 // get the leftRnd already
3212 int old = lS->swsData[lB].leftRnd;
3213 // This seems really weird. I suspect this if statement will never be true in a top to bottom sweepline
3214 // see if we reached this point, it means old >= lastChgtPt
3215 // and lastChgtPt is the leftmost point at the current y level of lastPointNo
3216 // so how can old have an x greater than lastPoint? The sweepline hasn't reached that position yet!
3217 if (getPoint(old).x[0] > getPoint(lastPointNo).x[0]) {
3218 lS->swsData[lB].leftRnd = lastPointNo;
3219 }
3220 }
3221 // same logic as in the upper block
3222 if (lS->swsData[lB].rightRnd < lastChgtPt) {
3223 lS->swsData[lB].rightRnd = lastPointNo;
3224 } else {
3225 int old = lS->swsData[lB].rightRnd;
3226 if (getPoint(old).x[0] < getPoint(lastPointNo).x[0])
3227 lS->swsData[lB].rightRnd = lastPointNo;
3228 }
3229 }
3230
3231 // it's an intersection event and rS is the right edge's shape and rB is the
3232 // right edge
3233 if (rS) {
3234 // get the edge on the right and set it in rBrd and rSrc
3235 SweepTree *rE = rS->swsData[rB].misc;
3236 if (rE->elem[RIGHT]) {
3237 SweepTree *rrE = static_cast < SweepTree * >(rE->elem[RIGHT]);
3238 chgts[nCh].rSrc = rrE->src;
3239 chgts[nCh].rBrd = rrE->bord;
3240 } else {
3241 chgts[nCh].rSrc = nullptr;
3242 chgts[nCh].rBrd = -1;
3243 }
3244
3245 // same code as above, except that it's on rS
3246 if (rS->swsData[rB].leftRnd < lastChgtPt) {
3247 rS->swsData[rB].leftRnd = lastPointNo;
3248 rS->swsData[rB].nextSh = shapeHead;
3249 rS->swsData[rB].nextBo = edgeHead;
3250 edgeHead = rB;
3251 shapeHead = rS;
3252 } else {
3253 int old = rS->swsData[rB].leftRnd;
3254 if (getPoint(old).x[0] > getPoint(lastPointNo).x[0]) {
3255 rS->swsData[rB].leftRnd = lastPointNo;
3256 }
3257 }
3258 if (rS->swsData[rB].rightRnd < lastChgtPt) {
3259 rS->swsData[rB].rightRnd = lastPointNo;
3260 } else {
3261 int old = rS->swsData[rB].rightRnd;
3262 if (getPoint(old).x[0] < getPoint(lastPointNo).x[0])
3263 rS->swsData[rB].rightRnd = lastPointNo;
3264 }
3265 } else { // if rS wasn't set, this is not an intersection event, so
3266 // check if there is an edge to the right and set rBrd
3267 SweepTree *lE = lS->swsData[lB].misc;
3268 if (lE && lE->elem[RIGHT]) {
3269 SweepTree *rlE = static_cast < SweepTree * >(lE->elem[RIGHT]);
3270 chgts[nCh].rSrc = rlE->src;
3271 chgts[nCh].rBrd = rlE->bord;
3272 } else {
3273 chgts[nCh].rSrc = nullptr;
3274 chgts[nCh].rBrd = -1;
3275 }
3276 }
3277}
3278
3279// is this a debug function? It's calling localized "printf" ...
3280void
3282{
3283 for (int i = 0; i < numberOfPoints(); i++)
3284 {
3285 pData[i].rx = getPoint(i).x;
3286 }
3287 for (int i = 0; i < numberOfEdges(); i++)
3288 {
3289 eData[i].rdx = getEdge(i).dx;
3290 }
3291 for (int i = 0; i < numberOfEdges(); i++)
3292 {
3293 for (int j = i + 1; j < numberOfEdges(); j++)
3294 {
3295 Geom::Point atx;
3296 double atL, atR;
3297 if (TesteIntersection (this, this, i, j, atx, atL, atR, false))
3298 {
3299 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]);
3300 }
3301 }
3302 }
3303 fflush (stdout);
3304}
3305
3306void
3307Shape::CheckEdges (int lastPointNo, int lastChgtPt, Shape * a, Shape * b,
3308 BooleanOp mod)
3309{
3310
3311 for (auto & chgt : chgts)
3312 {
3313 // if an edge addition event, we want to set curPoint to the point at which
3314 // the edge was added. chgt.ptNo is automatically updated after any possible sorting and merging
3315 // so thats good too :-)
3316 if (chgt.type == 0)
3317 {
3318 Shape *lS = chgt.src;
3319 int lB = chgt.bord;
3320 lS->swsData[lB].curPoint = chgt.ptNo;
3321 }
3322 }
3323 // for each event in events
3324 for (auto & chgt : chgts)
3325 {
3326// int chLeN=chgts[cCh].ptNo;
3327// int chRiN=chgts[cCh].ptNo;
3328 // do the main edge (by do I mean process it, see if anything needs to be drawn, if yes, draw it)
3329 if (chgt.src)
3330 {
3331 Shape *lS = chgt.src;
3332 int lB = chgt.bord;
3333 Avance (lastPointNo, lastChgtPt, lS, lB, a, b, mod);
3334 }
3335 // do the other edge (the right in intersection, wont exist if chgt wasn't an intersection event)
3336 if (chgt.osrc)
3337 {
3338 Shape *rS = chgt.osrc;
3339 int rB = chgt.obord;
3340 Avance (lastPointNo, lastChgtPt, rS, rB, a, b, mod);
3341 }
3342
3343 // See there are few cases due to which an edge will have a leftRnd >= lastChgtPt. Either the
3344 // edge had some event associated with it (addition/removal/intersection) at that y level. Or
3345 // there was some point in the previous y level that was on top of the edge, and thus an
3346 // adjacency was detected and the leftRnd/rightRnd were set accordingly. If you have neither of
3347 // these, leftRnd/rightRnd won't be set at all. If the case is former, that the edge had an
3348 // event at the previous y level, the blocks above will automatically call Avance on the edge.
3349 // However, for the latter, we have no chgt event associated with the edge. Thus, the blocks
3350 // below calls Shape::Avance on edges to the left and to the right of the unique (or the left)
3351 // edge. However, it's called only if leftRnd >= lastChgtPt.
3352 if (chgt.lSrc)
3353 {
3354 Shape *nSrc = chgt.lSrc;
3355 int nBrd = chgt.lBrd;
3356 while (nSrc->swsData[nBrd].leftRnd >= // <-- if yes, means some event occured to this event after lastChgtPt or an adjacency was detected
3357 lastChgtPt /*&& nSrc->swsData[nBrd].doneTo < lastChgtPt */ )
3358 {
3359 Avance (lastPointNo, lastChgtPt, nSrc, nBrd, a, b, mod);
3360
3361 SweepTree *node = nSrc->swsData[nBrd].misc;
3362 if (node == nullptr)
3363 break;
3364 node = static_cast < SweepTree * >(node->elem[LEFT]);
3365 if (node == nullptr)
3366 break;
3367 nSrc = node->src;
3368 nBrd = node->bord;
3369 }
3370 }
3371 if (chgt.rSrc)
3372 {
3373 Shape *nSrc = chgt.rSrc;
3374 int nBrd = chgt.rBrd;
3375 while (nSrc->swsData[nBrd].rightRnd >=
3376 lastChgtPt /*&& nSrc->swsData[nBrd].doneTo < lastChgtPt */ )
3377 {
3378 Avance (lastPointNo, lastChgtPt, nSrc, nBrd, a, b, mod);
3379
3380 SweepTree *node = nSrc->swsData[nBrd].misc;
3381 if (node == nullptr)
3382 break;
3383 node = static_cast < SweepTree * >(node->elem[RIGHT]);
3384 if (node == nullptr)
3385 break;
3386 nSrc = node->src;
3387 nBrd = node->bord;
3388 }
3389 }
3390 }
3391}
3392
3393void
3394Shape::Avance (int lastPointNo, int lastChgtPt, Shape * lS, int lB, Shape * /*a*/,
3395 Shape * b, BooleanOp mod)
3396{
3397 double dd = HalfRound (1); // get the smallest step you can take in the rounding grid.
3398 bool avoidDiag = false; // triggers some special diagonal avoiding code below
3399// if ( lastChgtPt > 0 && pts[lastChgtPt-1].y+dd == pts[lastChgtPt].y ) avoidDiag=true;
3400
3401 // my best guess is that direct acts kinda as an inverter. If set to true, edges are drawn
3402 // in the direction they should be drawn in, if set to false, they are inverted. This is needed
3403 // if the edge is coming from shape b and the mod is bool_op_diff or bool_op_symdiff. This is how
3404 // Livarot does these boolean operations.
3405 bool direct = true;
3406 if (lS == b && (mod == bool_op_diff || mod == bool_op_symdiff))
3407 direct = false;
3408 // get the leftRnd and rightRnd points of the edge lB. For most edges, leftRnd and rightRnd are identical
3409 // however when you have a horizontal edge, they can be different.
3410 int lftN = lS->swsData[lB].leftRnd;
3411 int rgtN = lS->swsData[lB].rightRnd;
3412 // doneTo acts as a marker. Once Avance processes an edge at a certain y level, it sets doneTo to the
3413 // right most point at that y level. See the end of this function to see how it does this.
3414 if (lS->swsData[lB].doneTo < lastChgtPt)
3415 {
3416 // the last point till which this edge has been drawn
3417 int lp = lS->swsData[lB].curPoint;
3418 // if the last point exists and lastChgtPt.y is just dd (1 rounded step) bigger than lastpoint.y
3419 // in simpler words, the last point drawn is just 1 rounded step above lastChgtPt
3420 // Look, if there is a potential edge to draw, that edge is going to have its upper endpoint at lp
3421 // and could have the lower endpoint lftN...rgtN whatever, but we are sure that it can't go any lower (downwards)
3422 // than lastChgtPt. If this "if" block evalues to true, there is a possibility we might an edge that would
3423 // be diagonal for example 1 rounded unit dd down and to the right. We would like to avoid this if there is
3424 // a point right below, so we can draw two edges, first down and then right. See the figure in the header
3425 // docs to see what I mean
3426 if (lp >= 0 && getPoint(lp).x[1] + dd == getPoint(lastChgtPt).x[1])
3427 avoidDiag = true;
3428 // if the edge is horizontal
3429 if (lS->eData[lB].rdx[1] == 0)
3430 {
3431 // tjs de gauche a droite et pas de diagonale -- > left to right horizonal edge won't be diagonal (since it's horizontal :P)
3432 if (lS->eData[lB].rdx[0] >= 0) // edge is left to right
3433 {
3434 for (int p = lftN; p <= rgtN; p++)
3435 {
3436 DoEdgeTo (lS, lB, p, direct, true);
3437 lp = p;
3438 }
3439 }
3440 else // edge is right to left
3441 {
3442 for (int p = lftN; p <= rgtN; p++)
3443 {
3444 DoEdgeTo (lS, lB, p, direct, false);
3445 lp = p;
3446 }
3447 }
3448 }
3449 else if (lS->eData[lB].rdx[1] > 0) // the edge is top to bottom
3450 {
3451 if (lS->eData[lB].rdx[0] >= 0) // edge is top to bottom and left to right
3452 {
3453
3454 for (int p = lftN; p <= rgtN; p++) // for the range lftN..rgtN
3455 {
3456 // if avoidDiag is true, point p is lftN and the point the edge is to be drawn to (lftN) has an x that's
3457 // 1 rounded unit (dd) greater than the last point drawn. So basically lftN is one unit down and one unit
3458 // to the right of lp
3459 if (avoidDiag && p == lftN && getPoint(lftN).x[0] == getPoint(lp).x[0] + dd)
3460 {
3461 // we would want to avoid the diagonal but only if there is a point (in our directed graph)
3462 // just to the left of the original lower endpoint and instead of doing a diagonal, we can create an edge to that point
3463 // and then from that point to the original endpoint. see the figure in the header docs to see what I mean
3464 if (lftN > 0 && lftN - 1 >= lastChgtPt // if there is a point in the figure just below lp and to the left of lftN
3465 && getPoint(lftN - 1).x[0] == getPoint(lp).x[0]) // that point has x equal to that of lp (right below it)
3466 {
3467 DoEdgeTo (lS, lB, lftN - 1, direct, true); // draw an edge to lftn - 1
3468 DoEdgeTo (lS, lB, lftN, direct, true); // then draw an edge to lftN
3469 }
3470 else
3471 {
3472 DoEdgeTo (lS, lB, lftN, direct, true);
3473 }
3474 }
3475 else
3476 {
3477 DoEdgeTo (lS, lB, p, direct, true);
3478 }
3479 lp = p;
3480 }
3481 }
3482 else
3483 {
3484
3485 for (int p = rgtN; p >= lftN; p--) // top to bottom and right to left
3486 {
3487 // exactly identical to the code above except that it's a diagonal down and to the left
3488 if (avoidDiag && p == rgtN && getPoint(rgtN).x[0] == getPoint(lp).x[0] - dd)
3489 {
3490 if (rgtN < numberOfPoints() && rgtN + 1 < lastPointNo // refer to first diagram in header docs to see why this condition
3491 && getPoint(rgtN + 1).x[0] == getPoint(lp).x[0])
3492 {
3493 DoEdgeTo (lS, lB, rgtN + 1, direct, true);
3494 DoEdgeTo (lS, lB, rgtN, direct, true);
3495 }
3496 else
3497 {
3498 DoEdgeTo (lS, lB, rgtN, direct, true);
3499 }
3500 }
3501 else
3502 {
3503 DoEdgeTo (lS, lB, p, direct, true);
3504 }
3505 lp = p;
3506 }
3507 }
3508 }
3509 else // edge is bottom to top
3510 {
3511 if (lS->eData[lB].rdx[0] >= 0) // edge is bottom to top and left to right
3512 {
3513
3514 for (int p = rgtN; p >= lftN; p--)
3515 {
3516 if (avoidDiag && p == rgtN && getPoint(rgtN).x[0] == getPoint(lp).x[0] - dd)
3517 {
3518 if (rgtN < numberOfPoints() && rgtN + 1 < lastPointNo
3519 && getPoint(rgtN + 1).x[0] == getPoint(lp).x[0])
3520 {
3521 DoEdgeTo (lS, lB, rgtN + 1, direct, false); // draw an edge that goes down
3522 DoEdgeTo (lS, lB, rgtN, direct, false); // draw an edge that goes left
3523 }
3524 else
3525 {
3526 DoEdgeTo (lS, lB, rgtN, direct, false);
3527 }
3528 }
3529 else
3530 {
3531 DoEdgeTo (lS, lB, p, direct, false);
3532 }
3533 lp = p;
3534 }
3535 }
3536 else
3537 {
3538 // bottom to top and right to left edge
3539 for (int p = lftN; p <= rgtN; p++)
3540 {
3541 // totally identical as the first block that I explain with a figure in the header docs
3542 if (avoidDiag && p == lftN && getPoint(lftN).x[0] == getPoint(lp).x[0] + dd)
3543 {
3544 if (lftN > 0 && lftN - 1 >= lastChgtPt
3545 && getPoint(lftN - 1).x[0] == getPoint(lp).x[0])
3546 {
3547 DoEdgeTo (lS, lB, lftN - 1, direct, false);
3548 DoEdgeTo (lS, lB, lftN, direct, false);
3549 }
3550 else
3551 {
3552 DoEdgeTo (lS, lB, lftN, direct, false);
3553 }
3554 }
3555 else
3556 {
3557 DoEdgeTo (lS, lB, p, direct, false);
3558 }
3559 lp = p;
3560 }
3561 }
3562 }
3563 lS->swsData[lB].curPoint = lp;
3564 }
3565 // see how doneTo is being set to lastPointNo - 1? See the figure in the header docs of this function and you'll see
3566 // what lastPointNo is, subtract one and you get the right most point that's just above lastPointNo. This marks that
3567 // this edge has been processed til that y level.
3568 lS->swsData[lB].doneTo = lastPointNo - 1;
3569}
3570
3571void
3572Shape::DoEdgeTo (Shape * iS, int iB, int iTo, bool direct, bool sens)
3573{
3574 int lp = iS->swsData[iB].curPoint;
3575 int ne = -1;
3576 if (sens)
3577 {
3578 if (direct)
3579 ne = AddEdge (lp, iTo);
3580 else
3581 ne = AddEdge (iTo, lp);
3582 }
3583 else
3584 {
3585 if (direct)
3586 ne = AddEdge (iTo, lp);
3587 else
3588 ne = AddEdge (lp, iTo);
3589 }
3590 if (ne >= 0 && _has_back_data)
3591 {
3592 ebData[ne].pathID = iS->ebData[iB].pathID;
3593 ebData[ne].pieceID = iS->ebData[iB].pieceID;
3594 if (iS->eData[iB].length < 0.00001)
3595 {
3596 ebData[ne].tSt = ebData[ne].tEn = iS->ebData[iB].tSt;
3597 }
3598 else
3599 {
3600 double bdl = iS->eData[iB].ilength;
3601 Geom::Point bpx = iS->pData[iS->getEdge(iB).st].rx;
3602 Geom::Point bdx = iS->eData[iB].rdx;
3603 Geom::Point psx = getPoint(getEdge(ne).st).x;
3604 Geom::Point pex = getPoint(getEdge(ne).en).x;
3605 Geom::Point psbx=psx-bpx;
3606 Geom::Point pebx=pex-bpx;
3607 double pst = dot(psbx,bdx) * bdl;
3608 double pet = dot(pebx,bdx) * bdl;
3609 pst = iS->ebData[iB].tSt * (1 - pst) + iS->ebData[iB].tEn * pst;
3610 pet = iS->ebData[iB].tSt * (1 - pet) + iS->ebData[iB].tEn * pet;
3611 ebData[ne].tEn = pet;
3612 ebData[ne].tSt = pst;
3613 }
3614 }
3615 iS->swsData[iB].curPoint = iTo;
3616 if (ne >= 0)
3617 {
3618 int cp = iS->swsData[iB].firstLinkedPoint;
3619 swsData[ne].firstLinkedPoint = iS->swsData[iB].firstLinkedPoint;
3620 while (cp >= 0)
3621 {
3622 pData[cp].askForWindingB = ne;
3623 cp = pData[cp].nextLinkedPoint;
3624 }
3625 iS->swsData[iB].firstLinkedPoint = -1;
3626 }
3627}
Side
Definition LivarotDefs.h:85
@ LEFT
Definition LivarotDefs.h:86
@ RIGHT
Definition LivarotDefs.h:87
FillRule
Definition LivarotDefs.h:67
@ fill_oddEven
Definition LivarotDefs.h:68
@ fill_nonZero
Definition LivarotDefs.h:69
@ fill_justDont
Definition LivarotDefs.h:71
@ fill_positive
Definition LivarotDefs.h:70
BooleanOp
Definition LivarotDefs.h:76
@ bool_op_symdiff
Definition LivarotDefs.h:80
@ bool_op_diff
Definition LivarotDefs.h:79
@ bool_op_cut
Definition LivarotDefs.h:81
@ bool_op_inters
Definition LivarotDefs.h:78
@ bool_op_union
Definition LivarotDefs.h:77
@ bool_op_slice
Definition LivarotDefs.h:82
@ shape_input_err
Definition LivarotDefs.h:29
@ shape_euler_err
Definition LivarotDefs.h:25
@ FIRST
Definition LivarotDefs.h:91
@ LAST
Definition LivarotDefs.h:92
TODO: insert short description here.
@ shape_polygon
Definition Shape.h:49
bool directedEulerian(Shape const *s)
Is the graph Eulerian?
Definition Shape.cpp:2117
3x3 affine transformation matrix.
AVLTree * elem[2]
Definition AVL.h:35
3x3 matrix representing an affine transformation.
Definition affine.h:70
Coord det() const
Calculate the determinant.
Definition affine.cpp:416
Two-dimensional point that doubles as a vector.
Definition point.h:66
A class to store/manipulate directed graphs.
Definition Shape.h:65
bool _has_edges_data
the eData array is allocated
Definition Shape.h:1069
void DoEdgeTo(Shape *iS, int iB, int iTo, bool direct, bool sens)
Draw edge to a passed point.
int PushIncidence(Shape *a, int cb, int pt, double theta)
void MakeSweepDestData(bool nVal)
Initialize the sweep destination data cache.
Definition Shape.cpp:149
void AddChgt(int lastPointNo, int lastChgtPt, Shape *&shapeHead, int &edgeHead, sTreeChangeType type, Shape *lS, int lB, Shape *rS, int rB)
Add the event in chgts.
std::vector< sweep_src_data > swsData
Definition Shape.h:1082
int Other(int p, int b) const
Definition Shape.h:181
sTreeChangeType
Enum describing all the events that can happen to a sweepline.
Definition Shape.h:82
@ INTERSECTION
Definition Shape.h:85
@ EDGE_INSERTED
Definition Shape.h:83
@ EDGE_REMOVED
Definition Shape.h:84
void CheckAdjacencies(int lastPointNo, int lastChgtPt, Shape *shapeHead, int edgeHead)
If there are points that lie on edges, mark this by modifying leftRnd and rightRnd variables.
void ResetSweep()
Prepare point data cache, edge data cache and sweep source cache.
int maxInc
Definition Shape.h:426
void AssembleAretes(FillRule directed=fill_nonZero)
int Booleen(Shape *a, Shape *b, BooleanOp mod, int cutPathID=-1)
void MakeBackData(bool nVal)
Definition Shape.cpp:169
bool _point_data_initialised
the pData array is up to date
Definition Shape.h:1068
int nbInc
Definition Shape.h:425
std::vector< sTreeChange > chgts
Definition Shape.h:424
void MakeSweepSrcData(bool nVal)
Initialize the sweep source data cache.
Definition Shape.cpp:129
void DisconnectStart(int b)
Definition Shape.cpp:1853
incidenceData * iData
Definition Shape.h:428
void SwapEdges(int a, int b)
Definition Shape.cpp:1191
int AddEdge(int st, int en)
Definition Shape.cpp:1052
int CyclePrevAt(int p, int b) const
Definition Shape.h:235
bool _has_back_data
Definition Shape.h:1073
bool _has_sweep_src_data
the swsData array is allocated
Definition Shape.h:1070
bool hasPoints() const
Do we have points?
Definition Shape.h:491
std::vector< raster_data > swrData
Definition Shape.h:1084
bool TesteAdjacency(Shape *iL, int ilb, const Geom::Point atx, int nPt, bool push)
bool _bbox_up_to_date
the leftX/rightX/topY/bottomY are up to date
Definition Shape.h:1074
void CheckEdges(int lastPointNo, int lastChgtPt, Shape *a, Shape *b, BooleanOp mod)
Check if there are edges to draw and draw them.
std::vector< back_data > ebData
Definition Shape.h:422
static double IHalfRound(double x)
Definition Shape.h:361
void MakePointData(bool nVal)
Initialize the point data cache.
Definition Shape.cpp:71
bool _need_edges_sorting
edges have been added: maybe they are not ordered clockwise
Definition Shape.h:1065
int Reoriente(Shape *a)
void ForceToPolygon()
int maxAr
Definition Shape.h:474
void Avance(int lastPointNo, int lastChgtPt, Shape *iS, int iB, Shape *a, Shape *b, BooleanOp mod)
Do the edge.
static double HalfRound(double x)
Definition Shape.h:356
int numberOfEdges() const
Returns number of edges.
Definition Shape.h:498
void CleanupSweep()
Clear point data cache, edge data cache and sweep source cache.
static double Round(double x)
Definition Shape.h:350
std::vector< edge_data > eData
Definition Shape.h:1081
int NextAt(int p, int b) const
Definition Shape.h:190
int numberOfPoints() const
Returns number of points.
Definition Shape.h:484
bool _has_points_data
the pData array is allocated
Definition Shape.h:1067
dg_arete const & getEdge(int n) const
Get an edge.
Definition Shape.h:548
void SortEdges()
Sort all edges (clockwise) around each point.
Definition Shape.cpp:1393
void SortPointsByOldInd(int s, int e)
Sort the points (take oldInd into account)
Definition Shape.cpp:663
int maxPt
Definition Shape.h:473
SweepTreeList * sTree
Definition Shape.h:430
void Inverse(int b)
Definition Shape.cpp:1924
void Reset(int n=0, int m=0)
Clear all data.
Definition Shape.cpp:235
void initialiseEdgeData()
Definition Shape.cpp:2070
void SortPointsRounded()
Sort all points by their rounded coordinates.
Definition Shape.cpp:475
std::vector< dg_point > _pts
Definition Shape.h:1076
void GetWindings(bool brutal=false)
Calculates the winding numbers to the left and right of all edges of this shape.
int ConvertToShape(Shape *a, FillRule directed=fill_nonZero, bool invert=false)
Using a given fill rule, find all intersections in the shape given, create a new intersection free sh...
bool _has_raster_data
the swrData array is allocated
Definition Shape.h:1072
int type
Definition Shape.h:477
friend class SweepEventQueue
Definition Shape.h:554
std::vector< dg_arete > _aretes
Definition Shape.h:1077
int AddPoint(const Geom::Point x)
Definition Shape.cpp:266
void MakeEdgeData(bool nVal)
Initialize the edge data cache.
Definition Shape.cpp:87
void TesteIntersection(SweepTree *t, Side s, bool onlyDiff)
Test if there is an intersection of an edge on a particular side.
void clearIncidenceData()
Definition Shape.cpp:2100
bool _has_sweep_dest_data
the swdData array is allocated
Definition Shape.h:1071
SweepEventQueue * sEvts
Definition Shape.h:431
int Winding(const Geom::Point px) const
Compute the winding number of the point given.
dg_point const & getPoint(int n) const
Get a point.
Definition Shape.h:537
std::vector< sweep_dest_data > swdData
Definition Shape.h:1083
void initialisePointData()
Definition Shape.cpp:2054
bool hasBackData() const
Do we have back data?
Definition Shape.h:526
void AssemblePoints(Shape *a)
void SubEdge(int e)
Definition Shape.cpp:1177
void Validate()
void DisconnectEnd(int b)
Definition Shape.cpp:1888
int CreateIncidence(Shape *a, int cb, int pt)
std::vector< point_data > pData
Definition Shape.h:1085
SweepEvent * add(SweepTree *iLeft, SweepTree *iRight, Geom::Point &iPt, double itl, double itr)
Add an intersection in the binary heap.
bool peek(SweepTree *&iLeft, SweepTree *&iRight, Geom::Point &oPt, double &itl, double &itr)
Look for the top most intersection in the heap.
bool extract(SweepTree *&iLeft, SweepTree *&iRight, Geom::Point &oPt, double &itl, double &itr)
Extract the top most intersection from the heap.
int size() const
Number of events currently stored.
The sweepline tree to store a linear sequence of edges that intersect with the sweepline in the exact...
SweepTree * add(Shape *iSrc, int iBord, int iWeight, int iStartPoint, Shape *iDst)
Create a new node and add it.
One node in the AVL tree of edges.
Definition sweep-tree.h:42
void SwapWithRight(SweepTreeList &list, SweepEventQueue &queue)
Swap nodes, or more exactly, swap the edges in them.
void RemoveEvent(SweepEventQueue &queue, Side s)
Remove event on the side s if it exists from event queue.
Shape * src
Definition sweep-tree.h:46
static T det(T a, T b, T c, T d)
Definition conic-5.cpp:95
double c[8][4]
Inkscape::XML::Node * node
unsigned long weight
Definition quantize.cpp:37
double ang(Point n1, Point n2)
Definition sanitize.cpp:89
void invert(const double v[16], double alpha[16])
Geom::Point dx
Definition Shape.h:463
int incidentEdge[2]
Definition Shape.h:452
Geom::Point x
Definition Shape.h:449
int totalDegree() const
Definition Shape.h:455
A structure that represents a change that took place in the sweepline.
Definition Shape.h:92
A container of intersection events.
TODO: insert short description here.
TODO: insert short description here.
void dot(Cairo::RefPtr< Cairo::Context > &cr, double x, double y)