Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
Path.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 <glib.h>
14#include "Path.h"
15
16#include <2geom/pathvector.h>
17
19
20/*
21 * manipulation of the path data: path description and polyline
22 * grunt work...
23 * at the end of this file, 2 utilitary functions to get the point and tangent to path associated with a (command no;abcissis)
24 */
25
27{
28 for (auto & i : descr_cmd) {
29 delete i;
30 }
31}
32
33// debug function do dump the path contents on stdout
35{
36 std::cout << "path: " << descr_cmd.size() << " commands." << std::endl;
37 for (auto i : descr_cmd) {
38 i->dump(std::cout);
39 std::cout << std::endl;
40 }
41
42 std::cout << std::endl;
43}
44
46{
47 for (auto & i : descr_cmd) {
48 delete i;
49 }
50
51 descr_cmd.clear();
52 descr_flags = 0;
53
54 forced_subdivisions.clear();
55}
56
57void Path::Copy(Path * who)
58{
60
61 for (auto & i : descr_cmd) {
62 delete i;
63 }
64
65 descr_cmd.clear();
66
67 for (auto i : who->descr_cmd)
68 {
69 descr_cmd.push_back(i->clone());
70 }
71
73}
74
79
81{
82 if ( (descr_flags & descr_doing_subpath) == 0 ) {
83 return -1;
84 }
85
86 if (descr_cmd.empty()) {
87 return -1;
88 }
89
90 descr_cmd.push_back(new PathDescrForced);
91 return descr_cmd.size() - 1;
92}
93
94
96{
97 if ( at < 0 || at > int(descr_cmd.size()) ) {
98 return;
99 }
100
101 if ( at == int(descr_cmd.size()) ) {
102 ForcePoint();
103 return;
104 }
105
106 descr_cmd.insert(descr_cmd.begin() + at, new PathDescrForced);
107}
108
110{
112 CloseSubpath();
113 } else {
114 // Nothing to close.
115 return -1;
116 }
117
118 descr_cmd.push_back(new PathDescrClose);
119
121
122 return descr_cmd.size() - 1;
123}
124
126{
128 CloseSubpath();
129 }
130
131 descr_cmd.push_back(new PathDescrMoveTo(iPt));
132
134 return descr_cmd.size() - 1;
135}
136
137void Path::InsertMoveTo(Geom::Point const &iPt, int at)
138{
139 if ( at < 0 || at > int(descr_cmd.size()) ) {
140 return;
141 }
142
143 if ( at == int(descr_cmd.size()) ) {
144 MoveTo(iPt);
145 return;
146 }
147
148 descr_cmd.insert(descr_cmd.begin() + at, new PathDescrMoveTo(iPt));
149}
150
152{
153 if (!( descr_flags & descr_doing_subpath )) {
154 return MoveTo (iPt);
155 }
156
157 descr_cmd.push_back(new PathDescrLineTo(iPt));
158 return descr_cmd.size() - 1;
159}
160
161void Path::InsertLineTo(Geom::Point const &iPt, int at)
162{
163 if ( at < 0 || at > int(descr_cmd.size()) ) {
164 return;
165 }
166
167 if ( at == int(descr_cmd.size()) ) {
168 LineTo(iPt);
169 return;
170 }
171
172 descr_cmd.insert(descr_cmd.begin() + at, new PathDescrLineTo(iPt));
173}
174
175int Path::CubicTo(Geom::Point const &iPt, Geom::Point const &iStD, Geom::Point const &iEnD)
176{
177 if ( (descr_flags & descr_doing_subpath) == 0) {
178 return MoveTo (iPt);
179 }
180
181 descr_cmd.push_back(new PathDescrCubicTo(iPt, iStD, iEnD));
182 return descr_cmd.size() - 1;
183}
184
185
186void Path::InsertCubicTo(Geom::Point const &iPt, Geom::Point const &iStD, Geom::Point const &iEnD, int at)
187{
188 if ( at < 0 || at > int(descr_cmd.size()) ) {
189 return;
190 }
191
192 if ( at == int(descr_cmd.size()) ) {
193 CubicTo(iPt,iStD,iEnD);
194 return;
195 }
196
197 descr_cmd.insert(descr_cmd.begin() + at, new PathDescrCubicTo(iPt, iStD, iEnD));
198}
199
200int Path::ArcTo(Geom::Point const &iPt, double iRx, double iRy, double angle,
201 bool iLargeArc, bool iClockwise)
202{
203 if ( (descr_flags & descr_doing_subpath) == 0 ) {
204 return MoveTo(iPt);
205 }
206
207 descr_cmd.push_back(new PathDescrArcTo(iPt, iRx, iRy, angle, iLargeArc, iClockwise));
208 return descr_cmd.size() - 1;
209}
210
211
212void Path::InsertArcTo(Geom::Point const &iPt, double iRx, double iRy, double angle,
213 bool iLargeArc, bool iClockwise, int at)
214{
215 if ( at < 0 || at > int(descr_cmd.size()) ) {
216 return;
217 }
218
219 if ( at == int(descr_cmd.size()) ) {
220 ArcTo(iPt, iRx, iRy, angle, iLargeArc, iClockwise);
221 return;
222 }
223
224 descr_cmd.insert(descr_cmd.begin() + at, new PathDescrArcTo(iPt, iRx, iRy,
225 angle, iLargeArc, iClockwise));
226}
227
228/*
229 * points of the polyline
230 */
231void
233{
234 if (! back) {
235 if (nVal) {
236 back = true;
237 ResetPoints();
238 }
239 } else {
240 if (! nVal) {
241 back = false;
242 ResetPoints();
243 }
244 }
245}
246
247
249{
250 pts.clear();
251}
252
253
254int Path::AddPoint(Geom::Point const &iPt, bool mvto)
255{
256 if (back) {
257 return AddPoint (iPt, -1, 0.0, mvto);
258 }
259
260 if ( !mvto && !pts.empty() && pts.back().p == iPt ) {
261 return -1;
262 }
263
264 int const n = pts.size();
265 pts.emplace_back(mvto ? polyline_moveto : polyline_lineto, iPt);
266 return n;
267}
268
269int Path::AddPoint(Geom::Point const &iPt, int ip, double it, bool mvto)
270{
271 if (! back) {
272 return AddPoint (iPt, mvto);
273 }
274
275 if ( !mvto && !pts.empty() && pts.back().p == iPt ) {
276 return -1;
277 }
278
279 int const n = pts.size();
280 pts.emplace_back(mvto ? polyline_moveto : polyline_lineto, iPt, ip, it);
281 return n;
282}
283
285{
286 if (pts.empty() || pts.back().isMoveTo != polyline_lineto) {
287 return -1;
288 }
289
290 int const n = pts.size();
291
292 if (back) {
293 pts.emplace_back(polyline_forced, pts[n - 1].p, pts[n - 1].piece, pts[n - 1].t);
294 } else {
295 pts.emplace_back(polyline_forced, pts[n - 1].p);
296 }
297
298 return n;
299}
300
301void Path::PolylineBoundingBox(double &l, double &t, double &r, double &b)
302{
303 l = t = r = b = 0.0;
304 if ( pts.empty() ) {
305 return;
306 }
307
308 std::vector<path_lineto>::const_iterator i = pts.begin();
309 l = r = i->p[Geom::X];
310 t = b = i->p[Geom::Y];
311 ++i;
312
313 for (; i != pts.end(); ++i) {
314 r = std::max(r, i->p[Geom::X]);
315 l = std::min(l, i->p[Geom::X]);
316 b = std::max(b, i->p[Geom::Y]);
317 t = std::min(t, i->p[Geom::Y]);
318 }
319}
320
321
328void Path::PointAt(int piece, double at, Geom::Point &pos)
329{
330 if (piece < 0 || piece >= int(descr_cmd.size())) {
331 // this shouldn't happen: the piece we are asked for doesn't
332 // exist in the path
333 pos = Geom::Point(0,0);
334 return;
335 }
336
337 PathDescr const *theD = descr_cmd[piece];
338 int const typ = theD->getType();
339 Geom::Point tgt;
340 double len;
341 double rad;
342
343 if (typ == descr_moveto) {
344
345 return PointAt (piece + 1, 0.0, pos);
346
347 } else if (typ == descr_close || typ == descr_forced) {
348
349 return PointAt (piece - 1, 1.0, pos);
350
351 } else if (typ == descr_lineto) {
352
353 PathDescrLineTo const *nData = dynamic_cast<PathDescrLineTo const *>(theD);
354 TangentOnSegAt(at, PrevPoint (piece - 1), *nData, pos, tgt, len);
355
356 } else if (typ == descr_arcto) {
357
358 PathDescrArcTo const *nData = dynamic_cast<PathDescrArcTo const *>(theD);
359 TangentOnArcAt(at,PrevPoint (piece - 1), *nData, pos, tgt, len, rad);
360
361 } else if (typ == descr_cubicto) {
362
363 PathDescrCubicTo const *nData = dynamic_cast<PathDescrCubicTo const *>(theD);
364 TangentOnCubAt(at, PrevPoint (piece - 1), *nData, false, pos, tgt, len, rad);
365 }
366}
367
368void Path::PointAndTangentAt(int piece, double at, Geom::Point &pos, Geom::Point &tgt)
369{
370 if (piece < 0 || piece >= int(descr_cmd.size())) {
371 // this shouldn't happen: the piece we are asked for doesn't exist in the path
372 pos = Geom::Point(0, 0);
373 return;
374 }
375
376 PathDescr const *theD = descr_cmd[piece];
377 int typ = theD->getType();
378 double len;
379 double rad;
380 if (typ == descr_moveto) {
381
382 return PointAndTangentAt(piece + 1, 0.0, pos, tgt);
383
384 } else if (typ == descr_close ) {
385
386 int cp = piece - 1;
387 while ( cp >= 0 && (descr_cmd[cp]->getType()) != descr_moveto ) {
388 cp--;
389 }
390 if ( cp >= 0 ) {
391 PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[cp]);
392 PathDescrLineTo dst(nData->p);
393 TangentOnSegAt(at, PrevPoint (piece - 1), dst, pos, tgt, len);
394 }
395
396 } else if ( typ == descr_forced) {
397
398 return PointAndTangentAt(piece - 1, 1.0, pos,tgt);
399
400 } else if (typ == descr_lineto) {
401
402 PathDescrLineTo const *nData = dynamic_cast<PathDescrLineTo const *>(theD);
403 TangentOnSegAt(at, PrevPoint (piece - 1), *nData, pos, tgt, len);
404
405 } else if (typ == descr_arcto) {
406
407 PathDescrArcTo const *nData = dynamic_cast<PathDescrArcTo const *>(theD);
408 TangentOnArcAt (at,PrevPoint (piece - 1), *nData, pos, tgt, len, rad);
409
410 } else if (typ == descr_cubicto) {
411
412 PathDescrCubicTo const *nData = dynamic_cast<PathDescrCubicTo const *>(theD);
413 TangentOnCubAt (at, PrevPoint (piece - 1), *nData, false, pos, tgt, len, rad);
414 }
415}
416
423{
425}
426
427void Path::FastBBox(double &l,double &t,double &r,double &b)
428{
429 l = t = r = b = 0;
430 bool empty = true;
431 Geom::Point lastP(0, 0);
432
433 for (auto & i : descr_cmd) {
434 int const typ = i->getType();
435 switch ( typ ) {
436 case descr_lineto:
437 {
438 PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(i);
439 if ( empty ) {
440 l = r = nData->p[Geom::X];
441 t = b = nData->p[Geom::Y];
442 empty = false;
443 } else {
444 if ( nData->p[Geom::X] < l ) {
445 l = nData->p[Geom::X];
446 }
447 if ( nData->p[Geom::X] > r ) {
448 r = nData->p[Geom::X];
449 }
450 if ( nData->p[Geom::Y] < t ) {
451 t = nData->p[Geom::Y];
452 }
453 if ( nData->p[Geom::Y] > b ) {
454 b = nData->p[Geom::Y];
455 }
456 }
457 lastP = nData->p;
458 }
459 break;
460
461 case descr_moveto:
462 {
463 PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo *>(i);
464 if ( empty ) {
465 l = r = nData->p[Geom::X];
466 t = b = nData->p[Geom::Y];
467 empty = false;
468 } else {
469 if ( nData->p[Geom::X] < l ) {
470 l = nData->p[Geom::X];
471 }
472 if ( nData->p[Geom::X] > r ) {
473 r = nData->p[Geom::X];
474 }
475 if ( nData->p[Geom::Y] < t ) {
476 t = nData->p[Geom::Y];
477 }
478 if ( nData->p[Geom::Y] > b ) {
479 b = nData->p[Geom::Y];
480 }
481 }
482 lastP = nData->p;
483 }
484 break;
485
486 case descr_arcto:
487 {
488 PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(i);
489 if ( empty ) {
490 l = r = nData->p[Geom::X];
491 t = b = nData->p[Geom::Y];
492 empty = false;
493 } else {
494 if ( nData->p[Geom::X] < l ) {
495 l = nData->p[Geom::X];
496 }
497 if ( nData->p[Geom::X] > r ) {
498 r = nData->p[Geom::X];
499 }
500 if ( nData->p[Geom::Y] < t ) {
501 t = nData->p[Geom::Y];
502 }
503 if ( nData->p[Geom::Y] > b ) {
504 b = nData->p[Geom::Y];
505 }
506 }
507 lastP = nData->p;
508 }
509 break;
510
511 case descr_cubicto:
512 {
513 PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(i);
514 if ( empty ) {
515 l = r = nData->p[Geom::X];
516 t = b = nData->p[Geom::Y];
517 empty = false;
518 } else {
519 if ( nData->p[Geom::X] < l ) {
520 l = nData->p[Geom::X];
521 }
522 if ( nData->p[Geom::X] > r ) {
523 r = nData->p[Geom::X];
524 }
525 if ( nData->p[Geom::Y] < t ) {
526 t = nData->p[Geom::Y];
527 }
528 if ( nData->p[Geom::Y] > b ) {
529 b = nData->p[Geom::Y];
530 }
531 }
532
533/* bug 249665: "...the calculation of the bounding-box for cubic-paths
534has some extra steps to make it work correctly in Win32 that unfortunately
535are unnecessary in Linux, generating wrong results. This only shows in
536Type1 fonts because they use cubic-paths instead of the
537bezier-paths used by True-Type fonts."
538*/
539
540#ifdef _WIN32
541 Geom::Point np = nData->p - nData->end;
542 if ( np[Geom::X] < l ) {
543 l = np[Geom::X];
544 }
545 if ( np[Geom::X] > r ) {
546 r = np[Geom::X];
547 }
548 if ( np[Geom::Y] < t ) {
549 t = np[Geom::Y];
550 }
551 if ( np[Geom::Y] > b ) {
552 b = np[Geom::Y];
553 }
554
555 np = lastP + nData->start;
556 if ( np[Geom::X] < l ) {
557 l = np[Geom::X];
558 }
559 if ( np[Geom::X] > r ) {
560 r = np[Geom::X];
561 }
562 if ( np[Geom::Y] < t ) {
563 t = np[Geom::Y];
564 }
565 if ( np[Geom::Y] > b ) {
566 b = np[Geom::Y];
567 }
568#endif
569
570 lastP = nData->p;
571 }
572 break;
573 }
574 }
575}
576
577std::string Path::svg_dump_path() const
578{
580
581 for (int i = 0; i < descr_cmd.size(); i++) {
582 auto const p = i == 0 ? Geom::Point() : PrevPoint(i - 1);
583 descr_cmd[i]->dumpSVG(os, p);
584 }
585
586 return os.str();
587}
588
589// Find out if the segment that corresponds to 'piece' is a straight line
590bool Path::IsLineSegment(int piece)
591{
592 if (piece < 0 || piece >= int(descr_cmd.size())) {
593 return false;
594 }
595
596 PathDescr const *theD = descr_cmd[piece];
597 int const typ = theD->getType();
598
599 return (typ == descr_lineto);
600}
601
602
603/*
604 Local Variables:
605 mode:c++
606 c-file-style:"stroustrup"
607 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
608 indent-tabs-mode:nil
609 fill-column:99
610 End:
611*/
612// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
TODO: insert short description here.
@ polyline_moveto
Definition Path.h:47
@ polyline_lineto
Definition Path.h:46
@ polyline_forced
Definition Path.h:48
3x3 matrix representing an affine transformation.
Definition affine.h:70
Two-dimensional point that doubles as a vector.
Definition point.h:66
std::string str() const
Path and its polyline approximation.
Definition Path.h:93
@ descr_doing_subpath
Definition Path.h:100
~Path()
Definition Path.cpp:26
int LineTo(Geom::Point const &ip)
Appends a LineTo path command.
Definition Path.cpp:151
void SetBackData(bool nVal)
Sets the back variable to the value passed in and clears the polyline approximation.
Definition Path.cpp:232
void Affiche()
Definition Path.cpp:34
void InsertLineTo(Geom::Point const &iPt, int at)
Definition Path.cpp:161
int Close()
Appends a close path command.
Definition Path.cpp:109
void ResetPoints()
Clears the polyline approximation.
Definition Path.cpp:248
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
void PointAt(int piece, double at, Geom::Point &pos)
Definition Path.cpp:328
int AddForcedPoint()
Adds a forced point to the polyline approximation's list of points.
Definition Path.cpp:284
std::string svg_dump_path() const
Definition Path.cpp:577
std::vector< path_lineto > pts
Definition Path.h:128
void Transform(const Geom::Affine &trans)
Apply a transformation on all path commands.
Definition Path.cpp:422
void LoadPathVector(Geom::PathVector const &pv, Geom::Affine const &tr, bool doTransformation)
Load a lib2geom Geom::PathVector in this path object.
void PointAndTangentAt(int piece, double at, Geom::Point &pos, Geom::Point &tgt)
Definition Path.cpp:368
int descr_flags
Definition Path.h:105
Geom::PathVector MakePathVector() const
Create a lib2geom Geom::PathVector from this Path object.
int MoveTo(Geom::Point const &ip)
Appends a MoveTo path command.
Definition Path.cpp:125
void InsertArcTo(Geom::Point const &ip, double iRx, double iRy, double angle, bool iLargeArc, bool iClockwise, int at)
Definition Path.cpp:212
std::vector< ForcedSubdivision > forced_subdivisions
Definition Path.h:140
void Copy(Path *who)
Clear all stored path commands, resets flags and imports path commands from the passed Path object.
Definition Path.cpp:57
bool back
Definition Path.h:131
const Geom::Point PrevPoint(const int i) const
bool IsLineSegment(int piece)
Definition Path.cpp:590
void InsertCubicTo(Geom::Point const &ip, Geom::Point const &iStD, Geom::Point const &iEnD, int at)
Definition Path.cpp:186
void FastBBox(double &l, double &t, double &r, double &b)
Definition Path.cpp:427
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
void CloseSubpath()
Definition Path.cpp:75
int AddPoint(Geom::Point const &iPt, bool mvto=false)
Adds a point to the polyline approximation's list of points.
Definition Path.cpp:254
void InsertMoveTo(Geom::Point const &iPt, int at)
Definition Path.cpp:137
void PolylineBoundingBox(double &l, double &t, double &r, double &b)
Definition Path.cpp:301
void InsertForcePoint(int at)
Definition Path.cpp:95
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
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
TODO: insert short description here.
@ descr_lineto
@ descr_arcto
@ descr_moveto
@ descr_cubicto
@ descr_close
@ descr_forced
PathVector - a sequence of subpaths.
auto len
Definition safe-printf.h:21
Elliptical Arc path command.
Close Path instruction.
Cubic Bezier path command.
A forced point path command.
A LineTo path command.
A MoveTo path command.
A base class for Livarot's path commands.
int getType() const