]> Shamusworld >> Repos - architektonas/blob - src/base/arc.cpp
Bugfixes related to removing Snapper class.
[architektonas] / src / base / arc.cpp
1 // arc.cpp
2 //
3 // Part of the Architektonas Project
4 // Originally part of QCad Community Edition by Andrew Mustun
5 // Extensively rewritten and refactored by James L. Hammons
6 // Portions copyright (C) 2001-2003 RibbonSoft
7 // Copyright (C) 2010 Underground Software
8 // See the README and GPLv2 files for licensing and warranty information
9 //
10 // JLH = James L. Hammons <jlhamm@acm.org>
11 //
12 // Who  When        What
13 // ---  ----------  -----------------------------------------------------------
14 // JLH  05/28/2010  Added this text. :-)
15 // JLH  06/16/2010  Moved implementation from header file to this file
16 //
17
18 #include "arc.h"
19
20 #include "constructionline.h"
21 #include "debug.h"
22 #include "graphicview.h"
23 #include "linetypepattern.h"
24 #include "information.h"
25 #include "mathextra.h"
26 #include "paintinterface.h"
27
28 /**
29  * Default constructor.
30  */
31 Arc::Arc(EntityContainer * parent, const ArcData & d):
32         AtomicEntity(parent), data(d)
33 {
34         calculateEndpoints();
35         calculateBorders();
36 }
37
38 /*virtual*/ Arc::~Arc()
39 {
40 }
41
42 /*virtual*/ Entity * Arc::clone()
43 {
44         Arc * a = new Arc(*this);
45         a->initId();
46         return a;
47 }
48
49 /**     @return RS2::EntityArc */
50 /*virtual*/ RS2::EntityType Arc::rtti() const
51 {
52         return RS2::EntityArc;
53 }
54
55 /** @return true */
56 /*virtual*/ bool Arc::isEdge() const
57 {
58         return true;
59 }
60
61 /** @return Copy of data that defines the arc. **/
62 ArcData Arc::getData() const
63 {
64         return data;
65 }
66
67 /*virtual*/ VectorSolutions Arc::getRefPoints()
68 {
69         VectorSolutions ret(startpoint, endpoint, data.center);
70         return ret;
71 }
72
73 /** Sets new arc parameters. **/
74 void Arc::setData(ArcData d)
75 {
76         data = d;
77 }
78
79 /** @return The center point (x) of this arc */
80 Vector Arc::getCenter() const
81 {
82         return data.center;
83 }
84
85 /** Sets new center. */
86 void Arc::setCenter(const Vector & c)
87 {
88         data.center = c;
89 }
90
91 /** @return The radius of this arc */
92 double Arc::getRadius() const
93 {
94         return data.radius;
95 }
96
97 /** Sets new radius. */
98 void Arc::setRadius(double r)
99 {
100         data.radius = r;
101 }
102
103 /** @return The start angle of this arc */
104 double Arc::getAngle1() const
105 {
106         return data.angle1;
107 }
108
109 /** Sets new start angle. */
110 void Arc::setAngle1(double a1)
111 {
112         data.angle1 = a1;
113 }
114
115 /** @return The end angle of this arc */
116 double Arc::getAngle2() const
117 {
118         return data.angle2;
119 }
120
121 /** Sets new end angle. */
122 void Arc::setAngle2(double a2)
123 {
124         data.angle2 = a2;
125 }
126
127 /**
128  * @return Direction 1. The angle at which the arc starts at
129  * the startpoint.
130  */
131 double Arc::getDirection1() const
132 {
133         if (!data.reversed)
134                 return Math::correctAngle(data.angle1 + M_PI / 2.0);
135
136         return Math::correctAngle(data.angle1 - M_PI / 2.0);
137 }
138
139 /**
140  * @return Direction 2. The angle at which the arc starts at
141  * the endpoint.
142  */
143 double Arc::getDirection2() const
144 {
145         if (!data.reversed)
146                 return Math::correctAngle(data.angle2 - M_PI / 2.0);
147
148         return Math::correctAngle(data.angle2 + M_PI / 2.0);
149 }
150
151 /**
152  * @retval true if the arc is reversed (clockwise),
153  * @retval false otherwise
154  */
155 bool Arc::isReversed() const
156 {
157         return data.reversed;
158 }
159
160 /** sets the reversed status. */
161 void Arc::setReversed(bool r)
162 {
163         data.reversed = r;
164 }
165
166 /** @return Start point of the entity. */
167 /*virtual*/ Vector Arc::getStartpoint() const
168 {
169         return startpoint;
170 }
171
172 /** @return End point of the entity. */
173 /*virtual*/ Vector Arc::getEndpoint() const
174 {
175         return endpoint;
176 }
177
178 /**
179  * Creates this arc from 3 given points which define the arc line.
180  *
181  * @param p1 1st point.
182  * @param p2 2nd point.
183  * @param p3 3rd point.
184  */
185 bool Arc::createFrom3P(const Vector & p1, const Vector & p2, const Vector & p3)
186 {
187         if (p1.distanceTo(p2) > RS_TOLERANCE
188             && p2.distanceTo(p3) > RS_TOLERANCE
189             && p3.distanceTo(p1) > RS_TOLERANCE)
190         {
191                 // middle points between 3 points:
192                 Vector mp1, mp2;
193                 Vector dir1, dir2;
194                 double a1, a2;
195
196                 // intersection of two middle lines
197                 mp1 = (p1 + p2) / 2.0;
198                 a1 = p1.angleTo(p2) + M_PI / 2.0;
199                 dir1.setPolar(100.0, a1);
200                 mp2 = (p2 + p3) / 2.0;
201                 a2 = p2.angleTo(p3) + M_PI / 2.0;
202                 dir2.setPolar(100.0, a2);
203
204                 ConstructionLineData d1(mp1, mp1 + dir1);
205                 ConstructionLineData d2(mp2, mp2 + dir2);
206                 ConstructionLine midLine1(NULL, d1);
207                 ConstructionLine midLine2(NULL, d2);
208
209                 VectorSolutions sol =
210                         Information::getIntersection(&midLine1, &midLine2);
211
212                 data.center = sol.get(0);
213                 data.radius = data.center.distanceTo(p3);
214                 data.angle1 = data.center.angleTo(p1);
215                 data.angle2 = data.center.angleTo(p3);
216                 data.reversed = Math::isAngleBetween(data.center.angleTo(p2),
217                                 data.angle1, data.angle2, true);
218
219                 if (sol.get(0).valid && data.radius < 1.0e14
220                     && data.radius > RS_TOLERANCE)
221                 {
222                         calculateEndpoints();
223                         calculateBorders();
224                         return true;
225                 }
226                 else
227                 {
228                         DEBUG->print("Arc::createFrom3P(): "
229                                 "Cannot create an arc with inf radius.");
230                         return false;
231                 }
232         }
233         else
234         {
235                 DEBUG->print("Arc::createFrom3P(): "
236                         "Cannot create an arc with radius 0.0.");
237                 return false;
238         }
239 }
240
241 /**
242  * Creates an arc from its startpoint, endpoint, start direction (angle)
243  * and radius.
244  *
245  * @retval true Successfully created arc
246  * @retval false Cannot creats arc (radius to small or endpoint to far away)
247  */
248 bool Arc::createFrom2PDirectionRadius(const Vector & startPoint, const Vector & endPoint, double direction1, double radius)
249 {
250         Vector ortho;
251         ortho.setPolar(radius, direction1 + M_PI / 2.0);
252         Vector center1 = startPoint + ortho;
253         Vector center2 = startPoint - ortho;
254
255         if (center1.distanceTo(endPoint) < center2.distanceTo(endPoint))
256                 data.center = center1;
257         else
258                 data.center = center2;
259
260         data.radius = radius;
261         data.angle1 = data.center.angleTo(startPoint);
262         data.angle2 = data.center.angleTo(endPoint);
263         data.reversed = false;
264
265         double diff = Math::correctAngle(getDirection1() - direction1);
266
267         if (fabs(diff - M_PI) < 1.0e-1)
268                 data.reversed = true;
269
270         calculateEndpoints();
271         calculateBorders();
272
273         return true;
274 }
275
276 /**
277  * Creates an arc from its startpoint, endpoint and bulge.
278  */
279 bool Arc::createFrom2PBulge(const Vector & startPoint, const Vector & endPoint, double bulge)
280 {
281         data.reversed = (bulge < 0.0);
282         double alpha = atan(bulge) * 4.0;
283
284         Vector middle = (startPoint + endPoint) / 2.0;
285         double dist = startPoint.distanceTo(endPoint) / 2.0;
286
287         // alpha can't be 0.0 at this point
288         data.radius = fabs(dist / sin(alpha / 2.0));
289
290         double wu = fabs(Math::pow(data.radius, 2.0) - Math::pow(dist, 2.0));
291         double h = sqrt(wu);
292         double angle = startPoint.angleTo(endPoint);
293
294         if (bulge > 0.0)
295                 angle += M_PI / 2.0;
296         else
297                 angle -= M_PI / 2.0;
298
299         if (fabs(alpha) > M_PI)
300                 h *= -1.0;
301
302         data.center.setPolar(h, angle);
303         data.center += middle;
304         data.angle1 = data.center.angleTo(startPoint);
305         data.angle2 = data.center.angleTo(endPoint);
306
307         calculateEndpoints();
308         calculateBorders();
309
310         return true;
311 }
312
313 /**
314  * Recalculates the endpoints using the angles and the radius.
315  */
316 void Arc::calculateEndpoints()
317 {
318         startpoint.set(data.center.x + cos(data.angle1) * data.radius,
319                 data.center.y + sin(data.angle1) * data.radius);
320         endpoint.set(data.center.x + cos(data.angle2) * data.radius,
321                 data.center.y + sin(data.angle2) * data.radius);
322 }
323
324 void Arc::calculateBorders()
325 {
326         double minX = std::min(startpoint.x, endpoint.x);
327         double minY = std::min(startpoint.y, endpoint.y);
328         double maxX = std::max(startpoint.x, endpoint.x);
329         double maxY = std::max(startpoint.y, endpoint.y);
330
331         double a1 = !isReversed() ? data.angle1 : data.angle2;
332         double a2 = !isReversed() ? data.angle2 : data.angle1;
333
334         // check for left limit:
335         if ((a1 < M_PI && a2 > M_PI)
336             || (a1 > a2 - 1.0e-12 && a2 > M_PI)
337             || (a1 > a2 - 1.0e-12 && a1 < M_PI) )
338
339                 minX = std::min(data.center.x - data.radius, minX);
340
341         // check for right limit:
342         if (a1 > a2 - 1.0e-12)
343                 maxX = std::max(data.center.x + data.radius, maxX);
344
345         // check for bottom limit:
346         if ((a1 < (M_PI_2 * 3) && a2 > (M_PI_2 * 3))
347             || (a1 > a2 - 1.0e-12    && a2 > (M_PI_2 * 3))
348             || (a1 > a2 - 1.0e-12    && a1 < (M_PI_2 * 3)) )
349
350                 minY = std::min(data.center.y - data.radius, minY);
351
352         // check for top limit:
353         if ((a1 < M_PI_2 && a2 > M_PI_2)
354             || (a1 > a2 - 1.0e-12   && a2 > M_PI_2)
355             || (a1 > a2 - 1.0e-12   && a1 < M_PI_2) )
356
357                 maxY = std::max(data.center.y + data.radius, maxY);
358
359         minV.set(minX, minY);
360         maxV.set(maxX, maxY);
361 }
362
363 Vector Arc::getNearestEndpoint(const Vector & coord, double * dist)
364 {
365         double dist1, dist2;
366         Vector * nearerPoint;
367
368         dist1 = startpoint.distanceTo(coord);
369         dist2 = endpoint.distanceTo(coord);
370
371         if (dist2 < dist1)
372         {
373                 if (dist != NULL)
374                         *dist = dist2;
375                 nearerPoint = &endpoint;
376         }
377         else
378         {
379                 if (dist != NULL)
380                         *dist = dist1;
381                 nearerPoint = &startpoint;
382         }
383
384         return *nearerPoint;
385 }
386
387 Vector Arc::getNearestPointOnEntity(const Vector & coord, bool onEntity, double * dist, Entity * * entity)
388 {
389         Vector vec(false);
390
391         if (entity != NULL)
392                 *entity = this;
393
394         double angle = (coord - data.center).angle();
395
396         if (onEntity == false || Math::isAngleBetween(angle,
397                     data.angle1, data.angle2, isReversed()))
398         {
399                 vec.setPolar(data.radius, angle);
400                 vec += data.center;
401         }
402
403         if (dist != NULL)
404                 *dist = fabs((vec - data.center).magnitude() - data.radius);
405
406         return vec;
407 }
408
409 Vector Arc::getNearestCenter(const Vector & coord, double * dist)
410 {
411         if (dist != NULL)
412                 *dist = coord.distanceTo(data.center);
413         return data.center;
414 }
415
416 Vector Arc::getNearestMiddle(const Vector & coord, double * dist)
417 {
418         Vector ret = getMiddlepoint();
419
420         if (dist != NULL)
421                 *dist = coord.distanceTo(ret);
422         return ret;
423 }
424
425 Vector Arc::getNearestDist(double distance, const Vector & coord, double * dist)
426 {
427         if (data.radius < 1.0e-6)
428         {
429                 if (dist != NULL)
430                         *dist = RS_MAXDOUBLE;
431                 return Vector(false);
432         }
433
434         double a1, a2;
435         Vector p1, p2;
436         double aDist = distance / data.radius;
437
438         if (isReversed())
439         {
440                 a1 = data.angle1 - aDist;
441                 a2 = data.angle2 + aDist;
442         }
443         else
444         {
445                 a1 = data.angle1 + aDist;
446                 a2 = data.angle2 - aDist;
447         }
448
449         p1.setPolar(data.radius, a1);
450         p1 += data.center;
451         p2.setPolar(data.radius, a2);
452         p2 += data.center;
453
454         double dist1, dist2;
455         Vector * nearerPoint;
456
457         dist1 = p1.distanceTo(coord);
458         dist2 = p2.distanceTo(coord);
459
460         if (dist2 < dist1)
461         {
462                 if (dist != NULL)
463                         *dist = dist2;
464                 nearerPoint = &p2;
465         }
466         else
467         {
468                 if (dist != NULL)
469                         *dist = dist1;
470                 nearerPoint = &p1;
471         }
472
473         return *nearerPoint;
474 }
475
476 Vector Arc::getNearestDist(double distance, bool startp)
477 {
478         if (data.radius < 1.0e-6)
479                 return Vector(false);
480
481         double a;
482         Vector p;
483         double aDist = distance / data.radius;
484
485         if (isReversed())
486         {
487                 if (startp)
488                         a = data.angle1 - aDist;
489                 else
490                         a = data.angle2 + aDist;
491         }
492         else
493         {
494                 if (startp)
495                         a = data.angle1 + aDist;
496                 else
497                         a = data.angle2 - aDist;
498         }
499
500         p.setPolar(data.radius, a);
501         p += data.center;
502
503         return p;
504 }
505
506 double Arc::getDistanceToPoint(const Vector & coord, Entity * * entity, RS2::ResolveLevel, double)
507 {
508         if (entity != NULL)
509                 *entity = this;
510
511         // check endpoints first:
512         double dist = coord.distanceTo(getStartpoint());
513
514         if (dist < 1.0e-4)
515                 return dist;
516         dist = coord.distanceTo(getEndpoint());
517
518         if (dist < 1.0e-4)
519                 return dist;
520
521         if (Math::isAngleBetween(data.center.angleTo(coord),
522                     data.angle1, data.angle2,
523                     isReversed()))
524
525                 return fabs((coord - data.center).magnitude() - data.radius);
526         else
527                 return RS_MAXDOUBLE;
528 }
529
530 void Arc::moveStartpoint(const Vector & pos)
531 {
532         // polyline arcs: move point not angle:
533         //if (parent!=NULL && parent->rtti()==RS2::EntityPolyline) {
534         double bulge = getBulge();
535         createFrom2PBulge(pos, getEndpoint(), bulge);
536         //}
537
538         // normal arc: move angle1
539         /*else {
540            data.angle1 = data.center.angleTo(pos);
541            calculateEndpoints();
542            calculateBorders();
543            }*/
544 }
545
546 void Arc::moveEndpoint(const Vector & pos)
547 {
548         // polyline arcs: move point not angle:
549         //if (parent!=NULL && parent->rtti()==RS2::EntityPolyline) {
550         double bulge = getBulge();
551         createFrom2PBulge(getStartpoint(), pos, bulge);
552         //}
553
554         // normal arc: move angle1
555         /*else {
556            data.angle2 = data.center.angleTo(pos);
557             calculateEndpoints();
558            calculateBorders();
559            }*/
560 }
561
562 void Arc::trimStartpoint(const Vector & pos)
563 {
564         data.angle1 = data.center.angleTo(pos);
565         calculateEndpoints();
566         calculateBorders();
567 }
568
569 void Arc::trimEndpoint(const Vector & pos)
570 {
571         data.angle2 = data.center.angleTo(pos);
572         calculateEndpoints();
573         calculateBorders();
574 }
575
576 RS2::Ending Arc::getTrimPoint(const Vector & coord, const Vector & trimPoint)
577 {
578         double angEl = data.center.angleTo(trimPoint);
579         double angM = data.center.angleTo(coord);
580
581         if (Math::getAngleDifference(angM, angEl) > M_PI)
582         {
583                 if (data.reversed)
584                         return RS2::EndingEnd;
585                 else
586                         return RS2::EndingStart;
587         }
588         else
589         {
590                 if (data.reversed)
591                         return RS2::EndingStart;
592                 else
593                         return RS2::EndingEnd;
594         }
595 }
596
597 void Arc::reverse()
598 {
599         double a = data.angle1;
600         data.angle1 = data.angle2;
601         data.angle2 = a;
602         data.reversed = !data.reversed;
603         calculateEndpoints();
604         calculateBorders();
605 }
606
607 void Arc::move(Vector offset)
608 {
609         data.center.move(offset);
610         calculateEndpoints();
611         calculateBorders();
612 }
613
614 void Arc::rotate(Vector center, double angle)
615 {
616         DEBUG->print("Arc::rotate");
617         data.center.rotate(center, angle);
618         data.angle1 = Math::correctAngle(data.angle1 + angle);
619         data.angle2 = Math::correctAngle(data.angle2 + angle);
620         calculateEndpoints();
621         calculateBorders();
622         DEBUG->print("Arc::rotate: OK");
623 }
624
625 void Arc::scale(Vector center, Vector factor)
626 {
627         // negative scaling: mirroring
628         if (factor.x < 0.0)
629                 mirror(data.center, data.center + Vector(0.0, 1.0));
630                 //factor.x*=-1;
631
632
633         if (factor.y < 0.0)
634                 mirror(data.center, data.center + Vector(1.0, 0.0));
635                 //factor.y*=-1;
636
637         data.center.scale(center, factor);
638         data.radius *= factor.x;
639
640         if (data.radius < 0.0)
641                 data.radius *= -1.0;
642         calculateEndpoints();
643         calculateBorders();
644 }
645
646 void Arc::mirror(Vector axisPoint1, Vector axisPoint2)
647 {
648         data.center.mirror(axisPoint1, axisPoint2);
649         data.reversed = (!data.reversed);
650         /*
651            startpoint.mirror(axisPoint1, axisPoint2);
652            endpoint.mirror(axisPoint1, axisPoint2);
653
654            data.angle1 = data.center.angleTo(startpoint);
655            data.angle2 = data.center.angleTo(endpoint);
656          */
657
658         Vector vec;
659         vec.setPolar(1.0, data.angle1);
660         vec.mirror(Vector(0.0, 0.0), axisPoint2 - axisPoint1);
661         data.angle1 = vec.angle();
662
663         vec.setPolar(1.0, data.angle2);
664         vec.mirror(Vector(0.0, 0.0), axisPoint2 - axisPoint1);
665         data.angle2 = vec.angle();
666
667         calculateEndpoints();
668         calculateBorders();
669 }
670
671 void Arc::moveRef(const Vector & ref, const Vector & offset)
672 {
673         if (ref.distanceTo(startpoint) < 1.0e-4)
674                 moveStartpoint(startpoint + offset);
675
676
677         if (ref.distanceTo(endpoint) < 1.0e-4)
678                 moveEndpoint(endpoint + offset);
679 }
680
681 void Arc::stretch(Vector firstCorner, Vector secondCorner, Vector offset)
682 {
683         if (getMin().isInWindow(firstCorner, secondCorner)
684             && getMax().isInWindow(firstCorner, secondCorner))
685
686                 move(offset);
687         else
688         {
689                 if (getStartpoint().isInWindow(firstCorner,
690                             secondCorner))
691                         moveStartpoint(getStartpoint() + offset);
692
693
694                 if (getEndpoint().isInWindow(firstCorner,
695                             secondCorner))
696                         moveEndpoint(getEndpoint() + offset);
697         }
698 }
699
700 void Arc::draw(PaintInterface * painter, GraphicView * view, double /*patternOffset*/)
701 {
702         if (painter == NULL || view == NULL)
703                 return;
704
705         //double styleFactor = getStyleFactor();
706
707         // simple style-less lines
708         if (getPen().getLineType() == RS2::SolidLine
709             || isSelected()
710             || view->getDrawingMode() == RS2::ModePreview)
711
712                 painter->drawArc(view->toGui(getCenter()),
713                         getRadius() * view->getFactor().x,
714                         getAngle1(), getAngle2(),
715                         isReversed());
716         else
717         {
718                 double styleFactor = getStyleFactor(view);
719
720                 if (styleFactor < 0.0)
721                 {
722                         painter->drawArc(view->toGui(getCenter()),
723                                 getRadius() * view->getFactor().x,
724                                 getAngle1(), getAngle2(),
725                                 isReversed());
726                         return;
727                 }
728
729                 // Pattern:
730                 LineTypePattern * pat;
731
732                 if (isSelected())
733                         pat = &patternSelected;
734                 else
735                         pat = view->getPattern(getPen().getLineType());
736
737                 if (pat == NULL)
738                         return;
739
740                 if (getRadius() < 1.0e-6)
741                         return;
742
743                 // Pen to draw pattern is always solid:
744                 Pen pen = painter->getPen();
745                 pen.setLineType(RS2::SolidLine);
746                 painter->setPen(pen);
747
748                 double a1;
749                 double a2;
750
751                 if (data.reversed)
752                 {
753                         a2 = getAngle1();
754                         a1 = getAngle2();
755                 }
756                 else
757                 {
758                         a1 = getAngle1();
759                         a2 = getAngle2();
760                 }
761
762                 double * da; // array of distances in x.
763                 int i;  // index counter
764
765                 double length = getAngleLength();
766
767                 // create scaled pattern:
768                 da = new double[pat->num];
769
770                 for (i = 0; i < pat->num; ++i)
771                         da[i] = fabs(pat->pattern[i] * styleFactor) / getRadius();
772
773                 double tot = 0.0;
774                 i = 0;
775                 bool done = false;
776                 double curA = a1;
777                 //double cx = getCenter().x * factor.x + offsetX;
778                 //double cy = - a->getCenter().y * factor.y + getHeight() - offsetY;
779                 Vector cp = view->toGui(getCenter());
780                 double r = getRadius() * view->getFactor().x;
781
782                 do
783                 {
784                         if (pat->pattern[i] > 0.0)
785                         {
786                                 if (tot + da[i] < length)
787                                         painter->drawArc(cp, r,
788                                                 curA,
789                                                 curA + da[i],
790                                                 false);
791                                 else
792                                         painter->drawArc(cp, r,
793                                                 curA,
794                                                 a2,
795                                                 false);
796                         }
797                         curA += da[i];
798                         tot += da[i];
799                         done = tot > length;
800
801                         i++;
802
803                         if (i >= pat->num)
804                                 i = 0;
805                 }
806                 while (!done);
807
808                 delete[] da;
809         }
810 }
811
812 /**
813  * @return Middle point of the entity.
814  */
815 Vector Arc::getMiddlepoint() const
816 {
817         double a;
818         Vector ret;
819
820         if (isReversed())
821                 a = data.angle1 - getAngleLength() / 2.0;
822         else
823                 a = data.angle1 + getAngleLength() / 2.0;
824         ret.setPolar(data.radius, a);
825         ret += data.center;
826
827         return ret;
828 }
829
830 /**
831  * @return Angle length in rad.
832  */
833 double Arc::getAngleLength() const
834 {
835         double ret = 0.0;
836
837         if (isReversed())
838         {
839                 if (data.angle1 < data.angle2)
840                         ret = data.angle1 + 2 * M_PI - data.angle2;
841                 else
842                         ret = data.angle1 - data.angle2;
843         }
844         else
845         {
846                 if (data.angle2 < data.angle1)
847                         ret = data.angle2 + 2 * M_PI - data.angle1;
848                 else
849                         ret = data.angle2 - data.angle1;
850         }
851
852         // full circle:
853         if (fabs(ret) < 1.0e-6)
854                 ret = 2 * M_PI;
855
856         return ret;
857 }
858
859 /**
860  * @return Length of the arc.
861  */
862 double Arc::getLength()
863 {
864         return getAngleLength() * data.radius;
865 }
866
867 /**
868  * Gets the arc's bulge (tangens of angle length divided by 4).
869  */
870 double Arc::getBulge() const
871 {
872         double bulge = tan(fabs(getAngleLength()) / 4.0);
873
874         if (isReversed())
875                 bulge *= -1;
876         return bulge;
877 }
878
879 /**
880  * Dumps the point's data to stdout.
881  */
882 std::ostream & operator<<(std::ostream & os, const Arc & a)
883 {
884         os << " Arc: " << a.data << "\n";
885         return os;
886 }
887