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
10 // JLH = James L. Hammons <jlhamm@acm.org>
13 // --- ---------- -----------------------------------------------------------
14 // JLH 05/28/2010 Added this text. :-)
15 // JLH 06/16/2010 Moved implementation from header file to this file
20 #include "rs_constructionline.h"
21 #include "rs_linetypepattern.h"
22 #include "rs_information.h"
24 #include "graphicview.h"
25 #include "paintinterface.h"
28 * Default constructor.
30 RS_Arc::RS_Arc(RS_EntityContainer * parent, const RS_ArcData & d):
31 RS_AtomicEntity(parent), data(d)
37 /*virtual*/ RS_Arc::~RS_Arc()
41 /*virtual*/ RS_Entity * RS_Arc::clone()
43 RS_Arc * a = new RS_Arc(*this);
48 /** @return RS2::EntityArc */
49 /*virtual*/ RS2::EntityType RS_Arc::rtti() const
51 return RS2::EntityArc;
55 /*virtual*/ bool RS_Arc::isEdge() const
60 /** @return Copy of data that defines the arc. **/
61 RS_ArcData RS_Arc::getData() const
66 /*virtual*/ VectorSolutions RS_Arc::getRefPoints()
68 VectorSolutions ret(startpoint, endpoint, data.center);
72 /** Sets new arc parameters. **/
73 void RS_Arc::setData(RS_ArcData d)
78 /** @return The center point (x) of this arc */
79 Vector RS_Arc::getCenter() const
84 /** Sets new center. */
85 void RS_Arc::setCenter(const Vector & c)
90 /** @return The radius of this arc */
91 double RS_Arc::getRadius() const
96 /** Sets new radius. */
97 void RS_Arc::setRadius(double r)
102 /** @return The start angle of this arc */
103 double RS_Arc::getAngle1() const
108 /** Sets new start angle. */
109 void RS_Arc::setAngle1(double a1)
114 /** @return The end angle of this arc */
115 double RS_Arc::getAngle2() const
120 /** Sets new end angle. */
121 void RS_Arc::setAngle2(double a2)
127 * @return Direction 1. The angle at which the arc starts at
130 double RS_Arc::getDirection1() const
133 return RS_Math::correctAngle(data.angle1 + M_PI / 2.0);
135 return RS_Math::correctAngle(data.angle1 - M_PI / 2.0);
139 * @return Direction 2. The angle at which the arc starts at
142 double RS_Arc::getDirection2() const
145 return RS_Math::correctAngle(data.angle2 - M_PI / 2.0);
147 return RS_Math::correctAngle(data.angle2 + M_PI / 2.0);
151 * @retval true if the arc is reversed (clockwise),
152 * @retval false otherwise
154 bool RS_Arc::isReversed() const
156 return data.reversed;
159 /** sets the reversed status. */
160 void RS_Arc::setReversed(bool r)
165 /** @return Start point of the entity. */
166 /*virtual*/ Vector RS_Arc::getStartpoint() const
171 /** @return End point of the entity. */
172 /*virtual*/ Vector RS_Arc::getEndpoint() const
178 * Creates this arc from 3 given points which define the arc line.
180 * @param p1 1st point.
181 * @param p2 2nd point.
182 * @param p3 3rd point.
184 bool RS_Arc::createFrom3P(const Vector & p1, const Vector & p2, const Vector & p3)
186 if (p1.distanceTo(p2) > RS_TOLERANCE
187 && p2.distanceTo(p3) > RS_TOLERANCE
188 && p3.distanceTo(p1) > RS_TOLERANCE)
190 // middle points between 3 points:
195 // intersection of two middle lines
196 mp1 = (p1 + p2) / 2.0;
197 a1 = p1.angleTo(p2) + M_PI / 2.0;
198 dir1.setPolar(100.0, a1);
199 mp2 = (p2 + p3) / 2.0;
200 a2 = p2.angleTo(p3) + M_PI / 2.0;
201 dir2.setPolar(100.0, a2);
203 RS_ConstructionLineData d1(mp1, mp1 + dir1);
204 RS_ConstructionLineData d2(mp2, mp2 + dir2);
205 RS_ConstructionLine midLine1(NULL, d1);
206 RS_ConstructionLine midLine2(NULL, d2);
208 VectorSolutions sol =
209 RS_Information::getIntersection(&midLine1, &midLine2);
211 data.center = sol.get(0);
212 data.radius = data.center.distanceTo(p3);
213 data.angle1 = data.center.angleTo(p1);
214 data.angle2 = data.center.angleTo(p3);
215 data.reversed = RS_Math::isAngleBetween(data.center.angleTo(p2),
216 data.angle1, data.angle2, true);
218 if (sol.get(0).valid && data.radius < 1.0e14
219 && data.radius > RS_TOLERANCE)
221 calculateEndpoints();
227 RS_DEBUG->print("RS_Arc::createFrom3P(): "
228 "Cannot create an arc with inf radius.");
234 RS_DEBUG->print("RS_Arc::createFrom3P(): "
235 "Cannot create an arc with radius 0.0.");
241 * Creates an arc from its startpoint, endpoint, start direction (angle)
244 * @retval true Successfully created arc
245 * @retval false Cannot creats arc (radius to small or endpoint to far away)
247 bool RS_Arc::createFrom2PDirectionRadius(const Vector & startPoint, const Vector & endPoint, double direction1, double radius)
250 ortho.setPolar(radius, direction1 + M_PI / 2.0);
251 Vector center1 = startPoint + ortho;
252 Vector center2 = startPoint - ortho;
254 if (center1.distanceTo(endPoint) < center2.distanceTo(endPoint))
255 data.center = center1;
257 data.center = center2;
259 data.radius = radius;
260 data.angle1 = data.center.angleTo(startPoint);
261 data.angle2 = data.center.angleTo(endPoint);
262 data.reversed = false;
264 double diff = RS_Math::correctAngle(getDirection1() - direction1);
266 if (fabs(diff - M_PI) < 1.0e-1)
267 data.reversed = true;
269 calculateEndpoints();
276 * Creates an arc from its startpoint, endpoint and bulge.
278 bool RS_Arc::createFrom2PBulge(const Vector & startPoint, const Vector & endPoint, double bulge)
280 data.reversed = (bulge < 0.0);
281 double alpha = atan(bulge) * 4.0;
283 Vector middle = (startPoint + endPoint) / 2.0;
284 double dist = startPoint.distanceTo(endPoint) / 2.0;
286 // alpha can't be 0.0 at this point
287 data.radius = fabs(dist / sin(alpha / 2.0));
289 double wu = fabs(RS_Math::pow(data.radius, 2.0) - RS_Math::pow(dist, 2.0));
291 double angle = startPoint.angleTo(endPoint);
298 if (fabs(alpha) > M_PI)
301 data.center.setPolar(h, angle);
302 data.center += middle;
303 data.angle1 = data.center.angleTo(startPoint);
304 data.angle2 = data.center.angleTo(endPoint);
306 calculateEndpoints();
313 * Recalculates the endpoints using the angles and the radius.
315 void RS_Arc::calculateEndpoints()
317 startpoint.set(data.center.x + cos(data.angle1) * data.radius,
318 data.center.y + sin(data.angle1) * data.radius);
319 endpoint.set(data.center.x + cos(data.angle2) * data.radius,
320 data.center.y + sin(data.angle2) * data.radius);
323 void RS_Arc::calculateBorders()
325 double minX = std::min(startpoint.x, endpoint.x);
326 double minY = std::min(startpoint.y, endpoint.y);
327 double maxX = std::max(startpoint.x, endpoint.x);
328 double maxY = std::max(startpoint.y, endpoint.y);
330 double a1 = !isReversed() ? data.angle1 : data.angle2;
331 double a2 = !isReversed() ? data.angle2 : data.angle1;
333 // check for left limit:
334 if ((a1 < M_PI && a2 > M_PI)
335 || (a1 > a2 - 1.0e-12 && a2 > M_PI)
336 || (a1 > a2 - 1.0e-12 && a1 < M_PI) )
338 minX = std::min(data.center.x - data.radius, minX);
340 // check for right limit:
341 if (a1 > a2 - 1.0e-12)
342 maxX = std::max(data.center.x + data.radius, maxX);
344 // check for bottom limit:
345 if ((a1 < (M_PI_2 * 3) && a2 > (M_PI_2 * 3))
346 || (a1 > a2 - 1.0e-12 && a2 > (M_PI_2 * 3))
347 || (a1 > a2 - 1.0e-12 && a1 < (M_PI_2 * 3)) )
349 minY = std::min(data.center.y - data.radius, minY);
351 // check for top limit:
352 if ((a1 < M_PI_2 && a2 > M_PI_2)
353 || (a1 > a2 - 1.0e-12 && a2 > M_PI_2)
354 || (a1 > a2 - 1.0e-12 && a1 < M_PI_2) )
356 maxY = std::max(data.center.y + data.radius, maxY);
358 minV.set(minX, minY);
359 maxV.set(maxX, maxY);
362 Vector RS_Arc::getNearestEndpoint(const Vector & coord, double * dist)
365 Vector * nearerPoint;
367 dist1 = startpoint.distanceTo(coord);
368 dist2 = endpoint.distanceTo(coord);
374 nearerPoint = &endpoint;
380 nearerPoint = &startpoint;
386 Vector RS_Arc::getNearestPointOnEntity(const Vector & coord, bool onEntity, double * dist, RS_Entity * * entity)
393 double angle = (coord - data.center).angle();
395 if (onEntity == false || RS_Math::isAngleBetween(angle,
396 data.angle1, data.angle2, isReversed()))
398 vec.setPolar(data.radius, angle);
403 *dist = fabs((vec - data.center).magnitude() - data.radius);
408 Vector RS_Arc::getNearestCenter(const Vector & coord, double * dist)
411 *dist = coord.distanceTo(data.center);
415 Vector RS_Arc::getNearestMiddle(const Vector & coord, double * dist)
417 Vector ret = getMiddlepoint();
420 *dist = coord.distanceTo(ret);
424 Vector RS_Arc::getNearestDist(double distance, const Vector & coord, double * dist)
426 if (data.radius < 1.0e-6)
429 *dist = RS_MAXDOUBLE;
430 return Vector(false);
435 double aDist = distance / data.radius;
439 a1 = data.angle1 - aDist;
440 a2 = data.angle2 + aDist;
444 a1 = data.angle1 + aDist;
445 a2 = data.angle2 - aDist;
448 p1.setPolar(data.radius, a1);
450 p2.setPolar(data.radius, a2);
454 Vector * nearerPoint;
456 dist1 = p1.distanceTo(coord);
457 dist2 = p2.distanceTo(coord);
475 Vector RS_Arc::getNearestDist(double distance, bool startp)
477 if (data.radius < 1.0e-6)
478 return Vector(false);
482 double aDist = distance / data.radius;
487 a = data.angle1 - aDist;
489 a = data.angle2 + aDist;
494 a = data.angle1 + aDist;
496 a = data.angle2 - aDist;
499 p.setPolar(data.radius, a);
505 double RS_Arc::getDistanceToPoint(const Vector & coord, RS_Entity * * entity, RS2::ResolveLevel, double)
510 // check endpoints first:
511 double dist = coord.distanceTo(getStartpoint());
515 dist = coord.distanceTo(getEndpoint());
520 if (RS_Math::isAngleBetween(data.center.angleTo(coord),
521 data.angle1, data.angle2,
524 return fabs((coord - data.center).magnitude() - data.radius);
529 void RS_Arc::moveStartpoint(const Vector & pos)
531 // polyline arcs: move point not angle:
532 //if (parent!=NULL && parent->rtti()==RS2::EntityPolyline) {
533 double bulge = getBulge();
534 createFrom2PBulge(pos, getEndpoint(), bulge);
537 // normal arc: move angle1
539 data.angle1 = data.center.angleTo(pos);
540 calculateEndpoints();
545 void RS_Arc::moveEndpoint(const Vector & pos)
547 // polyline arcs: move point not angle:
548 //if (parent!=NULL && parent->rtti()==RS2::EntityPolyline) {
549 double bulge = getBulge();
550 createFrom2PBulge(getStartpoint(), pos, bulge);
553 // normal arc: move angle1
555 data.angle2 = data.center.angleTo(pos);
556 calculateEndpoints();
561 void RS_Arc::trimStartpoint(const Vector & pos)
563 data.angle1 = data.center.angleTo(pos);
564 calculateEndpoints();
568 void RS_Arc::trimEndpoint(const Vector & pos)
570 data.angle2 = data.center.angleTo(pos);
571 calculateEndpoints();
575 RS2::Ending RS_Arc::getTrimPoint(const Vector & coord, const Vector & trimPoint)
577 double angEl = data.center.angleTo(trimPoint);
578 double angM = data.center.angleTo(coord);
580 if (RS_Math::getAngleDifference(angM, angEl) > M_PI)
583 return RS2::EndingEnd;
585 return RS2::EndingStart;
590 return RS2::EndingStart;
592 return RS2::EndingEnd;
596 void RS_Arc::reverse()
598 double a = data.angle1;
599 data.angle1 = data.angle2;
601 data.reversed = !data.reversed;
602 calculateEndpoints();
606 void RS_Arc::move(Vector offset)
608 data.center.move(offset);
609 calculateEndpoints();
613 void RS_Arc::rotate(Vector center, double angle)
615 RS_DEBUG->print("RS_Arc::rotate");
616 data.center.rotate(center, angle);
617 data.angle1 = RS_Math::correctAngle(data.angle1 + angle);
618 data.angle2 = RS_Math::correctAngle(data.angle2 + angle);
619 calculateEndpoints();
621 RS_DEBUG->print("RS_Arc::rotate: OK");
624 void RS_Arc::scale(Vector center, Vector factor)
626 // negative scaling: mirroring
628 mirror(data.center, data.center + Vector(0.0, 1.0));
633 mirror(data.center, data.center + Vector(1.0, 0.0));
636 data.center.scale(center, factor);
637 data.radius *= factor.x;
639 if (data.radius < 0.0)
641 calculateEndpoints();
645 void RS_Arc::mirror(Vector axisPoint1, Vector axisPoint2)
647 data.center.mirror(axisPoint1, axisPoint2);
648 data.reversed = (!data.reversed);
650 startpoint.mirror(axisPoint1, axisPoint2);
651 endpoint.mirror(axisPoint1, axisPoint2);
653 data.angle1 = data.center.angleTo(startpoint);
654 data.angle2 = data.center.angleTo(endpoint);
658 vec.setPolar(1.0, data.angle1);
659 vec.mirror(Vector(0.0, 0.0), axisPoint2 - axisPoint1);
660 data.angle1 = vec.angle();
662 vec.setPolar(1.0, data.angle2);
663 vec.mirror(Vector(0.0, 0.0), axisPoint2 - axisPoint1);
664 data.angle2 = vec.angle();
666 calculateEndpoints();
670 void RS_Arc::moveRef(const Vector & ref, const Vector & offset)
672 if (ref.distanceTo(startpoint) < 1.0e-4)
673 moveStartpoint(startpoint + offset);
676 if (ref.distanceTo(endpoint) < 1.0e-4)
677 moveEndpoint(endpoint + offset);
680 void RS_Arc::stretch(Vector firstCorner, Vector secondCorner, Vector offset)
682 if (getMin().isInWindow(firstCorner, secondCorner)
683 && getMax().isInWindow(firstCorner, secondCorner))
688 if (getStartpoint().isInWindow(firstCorner,
690 moveStartpoint(getStartpoint() + offset);
693 if (getEndpoint().isInWindow(firstCorner,
695 moveEndpoint(getEndpoint() + offset);
699 void RS_Arc::draw(PaintInterface * painter, GraphicView * view, double /*patternOffset*/)
701 if (painter == NULL || view == NULL)
704 //double styleFactor = getStyleFactor();
706 // simple style-less lines
707 if (getPen().getLineType() == RS2::SolidLine
709 || view->getDrawingMode() == RS2::ModePreview)
711 painter->drawArc(view->toGui(getCenter()),
712 getRadius() * view->getFactor().x,
713 getAngle1(), getAngle2(),
717 double styleFactor = getStyleFactor(view);
719 if (styleFactor < 0.0)
721 painter->drawArc(view->toGui(getCenter()),
722 getRadius() * view->getFactor().x,
723 getAngle1(), getAngle2(),
729 RS_LineTypePattern * pat;
732 pat = &patternSelected;
734 pat = view->getPattern(getPen().getLineType());
739 if (getRadius() < 1.0e-6)
742 // Pen to draw pattern is always solid:
743 RS_Pen pen = painter->getPen();
744 pen.setLineType(RS2::SolidLine);
745 painter->setPen(pen);
761 double * da; // array of distances in x.
762 int i; // index counter
764 double length = getAngleLength();
766 // create scaled pattern:
767 da = new double[pat->num];
769 for (i = 0; i < pat->num; ++i)
770 da[i] = fabs(pat->pattern[i] * styleFactor) / getRadius();
776 //double cx = getCenter().x * factor.x + offsetX;
777 //double cy = - a->getCenter().y * factor.y + getHeight() - offsetY;
778 Vector cp = view->toGui(getCenter());
779 double r = getRadius() * view->getFactor().x;
783 if (pat->pattern[i] > 0.0)
785 if (tot + da[i] < length)
786 painter->drawArc(cp, r,
791 painter->drawArc(cp, r,
812 * @return Middle point of the entity.
814 Vector RS_Arc::getMiddlepoint() const
820 a = data.angle1 - getAngleLength() / 2.0;
822 a = data.angle1 + getAngleLength() / 2.0;
823 ret.setPolar(data.radius, a);
830 * @return Angle length in rad.
832 double RS_Arc::getAngleLength() const
838 if (data.angle1 < data.angle2)
839 ret = data.angle1 + 2 * M_PI - data.angle2;
841 ret = data.angle1 - data.angle2;
845 if (data.angle2 < data.angle1)
846 ret = data.angle2 + 2 * M_PI - data.angle1;
848 ret = data.angle2 - data.angle1;
852 if (fabs(ret) < 1.0e-6)
859 * @return Length of the arc.
861 double RS_Arc::getLength()
863 return getAngleLength() * data.radius;
867 * Gets the arc's bulge (tangens of angle length divided by 4).
869 double RS_Arc::getBulge() const
871 double bulge = tan(fabs(getAngleLength()) / 4.0);
879 * Dumps the point's data to stdout.
881 std::ostream & operator<<(std::ostream & os, const RS_Arc & a)
883 os << " Arc: " << a.data << "\n";