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 // (C) 2010 Underground Software
8 // JLH = James L. Hammons <jlhamm@acm.org>
11 // --- ---------- -----------------------------------------------------------
12 // JLH 05/28/2010 Added this text. :-)
13 // JLH 06/16/2010 Moved implementation from header file to this file
18 #include "rs_constructionline.h"
19 #include "rs_linetypepattern.h"
20 #include "rs_information.h"
22 #include "graphicview.h"
23 #include "paintintf.h"
26 * Default constructor.
28 RS_Arc::RS_Arc(RS_EntityContainer * parent, const RS_ArcData & d):
29 RS_AtomicEntity(parent), data(d)
35 /*virtual*/ RS_Arc::~RS_Arc()
39 /*virtual*/ RS_Entity * RS_Arc::clone()
41 RS_Arc * a = new RS_Arc(*this);
46 /** @return RS2::EntityArc */
47 /*virtual*/ RS2::EntityType RS_Arc::rtti() const
49 return RS2::EntityArc;
53 /*virtual*/ bool RS_Arc::isEdge() const
58 /** @return Copy of data that defines the arc. **/
59 RS_ArcData RS_Arc::getData() const
64 /*virtual*/ VectorSolutions RS_Arc::getRefPoints()
66 VectorSolutions ret(startpoint, endpoint, data.center);
70 /** Sets new arc parameters. **/
71 void RS_Arc::setData(RS_ArcData d)
76 /** @return The center point (x) of this arc */
77 Vector RS_Arc::getCenter() const
82 /** Sets new center. */
83 void RS_Arc::setCenter(const Vector & c)
88 /** @return The radius of this arc */
89 double RS_Arc::getRadius() const
94 /** Sets new radius. */
95 void RS_Arc::setRadius(double r)
100 /** @return The start angle of this arc */
101 double RS_Arc::getAngle1() const
106 /** Sets new start angle. */
107 void RS_Arc::setAngle1(double a1)
112 /** @return The end angle of this arc */
113 double RS_Arc::getAngle2() const
118 /** Sets new end angle. */
119 void RS_Arc::setAngle2(double a2)
125 * @return Direction 1. The angle at which the arc starts at
128 double RS_Arc::getDirection1() const
131 return RS_Math::correctAngle(data.angle1 + M_PI / 2.0);
133 return RS_Math::correctAngle(data.angle1 - M_PI / 2.0);
137 * @return Direction 2. The angle at which the arc starts at
140 double RS_Arc::getDirection2() const
143 return RS_Math::correctAngle(data.angle2 - M_PI / 2.0);
145 return RS_Math::correctAngle(data.angle2 + M_PI / 2.0);
149 * @retval true if the arc is reversed (clockwise),
150 * @retval false otherwise
152 bool RS_Arc::isReversed() const
154 return data.reversed;
157 /** sets the reversed status. */
158 void RS_Arc::setReversed(bool r)
163 /** @return Start point of the entity. */
164 /*virtual*/ Vector RS_Arc::getStartpoint() const
169 /** @return End point of the entity. */
170 /*virtual*/ Vector RS_Arc::getEndpoint() const
176 * Creates this arc from 3 given points which define the arc line.
178 * @param p1 1st point.
179 * @param p2 2nd point.
180 * @param p3 3rd point.
182 bool RS_Arc::createFrom3P(const Vector & p1, const Vector & p2, const Vector & p3)
184 if (p1.distanceTo(p2) > RS_TOLERANCE
185 && p2.distanceTo(p3) > RS_TOLERANCE
186 && p3.distanceTo(p1) > RS_TOLERANCE)
188 // middle points between 3 points:
193 // intersection of two middle lines
194 mp1 = (p1 + p2) / 2.0;
195 a1 = p1.angleTo(p2) + M_PI / 2.0;
196 dir1.setPolar(100.0, a1);
197 mp2 = (p2 + p3) / 2.0;
198 a2 = p2.angleTo(p3) + M_PI / 2.0;
199 dir2.setPolar(100.0, a2);
201 RS_ConstructionLineData d1(mp1, mp1 + dir1);
202 RS_ConstructionLineData d2(mp2, mp2 + dir2);
203 RS_ConstructionLine midLine1(NULL, d1);
204 RS_ConstructionLine midLine2(NULL, d2);
206 VectorSolutions sol =
207 RS_Information::getIntersection(&midLine1, &midLine2);
209 data.center = sol.get(0);
210 data.radius = data.center.distanceTo(p3);
211 data.angle1 = data.center.angleTo(p1);
212 data.angle2 = data.center.angleTo(p3);
213 data.reversed = RS_Math::isAngleBetween(data.center.angleTo(p2),
214 data.angle1, data.angle2, true);
216 if (sol.get(0).valid && data.radius < 1.0e14
217 && data.radius > RS_TOLERANCE)
219 calculateEndpoints();
225 RS_DEBUG->print("RS_Arc::createFrom3P(): "
226 "Cannot create an arc with inf radius.");
232 RS_DEBUG->print("RS_Arc::createFrom3P(): "
233 "Cannot create an arc with radius 0.0.");
239 * Creates an arc from its startpoint, endpoint, start direction (angle)
242 * @retval true Successfully created arc
243 * @retval false Cannot creats arc (radius to small or endpoint to far away)
245 bool RS_Arc::createFrom2PDirectionRadius(const Vector & startPoint, const Vector & endPoint, double direction1, double radius)
248 ortho.setPolar(radius, direction1 + M_PI / 2.0);
249 Vector center1 = startPoint + ortho;
250 Vector center2 = startPoint - ortho;
252 if (center1.distanceTo(endPoint) < center2.distanceTo(endPoint))
253 data.center = center1;
255 data.center = center2;
257 data.radius = radius;
258 data.angle1 = data.center.angleTo(startPoint);
259 data.angle2 = data.center.angleTo(endPoint);
260 data.reversed = false;
262 double diff = RS_Math::correctAngle(getDirection1() - direction1);
264 if (fabs(diff - M_PI) < 1.0e-1)
265 data.reversed = true;
267 calculateEndpoints();
274 * Creates an arc from its startpoint, endpoint and bulge.
276 bool RS_Arc::createFrom2PBulge(const Vector & startPoint, const Vector & endPoint, double bulge)
278 data.reversed = (bulge < 0.0);
279 double alpha = atan(bulge) * 4.0;
281 Vector middle = (startPoint + endPoint) / 2.0;
282 double dist = startPoint.distanceTo(endPoint) / 2.0;
284 // alpha can't be 0.0 at this point
285 data.radius = fabs(dist / sin(alpha / 2.0));
287 double wu = fabs(RS_Math::pow(data.radius, 2.0) - RS_Math::pow(dist, 2.0));
289 double angle = startPoint.angleTo(endPoint);
296 if (fabs(alpha) > M_PI)
299 data.center.setPolar(h, angle);
300 data.center += middle;
301 data.angle1 = data.center.angleTo(startPoint);
302 data.angle2 = data.center.angleTo(endPoint);
304 calculateEndpoints();
311 * Recalculates the endpoints using the angles and the radius.
313 void RS_Arc::calculateEndpoints()
315 startpoint.set(data.center.x + cos(data.angle1) * data.radius,
316 data.center.y + sin(data.angle1) * data.radius);
317 endpoint.set(data.center.x + cos(data.angle2) * data.radius,
318 data.center.y + sin(data.angle2) * data.radius);
321 void RS_Arc::calculateBorders()
323 double minX = std::min(startpoint.x, endpoint.x);
324 double minY = std::min(startpoint.y, endpoint.y);
325 double maxX = std::max(startpoint.x, endpoint.x);
326 double maxY = std::max(startpoint.y, endpoint.y);
328 double a1 = !isReversed() ? data.angle1 : data.angle2;
329 double a2 = !isReversed() ? data.angle2 : data.angle1;
331 // check for left limit:
332 if ((a1 < M_PI && a2 > M_PI)
333 || (a1 > a2 - 1.0e-12 && a2 > M_PI)
334 || (a1 > a2 - 1.0e-12 && a1 < M_PI) )
336 minX = std::min(data.center.x - data.radius, minX);
338 // check for right limit:
339 if (a1 > a2 - 1.0e-12)
340 maxX = std::max(data.center.x + data.radius, maxX);
342 // check for bottom limit:
343 if ((a1 < (M_PI_2 * 3) && a2 > (M_PI_2 * 3))
344 || (a1 > a2 - 1.0e-12 && a2 > (M_PI_2 * 3))
345 || (a1 > a2 - 1.0e-12 && a1 < (M_PI_2 * 3)) )
347 minY = std::min(data.center.y - data.radius, minY);
349 // check for top limit:
350 if ((a1 < M_PI_2 && a2 > M_PI_2)
351 || (a1 > a2 - 1.0e-12 && a2 > M_PI_2)
352 || (a1 > a2 - 1.0e-12 && a1 < M_PI_2) )
354 maxY = std::max(data.center.y + data.radius, maxY);
356 minV.set(minX, minY);
357 maxV.set(maxX, maxY);
360 Vector RS_Arc::getNearestEndpoint(const Vector & coord, double * dist)
363 Vector * nearerPoint;
365 dist1 = startpoint.distanceTo(coord);
366 dist2 = endpoint.distanceTo(coord);
372 nearerPoint = &endpoint;
378 nearerPoint = &startpoint;
384 Vector RS_Arc::getNearestPointOnEntity(const Vector & coord, bool onEntity, double * dist, RS_Entity * * entity)
391 double angle = (coord - data.center).angle();
393 if (onEntity == false || RS_Math::isAngleBetween(angle,
394 data.angle1, data.angle2, isReversed()))
396 vec.setPolar(data.radius, angle);
401 *dist = fabs((vec - data.center).magnitude() - data.radius);
406 Vector RS_Arc::getNearestCenter(const Vector & coord, double * dist)
409 *dist = coord.distanceTo(data.center);
413 Vector RS_Arc::getNearestMiddle(const Vector & coord, double * dist)
415 Vector ret = getMiddlepoint();
418 *dist = coord.distanceTo(ret);
422 Vector RS_Arc::getNearestDist(double distance, const Vector & coord, double * dist)
424 if (data.radius < 1.0e-6)
427 *dist = RS_MAXDOUBLE;
428 return Vector(false);
433 double aDist = distance / data.radius;
437 a1 = data.angle1 - aDist;
438 a2 = data.angle2 + aDist;
442 a1 = data.angle1 + aDist;
443 a2 = data.angle2 - aDist;
446 p1.setPolar(data.radius, a1);
448 p2.setPolar(data.radius, a2);
452 Vector * nearerPoint;
454 dist1 = p1.distanceTo(coord);
455 dist2 = p2.distanceTo(coord);
473 Vector RS_Arc::getNearestDist(double distance, bool startp)
475 if (data.radius < 1.0e-6)
476 return Vector(false);
480 double aDist = distance / data.radius;
485 a = data.angle1 - aDist;
487 a = data.angle2 + aDist;
492 a = data.angle1 + aDist;
494 a = data.angle2 - aDist;
497 p.setPolar(data.radius, a);
503 double RS_Arc::getDistanceToPoint(const Vector & coord, RS_Entity * * entity, RS2::ResolveLevel, double)
508 // check endpoints first:
509 double dist = coord.distanceTo(getStartpoint());
513 dist = coord.distanceTo(getEndpoint());
518 if (RS_Math::isAngleBetween(data.center.angleTo(coord),
519 data.angle1, data.angle2,
522 return fabs((coord - data.center).magnitude() - data.radius);
527 void RS_Arc::moveStartpoint(const Vector & pos)
529 // polyline arcs: move point not angle:
530 //if (parent!=NULL && parent->rtti()==RS2::EntityPolyline) {
531 double bulge = getBulge();
532 createFrom2PBulge(pos, getEndpoint(), bulge);
535 // normal arc: move angle1
537 data.angle1 = data.center.angleTo(pos);
538 calculateEndpoints();
543 void RS_Arc::moveEndpoint(const Vector & pos)
545 // polyline arcs: move point not angle:
546 //if (parent!=NULL && parent->rtti()==RS2::EntityPolyline) {
547 double bulge = getBulge();
548 createFrom2PBulge(getStartpoint(), pos, bulge);
551 // normal arc: move angle1
553 data.angle2 = data.center.angleTo(pos);
554 calculateEndpoints();
559 void RS_Arc::trimStartpoint(const Vector & pos)
561 data.angle1 = data.center.angleTo(pos);
562 calculateEndpoints();
566 void RS_Arc::trimEndpoint(const Vector & pos)
568 data.angle2 = data.center.angleTo(pos);
569 calculateEndpoints();
573 RS2::Ending RS_Arc::getTrimPoint(const Vector & coord, const Vector & trimPoint)
575 double angEl = data.center.angleTo(trimPoint);
576 double angM = data.center.angleTo(coord);
578 if (RS_Math::getAngleDifference(angM, angEl) > M_PI)
581 return RS2::EndingEnd;
583 return RS2::EndingStart;
588 return RS2::EndingStart;
590 return RS2::EndingEnd;
594 void RS_Arc::reverse()
596 double a = data.angle1;
597 data.angle1 = data.angle2;
599 data.reversed = !data.reversed;
600 calculateEndpoints();
604 void RS_Arc::move(Vector offset)
606 data.center.move(offset);
607 calculateEndpoints();
611 void RS_Arc::rotate(Vector center, double angle)
613 RS_DEBUG->print("RS_Arc::rotate");
614 data.center.rotate(center, angle);
615 data.angle1 = RS_Math::correctAngle(data.angle1 + angle);
616 data.angle2 = RS_Math::correctAngle(data.angle2 + angle);
617 calculateEndpoints();
619 RS_DEBUG->print("RS_Arc::rotate: OK");
622 void RS_Arc::scale(Vector center, Vector factor)
624 // negative scaling: mirroring
626 mirror(data.center, data.center + Vector(0.0, 1.0));
631 mirror(data.center, data.center + Vector(1.0, 0.0));
634 data.center.scale(center, factor);
635 data.radius *= factor.x;
637 if (data.radius < 0.0)
639 calculateEndpoints();
643 void RS_Arc::mirror(Vector axisPoint1, Vector axisPoint2)
645 data.center.mirror(axisPoint1, axisPoint2);
646 data.reversed = (!data.reversed);
648 startpoint.mirror(axisPoint1, axisPoint2);
649 endpoint.mirror(axisPoint1, axisPoint2);
651 data.angle1 = data.center.angleTo(startpoint);
652 data.angle2 = data.center.angleTo(endpoint);
656 vec.setPolar(1.0, data.angle1);
657 vec.mirror(Vector(0.0, 0.0), axisPoint2 - axisPoint1);
658 data.angle1 = vec.angle();
660 vec.setPolar(1.0, data.angle2);
661 vec.mirror(Vector(0.0, 0.0), axisPoint2 - axisPoint1);
662 data.angle2 = vec.angle();
664 calculateEndpoints();
668 void RS_Arc::moveRef(const Vector & ref, const Vector & offset)
670 if (ref.distanceTo(startpoint) < 1.0e-4)
671 moveStartpoint(startpoint + offset);
674 if (ref.distanceTo(endpoint) < 1.0e-4)
675 moveEndpoint(endpoint + offset);
678 void RS_Arc::stretch(Vector firstCorner, Vector secondCorner, Vector offset)
680 if (getMin().isInWindow(firstCorner, secondCorner)
681 && getMax().isInWindow(firstCorner, secondCorner))
686 if (getStartpoint().isInWindow(firstCorner,
688 moveStartpoint(getStartpoint() + offset);
691 if (getEndpoint().isInWindow(firstCorner,
693 moveEndpoint(getEndpoint() + offset);
697 void RS_Arc::draw(PaintInterface * painter, GraphicView * view, double /*patternOffset*/)
699 if (painter == NULL || view == NULL)
702 //double styleFactor = getStyleFactor();
704 // simple style-less lines
705 if (getPen().getLineType() == RS2::SolidLine
707 || view->getDrawingMode() == RS2::ModePreview)
709 painter->drawArc(view->toGui(getCenter()),
710 getRadius() * view->getFactor().x,
711 getAngle1(), getAngle2(),
715 double styleFactor = getStyleFactor(view);
717 if (styleFactor < 0.0)
719 painter->drawArc(view->toGui(getCenter()),
720 getRadius() * view->getFactor().x,
721 getAngle1(), getAngle2(),
727 RS_LineTypePattern * pat;
730 pat = &patternSelected;
732 pat = view->getPattern(getPen().getLineType());
737 if (getRadius() < 1.0e-6)
740 // Pen to draw pattern is always solid:
741 RS_Pen pen = painter->getPen();
742 pen.setLineType(RS2::SolidLine);
743 painter->setPen(pen);
759 double * da; // array of distances in x.
760 int i; // index counter
762 double length = getAngleLength();
764 // create scaled pattern:
765 da = new double[pat->num];
767 for (i = 0; i < pat->num; ++i)
768 da[i] = fabs(pat->pattern[i] * styleFactor) / getRadius();
774 //double cx = getCenter().x * factor.x + offsetX;
775 //double cy = - a->getCenter().y * factor.y + getHeight() - offsetY;
776 Vector cp = view->toGui(getCenter());
777 double r = getRadius() * view->getFactor().x;
781 if (pat->pattern[i] > 0.0)
783 if (tot + da[i] < length)
784 painter->drawArc(cp, r,
789 painter->drawArc(cp, r,
810 * @return Middle point of the entity.
812 Vector RS_Arc::getMiddlepoint() const
818 a = data.angle1 - getAngleLength() / 2.0;
820 a = data.angle1 + getAngleLength() / 2.0;
821 ret.setPolar(data.radius, a);
828 * @return Angle length in rad.
830 double RS_Arc::getAngleLength() const
836 if (data.angle1 < data.angle2)
837 ret = data.angle1 + 2 * M_PI - data.angle2;
839 ret = data.angle1 - data.angle2;
843 if (data.angle2 < data.angle1)
844 ret = data.angle2 + 2 * M_PI - data.angle1;
846 ret = data.angle2 - data.angle1;
850 if (fabs(ret) < 1.0e-6)
857 * @return Length of the arc.
859 double RS_Arc::getLength()
861 return getAngleLength() * data.radius;
865 * Gets the arc's bulge (tangens of angle length divided by 4).
867 double RS_Arc::getBulge() const
869 double bulge = tan(fabs(getAngleLength()) / 4.0);
877 * Dumps the point's data to stdout.
879 std::ostream & operator<<(std::ostream & os, const RS_Arc & a)
881 os << " Arc: " << a.data << "\n";