Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
ShapeRaster.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
/*
5 * Authors: see git history
6 *
7 * Copyright (C) 2018 Authors
8 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
9 */
10/*
11 * ShapeRaster.cpp
12 * nlivarot
13 *
14 * Created by fred on Sat Jul 19 2003.
15 *
16 */
17
18#include "Shape.h"
19
20#include "livarot/float-line.h"
23#include "livarot/sweep-tree.h"
24
25/*
26 * polygon rasterization: the sweepline algorithm in all its glory
27 * nothing unusual in this implementation, so nothing special to say
28 */
29
30void Shape::BeginRaster(float &pos, int &curPt)
31{
32 if ( numberOfPoints() <= 1 || numberOfEdges() <= 1 ) {
33 curPt = 0;
34 pos = 0;
35 return;
36 }
37
38 MakeRasterData(true);
39 MakePointData(true);
40 MakeEdgeData(true);
41
42 if (sTree == nullptr) {
44 }
45 if (sEvts == nullptr) {
47 }
48
49 SortPoints();
50
51 curPt = 0;
52 pos = getPoint(0).x[1] - 1.0;
53
54 for (int i = 0; i < numberOfPoints(); i++) {
55 pData[i].pending = 0;
56 pData[i].nextLinkedPoint = -1;
57 pData[i].rx[0] = /*Round(*/getPoint(i).x[0]/*)*/;
58 pData[i].rx[1] = /*Round(*/getPoint(i).x[1]/*)*/;
59 }
60
61 for (int i = 0;i < numberOfEdges(); i++) {
62 swrData[i].misc = nullptr;
63 eData[i].rdx=pData[getEdge(i).en].rx - pData[getEdge(i).st].rx;
64 }
65}
66
67
69{
70 delete sTree;
71 sTree = nullptr;
72 delete sEvts;
73 sEvts = nullptr;
74
75 MakePointData(false);
76 MakeEdgeData(false);
77 MakeRasterData(false);
78}
79
80
81// 2 versions of the Scan() series to move the scanline to a given position withou actually computing coverages
82void Shape::Scan(float &pos, int &curP, float to, float step)
83{
84 if ( numberOfEdges() <= 1 ) {
85 return;
86 }
87
88 if ( pos == to ) {
89 return;
90 }
91
92 enum Direction {
93 DOWNWARDS,
94 UPWARDS
95 };
96
97 Direction const d = (pos < to) ? DOWNWARDS : UPWARDS;
98
99 // points of the polygon are sorted top-down, so we take them in order, starting with the one at index curP,
100 // until we reach the wanted position to.
101 // don't forget to update curP and pos when we're done
102 int curPt = curP;
103 while ( ( d == DOWNWARDS && curPt < numberOfPoints() && getPoint(curPt).x[1] <= to) ||
104 ( d == UPWARDS && curPt > 0 && getPoint(curPt - 1).x[1] >= to) )
105 {
106 int nPt = (d == DOWNWARDS) ? curPt++ : --curPt;
107
108 // treat a new point: remove and add edges incident to it
109 int nbUp;
110 int nbDn;
111 int upNo;
112 int dnNo;
113 _countUpDown(nPt, &nbUp, &nbDn, &upNo, &dnNo);
114
115 if ( d == DOWNWARDS ) {
116 if ( nbDn <= 0 ) {
117 upNo = -1;
118 }
119 if ( upNo >= 0 && swrData[upNo].misc == nullptr ) {
120 upNo = -1;
121 }
122 } else {
123 if ( nbUp <= 0 ) {
124 dnNo = -1;
125 }
126 if ( dnNo >= 0 && swrData[dnNo].misc == nullptr ) {
127 dnNo = -1;
128 }
129 }
130
131 if ( ( d == DOWNWARDS && nbUp > 0 ) || ( d == UPWARDS && nbDn > 0 ) ) {
132 // first remove edges coming from above or below, as appropriate
133 int cb = getPoint(nPt).incidentEdge[FIRST];
134 while ( cb >= 0 && cb < numberOfEdges() ) {
135
136 Shape::dg_arete const &e = getEdge(cb);
137 if ( (d == DOWNWARDS && nPt == std::max(e.st, e.en)) ||
138 (d == UPWARDS && nPt == std::min(e.st, e.en)) )
139 {
140 if ( ( d == DOWNWARDS && cb != upNo ) || ( d == UPWARDS && cb != dnNo ) ) {
141 // we salvage the edge upNo to plug the edges we'll be addingat its place
142 // but the other edge don't have this chance
143 SweepTree *node = swrData[cb].misc;
144 if ( node ) {
145 swrData[cb].misc = nullptr;
146 node->Remove(*sTree, *sEvts, true);
147 }
148 }
149 }
150 cb = NextAt(nPt, cb);
151 }
152 }
153
154 // if there is one edge going down and one edge coming from above, we don't Insert() the new edge,
155 // but replace the upNo edge by the new one (faster)
156 SweepTree* insertionNode = nullptr;
157 if ( dnNo >= 0 ) {
158 if ( upNo >= 0 ) {
159 int rmNo=(d == DOWNWARDS) ? upNo:dnNo;
160 int neNo=(d == DOWNWARDS) ? dnNo:upNo;
161 SweepTree* node = swrData[rmNo].misc;
162 swrData[rmNo].misc = nullptr;
163
164 int const P = (d == DOWNWARDS) ? nPt : Other(nPt, neNo);
165 node->ConvertTo(this, neNo, 1, P);
166
167 swrData[neNo].misc = node;
168 insertionNode = node;
169 CreateEdge(neNo, to, step);
170 } else {
171 // always DOWNWARDS
172 SweepTree* node = sTree->add(this, dnNo, 1, nPt, this);
173 swrData[dnNo].misc = node;
174 node->Insert(*sTree, *sEvts, this, nPt, true);
175 insertionNode = node;
176 CreateEdge(dnNo,to,step);
177 }
178 } else {
179 if ( upNo >= 0 ) {
180 // always UPWARDS
181 SweepTree* node = sTree->add(this, upNo, 1, nPt, this);
182 swrData[upNo].misc = node;
183 node->Insert(*sTree, *sEvts, this, nPt, true);
184 insertionNode = node;
185 CreateEdge(upNo,to,step);
186 }
187 }
188
189 // add the remaining edges
190 if ( ( d == DOWNWARDS && nbDn > 1 ) || ( d == UPWARDS && nbUp > 1 ) ) {
191 // si nbDn == 1 , alors dnNo a deja ete traite
192 int cb = getPoint(nPt).incidentEdge[FIRST];
193 while ( cb >= 0 && cb < numberOfEdges() ) {
194 Shape::dg_arete const &e = getEdge(cb);
195 if ( nPt == std::min(e.st, e.en) ) {
196 if ( cb != dnNo && cb != upNo ) {
197 SweepTree *node = sTree->add(this, cb, 1, nPt, this);
198 swrData[cb].misc = node;
199 node->InsertAt(*sTree, *sEvts, this, insertionNode, nPt, true);
200 CreateEdge(cb, to, step);
201 }
202 }
203 cb = NextAt(nPt,cb);
204 }
205 }
206 }
207
208 curP = curPt;
209 if ( curPt > 0 ) {
210 pos = getPoint(curPt - 1).x[1];
211 } else {
212 pos = to;
213 }
214
215 // the final touch: edges intersecting the sweepline must be update so that their intersection with
216 // said sweepline is correct.
217 pos = to;
218 if ( sTree->racine ) {
219 SweepTree* curS = static_cast<SweepTree*>(sTree->racine->Leftmost());
220 while ( curS ) {
221 int cb = curS->bord;
222 AvanceEdge(cb, to, true, step);
223 curS = static_cast<SweepTree*>(curS->elem[RIGHT]);
224 }
225 }
226}
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272// scan and compute coverage, FloatLigne version coverage of the line is bult in 2 parts: first a
273// set of rectangles of height the height of the line (here: "step") one rectangle for each portion
274// of the sweepline that is in the polygon at the beginning of the scan. then a set ot trapezoids
275// are added or removed to these rectangles, one trapezoid for each edge destroyed or edge crossing
276// the entire line. think of it as a refinement of the coverage by rectangles
277
278void Shape::Scan(float &pos, int &curP, float to, FloatLigne *line, bool exact, float step)
279{
280 if ( numberOfEdges() <= 1 ) {
281 return;
282 }
283
284 if ( pos >= to ) {
285 return;
286 }
287
288 // first step: the rectangles since we read the sweepline left to right, we know the
289 // boundaries of the rectangles are appended in a list, hence the AppendBord(). we salvage
290 // the guess value for the trapezoids the edges will induce
291
292 if ( sTree->racine ) {
293 SweepTree *curS = static_cast<SweepTree*>(sTree->racine->Leftmost());
294 while ( curS ) {
295
296 int lastGuess = -1;
297 int cb = curS->bord;
298
299 if ( swrData[cb].sens == false && curS->elem[LEFT] ) {
300
301 int lb = (static_cast<SweepTree*>(curS->elem[LEFT]))->bord;
302
303 lastGuess = line->AppendBord(swrData[lb].curX,
304 to - swrData[lb].curY,
305 swrData[cb].curX,
306 to - swrData[cb].curY,0.0);
307
308 swrData[lb].guess = lastGuess - 1;
309 swrData[cb].guess = lastGuess;
310 } else {
311 int lb = curS->bord;
312 swrData[lb].guess = -1;
313 }
314
315 curS=static_cast <SweepTree*> (curS->elem[RIGHT]);
316 }
317 }
318
319 int curPt = curP;
320 while ( curPt < numberOfPoints() && getPoint(curPt).x[1] <= to ) {
321
322 int nPt = curPt++;
323
324 // same thing as the usual Scan(), just with a hardcoded "indegree+outdegree=2" case, since
325 // it's the most common one
326
327 int nbUp;
328 int nbDn;
329 int upNo;
330 int dnNo;
331 if ( getPoint(nPt).totalDegree() == 2 ) {
332 _countUpDownTotalDegree2(nPt, &nbUp, &nbDn, &upNo, &dnNo);
333 } else {
334 _countUpDown(nPt, &nbUp, &nbDn, &upNo, &dnNo);
335 }
336
337 if ( nbDn <= 0 ) {
338 upNo = -1;
339 }
340 if ( upNo >= 0 && swrData[upNo].misc == nullptr ) {
341 upNo = -1;
342 }
343
344 if ( nbUp > 1 || ( nbUp == 1 && upNo < 0 ) ) {
345 int cb = getPoint(nPt).incidentEdge[FIRST];
346 while ( cb >= 0 && cb < numberOfEdges() ) {
347 Shape::dg_arete const &e = getEdge(cb);
348 if ( nPt == std::max(e.st, e.en) ) {
349 if ( cb != upNo ) {
350 SweepTree* node = swrData[cb].misc;
351 if ( node ) {
352 _updateIntersection(cb, nPt);
353 // create trapezoid for the chunk of edge intersecting with the line
354 DestroyEdge(cb, to, line);
355 node->Remove(*sTree, *sEvts, true);
356 }
357 }
358 }
359
360 cb = NextAt(nPt,cb);
361 }
362 }
363
364 // traitement du "upNo devient dnNo"
365 SweepTree *insertionNode = nullptr;
366 if ( dnNo >= 0 ) {
367 if ( upNo >= 0 ) {
368 SweepTree* node = swrData[upNo].misc;
369 _updateIntersection(upNo, nPt);
370 DestroyEdge(upNo, to, line);
371
372 node->ConvertTo(this, dnNo, 1, nPt);
373
374 swrData[dnNo].misc = node;
375 insertionNode = node;
376 CreateEdge(dnNo, to, step);
377 swrData[dnNo].guess = swrData[upNo].guess;
378 } else {
379 SweepTree *node = sTree->add(this, dnNo, 1, nPt, this);
380 swrData[dnNo].misc = node;
381 node->Insert(*sTree, *sEvts, this, nPt, true);
382 insertionNode = node;
383 CreateEdge(dnNo, to, step);
384 }
385 }
386
387 if ( nbDn > 1 ) { // si nbDn == 1 , alors dnNo a deja ete traite
388 int cb = getPoint(nPt).incidentEdge[FIRST];
389 while ( cb >= 0 && cb < numberOfEdges() ) {
390 Shape::dg_arete const &e = getEdge(cb);
391 if ( nPt == std::min(e.st, e.en) ) {
392 if ( cb != dnNo ) {
393 SweepTree *node = sTree->add(this, cb, 1, nPt, this);
394 swrData[cb].misc = node;
395 node->InsertAt(*sTree, *sEvts, this, insertionNode, nPt, true);
396 CreateEdge(cb, to, step);
397 }
398 }
399 cb = NextAt(nPt,cb);
400 }
401 }
402 }
403
404 curP = curPt;
405 if ( curPt > 0 ) {
406 pos = getPoint(curPt - 1).x[1];
407 } else {
408 pos = to;
409 }
410
411 // update intersections with the sweepline, and add trapezoids for edges crossing the line
412 pos = to;
413 if ( sTree->racine ) {
414 SweepTree* curS = static_cast<SweepTree*>(sTree->racine->Leftmost());
415 while ( curS ) {
416 int cb = curS->bord;
417 AvanceEdge(cb, to, line, exact, step);
418 curS = static_cast<SweepTree*>(curS->elem[RIGHT]);
419 }
420 }
421}
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442/*
443 * operations de bases pour la rasterization
444 *
445 */
446void Shape::CreateEdge(int no, float to, float step)
447{
448 int cPt;
449 Geom::Point dir;
450 if ( getEdge(no).st < getEdge(no).en ) {
451 cPt = getEdge(no).st;
452 swrData[no].sens = true;
453 dir = getEdge(no).dx;
454 } else {
455 cPt = getEdge(no).en;
456 swrData[no].sens = false;
457 dir = -getEdge(no).dx;
458 }
459
460 swrData[no].lastX = swrData[no].curX = getPoint(cPt).x[0];
461 swrData[no].lastY = swrData[no].curY = getPoint(cPt).x[1];
462
463 if ( fabs(dir[1]) < 0.000001 ) {
464 swrData[no].dxdy = 0;
465 } else {
466 swrData[no].dxdy = dir[0]/dir[1];
467 }
468
469 if ( fabs(dir[0]) < 0.000001 ) {
470 swrData[no].dydx = 0;
471 } else {
472 swrData[no].dydx = dir[1]/dir[0];
473 }
474
475 swrData[no].calcX = swrData[no].curX + (to - step - swrData[no].curY) * swrData[no].dxdy;
476 swrData[no].guess = -1;
477}
478
479
480void Shape::AvanceEdge(int no, float to, bool exact, float step)
481{
482 if ( exact ) {
483 Geom::Point dir;
484 Geom::Point stp;
485 if ( swrData[no].sens ) {
486 stp = getPoint(getEdge(no).st).x;
487 dir = getEdge(no).dx;
488 } else {
489 stp = getPoint(getEdge(no).en).x;
490 dir = -getEdge(no).dx;
491 }
492
493 if ( fabs(dir[1]) < 0.000001 ) {
494 swrData[no].calcX = stp[0] + dir[0];
495 } else {
496 swrData[no].calcX = stp[0] + ((to - stp[1]) * dir[0]) / dir[1];
497 }
498 } else {
499 swrData[no].calcX += step * swrData[no].dxdy;
500 }
501
502 swrData[no].lastX = swrData[no].curX;
503 swrData[no].lastY = swrData[no].curY;
504 swrData[no].curX = swrData[no].calcX;
505 swrData[no].curY = to;
506}
507
508/*
509 * specialisation par type de structure utilise
510 */
511
512void Shape::DestroyEdge(int no, float to, FloatLigne* line)
513{
514 if ( swrData[no].sens ) {
515
516 if ( swrData[no].curX < swrData[no].lastX ) {
517
518 swrData[no].guess = line->AddBordR(swrData[no].curX,
519 to - swrData[no].curY,
520 swrData[no].lastX,
521 to - swrData[no].lastY,
522 -swrData[no].dydx,
523 swrData[no].guess);
524
525 } else if ( swrData[no].curX > swrData[no].lastX ) {
526
527 swrData[no].guess = line->AddBord(swrData[no].lastX,
528 -(to - swrData[no].lastY),
529 swrData[no].curX,
530 -(to - swrData[no].curY),
531 swrData[no].dydx,
532 swrData[no].guess);
533 }
534
535 } else {
536
537 if ( swrData[no].curX < swrData[no].lastX ) {
538
539 swrData[no].guess = line->AddBordR(swrData[no].curX,
540 -(to - swrData[no].curY),
541 swrData[no].lastX,
542 -(to - swrData[no].lastY),
543 swrData[no].dydx,
544 swrData[no].guess);
545
546 } else if ( swrData[no].curX > swrData[no].lastX ) {
547
548 swrData[no].guess = line->AddBord(swrData[no].lastX,
549 to - swrData[no].lastY,
550 swrData[no].curX,
551 to - swrData[no].curY,
552 -swrData[no].dydx,
553 swrData[no].guess);
554 }
555 }
556}
557
558
559
560void Shape::AvanceEdge(int no, float to, FloatLigne *line, bool exact, float step)
561{
562 AvanceEdge(no,to,exact,step);
563
564 if ( swrData[no].sens ) {
565
566 if ( swrData[no].curX < swrData[no].lastX ) {
567
568 swrData[no].guess = line->AddBordR(swrData[no].curX,
569 to - swrData[no].curY,
570 swrData[no].lastX,
571 to - swrData[no].lastY,
572 -swrData[no].dydx,
573 swrData[no].guess);
574
575 } else if ( swrData[no].curX > swrData[no].lastX ) {
576
577 swrData[no].guess = line->AddBord(swrData[no].lastX,
578 -(to - swrData[no].lastY),
579 swrData[no].curX,
580 -(to - swrData[no].curY),
581 swrData[no].dydx,
582 swrData[no].guess);
583 }
584
585 } else {
586
587 if ( swrData[no].curX < swrData[no].lastX ) {
588
589 swrData[no].guess = line->AddBordR(swrData[no].curX,
590 -(to - swrData[no].curY),
591 swrData[no].lastX,
592 -(to - swrData[no].lastY),
593 swrData[no].dydx,
594 swrData[no].guess);
595
596 } else if ( swrData[no].curX > swrData[no].lastX ) {
597
598 swrData[no].guess = line->AddBord(swrData[no].lastX,
599 to - swrData[no].lastY,
600 swrData[no].curX,
601 to - swrData[no].curY,
602 -swrData[no].dydx,
603 swrData[no].guess);
604 }
605 }
606}
607
608
609
610
619void Shape::_countUpDown(int P, int *numberUp, int *numberDown, int *upEdge, int *downEdge) const
620{
621 *numberUp = 0;
622 *numberDown = 0;
623 *upEdge = -1;
624 *downEdge = -1;
625
626 int i = getPoint(P).incidentEdge[FIRST];
627
628 while ( i >= 0 && i < numberOfEdges() ) {
629 Shape::dg_arete const &e = getEdge(i);
630 if ( P == std::max(e.st, e.en) ) {
631 *upEdge = i;
632 (*numberUp)++;
633 }
634 if ( P == std::min(e.st, e.en) ) {
635 *downEdge = i;
636 (*numberDown)++;
637 }
638 i = NextAt(P, i);
639 }
640
641}
642
643
644
650 int *numberUp, int *numberDown, int *upEdge, int *downEdge) const
651{
652 *numberUp = 0;
653 *numberDown = 0;
654 *upEdge = -1;
655 *downEdge = -1;
656
657 for (int j : getPoint(P).incidentEdge) {
658 Shape::dg_arete const &e = getEdge(j);
659 if ( P == std::max(e.st, e.en) ) {
660 *upEdge = j;
661 (*numberUp)++;
662 }
663 if ( P == std::min(e.st, e.en) ) {
664 *downEdge = j;
665 (*numberDown)++;
666 }
667 }
668}
669
670
672{
673 swrData[e].lastX = swrData[e].curX;
674 swrData[e].lastY = swrData[e].curY;
675 swrData[e].curX = getPoint(p).x[0];
676 swrData[e].curY = getPoint(p).x[1];
677 swrData[e].misc = nullptr;
678}
679
680
681/*
682 Local Variables:
683 mode:c++
684 c-file-style:"stroustrup"
685 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
686 indent-tabs-mode:nil
687 fill-column:99
688 End:
689*/
690// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
@ LEFT
Definition LivarotDefs.h:87
@ RIGHT
Definition LivarotDefs.h:88
@ FIRST
Definition LivarotDefs.h:92
TODO: insert short description here.
AVLTree * elem[2]
Definition AVL.h:35
AVLTree * Leftmost()
Definition AVL.cpp:52
Coverage with floating-point boundaries.
Definition float-line.h:69
int AddBordR(float spos, float sval, float epos, float eval, float pente, int guess=-1)
Add a coverage portion.
int AppendBord(float spos, float sval, float epos, float eval, float pente)
Add a coverage portion by appending boundaries at the end of the list.
int AddBord(float spos, float sval, float epos, float eval, int guess=-1)
Add a coverage portion.
Two-dimensional point that doubles as a vector.
Definition point.h:66
void EndRaster()
int Other(int p, int b) const
Definition Shape.h:181
void CreateEdge(int no, float to, float step)
void _updateIntersection(int e, int p)
void Scan(float &pos, int &curP, float to, float step)
void _countUpDown(int P, int *numberUp, int *numberDown, int *upEdge, int *downEdge) const
std::vector< raster_data > swrData
Definition Shape.h:1084
void SortPoints()
Sort the points (all points) only if needed.
Definition Shape.cpp:467
void MakePointData(bool nVal)
Initialize the point data cache.
Definition Shape.cpp:71
int numberOfEdges() const
Returns number of edges.
Definition Shape.h:498
void BeginRaster(float &pos, int &curPt)
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
void AvanceEdge(int no, float to, bool exact, float step)
dg_arete const & getEdge(int n) const
Get an edge.
Definition Shape.h:548
void MakeRasterData(bool nVal)
Definition Shape.cpp:108
SweepTreeList * sTree
Definition Shape.h:430
void _countUpDownTotalDegree2(int P, int *numberUp, int *numberDown, int *upEdge, int *downEdge) const
Version of Shape::_countUpDown optimised for the case when getPoint(P).totalDegree() == 2.
friend class SweepEventQueue
Definition Shape.h:554
void MakeEdgeData(bool nVal)
Initialize the edge data cache.
Definition Shape.cpp:87
void DestroyEdge(int no, float to, FloatLigne *line)
SweepEventQueue * sEvts
Definition Shape.h:431
dg_point const & getPoint(int n) const
Get a point.
Definition Shape.h:537
std::vector< point_data > pData
Definition Shape.h:1085
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.
SweepTree * racine
One node in the AVL tree of edges.
Definition sweep-tree.h:42
TODO: insert short description here.
Inkscape::XML::Node * node
An edge in the directed graph.
Definition Shape.h:462
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 container of intersection events.
TODO: insert short description here.
TODO: insert short description here.