7089d2bf6b285b28f3594a04ec42e25a5eeda02d
[platform/core/uifw/lottie-player.git] / src / vector / vpath.cpp
1 #include "vpath.h"
2 #include <cassert>
3 #include <vector>
4 #include "vbezier.h"
5 #include "vdebug.h"
6 #include "vrect.h"
7
8 V_BEGIN_NAMESPACE
9
10 VPath::VPathData::VPathData()
11     : m_points(), m_elements(), m_segments(0), mStartPoint(), mNewSegment(true)
12 {
13 }
14
15 VPath::VPathData::VPathData(const VPathData &o)
16     : m_points(o.m_points),
17       m_elements(o.m_elements),
18       m_segments(o.m_segments),
19       mStartPoint(o.mStartPoint),
20       mNewSegment(o.mNewSegment)
21 {
22 }
23
24 void VPath::VPathData::transform(const VMatrix &m)
25 {
26     for (auto &i : m_points) {
27         i = m.map(i);
28     }
29 }
30
31 float VPath::VPathData::length() const
32 {
33     float len = 0.0;
34     int   i = 0;
35     for (auto e : m_elements) {
36         switch (e) {
37         case VPath::Element::MoveTo:
38             i++;
39             break;
40         case VPath::Element::LineTo: {
41             VPointF p0 = m_points[i - 1];
42             VPointF p = m_points[i++];
43             VBezier b = VBezier::fromPoints(p0, p0, p, p);
44             len += b.length();
45             break;
46         }
47         case VPath::Element::CubicTo: {
48             VPointF p0 = m_points[i - 1];
49             VPointF p = m_points[i++];
50             VPointF p1 = m_points[i++];
51             VPointF p2 = m_points[i++];
52             VBezier b = VBezier::fromPoints(p0, p, p1, p2);
53             len += b.length();
54             break;
55         }
56         case VPath::Element::Close:
57             break;
58         }
59     }
60
61     return len;
62 }
63
64 void VPath::VPathData::checkNewSegment()
65 {
66     if (mNewSegment) {
67         moveTo(0, 0);
68         mNewSegment = false;
69     }
70 }
71
72 void  VPath::VPathData::moveTo(float x, float y)
73 {
74     mStartPoint = {x, y};
75     mNewSegment = false;
76     m_elements.emplace_back(VPath::Element::MoveTo);
77     m_points.emplace_back(x,y);
78     m_segments++;
79 }
80
81 void  VPath::VPathData::lineTo(float x, float y)
82 {
83     checkNewSegment();
84     m_elements.emplace_back(VPath::Element::LineTo);
85     m_points.emplace_back(x,y);
86 }
87
88 void  VPath::VPathData::cubicTo(float cx1, float cy1, float cx2, float cy2,
89                                 float ex, float ey)
90 {
91     checkNewSegment();
92     m_elements.emplace_back(VPath::Element::CubicTo);
93     m_points.emplace_back(cx1, cy1);
94     m_points.emplace_back(cx2, cy2);
95     m_points.emplace_back(ex, ey);
96 }
97
98 void VPath::VPathData::close()
99 {
100     if (isEmpty()) return;
101
102     const VPointF &lastPt = m_points.back();
103     if (!fuzzyCompare(mStartPoint, lastPt)) {
104         lineTo(mStartPoint.x(), mStartPoint.y());
105     }
106     m_elements.push_back(VPath::Element::Close);
107     mNewSegment = true;
108 }
109
110 void VPath::VPathData::reset()
111 {
112     if (isEmpty()) return;
113
114     m_elements.clear();
115     m_points.clear();
116     m_segments = 0;
117 }
118
119 int VPath::VPathData::segments() const
120 {
121     return m_segments;
122 }
123
124 void VPath::VPathData::reserve(int pts, int elms)
125 {
126     if (m_points.capacity() < m_points.size() + pts)
127         m_points.reserve(m_points.size() + pts);
128     if (m_elements.capacity() < m_elements.size() + elms)
129         m_elements.reserve(m_elements.size() + elms);
130 }
131
132 static VPointF curvesForArc(const VRectF &, float, float, VPointF *, int *);
133 static constexpr float PATH_KAPPA = 0.5522847498;
134
135 void VPath::VPathData::arcTo(const VRectF &rect, float startAngle,
136                              float sweepLength, bool forceMoveTo)
137 {
138     int     point_count = 0;
139     VPointF pts[15];
140     VPointF curve_start =
141         curvesForArc(rect, startAngle, sweepLength, pts, &point_count);
142
143     reserve(point_count + 1, point_count / 3 + 1);
144     if (isEmpty() || forceMoveTo) {
145         moveTo(curve_start.x(), curve_start.y());
146     } else {
147         lineTo(curve_start.x(), curve_start.y());
148     }
149     for (int i = 0; i < point_count; i += 3) {
150         cubicTo(pts[i].x(), pts[i].y(), pts[i + 1].x(), pts[i + 1].y(),
151                 pts[i + 2].x(), pts[i + 2].y());
152     }
153 }
154
155 void VPath::VPathData::addCircle(float cx, float cy, float radius,
156                                  VPath::Direction dir)
157 {
158     addOval(VRectF(cx - radius, cy - radius, 2 * radius, 2 * radius), dir);
159 }
160
161 void VPath::VPathData::addOval(const VRectF &rect, VPath::Direction dir)
162 {
163     if (rect.isNull()) return;
164
165     float x = rect.x();
166     float y = rect.y();
167
168     float w = rect.width();
169     float w2 = rect.width() / 2;
170     float w2k = w2 * PATH_KAPPA;
171
172     float h = rect.height();
173     float h2 = rect.height() / 2;
174     float h2k = h2 * PATH_KAPPA;
175
176     reserve(14, 7);  // 1Move + 4Cubic + 1Close
177     if (dir == VPath::Direction::CW) {
178         // moveto 12 o'clock.
179         moveTo(x + w2, y);
180         // 12 -> 3 o'clock
181         cubicTo(x + w2 + w2k, y, x + w, y + h2 - h2k,
182                 x + w, y + h2);
183         // 3 -> 6 o'clock
184         cubicTo(x + w, y + h2 + h2k, x + w2 + w2k, y + h,
185                 x + w2, y + h);
186         // 6 -> 9 o'clock
187         cubicTo(x + w2 - w2k, y + h, x, y + h2 + h2k,
188                 x, y + h2);
189         // 9 -> 12 o'clock
190         cubicTo(x, y + h2 - h2k, x + w2 - w2k, y,
191                 x + w2, y);
192     } else {
193         // moveto 12 o'clock.
194         moveTo(x + w2, y);
195         // 12 -> 9 o'clock
196         cubicTo(x + w2 - w2k, y, x, y + h2 - h2k,
197                 x, y + h2);
198         // 9 -> 6 o'clock
199         cubicTo(x, y + h2 + h2k, x + w2 - w2k, y + h,
200                 x + w2, y + h);
201         // 6 -> 3 o'clock
202         cubicTo(x + w2 + w2k, y + h, x + w, y + h2 + h2k,
203                 x + w, y + h2);
204         // 3 -> 12 o'clock
205         cubicTo(x + w, y + h2 - h2k, x + w2 + w2k, y,
206                 x + w2, y);
207     }
208     close();
209 }
210
211 void VPath::VPathData::addRect(const VRectF &rect, VPath::Direction dir)
212 {
213     if (rect.isNull()) return;
214
215     float x = rect.x();
216     float y = rect.y();
217     float w = rect.width();
218     float h = rect.height();
219
220     reserve(6, 6);  // 1Move + 4Line + 1Close
221     if (dir == VPath::Direction::CW) {
222         moveTo(x + w, y);
223         lineTo(x + w, y + h);
224         lineTo(x, y + h);
225         lineTo(x, y);
226         close();
227     } else {
228         moveTo(x + w, y);
229         lineTo(x, y);
230         lineTo(x, y + h);
231         lineTo(x + w, y + h);
232         close();
233     }
234 }
235
236 void VPath::VPathData::addRoundRect(const VRectF &rect, float rx, float ry,
237                                     VPath::Direction dir)
238 {
239     if (vCompare(rx, 0.f) || vCompare(ry, 0.f)) {
240         addRect(rect, dir);
241         return;
242     }
243
244     float x = rect.x();
245     float y = rect.y();
246     float w = rect.width();
247     float h = rect.height();
248     // clamp the rx and ry radius value.
249     rx = 2 * rx;
250     ry = 2 * ry;
251     if (rx > w) rx = w;
252     if (ry > h) ry = h;
253
254     reserve(14, 7);  // 1Move + 4Cubic + 1Close
255     if (dir == VPath::Direction::CW) {
256         moveTo(x + w, y + ry / 2.f);
257         arcTo(VRectF(x + w - rx, y + h - ry, rx, ry), 0, -90, false);
258         arcTo(VRectF(x, y + h - ry, rx, ry), -90, -90, false);
259         arcTo(VRectF(x, y, rx, ry), -180, -90, false);
260         arcTo(VRectF(x + w - rx, y, rx, ry), -270, -90, false);
261         close();
262     } else {
263         moveTo(x + w, y + ry / 2.f);
264         arcTo(VRectF(x + w - rx, y, rx, ry), 0, 90, false);
265         arcTo(VRectF(x, y, rx, ry), 90, 90, false);
266         arcTo(VRectF(x, y + h - ry, rx, ry), 180, 90, false);
267         arcTo(VRectF(x + w - rx, y + h - ry, rx, ry), 270, 90, false);
268         close();
269     }
270 }
271
272 static float tForArcAngle(float angle);
273 void         findEllipseCoords(const VRectF &r, float angle, float length,
274                                VPointF *startPoint, VPointF *endPoint)
275 {
276     if (r.isNull()) {
277         if (startPoint) *startPoint = VPointF();
278         if (endPoint) *endPoint = VPointF();
279         return;
280     }
281
282     float w2 = r.width() / 2;
283     float h2 = r.height() / 2;
284
285     float    angles[2] = {angle, angle + length};
286     VPointF *points[2] = {startPoint, endPoint};
287
288     for (int i = 0; i < 2; ++i) {
289         if (!points[i]) continue;
290
291         float theta = angles[i] - 360 * floor(angles[i] / 360);
292         float t = theta / 90;
293         // truncate
294         int quadrant = int(t);
295         t -= quadrant;
296
297         t = tForArcAngle(90 * t);
298
299         // swap x and y?
300         if (quadrant & 1) t = 1 - t;
301
302         float a, b, c, d;
303         VBezier::coefficients(t, a, b, c, d);
304         VPointF p(a + b + c * PATH_KAPPA, d + c + b * PATH_KAPPA);
305
306         // left quadrants
307         if (quadrant == 1 || quadrant == 2) p.rx() = -p.x();
308
309         // top quadrants
310         if (quadrant == 0 || quadrant == 1) p.ry() = -p.y();
311
312         *points[i] = r.center() + VPointF(w2 * p.x(), h2 * p.y());
313     }
314 }
315
316 static float tForArcAngle(float angle)
317 {
318     float radians, cos_angle, sin_angle, tc, ts, t;
319
320     if (vCompare(angle, 0.f)) return 0;
321     if (vCompare(angle, 90.0f)) return 1;
322
323     radians = (angle / 180) * M_PI;
324
325     cos_angle = cos(radians);
326     sin_angle = sin(radians);
327
328     // initial guess
329     tc = angle / 90;
330
331     // do some iterations of newton's method to approximate cos_angle
332     // finds the zero of the function b.pointAt(tc).x() - cos_angle
333     tc -= ((((2 - 3 * PATH_KAPPA) * tc + 3 * (PATH_KAPPA - 1)) * tc) * tc + 1 -
334            cos_angle)  // value
335           / (((6 - 9 * PATH_KAPPA) * tc + 6 * (PATH_KAPPA - 1)) *
336              tc);  // derivative
337     tc -= ((((2 - 3 * PATH_KAPPA) * tc + 3 * (PATH_KAPPA - 1)) * tc) * tc + 1 -
338            cos_angle)  // value
339           / (((6 - 9 * PATH_KAPPA) * tc + 6 * (PATH_KAPPA - 1)) *
340              tc);  // derivative
341
342     // initial guess
343     ts = tc;
344     // do some iterations of newton's method to approximate sin_angle
345     // finds the zero of the function b.pointAt(tc).y() - sin_angle
346     ts -= ((((3 * PATH_KAPPA - 2) * ts - 6 * PATH_KAPPA + 3) * ts +
347             3 * PATH_KAPPA) *
348                ts -
349            sin_angle) /
350           (((9 * PATH_KAPPA - 6) * ts + 12 * PATH_KAPPA - 6) * ts +
351            3 * PATH_KAPPA);
352     ts -= ((((3 * PATH_KAPPA - 2) * ts - 6 * PATH_KAPPA + 3) * ts +
353             3 * PATH_KAPPA) *
354                ts -
355            sin_angle) /
356           (((9 * PATH_KAPPA - 6) * ts + 12 * PATH_KAPPA - 6) * ts +
357            3 * PATH_KAPPA);
358
359     // use the average of the t that best approximates cos_angle
360     // and the t that best approximates sin_angle
361     t = 0.5 * (tc + ts);
362     return t;
363 }
364
365 // The return value is the starting point of the arc
366 static VPointF curvesForArc(const VRectF &rect, float startAngle,
367                             float sweepLength, VPointF *curves,
368                             int *point_count)
369 {
370     if (rect.isNull()) {
371         return VPointF();
372     }
373
374     float x = rect.x();
375     float y = rect.y();
376
377     float w = rect.width();
378     float w2 = rect.width() / 2;
379     float w2k = w2 * PATH_KAPPA;
380
381     float h = rect.height();
382     float h2 = rect.height() / 2;
383     float h2k = h2 * PATH_KAPPA;
384
385     VPointF points[16] = {
386         // start point
387         VPointF(x + w, y + h2),
388
389         // 0 -> 270 degrees
390         VPointF(x + w, y + h2 + h2k), VPointF(x + w2 + w2k, y + h),
391         VPointF(x + w2, y + h),
392
393         // 270 -> 180 degrees
394         VPointF(x + w2 - w2k, y + h), VPointF(x, y + h2 + h2k),
395         VPointF(x, y + h2),
396
397         // 180 -> 90 degrees
398         VPointF(x, y + h2 - h2k), VPointF(x + w2 - w2k, y), VPointF(x + w2, y),
399
400         // 90 -> 0 degrees
401         VPointF(x + w2 + w2k, y), VPointF(x + w, y + h2 - h2k),
402         VPointF(x + w, y + h2)};
403
404     if (sweepLength > 360)
405         sweepLength = 360;
406     else if (sweepLength < -360)
407         sweepLength = -360;
408
409     // Special case fast paths
410     if (startAngle == 0.0) {
411         if (sweepLength == 360.0) {
412             for (int i = 11; i >= 0; --i) curves[(*point_count)++] = points[i];
413             return points[12];
414         } else if (sweepLength == -360.0) {
415             for (int i = 1; i <= 12; ++i) curves[(*point_count)++] = points[i];
416             return points[0];
417         }
418     }
419
420     int startSegment = int(floor(startAngle / 90));
421     int endSegment = int(floor((startAngle + sweepLength) / 90));
422
423     float startT = (startAngle - startSegment * 90) / 90;
424     float endT = (startAngle + sweepLength - endSegment * 90) / 90;
425
426     int delta = sweepLength > 0 ? 1 : -1;
427     if (delta < 0) {
428         startT = 1 - startT;
429         endT = 1 - endT;
430     }
431
432     // avoid empty start segment
433     if (vIsZero(startT - float(1))) {
434         startT = 0;
435         startSegment += delta;
436     }
437
438     // avoid empty end segment
439     if (vIsZero(endT)) {
440         endT = 1;
441         endSegment -= delta;
442     }
443
444     startT = tForArcAngle(startT * 90);
445     endT = tForArcAngle(endT * 90);
446
447     const bool splitAtStart = !vIsZero(startT);
448     const bool splitAtEnd = !vIsZero(endT - float(1));
449
450     const int end = endSegment + delta;
451
452     // empty arc?
453     if (startSegment == end) {
454         const int quadrant = 3 - ((startSegment % 4) + 4) % 4;
455         const int j = 3 * quadrant;
456         return delta > 0 ? points[j + 3] : points[j];
457     }
458
459     VPointF startPoint, endPoint;
460     findEllipseCoords(rect, startAngle, sweepLength, &startPoint, &endPoint);
461
462     for (int i = startSegment; i != end; i += delta) {
463         const int quadrant = 3 - ((i % 4) + 4) % 4;
464         const int j = 3 * quadrant;
465
466         VBezier b;
467         if (delta > 0)
468             b = VBezier::fromPoints(points[j + 3], points[j + 2], points[j + 1],
469                                     points[j]);
470         else
471             b = VBezier::fromPoints(points[j], points[j + 1], points[j + 2],
472                                     points[j + 3]);
473
474         // empty arc?
475         if (startSegment == endSegment && vCompare(startT, endT))
476             return startPoint;
477
478         if (i == startSegment) {
479             if (i == endSegment && splitAtEnd)
480                 b = b.onInterval(startT, endT);
481             else if (splitAtStart)
482                 b = b.onInterval(startT, 1);
483         } else if (i == endSegment && splitAtEnd) {
484             b = b.onInterval(0, endT);
485         }
486
487         // push control points
488         curves[(*point_count)++] = b.pt2();
489         curves[(*point_count)++] = b.pt3();
490         curves[(*point_count)++] = b.pt4();
491     }
492
493     curves[*(point_count)-1] = endPoint;
494
495     return startPoint;
496 }
497
498 void VPath::VPathData::addPolystar(float points, float innerRadius,
499                                    float outerRadius, float innerRoundness,
500                                    float outerRoundness, float startAngle,
501                                    float cx, float cy, VPath::Direction dir)
502 {
503     const static float POLYSTAR_MAGIC_NUMBER = 0.47829 / 0.28;
504     float              currentAngle = (startAngle - 90.0) * M_PI / 180.0;
505     float              x;
506     float              y;
507     float              previousX;
508     float              previousY;
509     float              partialPointRadius = 0;
510     float              anglePerPoint = (float)(2.0 * M_PI / points);
511     float              halfAnglePerPoint = anglePerPoint / 2.0;
512     float              partialPointAmount = points - (int)points;
513     bool               longSegment = false;
514     int                numPoints = (int)ceil(points) * 2.0;
515     float              angleDir = ((dir == VPath::Direction::CW) ? 1.0 : -1.0);
516     bool               hasRoundness = false;
517
518     innerRoundness /= 100.0;
519     outerRoundness /= 100.0;
520
521     if (partialPointAmount != 0) {
522         currentAngle +=
523             halfAnglePerPoint * (1.0 - partialPointAmount) * angleDir;
524     }
525
526     if (partialPointAmount != 0) {
527         partialPointRadius =
528             innerRadius + partialPointAmount * (outerRadius - innerRadius);
529         x = (float)(partialPointRadius * cos(currentAngle));
530         y = (float)(partialPointRadius * sin(currentAngle));
531         currentAngle += anglePerPoint * partialPointAmount / 2.0 * angleDir;
532     } else {
533         x = (float)(outerRadius * cos(currentAngle));
534         y = (float)(outerRadius * sin(currentAngle));
535         currentAngle += halfAnglePerPoint * angleDir;
536     }
537
538     if (vIsZero(innerRoundness) && vIsZero(outerRoundness)) {
539         reserve(numPoints + 2, numPoints + 3);
540     } else {
541         reserve(numPoints * 3 + 2, numPoints + 3);
542         hasRoundness = true;
543     }
544
545     moveTo(x + cx, y + cy);
546
547     for (int i = 0; i < numPoints; i++) {
548         float radius = longSegment ? outerRadius : innerRadius;
549         float dTheta = halfAnglePerPoint;
550         if (partialPointRadius != 0 && i == numPoints - 2) {
551             dTheta = anglePerPoint * partialPointAmount / 2.0;
552         }
553         if (partialPointRadius != 0 && i == numPoints - 1) {
554             radius = partialPointRadius;
555         }
556         previousX = x;
557         previousY = y;
558         x = (float)(radius * cos(currentAngle));
559         y = (float)(radius * sin(currentAngle));
560
561         if (hasRoundness) {
562             float cp1Theta =
563                 (float)(atan2(previousY, previousX) - M_PI / 2.0 * angleDir);
564             float cp1Dx = (float)cos(cp1Theta);
565             float cp1Dy = (float)sin(cp1Theta);
566             float cp2Theta = (float)(atan2(y, x) - M_PI / 2.0 * angleDir);
567             float cp2Dx = (float)cos(cp2Theta);
568             float cp2Dy = (float)sin(cp2Theta);
569
570             float cp1Roundness = longSegment ? innerRoundness : outerRoundness;
571             float cp2Roundness = longSegment ? outerRoundness : innerRoundness;
572             float cp1Radius = longSegment ? innerRadius : outerRadius;
573             float cp2Radius = longSegment ? outerRadius : innerRadius;
574
575             float cp1x = cp1Radius * cp1Roundness * POLYSTAR_MAGIC_NUMBER *
576                          cp1Dx / points;
577             float cp1y = cp1Radius * cp1Roundness * POLYSTAR_MAGIC_NUMBER *
578                          cp1Dy / points;
579             float cp2x = cp2Radius * cp2Roundness * POLYSTAR_MAGIC_NUMBER *
580                          cp2Dx / points;
581             float cp2y = cp2Radius * cp2Roundness * POLYSTAR_MAGIC_NUMBER *
582                          cp2Dy / points;
583
584             if ((partialPointAmount != 0) &&
585                 ((i == 0) || (i == numPoints - 1))) {
586                 cp1x *= partialPointAmount;
587                 cp1y *= partialPointAmount;
588                 cp2x *= partialPointAmount;
589                 cp2y *= partialPointAmount;
590             }
591
592             cubicTo(previousX - cp1x + cx, previousY - cp1y + cy,
593                     x + cp2x + cx, y + cp2y + cy,
594                     x + cx, y + cy);
595         } else {
596             lineTo(x + cx, y + cy);
597         }
598
599         currentAngle += dTheta * angleDir;
600         longSegment = !longSegment;
601     }
602
603     close();
604 }
605
606 void VPath::VPathData::addPolygon(float points, float radius, float roundness,
607                                   float startAngle, float cx, float cy,
608                                   VPath::Direction dir)
609 {
610     // TODO: Need to support floating point number for number of points
611     const static float POLYGON_MAGIC_NUMBER = 0.25;
612     float              currentAngle = (startAngle - 90.0) * M_PI / 180.0;
613     float              x;
614     float              y;
615     float              previousX;
616     float              previousY;
617     float              anglePerPoint = (float)(2.0 * M_PI / floor(points));
618     int                numPoints = (int)floor(points);
619     float              angleDir = ((dir == VPath::Direction::CW) ? 1.0 : -1.0);
620     bool               hasRoundness = false;
621
622     roundness /= 100.0;
623
624     currentAngle = (currentAngle - 90.0) * M_PI / 180.0;
625     x = (float)(radius * cos(currentAngle));
626     y = (float)(radius * sin(currentAngle));
627     currentAngle += anglePerPoint * angleDir;
628
629     if (vIsZero(roundness)) {
630         reserve(numPoints + 2, numPoints + 3);
631     } else {
632         reserve(numPoints * 3 + 2, numPoints + 3);
633         hasRoundness = true;
634     }
635
636     moveTo(x + cx, y + cy);
637
638     for (int i = 0; i < numPoints; i++) {
639         previousX = x;
640         previousY = y;
641         x = (float)(radius * cos(currentAngle));
642         y = (float)(radius * sin(currentAngle));
643
644         if (hasRoundness) {
645             float cp1Theta =
646                 (float)(atan2(previousY, previousX) - M_PI / 2.0 * angleDir);
647             float cp1Dx = (float)cos(cp1Theta);
648             float cp1Dy = (float)sin(cp1Theta);
649             float cp2Theta = (float)(atan2(y, x) - M_PI / 2.0 * angleDir);
650             float cp2Dx = (float)cos(cp2Theta);
651             float cp2Dy = (float)sin(cp2Theta);
652
653             float cp1x = radius * roundness * POLYGON_MAGIC_NUMBER * cp1Dx;
654             float cp1y = radius * roundness * POLYGON_MAGIC_NUMBER * cp1Dy;
655             float cp2x = radius * roundness * POLYGON_MAGIC_NUMBER * cp2Dx;
656             float cp2y = radius * roundness * POLYGON_MAGIC_NUMBER * cp2Dy;
657
658             cubicTo(previousX - cp1x + cx, previousY - cp1y + cy,
659                     x + cp2x + cx, y + cp2y + cy, x, y);
660         } else {
661             lineTo(x + cx, y + cy);
662         }
663
664         currentAngle += anglePerPoint * angleDir;
665     }
666
667     close();
668 }
669
670 void VPath::VPathData::addPath(const VPathData &path)
671 {
672     int segment = path.segments();
673
674     // make sure enough memory available
675     if (m_points.capacity() < m_points.size() + path.m_points.size())
676         m_points.reserve(m_points.size() + path.m_points.size());
677
678     if (m_elements.capacity() < m_elements.size() + path.m_elements.size())
679         m_elements.reserve(m_elements.size() + path.m_elements.size());
680
681     std::copy(path.m_points.begin(), path.m_points.end(), back_inserter(m_points));
682     std::copy(path.m_elements.begin(), path.m_elements.end(), back_inserter(m_elements));
683
684     m_segments += segment;
685 }
686
687 V_END_NAMESPACE