Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
ShapeMisc.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 "livarot/Shape.h"
14#include "livarot/Path.h"
16#include <glib.h>
17#include <cstdio>
18#include <cstdlib>
19#include <cstring>
20#include <2geom/point.h>
21#include <2geom/affine.h>
22
23/*
24 * polygon offset and polyline to path reassembling (when using back data)
25 */
26
27// until i find something better
28#define MiscNormalize(v) {\
29 double _l=sqrt(dot(v,v)); \
30 if ( _l < 0.0000001 ) { \
31 v[0]=v[1]=0; \
32 } else { \
33 v/=_l; \
34 }\
35}
36
37// extracting the contour of an uncrossed polygon: a mere depth first search
38// more precisely that's extracting an eulerian path from a graph, but here we want to split
39// the polygon into contours and avoid holes. so we take a "next counter-clockwise edge first" approach
40// (make a checkboard and extract its contours to see the difference)
41void
43{
44 // this function is quite similar to Shape::GetWindings so please check it out
45 // first to learn the overall technique and I'll make sure to comment the parts
46 // that are different
47
48 if (numberOfPoints() <= 1 || numberOfEdges() <= 1)
49 return;
50
51 // prepare
52 dest->Reset ();
53
54 MakePointData (true);
55 MakeEdgeData (true);
56 MakeSweepDestData (true);
57
58 for (int i = 0; i < numberOfPoints(); i++)
59 {
60 pData[i].rx[0] = Round (getPoint(i).x[0]);
61 pData[i].rx[1] = Round (getPoint(i).x[1]);
62 }
63 for (int i = 0; i < numberOfEdges(); i++)
64 {
65 eData[i].rdx = pData[getEdge(i).en].rx - pData[getEdge(i).st].rx;
66 }
67
68 // sort edge clockwise, with the closest after midnight being first in the doubly-linked list
69 // that's vital to the algorithm...
70 SortEdges ();
71
72 // depth-first search implies: we make a stack of edges traversed.
73 // precParc: previous in the stack
74 // suivParc: next in the stack
75 for (int i = 0; i < numberOfEdges(); i++)
76 {
77 swdData[i].misc = 0;
78 swdData[i].precParc = swdData[i].suivParc = -1;
79 }
80
81 int searchInd = 0;
82
83 int lastPtUsed = 0;
84 do
85 {
86 // first get a starting point, and a starting edge
87 // -> take the upper left point, and take its first edge
88 // points traversed have swdData[].misc != 0, so it's easy
89 int startBord = -1;
90 {
91 int fi = 0;
92 for (fi = lastPtUsed; fi < numberOfPoints(); fi++)
93 {
94 if (getPoint(fi).incidentEdge[FIRST] >= 0 && swdData[getPoint(fi).incidentEdge[FIRST]].misc == 0)
95 break;
96 }
97 lastPtUsed = fi + 1;
98 if (fi < numberOfPoints())
99 {
100 int bestB = getPoint(fi).incidentEdge[FIRST];
101 // we get the edge that starts at this point since we wanna follow the direction of the edges
102 while (bestB >= 0 && getEdge(bestB).st != fi)
103 bestB = NextAt (fi, bestB);
104 if (bestB >= 0)
105 {
106 startBord = bestB;
107 dest->MoveTo (getPoint(getEdge(startBord).en).x);
108 }
109 }
110 }
111 // and walk the graph, doing contours when needed
112 if (startBord >= 0)
113 {
114 // parcours en profondeur pour mettre les leF et riF a leurs valeurs
115 swdData[startBord].misc = 1;
116 // printf("part de %d\n",startBord);
117 int curBord = startBord;
118 bool back = false; // a variable that if true, means we are back tracking
119 swdData[curBord].precParc = -1;
120 swdData[curBord].suivParc = -1;
121 do
122 {
123 int cPt = getEdge(curBord).en; // get the end point, we want to follow the direction of the edge
124 int nb = curBord;
125 // printf("de curBord= %d au point %i -> ",curBord,cPt);
126 // get next edge
127 do
128 {
129 int nnb = CycleNextAt (cPt, nb); // get the next (clockwise) edge (I don't see why the comment at the top says anti clockwise edge first)
130 if (nnb == nb)
131 {
132 // cul-de-sac
133 nb = -1;
134 break;
135 }
136 nb = nnb;
137 if (nb < 0 || nb == curBord) // if we got to the same edge, we break, cuz now we need to back track
138 break;
139 }
140 while (swdData[nb].misc != 0 || getEdge(nb).st != cPt); // keep finding a new edge until we find an edge that we haven't seen or an edge
141 // that starts at the point
142
143 if (nb < 0 || nb == curBord)
144 {
145 // if we are here, means there was no new edge, so we start back tracking
146 // no next edge: end of this contour, we get back
147 if (back == false)
148 dest->Close ();
149 back = true;
150 // retour en arriere
151 curBord = swdData[curBord].precParc; // set previous edge to current edge and back is true to indicate we are backtracking
152 // printf("retour vers %d\n",curBord);
153 if (curBord < 0) // break if no edge exists before this one
154 break;
155 }
156 else
157 {
158 // did we get this new edge after we started backtracking? if yes, we need a moveTo
159 // new edge, maybe for a new contour
160 if (back)
161 {
162 // we were backtracking, so if we have a new edge, that means we're creating a new contour
163 dest->MoveTo (getPoint(cPt).x);
164 back = false; // we are no longer backtracking, we will follow this new edge now
165 }
166 swdData[nb].misc = 1;
167 swdData[nb].ind = searchInd++;
168 swdData[nb].precParc = curBord;
169 swdData[curBord].suivParc = nb;
170 curBord = nb;
171 // printf("suite %d\n",curBord);
172 {
173 // add that edge
174 dest->LineTo (getPoint(getEdge(nb).en).x); // add a line segment
175 }
176 }
177 }
178 while (true /*swdData[curBord].precParc >= 0 */ );
179 // fin du cas non-oriente
180 }
181 }
182 while (lastPtUsed < numberOfPoints());
183
184 MakePointData (false);
185 MakeEdgeData (false);
186 MakeSweepDestData (false);
187}
188
189// same as before, but each time we have a contour, try to reassemble the segments on it to make chunks of
190// the original(s) path(s)
191// originals are in the orig array, whose size is nbP
192void Shape::ConvertToForme(Path *dest, int nbP, Path *const *orig, bool never_split)
193{
194 // the function is similar to the other version of ConvertToForme, I'm adding comments
195 // where there are significant differences to explain
196 if (numberOfPoints() <= 1 || numberOfEdges() <= 1)
197 return;
198// if (Eulerian (true) == false)
199// return;
200
201 if (_has_back_data == false)
202 {
203 ConvertToForme (dest);
204 return;
205 }
206
207 dest->Reset ();
208
209 MakePointData (true);
210 MakeEdgeData (true);
211 MakeSweepDestData (true);
212
213 for (int i = 0; i < numberOfPoints(); i++)
214 {
215 pData[i].rx[0] = Round (getPoint(i).x[0]);
216 pData[i].rx[1] = Round (getPoint(i).x[1]);
217 }
218 for (int i = 0; i < numberOfEdges(); i++)
219 {
220 eData[i].rdx = pData[getEdge(i).en].rx - pData[getEdge(i).st].rx;
221 }
222
223 SortEdges ();
224
225 for (int i = 0; i < numberOfEdges(); i++)
226 {
227 swdData[i].misc = 0;
228 swdData[i].precParc = swdData[i].suivParc = -1;
229 }
230
231 int searchInd = 0;
232
233 int lastPtUsed = 0;
234 do
235 {
236 int startBord = -1;
237 {
238 int fi = 0;
239 for (fi = lastPtUsed; fi < numberOfPoints(); fi++)
240 {
241 if (getPoint(fi).incidentEdge[FIRST] >= 0 && swdData[getPoint(fi).incidentEdge[FIRST]].misc == 0)
242 break;
243 }
244 lastPtUsed = fi + 1;
245 if (fi < numberOfPoints())
246 {
247 int bestB = getPoint(fi).incidentEdge[FIRST];
248 while (bestB >= 0 && getEdge(bestB).st != fi)
249 bestB = NextAt (fi, bestB);
250 if (bestB >= 0)
251 {
252 startBord = bestB; // no moveTo here unlike the other ConvertToForme because we want AddContour to do all the contour extraction stuff
253 }
254 }
255 }
256 if (startBord >= 0)
257 {
258 // parcours en profondeur pour mettre les leF et riF a leurs valeurs
259 swdData[startBord].misc = 1;
260 //printf("part de %d\n",startBord);
261 int curBord = startBord;
262 bool back = false;
263 swdData[curBord].precParc = -1;
264 swdData[curBord].suivParc = -1;
265 int curStartPt=getEdge(curBord).st; // we record the start point of the current edge (actually the start edge at the moment)
266 do
267 {
268 int cPt = getEdge(curBord).en;
269 int nb = curBord;
270 //printf("de curBord= %d au point %i -> ",curBord,cPt);
271 do
272 {
273 int nnb = CycleNextAt (cPt, nb);
274 if (nnb == nb)
275 {
276 // cul-de-sac
277 nb = -1;
278 break;
279 }
280 nb = nnb;
281 if (nb < 0 || nb == curBord) // if we get the same edge, we break
282 break;
283 }
284 while (swdData[nb].misc != 0 || getEdge(nb).st != cPt);
285
286 if (nb < 0 || nb == curBord)
287 { // we are backtracking
288 if (back == false) // if we weren't backtracking
289 {
290 if (curBord == startBord || curBord < 0) // is the current edge the one we started on?
291 {
292 // probleme -> on vire le moveto
293 // dest->descr_nb--;
294 }
295 else // if not, we now have a contour to add
296 {
297 swdData[curBord].suivParc = -1;
298 AddContour(dest, nbP, orig, startBord, never_split);
299 }
300 // dest->Close();
301 }
302 back = true; // we are backtracking now
303 // retour en arriere
304 curBord = swdData[curBord].precParc; // get the previous edge
305 //printf("retour vers %d\n",curBord);
306 if (curBord < 0) // if no previous edge, we break
307 break;
308 }
309 else
310 {
311 if (back)
312 { // if we are backtracking, now we go forward (since we have found a new edge to explore)
313 back = false;
314 startBord = nb;
315 curStartPt=getEdge(nb).st; // reset curStartPt to this as a new contour is starting here
316 } else {
317 if ( getEdge(curBord).en == curStartPt ) { // if we are going forward and the endpoint of this edge is the actual start point
318 //printf("contour %i ",curStartPt);
319 swdData[curBord].suivParc = -1; // why tho? seems useless since we set it right after this block ends
320 AddContour(dest, nbP, orig, startBord, never_split); // add the contour
321 startBord=nb; // set startBord to this edge
322 }
323 }
324 swdData[nb].misc = 1;
325 swdData[nb].ind = searchInd++;
326 swdData[nb].precParc = curBord;
327 swdData[curBord].suivParc = nb;
328 curBord = nb;
329 //printf("suite %d\n",curBord);
330 }
331 }
332 while (true /*swdData[curBord].precParc >= 0 */ );
333 // fin du cas non-oriente
334 }
335 }
336 while (lastPtUsed < numberOfPoints());
337
338 MakePointData (false);
339 MakeEdgeData (false);
340 MakeSweepDestData (false);
341}
342
343void Shape::ConvertToFormeNested(Path *dest, int nbP, Path *const *orig, int &nbNest, int *&nesting, int *&contStart, bool never_split)
344{
345 nesting=nullptr;
346 contStart=nullptr;
347 nbNest=0;
348
349 if (numberOfPoints() <= 1 || numberOfEdges() <= 1)
350 return;
351 // if (Eulerian (true) == false)
352 // return;
353
354 if (_has_back_data == false)
355 {
356 ConvertToForme (dest);
357 return;
358 }
359
360 dest->Reset ();
361
362// MakePointData (true);
363 MakeEdgeData (true);
364 MakeSweepDestData (true);
365
366 for (int i = 0; i < numberOfPoints(); i++)
367 {
368 pData[i].rx[0] = Round (getPoint(i).x[0]);
369 pData[i].rx[1] = Round (getPoint(i).x[1]);
370 }
371 for (int i = 0; i < numberOfEdges(); i++)
372 {
373 eData[i].rdx = pData[getEdge(i).en].rx - pData[getEdge(i).st].rx;
374 }
375
376 SortEdges ();
377
378 for (int i = 0; i < numberOfEdges(); i++)
379 {
380 swdData[i].misc = 0;
381 swdData[i].precParc = swdData[i].suivParc = -1;
382 }
383
384 int searchInd = 0;
385
386 int lastPtUsed = 0;
387 int parentContour=-1;
388 do
389 {
390 int childEdge = -1;
391 bool foundChild = false;
392 int startBord = -1;
393 {
394 int fi = 0;
395 for (fi = lastPtUsed; fi < numberOfPoints(); fi++)
396 {
397 if (getPoint(fi).incidentEdge[FIRST] >= 0 && swdData[getPoint(fi).incidentEdge[FIRST]].misc == 0)
398 break;
399 }
400 {
401 if (pData.size()<= fi || fi == numberOfPoints()) {
402 parentContour=-1;
403 } else {
404 int askTo = pData[fi].askForWindingB;
405 if (askTo < 0 || askTo >= numberOfEdges() ) {
406 parentContour=-1;
407 } else {
408 if (getEdge(askTo).prevS >= 0) {
409 parentContour = swdData[askTo].misc;
410 parentContour-=1; // pour compenser le decalage
411 }
412 childEdge = getPoint(fi).incidentEdge[FIRST];
413 }
414 }
415 }
416 lastPtUsed = fi + 1;
417 if (fi < numberOfPoints())
418 {
419 int bestB = getPoint(fi).incidentEdge[FIRST];
420 while (bestB >= 0 && getEdge(bestB).st != fi)
421 bestB = NextAt (fi, bestB);
422 if (bestB >= 0)
423 {
424 startBord = bestB;
425 }
426 }
427 }
428 if (startBord >= 0)
429 {
430 // parcours en profondeur pour mettre les leF et riF a leurs valeurs
431 swdData[startBord].misc = 1 + nbNest;
432 if (startBord == childEdge) {
433 foundChild = true;
434 }
435 //printf("part de %d\n",startBord);
436 int curBord = startBord;
437 bool back = false;
438 swdData[curBord].precParc = -1;
439 swdData[curBord].suivParc = -1;
440 int curStartPt=getEdge(curBord).st;
441 do
442 {
443 int cPt = getEdge(curBord).en;
444 int nb = curBord;
445 //printf("de curBord= %d au point %i -> ",curBord,cPt);
446 do
447 {
448 int nnb = CycleNextAt (cPt, nb);
449 if (nnb == nb)
450 {
451 // cul-de-sac
452 nb = -1;
453 break;
454 }
455 nb = nnb;
456 if (nb < 0 || nb == curBord)
457 break;
458 }
459 while (swdData[nb].misc != 0 || getEdge(nb).st != cPt);
460
461 if (nb < 0 || nb == curBord)
462 {
463 if (back == false)
464 {
465 if (curBord == startBord || curBord < 0)
466 {
467 // probleme -> on vire le moveto
468 // dest->descr_nb--;
469 }
470 else
471 {
472// bool escapePath=false;
473// int tb=curBord;
474// while ( tb >= 0 && tb < numberOfEdges() ) {
475// if ( ebData[tb].pathID == wildPath ) {
476// escapePath=true;
477// break;
478// }
479// tb=swdData[tb].precParc;
480// }
481 nesting=(int*)g_realloc(nesting,(nbNest+1)*sizeof(int));
482 contStart=(int*)g_realloc(contStart,(nbNest+1)*sizeof(int));
483 contStart[nbNest]=dest->descr_cmd.size();
484 if (foundChild) {
485 nesting[nbNest++]=parentContour;
486 foundChild = false;
487 } else {
488 nesting[nbNest++]=-1; // contient des bouts de coupure -> a part
489 }
490 swdData[curBord].suivParc = -1;
491 AddContour(dest, nbP, orig, startBord, never_split);
492 }
493 // dest->Close();
494 }
495 back = true;
496 // retour en arriere
497 curBord = swdData[curBord].precParc;
498 //printf("retour vers %d\n",curBord);
499 if (curBord < 0)
500 break;
501 }
502 else
503 {
504 if (back)
505 {
506 back = false;
507 startBord = nb;
508 curStartPt=getEdge(nb).st;
509 } else {
510 if ( getEdge(curBord).en == curStartPt ) {
511 //printf("contour %i ",curStartPt);
512
513// bool escapePath=false;
514// int tb=curBord;
515// while ( tb >= 0 && tb < numberOfEdges() ) {
516// if ( ebData[tb].pathID == wildPath ) {
517// escapePath=true;
518// break;
519// }
520// tb=swdData[tb].precParc;
521// }
522 nesting=(int*)g_realloc(nesting,(nbNest+1)*sizeof(int));
523 contStart=(int*)g_realloc(contStart,(nbNest+1)*sizeof(int));
524 contStart[nbNest]=dest->descr_cmd.size();
525 if (foundChild) {
526 nesting[nbNest++]=parentContour;
527 foundChild = false;
528 } else {
529 nesting[nbNest++]=-1; // contient des bouts de coupure -> a part
530 }
531 swdData[curBord].suivParc = -1;
532 AddContour(dest, nbP, orig, startBord, never_split);
533 startBord=nb;
534 }
535 }
536 swdData[nb].misc = 1 + nbNest;
537 swdData[nb].ind = searchInd++;
538 swdData[nb].precParc = curBord;
539 swdData[curBord].suivParc = nb;
540 curBord = nb;
541 if (nb == childEdge) {
542 foundChild = true;
543 }
544 //printf("suite %d\n",curBord);
545 }
546 }
547 while (true /*swdData[curBord].precParc >= 0 */ );
548 // fin du cas non-oriente
549 }
550 }
551 while (lastPtUsed < numberOfPoints());
552
553 MakePointData (false);
554 MakeEdgeData (false);
555 MakeSweepDestData (false);
556}
557
558
559int
560Shape::MakeTweak (int mode, Shape *a, double power, JoinType join, double miter, bool do_profile, Geom::Point c, Geom::Point vector, double radius, Geom::Affine *i2doc)
561{
562 Reset (0, 0);
564
565 bool done_something = false;
566
567 if (power == 0)
568 {
569 _pts = a->_pts;
570 if (numberOfPoints() > maxPt)
571 {
573 if (_has_points_data) {
574 pData.resize(maxPt);
576 _bbox_up_to_date = false;
577 }
578 }
579
580 _aretes = a->_aretes;
581 if (numberOfEdges() > maxAr)
582 {
584 if (_has_edges_data)
585 eData.resize(maxAr);
587 swsData.resize(maxAr);
589 swdData.resize(maxAr);
591 swrData.resize(maxAr);
592 if (_has_back_data)
593 ebData.resize(maxAr);
594 }
595 return 0;
596 }
597 if (a->numberOfPoints() <= 1 || a->numberOfEdges() <= 1 || a->type != shape_polygon)
598 return shape_input_err;
599
600 a->SortEdges ();
601
602 a->MakeSweepDestData (true);
603 a->MakeSweepSrcData (true);
604
605 for (int i = 0; i < a->numberOfEdges(); i++)
606 {
607 int stB = -1, enB = -1;
608 if (power <= 0 || mode == tweak_mode_push || mode == tweak_mode_repel || mode == tweak_mode_roughen) {
609 stB = a->CyclePrevAt (a->getEdge(i).st, i);
610 enB = a->CycleNextAt (a->getEdge(i).en, i);
611 } else {
612 stB = a->CycleNextAt (a->getEdge(i).st, i);
613 enB = a->CyclePrevAt (a->getEdge(i).en, i);
614 }
615
616 Geom::Point stD = a->getEdge(stB).dx;
617 Geom::Point seD = a->getEdge(i).dx;
618 Geom::Point enD = a->getEdge(enB).dx;
619
620 double stL = sqrt (dot(stD,stD));
621 double seL = sqrt (dot(seD,seD));
622 //double enL = sqrt (dot(enD,enD));
623 MiscNormalize (stD);
624 MiscNormalize (enD);
625 MiscNormalize (seD);
626
627 Geom::Point ptP;
628 int stNo, enNo;
629 ptP = a->getPoint(a->getEdge(i).st).x;
630
631 Geom::Point to_center = ptP * (*i2doc) - c;
632 Geom::Point to_center_normalized = (1/Geom::L2(to_center)) * to_center;
633
634 double this_power;
635 if (do_profile && i2doc) {
636 double alpha = 1;
637 double x;
638 if (mode == tweak_mode_repel) {
639 x = (Geom::L2(to_center)/radius);
640 } else {
641 x = (Geom::L2(ptP * (*i2doc) - c)/radius);
642 }
643 if (x > 1) {
644 this_power = 0;
645 } else if (x <= 0) {
646 if (mode == tweak_mode_repel) {
647 this_power = 0;
648 } else {
649 this_power = power;
650 }
651 } else {
652 this_power = power * (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
653 }
654 } else {
655 if (mode == tweak_mode_repel) {
656 this_power = 0;
657 } else {
658 this_power = power;
659 }
660 }
661
662 if (this_power != 0)
663 done_something = true;
664
665 double scaler = 1 / (*i2doc).descrim();
666
667 Geom::Point this_vec(0,0);
668 if (mode == tweak_mode_push) {
669 Geom::Affine tovec (*i2doc);
670 tovec[4] = tovec[5] = 0;
671 tovec = tovec.inverse();
672 this_vec = this_power * (vector * tovec) ;
673 } else if (mode == tweak_mode_repel) {
674 this_vec = this_power * scaler * to_center_normalized;
675 } else if (mode == tweak_mode_roughen) {
676 double angle = g_random_double_range(0, 2*M_PI);
677 this_vec = g_random_double_range(0, 1) * this_power * scaler * Geom::Point(sin(angle), cos(angle));
678 }
679
680 int usePathID=-1;
681 int usePieceID=0;
682 double useT=0.0;
683 if ( a->_has_back_data ) {
684 if ( a->ebData[i].pathID >= 0 && a->ebData[stB].pathID == a->ebData[i].pathID && a->ebData[stB].pieceID == a->ebData[i].pieceID
685 && a->ebData[stB].tEn == a->ebData[i].tSt ) {
686 usePathID=a->ebData[i].pathID;
687 usePieceID=a->ebData[i].pieceID;
688 useT=a->ebData[i].tSt;
689 } else {
690 usePathID=a->ebData[i].pathID;
691 usePieceID=0;
692 useT=0;
693 }
694 }
695
697 Path::DoLeftJoin (this, 0, join, ptP+this_vec, stD+this_vec, seD+this_vec, miter, stL, seL,
698 stNo, enNo,usePathID,usePieceID,useT);
699 a->swsData[i].stPt = enNo;
700 a->swsData[stB].enPt = stNo;
701 } else {
702 if (power > 0) {
703 Path::DoRightJoin (this, this_power * scaler, join, ptP, stD, seD, miter, stL, seL,
704 stNo, enNo,usePathID,usePieceID,useT);
705 a->swsData[i].stPt = enNo;
706 a->swsData[stB].enPt = stNo;
707 } else {
708 Path::DoLeftJoin (this, -this_power * scaler, join, ptP, stD, seD, miter, stL, seL,
709 stNo, enNo,usePathID,usePieceID,useT);
710 a->swsData[i].stPt = enNo;
711 a->swsData[stB].enPt = stNo;
712 }
713 }
714 }
715
716 if (power < 0 || mode == tweak_mode_push || mode == tweak_mode_repel || mode == tweak_mode_roughen)
717 {
718 for (int i = 0; i < numberOfEdges(); i++)
719 Inverse (i);
720 }
721
722 if ( _has_back_data ) {
723 for (int i = 0; i < a->numberOfEdges(); i++)
724 {
725 int nEd=AddEdge (a->swsData[i].stPt, a->swsData[i].enPt);
726 ebData[nEd]=a->ebData[i];
727 }
728 } else {
729 for (int i = 0; i < a->numberOfEdges(); i++)
730 {
731 AddEdge (a->swsData[i].stPt, a->swsData[i].enPt);
732 }
733 }
734
735 a->MakeSweepSrcData (false);
736 a->MakeSweepDestData (false);
737
738 return (done_something? 0 : shape_nothing_to_do);
739}
740
741
742// offsets
743// take each edge, offset it, and make joins with previous at edge start and next at edge end (previous and
744// next being with respect to the clockwise order)
745// you gotta be very careful with the join, as anything but the right one will fuck everything up
746// see PathStroke.cpp for the "right" joins
747int
748Shape::MakeOffset (Shape * a, double dec, JoinType join, double miter, bool do_profile, double cx, double cy, double radius, Geom::Affine *i2doc)
749{
750 Reset (0, 0);
752
753 bool done_something = false;
754
755 if (dec == 0)
756 {
757 _pts = a->_pts;
758 if (numberOfPoints() > maxPt)
759 {
761 if (_has_points_data) {
762 pData.resize(maxPt);
764 _bbox_up_to_date = false;
765 }
766 }
767
768 _aretes = a->_aretes;
769 if (numberOfEdges() > maxAr)
770 {
772 if (_has_edges_data)
773 eData.resize(maxAr);
775 swsData.resize(maxAr);
777 swdData.resize(maxAr);
779 swrData.resize(maxAr);
780 if (_has_back_data)
781 ebData.resize(maxAr);
782 }
783 return 0;
784 }
785 if (a->numberOfPoints() <= 1 || a->numberOfEdges() <= 1 || a->type != shape_polygon)
786 return shape_input_err;
787
788 a->SortEdges ();
789
790 a->MakeSweepDestData (true);
791 a->MakeSweepSrcData (true);
792
793 for (int i = 0; i < a->numberOfEdges(); i++)
794 {
795 // int stP=a->swsData[i].stPt/*,enP=a->swsData[i].enPt*/;
796 int stB = -1, enB = -1;
797 if (dec > 0)
798 {
799 stB = a->CycleNextAt (a->getEdge(i).st, i);
800 enB = a->CyclePrevAt (a->getEdge(i).en, i);
801 }
802 else
803 {
804 stB = a->CyclePrevAt (a->getEdge(i).st, i);
805 enB = a->CycleNextAt (a->getEdge(i).en, i);
806 }
807
808 Geom::Point stD = a->getEdge(stB).dx;
809 Geom::Point seD = a->getEdge(i).dx;
810 Geom::Point enD = a->getEdge(enB).dx;
811
812 double stL = sqrt (dot(stD,stD));
813 double seL = sqrt (dot(seD,seD));
814 //double enL = sqrt (dot(enD,enD));
815 MiscNormalize (stD);
816 MiscNormalize (enD);
817 MiscNormalize (seD);
818
819 Geom::Point ptP;
820 int stNo, enNo;
821 ptP = a->getPoint(a->getEdge(i).st).x;
822
823 double this_dec;
824 if (do_profile && i2doc) {
825 double alpha = 1;
826 double x = (Geom::L2(ptP * (*i2doc) - Geom::Point(cx,cy))/radius);
827 if (x > 1) {
828 this_dec = 0;
829 } else if (x <= 0) {
830 this_dec = dec;
831 } else {
832 this_dec = dec * (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
833 }
834 } else {
835 this_dec = dec;
836 }
837
838 if (this_dec != 0)
839 done_something = true;
840
841 int usePathID=-1;
842 int usePieceID=0;
843 double useT=0.0;
844 if ( a->_has_back_data ) {
845 if ( a->ebData[i].pathID >= 0 && a->ebData[stB].pathID == a->ebData[i].pathID && a->ebData[stB].pieceID == a->ebData[i].pieceID
846 && a->ebData[stB].tEn == a->ebData[i].tSt ) {
847 usePathID=a->ebData[i].pathID;
848 usePieceID=a->ebData[i].pieceID;
849 useT=a->ebData[i].tSt;
850 } else {
851 usePathID=a->ebData[i].pathID;
852 usePieceID=0;
853 useT=0;
854 }
855 }
856 if (dec > 0)
857 {
858 Path::DoRightJoin (this, this_dec, join, ptP, stD, seD, miter, stL, seL,
859 stNo, enNo,usePathID,usePieceID,useT);
860 a->swsData[i].stPt = enNo;
861 a->swsData[stB].enPt = stNo;
862 }
863 else
864 {
865 Path::DoLeftJoin (this, -this_dec, join, ptP, stD, seD, miter, stL, seL,
866 stNo, enNo,usePathID,usePieceID,useT);
867 a->swsData[i].stPt = enNo;
868 a->swsData[stB].enPt = stNo;
869 }
870 }
871
872 if (dec < 0)
873 {
874 for (int i = 0; i < numberOfEdges(); i++)
875 Inverse (i);
876 }
877
878 if ( _has_back_data ) {
879 for (int i = 0; i < a->numberOfEdges(); i++)
880 {
881 int nEd=AddEdge (a->swsData[i].stPt, a->swsData[i].enPt);
882 ebData[nEd]=a->ebData[i];
883 }
884 } else {
885 for (int i = 0; i < a->numberOfEdges(); i++)
886 {
887 AddEdge (a->swsData[i].stPt, a->swsData[i].enPt);
888 }
889 }
890
891 a->MakeSweepSrcData (false);
892 a->MakeSweepDestData (false);
893
894 return (done_something? 0 : shape_nothing_to_do);
895}
896
897// we found a contour, now reassemble the edges on it, instead of dumping them in the Path "dest" as a
898// polyline. since it was a DFS, the precParc and suivParc make a nice doubly-linked list of the edges in
899// the contour. the first edge of the contour is start_edge.
900void Shape::AddContour(Path *dest, int num_orig, Path *const *orig, int start_edge, bool never_split)
901{
902 int edge = start_edge;
903
904 // Move to the starting point.
905 dest->MoveTo(getPoint(getEdge(edge).st).x);
906
907 while (true) {
908 // Get the piece and path id.
909 int nPiece = ebData[edge].pieceID;
910 int nPath = ebData[edge].pathID;
911
912 // If the path id is invalid in any way, just add the edge as a line segment.
913 if (nPath < 0 || nPath >= num_orig || !orig[nPath]) {
914 dest->LineTo(getPoint(getEdge(edge).en).x);
915 edge = swdData[edge].suivParc;
916 continue;
917 }
918
919 // Get pointer to the path where this edge came from.
920 auto from = orig[nPath];
921
922 // If the piece id is invalid in any way, again just add the edge as a line segment.
923 if (nPiece < 0 || nPiece >= from->descr_cmd.size()) {
924 dest->LineTo(getPoint(getEdge(edge).en).x);
925 edge = swdData[edge].suivParc;
926 continue;
927 }
928
929 // Handle the path command. This consumes multiple edges, and sets edge to the next edge to process.
930 switch (from->descr_cmd[nPiece]->getType()) {
931 case descr_lineto:
932 edge = ReFormeLineTo(edge, dest, never_split);
933 break;
934 case descr_arcto:
935 edge = ReFormeArcTo(edge, dest, from, never_split);
936 break;
937 case descr_cubicto:
938 edge = ReFormeCubicTo(edge, dest, from, never_split);
939 break;
940 default:
941 // Shouldn't happen, but if it does, just add a line segment.
942 dest->LineTo(getPoint(getEdge(edge).en).x);
943 edge = swdData[edge].suivParc;
944 break;
945 }
946
947 // No more edges - exit.
948 if (edge < 0) {
949 break;
950 }
951
952 // Insert forced points.
953 // Although forced points make no difference to the dumped SVG path, they do have some internal use.
954 // For example, some functions like ConvertForcedToMoveTo() pay attention to them.
955 // It's not clear exactly when or why these points are inserted, or when oldDegree and totalDegree() differ
956 if (!never_split) {
957 if (getPoint(getEdge(edge).st).totalDegree() > 2) {
958 dest->ForcePoint();
959 } else if (getPoint(getEdge(edge).st).oldDegree > 2 && getPoint(getEdge(edge).st).totalDegree() == 2) {
960 if (_has_back_data) {
961 int prevEdge = getPoint(getEdge(edge).st).incidentEdge[FIRST];
962 int nextEdge = getPoint(getEdge(edge).st).incidentEdge[LAST];
963 if (getEdge(prevEdge).en != getEdge(edge).st) {
964 std::swap(prevEdge, nextEdge);
965 }
966 if (ebData[prevEdge].pieceID == ebData[nextEdge].pieceID && ebData[prevEdge].pathID == ebData[nextEdge].pathID) {
967 if (std::abs(ebData[prevEdge].tEn - ebData[nextEdge].tSt) < 0.05) {
968 } else {
969 dest->ForcePoint();
970 }
971 } else {
972 dest->ForcePoint();
973 }
974 } else {
975 dest->ForcePoint();
976 }
977 }
978 }
979 }
980
981 dest->Close();
982}
983
984int Shape::ReFormeLineTo(int bord, Path *dest, bool never_split)
985{
986 int nPiece = ebData[bord].pieceID;
987 int nPath = ebData[bord].pathID;
988 double te = ebData[bord].tEn;
989 Geom::Point nx = getPoint(getEdge(bord).en).x;
990 bord = swdData[bord].suivParc;
991 while (bord >= 0)
992 {
993 if (!never_split && (getPoint(getEdge(bord).st).totalDegree() > 2 || getPoint(getEdge(bord).st).oldDegree > 2))
994 {
995 break;
996 }
997 if (ebData[bord].pieceID == nPiece && ebData[bord].pathID == nPath)
998 {
999 if (fabs (te - ebData[bord].tSt) > 0.0001)
1000 break;
1001 nx = getPoint(getEdge(bord).en).x;
1002 te = ebData[bord].tEn;
1003 }
1004 else
1005 {
1006 break;
1007 }
1008 bord = swdData[bord].suivParc;
1009 }
1010 {
1011 dest->LineTo (nx);
1012 }
1013 return bord;
1014}
1015
1016int Shape::ReFormeArcTo(int bord, Path *dest, Path *from, bool never_split)
1017{
1018 int nPiece = ebData[bord].pieceID;
1019 int nPath = ebData[bord].pathID;
1020 double ts = ebData[bord].tSt, te = ebData[bord].tEn;
1021 Geom::Point nx = getPoint(getEdge(bord).en).x;
1022 bord = swdData[bord].suivParc;
1023 while (bord >= 0)
1024 {
1025 if (!never_split && (getPoint(getEdge(bord).st).totalDegree() > 2 || getPoint(getEdge(bord).st).oldDegree > 2))
1026 {
1027 break;
1028 }
1029 if (ebData[bord].pieceID == nPiece && ebData[bord].pathID == nPath)
1030 {
1031 if (fabs (te - ebData[bord].tSt) > 0.0001)
1032 {
1033 break;
1034 }
1035 nx = getPoint(getEdge(bord).en).x;
1036 te = ebData[bord].tEn;
1037 }
1038 else
1039 {
1040 break;
1041 }
1042 bord = swdData[bord].suivParc;
1043 }
1044
1045 double sang, eang;
1046 PathDescrArcTo* nData = dynamic_cast<PathDescrArcTo *>(from->descr_cmd[nPiece]);
1047 bool nLarge = nData->large;
1048 bool nClockwise = nData->clockwise;
1049 Path::ArcAngles (from->PrevPoint (nPiece - 1), nData->p,nData->rx,nData->ry,nData->angle*M_PI/180.0, nLarge, nClockwise, sang, eang);
1050 if (nClockwise)
1051 {
1052 if (sang < eang)
1053 sang += 2 * M_PI;
1054 }
1055 else
1056 {
1057 if (sang > eang)
1058 sang -= 2 * M_PI;
1059 }
1060 double delta = eang - sang;
1061 double ndelta = delta * (te - ts);
1062 if (ts > te)
1063 nClockwise = !nClockwise;
1064 if (ndelta < 0)
1065 ndelta = -ndelta;
1066 if (ndelta > M_PI)
1067 nLarge = true;
1068 else
1069 nLarge = false;
1070
1071 dest->ArcTo (nx, nData->rx,nData->ry,nData->angle, nLarge, nClockwise);
1072
1073 return bord;
1074}
1075
1076int Shape::ReFormeCubicTo(int bord, Path *dest, Path *from, bool never_split)
1077{
1078 int nPiece = ebData[bord].pieceID;
1079 int nPath = ebData[bord].pathID;
1080 double ts = ebData[bord].tSt, te = ebData[bord].tEn;
1081 Geom::Point nx = getPoint(getEdge(bord).en).x;
1082 bord = swdData[bord].suivParc;
1083 while (bord >= 0)
1084 {
1085 if (!never_split && (getPoint(getEdge(bord).st).totalDegree() > 2 || getPoint(getEdge(bord).st).oldDegree > 2))
1086 {
1087 break;
1088 }
1089 if (ebData[bord].pieceID == nPiece && ebData[bord].pathID == nPath)
1090 {
1091 if (fabs (te - ebData[bord].tSt) > 0.0001)
1092 {
1093 break;
1094 }
1095 nx = getPoint(getEdge(bord).en).x;
1096 te = ebData[bord].tEn;
1097 }
1098 else
1099 {
1100 break;
1101 }
1102 bord = swdData[bord].suivParc;
1103 }
1104 Geom::Point prevx = from->PrevPoint (nPiece - 1);
1105
1106 Geom::Point sDx, eDx;
1107 {
1108 PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(from->descr_cmd[nPiece]);
1109 Path::CubicTangent (ts, sDx, prevx,nData->start,nData->p,nData->end);
1110 Path::CubicTangent (te, eDx, prevx,nData->start,nData->p,nData->end);
1111 }
1112 sDx *= (te - ts);
1113 eDx *= (te - ts);
1114 {
1115 dest->CubicTo (nx,sDx,eDx);
1116 }
1117 return bord;
1118}
1119
1120#undef MiscNormalize
Cartesian point / 2D vector and related operations.
@ shape_input_err
Definition LivarotDefs.h:29
@ shape_nothing_to_do
Definition LivarotDefs.h:30
@ FIRST
Definition LivarotDefs.h:91
@ LAST
Definition LivarotDefs.h:92
JoinType
Definition LivarotDefs.h:60
TODO: insert short description here.
TODO: insert short description here.
@ tweak_mode_repel
Definition Shape.h:33
@ tweak_mode_push
Definition Shape.h:32
@ tweak_mode_roughen
Definition Shape.h:34
@ shape_polygon
Definition Shape.h:49
3x3 affine transformation matrix.
3x3 matrix representing an affine transformation.
Definition affine.h:70
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
Two-dimensional point that doubles as a vector.
Definition point.h:66
Path and its polyline approximation.
Definition Path.h:93
int LineTo(Geom::Point const &ip)
Appends a LineTo path command.
Definition Path.cpp:151
int Close()
Appends a close path command.
Definition Path.cpp:109
int ArcTo(Geom::Point const &ip, double iRx, double iRy, double angle, bool iLargeArc, bool iClockwise)
Appends an ArcTo path command.
Definition Path.cpp:200
int MoveTo(Geom::Point const &ip)
Appends a MoveTo path command.
Definition Path.cpp:125
const Geom::Point PrevPoint(const int i) const
int CubicTo(Geom::Point const &ip, Geom::Point const &iStD, Geom::Point const &iEnD)
Appends a CubicBezier path command.
Definition Path.cpp:175
std::vector< PathDescr * > descr_cmd
Definition Path.h:108
int ForcePoint()
Appends a forced point path command.
Definition Path.cpp:80
void Reset()
Clears all stored path commands and resets flags that are used by command functions while adding path...
Definition Path.cpp:45
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 MakeSweepDestData(bool nVal)
Initialize the sweep destination data cache.
Definition Shape.cpp:149
void AddContour(Path *dest, int num_orig, Path *const *orig, int start_edge, bool never_split=false)
Add a contour.
std::vector< sweep_src_data > swsData
Definition Shape.h:1082
void ConvertToForme(Path *dest)
Extract contours from a directed graph.
Definition ShapeMisc.cpp:42
void MakeBackData(bool nVal)
Definition Shape.cpp:169
bool _point_data_initialised
the pData array is up to date
Definition Shape.h:1068
void MakeSweepSrcData(bool nVal)
Initialize the sweep source data cache.
Definition Shape.cpp:129
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
std::vector< raster_data > swrData
Definition Shape.h:1084
bool _bbox_up_to_date
the leftX/rightX/topY/bottomY are up to date
Definition Shape.h:1074
int CycleNextAt(int p, int b) const
Definition Shape.h:216
std::vector< back_data > ebData
Definition Shape.h:422
void MakePointData(bool nVal)
Initialize the point data cache.
Definition Shape.cpp:71
int maxAr
Definition Shape.h:474
int ReFormeLineTo(int bord, Path *dest, bool never_split)
void ConvertToFormeNested(Path *dest, int nbP, Path *const *orig, int &nbNest, int *&nesting, int *&contStart, bool never_split=false)
int numberOfEdges() const
Returns number of edges.
Definition Shape.h:498
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
int maxPt
Definition Shape.h:473
int MakeTweak(int mode, Shape *a, double dec, JoinType join, double miter, bool do_profile, Geom::Point c, Geom::Point vector, double radius, Geom::Affine *i2doc)
void Inverse(int b)
Definition Shape.cpp:1924
void Reset(int n=0, int m=0)
Clear all data.
Definition Shape.cpp:235
std::vector< dg_point > _pts
Definition Shape.h:1076
int MakeOffset(Shape *of, double dec, JoinType join, double miter, bool do_profile=false, double cx=0, double cy=0, double radius=0, Geom::Affine *i2doc=nullptr)
bool _has_raster_data
the swrData array is allocated
Definition Shape.h:1072
int ReFormeArcTo(int bord, Path *dest, Path *orig, bool never_split)
int type
Definition Shape.h:477
std::vector< dg_arete > _aretes
Definition Shape.h:1077
void MakeEdgeData(bool nVal)
Initialize the edge data cache.
Definition Shape.cpp:87
bool _has_sweep_dest_data
the swdData array is allocated
Definition Shape.h:1071
dg_point const & getPoint(int n) const
Get a point.
Definition Shape.h:537
std::vector< sweep_dest_data > swdData
Definition Shape.h:1083
int ReFormeCubicTo(int bord, Path *dest, Path *orig, bool never_split)
std::vector< point_data > pData
Definition Shape.h:1085
double c[8][4]
Geom::Point orig
SBasis L2(D2< SBasis > const &a, unsigned k)
Definition d2-sbasis.cpp:42
int mode
TODO: insert short description here.
@ descr_lineto
@ descr_arcto
@ descr_cubicto
Elliptical Arc path command.
Cubic Bezier path command.
Geom::Point dx
Definition Shape.h:463
int incidentEdge[2]
Definition Shape.h:452
Geom::Point x
Definition Shape.h:449
int delta
void dot(Cairo::RefPtr< Cairo::Context > &cr, double x, double y)