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 "constructionline.h"
22 #include "graphicview.h"
23 #include "linetypepattern.h"
24 #include "information.h"
25 #include "mathextra.h"
26 #include "paintinterface.h"
29 * Default constructor.
31 Arc::Arc(EntityContainer * parent, const ArcData & d):
32 AtomicEntity(parent), data(d)
38 /*virtual*/ Arc::~Arc()
42 /*virtual*/ Entity * Arc::clone()
44 Arc * a = new Arc(*this);
49 /** @return RS2::EntityArc */
50 /*virtual*/ RS2::EntityType Arc::rtti() const
52 return RS2::EntityArc;
56 /*virtual*/ bool Arc::isEdge() const
61 /** @return Copy of data that defines the arc. **/
62 ArcData Arc::getData() const
67 /*virtual*/ VectorSolutions Arc::getRefPoints()
69 VectorSolutions ret(startpoint, endpoint, data.center);
73 /** Sets new arc parameters. **/
74 void Arc::setData(ArcData d)
79 /** @return The center point (x) of this arc */
80 Vector Arc::getCenter() const
85 /** Sets new center. */
86 void Arc::setCenter(const Vector & c)
91 /** @return The radius of this arc */
92 double Arc::getRadius() const
97 /** Sets new radius. */
98 void Arc::setRadius(double r)
103 /** @return The start angle of this arc */
104 double Arc::getAngle1() const
109 /** Sets new start angle. */
110 void Arc::setAngle1(double a1)
115 /** @return The end angle of this arc */
116 double Arc::getAngle2() const
121 /** Sets new end angle. */
122 void Arc::setAngle2(double a2)
128 * @return Direction 1. The angle at which the arc starts at
131 double Arc::getDirection1() const
134 return Math::correctAngle(data.angle1 + M_PI / 2.0);
136 return Math::correctAngle(data.angle1 - M_PI / 2.0);
140 * @return Direction 2. The angle at which the arc starts at
143 double Arc::getDirection2() const
146 return Math::correctAngle(data.angle2 - M_PI / 2.0);
148 return Math::correctAngle(data.angle2 + M_PI / 2.0);
152 * @retval true if the arc is reversed (clockwise),
153 * @retval false otherwise
155 bool Arc::isReversed() const
157 return data.reversed;
160 /** sets the reversed status. */
161 void Arc::setReversed(bool r)
166 /** @return Start point of the entity. */
167 /*virtual*/ Vector Arc::getStartpoint() const
172 /** @return End point of the entity. */
173 /*virtual*/ Vector Arc::getEndpoint() const
179 * Creates this arc from 3 given points which define the arc line.
181 * @param p1 1st point.
182 * @param p2 2nd point.
183 * @param p3 3rd point.
185 bool Arc::createFrom3P(const Vector & p1, const Vector & p2, const Vector & p3)
187 if (p1.distanceTo(p2) > RS_TOLERANCE
188 && p2.distanceTo(p3) > RS_TOLERANCE
189 && p3.distanceTo(p1) > RS_TOLERANCE)
191 // middle points between 3 points:
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);
204 ConstructionLineData d1(mp1, mp1 + dir1);
205 ConstructionLineData d2(mp2, mp2 + dir2);
206 ConstructionLine midLine1(NULL, d1);
207 ConstructionLine midLine2(NULL, d2);
209 VectorSolutions sol =
210 Information::getIntersection(&midLine1, &midLine2);
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);
219 if (sol.get(0).valid && data.radius < 1.0e14
220 && data.radius > RS_TOLERANCE)
222 calculateEndpoints();
228 DEBUG->print("Arc::createFrom3P(): "
229 "Cannot create an arc with inf radius.");
235 DEBUG->print("Arc::createFrom3P(): "
236 "Cannot create an arc with radius 0.0.");
242 * Creates an arc from its startpoint, endpoint, start direction (angle)
245 * @retval true Successfully created arc
246 * @retval false Cannot creats arc (radius to small or endpoint to far away)
248 bool Arc::createFrom2PDirectionRadius(const Vector & startPoint, const Vector & endPoint, double direction1, double radius)
251 ortho.setPolar(radius, direction1 + M_PI / 2.0);
252 Vector center1 = startPoint + ortho;
253 Vector center2 = startPoint - ortho;
255 if (center1.distanceTo(endPoint) < center2.distanceTo(endPoint))
256 data.center = center1;
258 data.center = center2;
260 data.radius = radius;
261 data.angle1 = data.center.angleTo(startPoint);
262 data.angle2 = data.center.angleTo(endPoint);
263 data.reversed = false;
265 double diff = Math::correctAngle(getDirection1() - direction1);
267 if (fabs(diff - M_PI) < 1.0e-1)
268 data.reversed = true;
270 calculateEndpoints();
277 * Creates an arc from its startpoint, endpoint and bulge.
279 bool Arc::createFrom2PBulge(const Vector & startPoint, const Vector & endPoint, double bulge)
281 data.reversed = (bulge < 0.0);
282 double alpha = atan(bulge) * 4.0;
284 Vector middle = (startPoint + endPoint) / 2.0;
285 double dist = startPoint.distanceTo(endPoint) / 2.0;
287 // alpha can't be 0.0 at this point
288 data.radius = fabs(dist / sin(alpha / 2.0));
290 double wu = fabs(Math::pow(data.radius, 2.0) - Math::pow(dist, 2.0));
292 double angle = startPoint.angleTo(endPoint);
299 if (fabs(alpha) > M_PI)
302 data.center.setPolar(h, angle);
303 data.center += middle;
304 data.angle1 = data.center.angleTo(startPoint);
305 data.angle2 = data.center.angleTo(endPoint);
307 calculateEndpoints();
314 * Recalculates the endpoints using the angles and the radius.
316 void Arc::calculateEndpoints()
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);
324 void Arc::calculateBorders()
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);
331 double a1 = !isReversed() ? data.angle1 : data.angle2;
332 double a2 = !isReversed() ? data.angle2 : data.angle1;
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) )
339 minX = std::min(data.center.x - data.radius, minX);
341 // check for right limit:
342 if (a1 > a2 - 1.0e-12)
343 maxX = std::max(data.center.x + data.radius, maxX);
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)) )
350 minY = std::min(data.center.y - data.radius, minY);
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) )
357 maxY = std::max(data.center.y + data.radius, maxY);
359 minV.set(minX, minY);
360 maxV.set(maxX, maxY);
363 Vector Arc::getNearestEndpoint(const Vector & coord, double * dist)
366 Vector * nearerPoint;
368 dist1 = startpoint.distanceTo(coord);
369 dist2 = endpoint.distanceTo(coord);
375 nearerPoint = &endpoint;
381 nearerPoint = &startpoint;
387 Vector Arc::getNearestPointOnEntity(const Vector & coord, bool onEntity, double * dist, Entity * * entity)
394 double angle = (coord - data.center).angle();
396 if (onEntity == false || Math::isAngleBetween(angle,
397 data.angle1, data.angle2, isReversed()))
399 vec.setPolar(data.radius, angle);
404 *dist = fabs((vec - data.center).magnitude() - data.radius);
409 Vector Arc::getNearestCenter(const Vector & coord, double * dist)
412 *dist = coord.distanceTo(data.center);
416 Vector Arc::getNearestMiddle(const Vector & coord, double * dist)
418 Vector ret = getMiddlepoint();
421 *dist = coord.distanceTo(ret);
425 Vector Arc::getNearestDist(double distance, const Vector & coord, double * dist)
427 if (data.radius < 1.0e-6)
430 *dist = RS_MAXDOUBLE;
431 return Vector(false);
436 double aDist = distance / data.radius;
440 a1 = data.angle1 - aDist;
441 a2 = data.angle2 + aDist;
445 a1 = data.angle1 + aDist;
446 a2 = data.angle2 - aDist;
449 p1.setPolar(data.radius, a1);
451 p2.setPolar(data.radius, a2);
455 Vector * nearerPoint;
457 dist1 = p1.distanceTo(coord);
458 dist2 = p2.distanceTo(coord);
476 Vector Arc::getNearestDist(double distance, bool startp)
478 if (data.radius < 1.0e-6)
479 return Vector(false);
483 double aDist = distance / data.radius;
488 a = data.angle1 - aDist;
490 a = data.angle2 + aDist;
495 a = data.angle1 + aDist;
497 a = data.angle2 - aDist;
500 p.setPolar(data.radius, a);
506 double Arc::getDistanceToPoint(const Vector & coord, Entity * * entity, RS2::ResolveLevel, double)
511 // check endpoints first:
512 double dist = coord.distanceTo(getStartpoint());
516 dist = coord.distanceTo(getEndpoint());
521 if (Math::isAngleBetween(data.center.angleTo(coord),
522 data.angle1, data.angle2,
525 return fabs((coord - data.center).magnitude() - data.radius);
530 void Arc::moveStartpoint(const Vector & pos)
532 // polyline arcs: move point not angle:
533 //if (parent!=NULL && parent->rtti()==RS2::EntityPolyline) {
534 double bulge = getBulge();
535 createFrom2PBulge(pos, getEndpoint(), bulge);
538 // normal arc: move angle1
540 data.angle1 = data.center.angleTo(pos);
541 calculateEndpoints();
546 void Arc::moveEndpoint(const Vector & pos)
548 // polyline arcs: move point not angle:
549 //if (parent!=NULL && parent->rtti()==RS2::EntityPolyline) {
550 double bulge = getBulge();
551 createFrom2PBulge(getStartpoint(), pos, bulge);
554 // normal arc: move angle1
556 data.angle2 = data.center.angleTo(pos);
557 calculateEndpoints();
562 void Arc::trimStartpoint(const Vector & pos)
564 data.angle1 = data.center.angleTo(pos);
565 calculateEndpoints();
569 void Arc::trimEndpoint(const Vector & pos)
571 data.angle2 = data.center.angleTo(pos);
572 calculateEndpoints();
576 RS2::Ending Arc::getTrimPoint(const Vector & coord, const Vector & trimPoint)
578 double angEl = data.center.angleTo(trimPoint);
579 double angM = data.center.angleTo(coord);
581 if (Math::getAngleDifference(angM, angEl) > M_PI)
584 return RS2::EndingEnd;
586 return RS2::EndingStart;
591 return RS2::EndingStart;
593 return RS2::EndingEnd;
599 double a = data.angle1;
600 data.angle1 = data.angle2;
602 data.reversed = !data.reversed;
603 calculateEndpoints();
607 void Arc::move(Vector offset)
609 data.center.move(offset);
610 calculateEndpoints();
614 void Arc::rotate(Vector center, double angle)
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();
622 DEBUG->print("Arc::rotate: OK");
625 void Arc::scale(Vector center, Vector factor)
627 // negative scaling: mirroring
629 mirror(data.center, data.center + Vector(0.0, 1.0));
634 mirror(data.center, data.center + Vector(1.0, 0.0));
637 data.center.scale(center, factor);
638 data.radius *= factor.x;
640 if (data.radius < 0.0)
642 calculateEndpoints();
646 void Arc::mirror(Vector axisPoint1, Vector axisPoint2)
648 data.center.mirror(axisPoint1, axisPoint2);
649 data.reversed = (!data.reversed);
651 startpoint.mirror(axisPoint1, axisPoint2);
652 endpoint.mirror(axisPoint1, axisPoint2);
654 data.angle1 = data.center.angleTo(startpoint);
655 data.angle2 = data.center.angleTo(endpoint);
659 vec.setPolar(1.0, data.angle1);
660 vec.mirror(Vector(0.0, 0.0), axisPoint2 - axisPoint1);
661 data.angle1 = vec.angle();
663 vec.setPolar(1.0, data.angle2);
664 vec.mirror(Vector(0.0, 0.0), axisPoint2 - axisPoint1);
665 data.angle2 = vec.angle();
667 calculateEndpoints();
671 void Arc::moveRef(const Vector & ref, const Vector & offset)
673 if (ref.distanceTo(startpoint) < 1.0e-4)
674 moveStartpoint(startpoint + offset);
677 if (ref.distanceTo(endpoint) < 1.0e-4)
678 moveEndpoint(endpoint + offset);
681 void Arc::stretch(Vector firstCorner, Vector secondCorner, Vector offset)
683 if (getMin().isInWindow(firstCorner, secondCorner)
684 && getMax().isInWindow(firstCorner, secondCorner))
689 if (getStartpoint().isInWindow(firstCorner,
691 moveStartpoint(getStartpoint() + offset);
694 if (getEndpoint().isInWindow(firstCorner,
696 moveEndpoint(getEndpoint() + offset);
700 void Arc::draw(PaintInterface * painter, GraphicView * view, double /*patternOffset*/)
702 if (painter == NULL || view == NULL)
705 //double styleFactor = getStyleFactor();
707 // simple style-less lines
708 if (getPen().getLineType() == RS2::SolidLine
710 || view->getDrawingMode() == RS2::ModePreview)
712 painter->drawArc(view->toGui(getCenter()),
713 getRadius() * view->getFactor().x,
714 getAngle1(), getAngle2(),
718 double styleFactor = getStyleFactor(view);
720 if (styleFactor < 0.0)
722 painter->drawArc(view->toGui(getCenter()),
723 getRadius() * view->getFactor().x,
724 getAngle1(), getAngle2(),
730 LineTypePattern * pat;
733 pat = &patternSelected;
735 pat = view->getPattern(getPen().getLineType());
740 if (getRadius() < 1.0e-6)
743 // Pen to draw pattern is always solid:
744 Pen pen = painter->getPen();
745 pen.setLineType(RS2::SolidLine);
746 painter->setPen(pen);
762 double * da; // array of distances in x.
763 int i; // index counter
765 double length = getAngleLength();
767 // create scaled pattern:
768 da = new double[pat->num];
770 for (i = 0; i < pat->num; ++i)
771 da[i] = fabs(pat->pattern[i] * styleFactor) / getRadius();
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;
784 if (pat->pattern[i] > 0.0)
786 if (tot + da[i] < length)
787 painter->drawArc(cp, r,
792 painter->drawArc(cp, r,
813 * @return Middle point of the entity.
815 Vector Arc::getMiddlepoint() const
821 a = data.angle1 - getAngleLength() / 2.0;
823 a = data.angle1 + getAngleLength() / 2.0;
824 ret.setPolar(data.radius, a);
831 * @return Angle length in rad.
833 double Arc::getAngleLength() const
839 if (data.angle1 < data.angle2)
840 ret = data.angle1 + 2 * M_PI - data.angle2;
842 ret = data.angle1 - data.angle2;
846 if (data.angle2 < data.angle1)
847 ret = data.angle2 + 2 * M_PI - data.angle1;
849 ret = data.angle2 - data.angle1;
853 if (fabs(ret) < 1.0e-6)
860 * @return Length of the arc.
862 double Arc::getLength()
864 return getAngleLength() * data.radius;
868 * Gets the arc's bulge (tangens of angle length divided by 4).
870 double Arc::getBulge() const
872 double bulge = tan(fabs(getAngleLength()) / 4.0);
880 * Dumps the point's data to stdout.
882 std::ostream & operator<<(std::ostream & os, const Arc & a)
884 os << " Arc: " << a.data << "\n";