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