1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
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
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
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
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
|
// SPDX-License-Identifier: GPL-2.0-or-later
/** @file
* TODO: insert short description here
*//*
* Authors: see git history
*
* Copyright (C) 2014 Authors
* Released under GNU GPL v2+, read the file 'COPYING' for more information.
*/
/*
* Path.h
* nlivarot
*
* Created by fred on Tue Jun 17 2003.
*
*/
#ifndef my_path
#define my_path
#include <vector>
#include "LivarotDefs.h"
#include <2geom/point.h>
struct PathDescr;
struct PathDescrLineTo;
struct PathDescrArcTo;
struct PathDescrCubicTo;
struct PathDescrBezierTo;
struct PathDescrIntermBezierTo;
class SPStyle;
/*
* the Path class: a structure to hold path description and their polyline approximation (not kept in sync)
* the path description is built with regular commands like MoveTo() LineTo(), etc
* the polyline approximation is built by a call to Convert() or its variants
* another possibility would be to call directly the AddPoint() functions, but that is not encouraged
* the conversion to polyline can salvage data as to where on the path each polyline's point lies; use
* ConvertWithBackData() for this. after this call, it's easy to rewind the polyline: sequences of points
* of the same path command can be reassembled in a command
*/
// polyline description commands
enum
{
polyline_lineto = 0, // a lineto
polyline_moveto = 1, // a moveto
polyline_forced = 2 // a forced point, ie a point that was an angle or an intersection in a previous life
// or more realistically a control point in the path description that created the polyline
// forced points are used as "breakable" points for the polyline -> cubic bezier patch operations
// each time the bezier fitter encounters such a point in the polyline, it decreases its treshhold,
// so that it is more likely to cut the polyline at that position and produce a bezier patch
};
class Shape;
// path creation: 2 phases: first the path is given as a succession of commands (MoveTo, LineTo, CurveTo...); then it
// is converted in a polyline
// a polylone can be stroked or filled to make a polygon
/**
* Path and its polyline approximation.
*
* A Path is exactly analogous to an SVG path element. Like the SVG path element, this class
* stores path commands. A Path can be approximated by line segments and this approximation
* is known as a "polyline approximation". Internally, the polyline approximation is stored
* as a set of points.
*
* Each path command (except the MoveTo), creates a new segment. A path segment can be defined
* as a function of time over the interval [0, 1]. Each point in the polyline approximation can
* store the index of the path command that created the path segment that it came from and the time
* value at which it existed. The midpoint of a line segment would be at \f[ t = 0.5 \f] for
* example. This information is known as "back data" since it preserves the information about the
* original segments that existed in the path and can help us recreate them or their portions back.
* Note that the first point of a subpath stores the index of the moveTo command.
*
* To use this class create a new instance. Call the command functions such as Path::MoveTo,
* Path::LineTo, Path::CubicTo, etc to append path commands. Then call one of Path::Convert,
* Path::ConvertEvenLines or Path::ConvertWithBackData to generate the polyline approximation.
* Then you can do simplification by calling Path::Simplify or fill a Shape by calling Path::Fill
* on the shape to use features such as Offsetting, Boolean Operations and Tweaking.
*
* Path *path = new Path;
* path->MoveTo(Geom::Point(10, 10));
* path->LineTo(Geom::Point(100, 10));
* path->LineTo(Geom::Point(100, 100));
* path->Close();
* path->ConvertEvenLines(0.001); // You can use the other variants too
* // insteresting stuff here
*
*/
class Path
{
friend class Shape;
public:
// flags for the path construction
enum
{
descr_ready = 0,
descr_adding_bezier = 1, // we're making a bezier spline, so you can expect pending_bezier_* to have a value
descr_doing_subpath = 2, // we're doing a path, so there is a moveto somewhere
descr_delayed_bezier = 4,// the bezier spline we're doing was initiated by a TempBezierTo(), so we'll need an endpoint
descr_dirty = 16 // the path description was modified
};
// some data for the construction: what's pending, and some flags
int descr_flags;
int pending_bezier_cmd;
int pending_bezier_data;
int pending_moveto_cmd;
int pending_moveto_data;
std::vector<PathDescr*> descr_cmd; /*!< A vector of owned pointers to path commands. */
/**
* Points of the polyline approximation.
*
* Since the polyline approximation approximates a Path which can have multiple subpaths, the
* approximation can also have a set of continuous polylines.
*/
struct path_lineto
{
path_lineto(bool m, Geom::Point pp) : isMoveTo(m), p(pp), piece(-1), t(0), closed(false) {}
path_lineto(bool m, Geom::Point pp, int pie, double tt) : isMoveTo(m), p(pp), piece(pie), t(tt), closed(false) {}
int isMoveTo; /*!< A flag that stores one of polyline_lineto, polyline_moveto, polyline_forced */
Geom::Point p; /*!< The point itself. */
int piece; /*!< Index of the path command that created the path segment that this point comes from.*/
double t; /*!< The time at which this point exists in the path segment. A value between 0 and 1. */
bool closed; /*!< True indicates that subpath is closed (this point is the last point of a closed subpath) */
};
std::vector<path_lineto> pts; /*!< A vector storing the polyline approximation points. */
bool back; /*!< If true, indicates that the polyline approximation is going to have backdata.
No need to set this manually though. When Path::Convert or any of its variants is called, it's set automatically. */
Path();
virtual ~Path();
// creation of the path description
/**
* Clears all stored path commands and resets flags that are used by command functions while adding path
* commands.
*/
void Reset();
/**
* Clear all stored path commands, resets flags and imports path commands from the passed Path
* object.
*
* @param who Path object whose path commands to copy.
*/
void Copy (Path * who);
/**
* Appends a forced point path command.
*
* Forced points are places in the path which are preferred to be kept in the simplification
* algorithm. The simplification algorithm will try to retain those points. This can be beneficial
* in situations such as self-intersections where we would want the intersection point to remain
* unchanged after any simplification is done.
*
* TODO: Confirm this with some testing.
*
* A forced point command can't be appended if there is no active subpath that we are drawing on.
* If you imagine calling these command functions as giving instructions to a pen, a forced point
* command requires that the pen is already touching the canvas. The pen is not on the canvas
* when you instantiate the Path object and it also leaves it when you call Path::Close. The term
* "active subpath" simply means that the pen is already touching the canvas.
*
* @return Index of the path command in the path commands array if it got appended, -1 otherwise.
*/
int ForcePoint();
/**
* Appends a close path command.
*
* Close path command can't be appended if there is no acive subpath.
*
* @return Index of the path command in the path commands array if it got appended, -1 otherwise.
*/
int Close();
/** Appends a MoveTo path command.
*
* @param ip The point to move to.
*
* @return The index of the path description added.
*/
int MoveTo ( Geom::Point const &ip);
/** Appends a LineTo path command.
*
* @param ip The point to draw a line to.
*
* @return The index of the path description added.
*/
int LineTo ( Geom::Point const &ip);
/**
* Appends a CubicBezier path command.
*
* In order to understand the parameters let p0, p1, p2, p3 denote the four points of a
* cubic Bezier curve. p0 is the start point. p3 is the end point. p1 and p2 are the
* two control points.
*
* @param ip The final point of the bezier curve or p3.
* @param iStD 3 * (p1 - p0). Weird way to store it but that's how it is.
* @param iEnD 3 * (p3 - p2). Weird way to store it but that's how it is.
*
* @return The index of the path description added.
*/
int CubicTo ( Geom::Point const &ip, Geom::Point const &iStD, Geom::Point const &iEnD);
/**
* Appends an ArcTo path command.
*
* The parameters are identical to the SVG elliptical arc command.
*
* @param ip The final point of the arc.
* @param iRx The radius in the x direction.
* @param iRy The radius in the y direction.
* @param angle The angle w.r.t x axis in degrees. TODO: Confirm this
* @param iLargeArc If true, it's the larger arc, if false, it's the smaller one.
* @param iClockwise If true, it's the clockwise arc, if false, it's the anti-clockwise one.
*
* @return The index of the path description added.
*/
int ArcTo ( Geom::Point const &ip, double iRx, double iRy, double angle, bool iLargeArc, bool iClockwise);
/**
* Adds a control point for the last quadratic bezier spline command.
*
* Adds a control point to the quadratic bezier spline that was last inserted with a call to
* Path::BezierTo.
*
* @param ip The control point.
*
* @return The index of the path description added.
*/
int IntermBezierTo ( Geom::Point const &ip); // add a quadratic bezier spline control point
/**
* Appends a quadratic bezier spline path command.
*
* A quadratic bezier spline is basically a set of quadratic bezier curves. To simply illustrate
* how this spline is made up, let's define some terms first. Let midpoint(a, b) represent the
* midpoint of the points a and b. Let quad(a, b, c) represent a quadratic Bezier curve with a
* as the start point, b as the control point and c as the end point.
*
* Given a set of points: st, p1, p2, p3, p4, en where st and en are the endpoints and the rest
* are control points, we will have the following quadratic Bezier curves connected end to end.
*
* quad(st, p1, midpoint(p1, p2))
* quad(midpoint(p1, p2), p2, midpoint(p2, p3))
* quad(midpoint(p2, p3), p3, midpoint(p3, p4))
* quad(midpoint(p3, p4), p4, en)
*
* No need to specify the number of control points. That'll be done automatically as you call
* Path::IntermBezierTo to add the control points. The sequence of instructions are like:
* 1. Call Path::BezierTo with the final point.
* 2. Call Path::IntermBezierTo with control points. One call for each control point.
* 3. Call Path::EndBezierTo to mark the end of the quadratic bezier spline command.
*
* Basically, the interface has been designed in such a way that you specify the final point and
* then add control points one by one as many as you like. Once you're done, you call
* Path::EndBezierTo to inform that you're done adding points for the spline.
*
* @param ip The final point of the quadratic bezier spline.
*
* @return The index of the path description added.
*/
int BezierTo ( Geom::Point const &ip); // quadratic bezier spline to this point (control points can be added after this)
/**
* Finish any ongoing BezierTo instruction.
*
* Once Path::BezierTo has been called, the object expects you to specify control points by
* calling Path::IntermBezierTo for each control point. Once you're done specifying the control
* points you call Path::EndBezierTo to finish the quadratic bezier spline.
*
* @return -1 all the time.
*/
int EndBezierTo();
/**
* Appends a quadratic bezier spline path command (without specifying a final point).
*
* If you use Path::BezierTo, you have to specify the final point of the spline first and then
* follow it with all the control points. However, this is kinda counter-intuitive. Visually, we
* would look at the control points first and then the final end point. This function allows a
* similar mechanism. You can start a quadratic bezier spline without mentioning any final point,
* specify as many control points as you like and then while finishing it, you can specify the
* final point of the spline.
*
* The sequence of instructions would be:
* 1. Path::TempBezierTo to start.
* 2. Path::IntermBezierTo to specify control points. One call for each control point.
* 3. Path::EndBezierTo(Geom::Point const&) passing the final point of the quadratic bezier spline and finish the
* quadratic bezier spline command.
*
* @return Index of the description added.
*/
int TempBezierTo(); // start a quadratic bezier spline (control points can be added after this)
/**
* Finish any ongoing TempBezierTo instruction.
*
* Used to specify the final point of a quadratic bezier spline which was started by calling
* Path::TempBezierTo.
*
* @param ip The final point.
*
* @return -1 all the time.
*/
int EndBezierTo ( Geom::Point const &ip); // ends a quadratic bezier spline (for curves started with TempBezierTo)
// transforms a description in a polyline (for stroking and filling)
// treshhold is the max length^2 (sort of)
/**
* Creates a polyline approximation of the path. Doesn't keep any back data. Line segments are
* not split into smaller line segments.
*
* Threshold has no strict definition. It means different things for each path segment.
*
* @param threshhold The error threshold used to approximate curves by line segments. The smaller
* this is, the more line segments there will be.
*/
void Convert (double treshhold);
/**
* Creates a polyline approximation of the path. Line segments are split into further smaller line segments
* such that each of those line segments is no bigger than threshold.
*
* Breaking up into further smaller line segments is useful for path simplification as you can
* then fit cubic Bezier patches on those small line segments.
*
* Threshold has no strict definition. It means different things for each path segment.
*
* @param threshhold The error threshold used to approximate the path. The smaller this is, the
* more line segments there will be and the better the polyline approximation would be.
*/
void ConvertEvenLines (double treshhold); // decomposes line segments too, for later recomposition
/**
* Creates a polyline approximation of the path. Line segments are
* not split into smaller line segments. Stores back data for later recomposition.
*
* Threshold has no strict definition. It means different things for each path segment.
*
* @param threshhold The error threshold used to approximate the path. The smaller this is, the
* more line segments there will be and the better the polyline approximation would be.
*/
void ConvertWithBackData (double treshhold);
// creation of the polyline (you can tinker with these function if you want)
/**
* Sets the back variable to the value passed in and clears the polyline approximation.
*
* @param nVal True if we are going to have backdata and false otherwise.
*/
void SetBackData (bool nVal); // has back data?
/**
* Clears the polyline approximation.
*/
void ResetPoints(); // resets to the empty polyline
/**
* Adds a point to the polyline approximation's list of points.
*
* This is used internally by Path::Convert and its variants, so you'd not need to use it by
* yourself.
*
* If back variable of the instance is set to true, dummy back data will be used with the point.
* Piece being -1 and time being 0. Since this function doesn't take any back data you'll have to
* fill in something.
*
* The point doesn't get added if it's a lineto and the point before it has the same coordinates.
*
* @param iPt The point itself.
* @param mvto If true, it's a moveTo otherwise it's a lineto.
*
* @return Index of the point added if it was added, -1 otherwise.
*/
int AddPoint ( Geom::Point const &iPt, bool mvto = false); // add point
/**
* Adds a point to the polyline approximation's list of points. Let's you specify back data.
*
* This is used internally by Path::Convert and its variants, so you'd not need to use it by
* yourself.
*
* @param iPt The point itself.
* @param ip The index of the path command that created the segment that this point belongs to.
* @param it The time in that path segment at which this point exists. 0 is beginning and 1
* is end.
* @param mvto If true, it's a moveTo otherwise it's a lineto.
*
* The point doesn't get added if it's a lineto and the point before it has the same coordinates.
*
* @return Index of the point added if it was added, -1 otherwise.
*/
int AddPoint ( Geom::Point const &iPt, int ip, double it, bool mvto = false);
/**
* Adds a forced point to the polyline approximation's list of points without specifying any back data.
*
* The argument of this function is useless. The point that gets added as a forced point has the
* same coordinates as the last point that was added. If no points exist or the last one isn't a
* lineto, nothing gets added.
*
* Dummy back data will be used if the back variable of the instance is true.
*
* @param iPt Unused argument.
*
* @return Index of the point added if it was added, -1 otherwise.
*/
int AddForcedPoint ( Geom::Point const &iPt); // add point
/**
* Add a forced point to the polyline approximation's list of points while specifying backdata.
*
* The argument of this function is useless. The point that gets added as a forced point has the
* same coordinates as the last point that was added. If no points exist or the last one isn't a
* lineto, nothing gets added. The back data is also picked up from the last point that was
* added.
*
* @param iPt Unused argument.
* @param ip Unused argument.
* @param it Unused argument.
*
* @return Index of the point added if it was added, -1 otherwise.
*/
int AddForcedPoint ( Geom::Point const &iPt, int ip, double it);
/**
* Replace the last point in the polyline approximation's list of points with the passed one.
*
* Nothing gets added if no points exist already.
*
* @param iPt The point to replace the last one with.
*
* @return Index of the last point added if it was added, -1 otherwise.
*/
int ReplacePoint(Geom::Point const &iPt); // replace point
// transform in a polygon (in a graph, in fact; a subsequent call to ConvertToShape is needed)
// - fills the polyline; justAdd=true doesn't reset the Shape dest, but simply adds the polyline into it
// closeIfNeeded=false prevent the function from closing the path (resulting in a non-eulerian graph
// pathID is a identification number for the path, and is used for recomposing curves from polylines
// give each different Path a different ID, and feed the appropriate orig[] to the ConvertToForme() function
/**
* Fills the shape with the polyline approximation stored in this object.
*
* For each line segment in the polyline approximation, an edge is created in the shape.
*
* One important point to highlight is the closeIfNeeded argument. For each subpath (where a
* sub path is a moveTo followed by one or more lineTo points) you can either have the start and end
* points being identical or very close (a closed contour) or have them apart (an open contour).
* If you set closeIfNeeded to true, it'll automatically add a closing segment if needed and
* close an open contour by itself. If your contour is already closed, it makes sure that the
* first and last point are the same ones in the graph (instead of being two indentical points).
* If closeIfNeeded is false, it just doesn't care at all. Even if your contour is closed, the
* first and last point will be separate (even though they would be duplicates).
*
* @param dest The shape to fill.
* @param pathID A unique number for this path. The shape will associate this number with each
* edge that comes from this path. Later on, when you use Shape::ConvertToForme you'll pass an array
* of Path objects (named orig) and the shape will use that pathID to do orig[pathID] and get the
* original path information.
* @param justAdd If set to true, this will function will just fill stuff in without resetting
* any existing stuff in Shape. If set to false, it'll make sure to reset the shape and already
* make room for the maximum number of possible points and edges.
* @param closeIfNeeded If set to true, the graph will be closed always. Otherwise, it won't be
* closed.
* @param invert If set to true, the graph is drawn exactly in the manner opposite to the actual
* polyline approximation that this object stores, if false, it's stored indentical to how it's
* in the polyline approximation.
*
* @todo "the graph is drawn exactly in the manner opposite"? Does this mean the edges of the
* directed graph are reversed?
*/
void Fill(Shape *dest, int pathID = -1, bool justAdd = false,
bool closeIfNeeded = true, bool invert = false);
// - stroke the path; usual parameters: type of cap=butt, type of join=join and miter (see LivarotDefs.h)
// doClose treat the path as closed (ie a loop)
void Stroke(Shape *dest, bool doClose, double width, JoinType join,
ButtType butt, double miter, bool justAdd = false);
// build a Path that is the outline of the Path instance's description (the result is stored in dest)
// it doesn't compute the exact offset (it's way too complicated, but an approximation made of cubic bezier patches
// and segments. the algorithm was found in a plugin for Impress (by Chris Cox), but i can't find it back...
void Outline(Path *dest, double width, JoinType join, ButtType butt,
double miter);
// half outline with edges having the same direction as the original
void OutsideOutline(Path *dest, double width, JoinType join, ButtType butt,
double miter);
// half outline with edges having the opposite direction as the original
void InsideOutline (Path * dest, double width, JoinType join, ButtType butt,
double miter);
// polyline to cubic bezier patches
/**
* Simplify the path.
*
* Fit the least possible number of cubic Bezier patches on the polyline approximation while
* respecting the threshold (keeping the error small). The function clears existing path commands
* and the resulting cubic Bezier patches will be pushed as path commands in the instance.
*
* The algorithm to fit cubic Bezier curves on the polyline approximation's points.
*
* http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/INT-APP/CURVE-APP-global.html
*
* @param threshold The threshold for simplification. A measure of how much error is okay. The
* smaller this number is, the more conservative the fitting algorithm will be.
*/
void Simplify (double treshhold);
/**
* Simplify the path with a different approach.
*
* This function is also supposed to do simplification but by merging (coalescing) existing
* path descriptions instead of doing any fitting. But I seriously doubt whether this is useful
* at all or works at all. More experimentation needed. TODO
*
* @param threshold The threshold for simplification.
*/
void Coalesce (double tresh);
// utilities
// piece is a command no in the command list
// "at" is an abcissis on the path portion associated with this command
// 0=beginning of portion, 1=end of portion.
void PointAt (int piece, double at, Geom::Point & pos);
void PointAndTangentAt (int piece, double at, Geom::Point & pos, Geom::Point & tgt);
// last control point before the command i (i included)
// used when dealing with quadratic bezier spline, cause these can contain arbitrarily many commands
const Geom::Point PrevPoint (const int i) const;
// dash the polyline
// the result is stored in the polyline, so you lose the original. make a copy before if needed
void DashPolyline(float head,float tail,float body,int nbD, const float dashs[],bool stPlain,float stOffset);
void DashPolylineFromStyle(SPStyle *style, float scale, float min_len);
//utilitaire pour inkscape
/**
* Load a lib2geom Geom::Path in this path object.
*
* The Geom::Path object is read and path commands making it up are appended in the Path object.
*
* @param path The Geom::Path object to load.
* @param tr A transformation matrix.
* @param doTransformation If set to true, the transformation matrix tr is applied on the path
* before it's loaded in this path object.
* @param append If set to true, any existing path commands in this object are retained. If
* set to false, any existing path commands will be cleared.
*/
void LoadPath(Geom::Path const &path, Geom::Affine const &tr, bool doTransformation, bool append = false);
/**
* Load a lib2geom Geom::PathVector in this path object. (supports transformation)
*
* Any existing path commands in this object are not cleared.
*
* @param pv The Geom::PathVector object to load.
* @param tr A transformation to apply on each path.
* @param doTransformation If set to true, the transformation in tr is applied.
*/
void LoadPathVector(Geom::PathVector const &pv, Geom::Affine const &tr, bool doTransformation);
/**
* Load a lib2geom Geom::PathVector in this path object.
*
* Any existing path commands in this object are not cleared.
*
* @param pv A reference to the Geom::PathVector object to load.
*/
void LoadPathVector(Geom::PathVector const &pv);
/**
* Create a lib2geom Geom::PathVector from this Path object.
*
* Looks like the time this was written Geom::PathBuilder didn't exist or maybe
* the author wasn't aware of it.
*
* @return The Geom::PathVector created.
*/
Geom::PathVector MakePathVector();
/**
* Apply a transformation on all path commands.
*
* Done by calling the transform method on each path command.
*
* @param trans The transformation to apply.
*/
void Transform(const Geom::Affine &trans);
// decompose le chemin en ses sous-chemin
// killNoSurf=true -> oublie les chemins de surface nulle
Path** SubPaths(int &outNb,bool killNoSurf);
// pour recuperer les trous
// nbNest= nombre de contours
// conts= debut de chaque contour
// nesting= parent de chaque contour
Path** SubPathsWithNesting(int &outNb,bool killNoSurf,int nbNest,int* nesting,int* conts);
// surface du chemin (considere comme ferme)
double Surface();
void PolylineBoundingBox(double &l,double &t,double &r,double &b);
void FastBBox(double &l,double &t,double &r,double &b);
// longueur (totale des sous-chemins)
double Length();
void ConvertForcedToMoveTo();
void ConvertForcedToVoid();
struct cut_position {
int piece;
double t;
};
cut_position* CurvilignToPosition(int nbCv,double* cvAbs,int &nbCut);
cut_position PointToCurvilignPosition(Geom::Point const &pos, unsigned seg = 0) const;
//Should this take a cut_position as a param?
double PositionToLength(int piece, double t);
// caution: not tested on quadratic b-splines, most certainly buggy
void ConvertPositionsToMoveTo(int nbPos,cut_position* poss);
void ConvertPositionsToForced(int nbPos,cut_position* poss);
void Affiche();
char *svg_dump_path() const;
bool IsLineSegment(int piece);
private:
// utilitary functions for the path construction
void CancelBezier ();
void CloseSubpath();
void InsertMoveTo (Geom::Point const &iPt,int at);
void InsertForcePoint (int at);
void InsertLineTo (Geom::Point const &iPt,int at);
void InsertArcTo (Geom::Point const &ip, double iRx, double iRy, double angle, bool iLargeArc, bool iClockwise,int at);
void InsertCubicTo (Geom::Point const &ip, Geom::Point const &iStD, Geom::Point const &iEnD,int at);
void InsertBezierTo (Geom::Point const &iPt,int iNb,int at);
void InsertIntermBezierTo (Geom::Point const &iPt,int at);
// creation of dashes: take the polyline given by spP (length spL) and dash it according to head, body, etc. put the result in
// the polyline of this instance
void DashSubPath(int spL, int spP, std::vector<path_lineto> const &orig_pts, float head,float tail,float body,int nbD, const float dashs[],bool stPlain,float stOffset);
// Functions used by the conversion.
// they append points to the polyline
/**
* The function is quite similar to RecCubicTo. Some of the maths, specially that in
* ArcAnglesAndCenter is too cryptic and I have not spent enough time deriving it yet either. The
* important thing is how the Arc is split into line segments and that I can explain. Given the
* threshold and the two radii, a maximum angle is calculated. This angle is a measure of how big
* a sub-arc you can substitute with a line segment without breaking the threshold. Then you
* divide the whole arc into sectors such that each one's angle is under or equal to maximum
* angle.
*
* @image html livarot-images/arc-threshold.svg
*
* In this image, the red dashed arc is the actual arc that was to be approximated. The blue arcs
* are sectors, each one having an angle equal to or smaller than maximum angle (which is 20
* degrees) in this example. The final polyline approximation is shown by the pink dotted line
* segments.
*
* TODO: Understand the maths in ArcAnglesAndCenter and how the maximum angle is calculated.
*
*/
void DoArc ( Geom::Point const &iS, Geom::Point const &iE, double rx, double ry,
double angle, bool large, bool wise, double tresh);
/**
* Approximate the passed cubic bezier with line segments.
*
* Basically the function checks if the passed cubic Bezier is "small enough" and if
* it is, it does nothing, if it however isn't "small enough", it splits the cubic Bezier
* curve into two cubic Bezier curves (split at mid point), recursively calls itself on the
* left cubic, add the midpoint to the polyline approximation, call itself on the right
* cubic and done. lev is the maximum recursion depth possible, once it's reached, the function
* returns doing nothing immediately. See the code to understand more about maxL.
*
* The way the algorithm checks if the curve is "small enough" is maths so I'll try to
* explain it here so you can see the equations printed and probably refer it when reading code.
*
* Let \f$\vec{p_{0}}\f$, \f$\vec{p_{1}}\f$, \f$\vec{p_{2}}\f$ and \f$\vec{p_{3}}\f$ be the four
* points that define a cubic Bezier. The first is the start point, last is the end point,
* the two in between are the control points. Given this let me relate these points to the
* arguments that were passed in.
*
* \f[ \vec{iS} = \vec{p_{0}}\f]
* \f[ \vec{iE} = \vec{p_{3}}\f]
* \f[ \vec{iSd} = 3 (\vec{p_{1}} - \vec{p_{0}})\f]
* \f[ \vec{iEd} = 3 (\vec{p_{3}} - \vec{p_{2}})\f]
*
* This is just how livarot represents a Cubic Bezier, nothing I can do about that. The code
* starts by calculating a vector from start point to end point.
*
* \f[ \vec{se} = \vec{iE} - \vec{iS} ]\f
*
* If the length of \f$\vec{se}\f$ is smaller than 0.01, then the cubic bezier's endpoints are
* kinda close, but if the control points are too far away, it can still be a long tall curve,
* so let's see the control points and see how far away they are from the \f$\vec{se}\f$ vector.
* To do that, we measure the lengths of \f$\vec{iSd}\f$ and \f$\vec{iEd}\f$. If both are below
* threshold, we return immediately since it indicates the cubic bezier is "small enough".
*
* if the length is greater than 0.01, we still check the y projections of the control handles
* on the line between start and end points, if these projections are limited by the threshold
* and we didn't mess up the maxL restriction, we are good.
*
* If we ran out of recursion levels, we return anyways. In case this cubic bezier isn't small
* enough, we split it in two parts. There are math equations in the code that do this and I
* spent hours deriving it and they are totally correct. Basically take the usual maths to split
* a cubic Bezier into two parts and just account for the factor of 3 in the control handles
* that livarot adds and you'll end up with correct equations.
*
* TODO: Add derivation here maybe?
*
*/
void RecCubicTo ( Geom::Point const &iS, Geom::Point const &iSd, Geom::Point const &iE, Geom::Point const &iEd, double tresh, int lev,
double maxL = -1.0);
void RecBezierTo ( Geom::Point const &iPt, Geom::Point const &iS, Geom::Point const &iE, double treshhold, int lev, double maxL = -1.0);
void DoArc ( Geom::Point const &iS, Geom::Point const &iE, double rx, double ry,
double angle, bool large, bool wise, double tresh, int piece);
void RecCubicTo ( Geom::Point const &iS, Geom::Point const &iSd, Geom::Point const &iE, Geom::Point const &iEd, double tresh, int lev,
double st, double et, int piece);
void RecBezierTo ( Geom::Point const &iPt, Geom::Point const &iS, const Geom::Point &iE, double treshhold, int lev, double st, double et,
int piece);
// don't pay attention
struct offset_orig
{
Path *orig;
int piece;
double tSt, tEn;
double off_dec;
};
void DoArc ( Geom::Point const &iS, Geom::Point const &iE, double rx, double ry,
double angle, bool large, bool wise, double tresh, int piece,
offset_orig & orig);
void RecCubicTo ( Geom::Point const &iS, Geom::Point const &iSd, Geom::Point const &iE, Geom::Point const &iEd, double tresh, int lev,
double st, double et, int piece, offset_orig & orig);
void RecBezierTo ( Geom::Point const &iPt, Geom::Point const &iS, Geom::Point const &iE, double treshhold, int lev, double st, double et,
int piece, offset_orig & orig);
static void ArcAngles ( Geom::Point const &iS, Geom::Point const &iE, double rx,
double ry, double angle, bool large, bool wise,
double &sang, double &eang);
static void QuadraticPoint (double t, Geom::Point &oPt, Geom::Point const &iS, Geom::Point const &iM, Geom::Point const &iE);
static void CubicTangent (double t, Geom::Point &oPt, Geom::Point const &iS,
Geom::Point const &iSd, Geom::Point const &iE,
Geom::Point const &iEd);
struct outline_callback_data
{
Path *orig;
int piece;
double tSt, tEn;
Path *dest;
double x1, y1, x2, y2;
union
{
struct
{
double dx1, dy1, dx2, dy2;
}
c;
struct
{
double mx, my;
}
b;
struct
{
double rx, ry, angle;
bool clock, large;
double stA, enA;
}
a;
}
d;
};
typedef void (outlineCallback) (outline_callback_data * data, double tol, double width);
struct outline_callbacks
{
outlineCallback *cubicto;
outlineCallback *bezierto;
outlineCallback *arcto;
};
void SubContractOutline (int off, int num_pd,
Path * dest, outline_callbacks & calls,
double tolerance, double width, JoinType join,
ButtType butt, double miter, bool closeIfNeeded,
bool skipMoveto, Geom::Point & lastP, Geom::Point & lastT);
void DoStroke(int off, int N, Shape *dest, bool doClose, double width, JoinType join,
ButtType butt, double miter, bool justAdd = false);
static void TangentOnSegAt(double at, Geom::Point const &iS, PathDescrLineTo const &fin,
Geom::Point &pos, Geom::Point &tgt, double &len);
static void TangentOnArcAt(double at, Geom::Point const &iS, PathDescrArcTo const &fin,
Geom::Point &pos, Geom::Point &tgt, double &len, double &rad);
static void TangentOnCubAt (double at, Geom::Point const &iS, PathDescrCubicTo const &fin, bool before,
Geom::Point &pos, Geom::Point &tgt, double &len, double &rad);
static void TangentOnBezAt (double at, Geom::Point const &iS,
PathDescrIntermBezierTo & mid,
PathDescrBezierTo & fin, bool before,
Geom::Point & pos, Geom::Point & tgt, double &len, double &rad);
static void OutlineJoin (Path * dest, Geom::Point pos, Geom::Point stNor, Geom::Point enNor,
double width, JoinType join, double miter, int nType);
static bool IsNulCurve (std::vector<PathDescr*> const &cmd, int curD, Geom::Point const &curX);
static void RecStdCubicTo (outline_callback_data * data, double tol,
double width, int lev);
static void StdCubicTo (outline_callback_data * data, double tol,
double width);
static void StdBezierTo (outline_callback_data * data, double tol,
double width);
static void RecStdArcTo (outline_callback_data * data, double tol,
double width, int lev);
static void StdArcTo (outline_callback_data * data, double tol, double width);
// fonctions annexes pour le stroke
static void DoButt (Shape * dest, double width, ButtType butt, Geom::Point pos,
Geom::Point dir, int &leftNo, int &rightNo);
static void DoJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
Geom::Point prev, Geom::Point next, double miter, double prevL,
double nextL, int *stNo, int *enNo);
static void DoLeftJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
Geom::Point prev, Geom::Point next, double miter, double prevL,
double nextL, int &leftStNo, int &leftEnNo,int pathID=-1,int pieceID=0,double tID=0.0);
static void DoRightJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
Geom::Point prev, Geom::Point next, double miter, double prevL,
double nextL, int &rightStNo, int &rightEnNo,int pathID=-1,int pieceID=0,double tID=0.0);
static void RecRound (Shape * dest, int sNo, int eNo,
Geom::Point const &iS, Geom::Point const &iE,
Geom::Point const &nS, Geom::Point const &nE,
Geom::Point &origine,float width);
/**
* Simpilfy a sequence of points.
*
* Fit cubic Bezier patches on the sequence of points defined by the passed parameters. This
* sequence is just a subset of the polyline approximation points stored in the Path object.
*
* @param off The offset to the first point to process.
* @param N The total number of points in the sequence.
* @param threshhold The threshold to respect during simplification. The higher this number is,
* the more relaxed you're making the simplifier. The smaller the number, the more strict you're
* making the simplifier.
*/
void DoSimplify(int off, int N, double treshhold);
/**
* Fit a cubic Bezier patch on the sequence of points.
*
* @param off The index of the first point of the sequence to fit on.
* @param N The total number of points you want to fit on.
* @param res Reference to the Cubic Bezier description where the resulting control points will
* be stored.
* @param worstP Reference to a point index. This will be changed to whichever point measures the
* highest deviation from the fitted curve.
*
* @return True if the fit respected threshold, false otherwise.
*/
bool AttemptSimplify(int off, int N, double treshhold, PathDescrCubicTo &res, int &worstP);
/*
* The actual fitting code that takes a sequence and fits stuff on it.
*
* Totally based on the algorithm from:
* http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/INT-APP/CURVE-APP-global.html
*
* @param start The start point of the cubic bezier which is already known.
* @param res The cubic Bezier path command that function will populate after doing the maths.
* @param Xk The array of X coordinates of the point to fit.
* @param Yk The array of Y coordinates of the point to fit.
* @param Qk An array to store some intermediate values.
* @param tk The time values for the points.
* @param nbPt The total points to fit on.
*
* @return True if the fit was done correctly, false if something bad happened. (Non-invertible
* matrix).
*/
static bool FitCubic(Geom::Point const &start,
PathDescrCubicTo &res,
double *Xk, double *Yk, double *Qk, double *tk, int nbPt);
/**
* Structure to keep some data for fitting.
*
* Note that the pointers in the structure are going to store arrays. The comments explain what
* each element of a particular array stores. Also note that the length mentioned in the comment
* for tk and lk is not the straight line distance but the length as measured by walking on the
* line segments connecting the points.
*/
struct fitting_tables {
int nbPt; /*!< The points to fit on in a particular iteration */
int maxPt; /*!< Maximum number of points these arrays here can store */
int inPt; /*!< Total points whose X, Y, lk are all populated here */
double *Xk; /*!< X coordinate of the point */
double *Yk; /*!< Y coordinate of the point */
double *Qk; /*!< A special value needed by the fitting algorithm */
double *tk; /*!< A number between 0 and 1 that is the fraction (length b/w first point to this point along the line segments)/(total length) */
double *lk; /*!< Length of the line segment from the previous point to this point */
char *fk; /*!< A flag if 0x01 indicates forced point and if 0x00 indicates a normal point */
double totLen; /*!< Total length of the polyline or you can say the sum of lengths of all line segments. */
};
/**
* Fit Cubic Bezier patch using the fitting table data.
*
* @param data The fitting_tables data needed for fitting. ExtendFit sets that up for this
* function.
* @param threshhold The threshold to respect.
* @param res The cubic Bezier command which this function will populate.
* @param worstP The point with the worst error.
*/
bool AttemptSimplify (fitting_tables &data,double treshhold, PathDescrCubicTo & res,int &worstP);
/**
* Fit Cubic Bezier patch on the points.
*
* This uses data already calculated by probably the same function if it exists.
* The data that's reused is apparently the X, Y and lk values. However, I think there is a
* problem with this caching mechanism. See the inline comments of ExtendFit.
*
* This function prepares data in fitting tables and calls the AttemptSimplify version that takes
* fitting_tables data.
*
* @param off The offset to the first point.
* @param N The total number of points in that sequence.
* @param data The data structure which keeps data saved for later use by the same function.
* @param threshhold The threshold to respect.
* @param res cubic Bezier path command where this function will store the control point handles.
* @param worstP Function will set this to the point with the worst error.
*
* @return True if the threshold was respected, otherwise false.
*/
bool ExtendFit(int off, int N, fitting_tables &data,double treshhold, PathDescrCubicTo & res,int &worstP);
/**
* Peform an iteration of Newton-Raphson to improve t values.
*
* TODO: Place derivation here with embedded latex maybe.
*/
double RaffineTk (Geom::Point pt, Geom::Point p0, Geom::Point p1, Geom::Point p2, Geom::Point p3, double it);
void FlushPendingAddition(Path* dest,PathDescr *lastAddition,PathDescrCubicTo &lastCubic,int lastAD);
private:
/**
* Add a Geom::Curve's equivalent path description.
*
* Any straight curve (line or otherwise that's straight) is added as line. CubicBezier
* and EllipticalArcs are handled manually, while any other Geom::Curve type is handled by
* converting to cubic beziers using Geom::cubicbezierpath_from_sbasis and recursively calling
* the same function.
*
* There is one special reason for using is_straight_curve to figure out if a CubicBezier is
* actually a line and making sure that it is added as a line not as a straight line CubicBezier
* (a CubicBezier with control points being the same as end points). Sometimes when you're drawing
* straight line segments with the Bezier (pen) tool, Inkscape would place a straight CubicBezier
* instead of a line segment. The call to Path::Convert or Path::ConvertWithBackData would break
* up this line segment into smaller line segments which is not what we want (we want it to break
* only real curves) not curves that are actually just straight lines.
*
* @param c The Geom::Curve whose path description to create/add.
*/
void AddCurve(Geom::Curve const &c);
};
#endif
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
|