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. :-)
17 #include "rs_constructionline.h"
18 #include "rs_linetypepattern.h"
19 #include "rs_information.h"
21 #include "rs_graphicview.h"
22 #include "paintintf.h"
25 * Default constructor.
27 RS_Arc::RS_Arc(RS_EntityContainer* parent,
29 : RS_AtomicEntity(parent), data(d) {
37 * Creates this arc from 3 given points which define the arc line.
39 * @param p1 1st point.
40 * @param p2 2nd point.
41 * @param p3 3rd point.
43 bool RS_Arc::createFrom3P(const Vector& p1, const Vector& p2,
45 if (p1.distanceTo(p2)>RS_TOLERANCE &&
46 p2.distanceTo(p3)>RS_TOLERANCE &&
47 p3.distanceTo(p1)>RS_TOLERANCE) {
49 // middle points between 3 points:
54 // intersection of two middle lines
56 a1 = p1.angleTo(p2) + M_PI/2.0;
57 dir1.setPolar(100.0, a1);
59 a2 = p2.angleTo(p3) + M_PI/2.0;
60 dir2.setPolar(100.0, a2);
62 RS_ConstructionLineData d1(mp1, mp1 + dir1);
63 RS_ConstructionLineData d2(mp2, mp2 + dir2);
64 RS_ConstructionLine midLine1(NULL, d1);
65 RS_ConstructionLine midLine2(NULL, d2);
68 RS_Information::getIntersection(&midLine1, &midLine2);
70 data.center = sol.get(0);
71 data.radius = data.center.distanceTo(p3);
72 data.angle1 = data.center.angleTo(p1);
73 data.angle2 = data.center.angleTo(p3);
74 data.reversed = RS_Math::isAngleBetween(data.center.angleTo(p2),
75 data.angle1, data.angle2, true);
77 if (sol.get(0).valid && data.radius<1.0e14 &&
78 data.radius>RS_TOLERANCE) {
83 RS_DEBUG->print("RS_Arc::createFrom3P(): "
84 "Cannot create an arc with inf radius.");
88 RS_DEBUG->print("RS_Arc::createFrom3P(): "
89 "Cannot create an arc with radius 0.0.");
97 * Creates an arc from its startpoint, endpoint, start direction (angle)
100 * @retval true Successfully created arc
101 * @retval false Cannot creats arc (radius to small or endpoint to far away)
103 bool RS_Arc::createFrom2PDirectionRadius(const Vector& startPoint,
104 const Vector& endPoint,
105 double direction1, double radius) {
108 ortho.setPolar(radius, direction1 + M_PI/2.0);
109 Vector center1 = startPoint + ortho;
110 Vector center2 = startPoint - ortho;
112 if (center1.distanceTo(endPoint) < center2.distanceTo(endPoint)) {
113 data.center = center1;
115 data.center = center2;
118 data.radius = radius;
119 data.angle1 = data.center.angleTo(startPoint);
120 data.angle2 = data.center.angleTo(endPoint);
121 data.reversed = false;
123 double diff = RS_Math::correctAngle(getDirection1()-direction1);
124 if (fabs(diff-M_PI)<1.0e-1) {
125 data.reversed = true;
128 calculateEndpoints();
137 * Creates an arc from its startpoint, endpoint and bulge.
139 bool RS_Arc::createFrom2PBulge(const Vector& startPoint, const Vector& endPoint,
141 data.reversed = (bulge<0.0);
142 double alpha = atan(bulge)*4.0;
144 Vector middle = (startPoint+endPoint)/2.0;
145 double dist = startPoint.distanceTo(endPoint)/2.0;
147 // alpha can't be 0.0 at this point
148 data.radius = fabs(dist / sin(alpha/2.0));
150 double wu = fabs(RS_Math::pow(data.radius, 2.0) - RS_Math::pow(dist, 2.0));
152 double angle = startPoint.angleTo(endPoint);
160 if (fabs(alpha)>M_PI) {
164 data.center.setPolar(h, angle);
166 data.angle1 = data.center.angleTo(startPoint);
167 data.angle2 = data.center.angleTo(endPoint);
169 calculateEndpoints();
178 * Recalculates the endpoints using the angles and the radius.
180 void RS_Arc::calculateEndpoints() {
181 startpoint.set(data.center.x + cos(data.angle1) * data.radius,
182 data.center.y + sin(data.angle1) * data.radius);
183 endpoint.set(data.center.x + cos(data.angle2) * data.radius,
184 data.center.y + sin(data.angle2) * data.radius);
188 void RS_Arc::calculateBorders() {
189 double minX = std::min(startpoint.x, endpoint.x);
190 double minY = std::min(startpoint.y, endpoint.y);
191 double maxX = std::max(startpoint.x, endpoint.x);
192 double maxY = std::max(startpoint.y, endpoint.y);
194 double a1 = !isReversed() ? data.angle1 : data.angle2;
195 double a2 = !isReversed() ? data.angle2 : data.angle1;
197 // check for left limit:
198 if ((a1<M_PI && a2>M_PI) ||
199 (a1>a2-1.0e-12 && a2>M_PI) ||
200 (a1>a2-1.0e-12 && a1<M_PI) ) {
202 minX = std::min(data.center.x - data.radius, minX);
205 // check for right limit:
206 if (a1 > a2-1.0e-12) {
207 maxX = std::max(data.center.x + data.radius, maxX);
210 // check for bottom limit:
211 if ((a1<(M_PI_2*3) && a2>(M_PI_2*3)) ||
212 (a1>a2-1.0e-12 && a2>(M_PI_2*3)) ||
213 (a1>a2-1.0e-12 && a1<(M_PI_2*3)) ) {
215 minY = std::min(data.center.y - data.radius, minY);
218 // check for top limit:
219 if ((a1<M_PI_2 && a2>M_PI_2) ||
220 (a1>a2-1.0e-12 && a2>M_PI_2) ||
221 (a1>a2-1.0e-12 && a1<M_PI_2) ) {
223 maxY = std::max(data.center.y + data.radius, maxY);
226 minV.set(minX, minY);
227 maxV.set(maxX, maxY);
232 VectorSolutions RS_Arc::getRefPoints() {
233 VectorSolutions ret(startpoint, endpoint, data.center);
238 Vector RS_Arc::getNearestEndpoint(const Vector& coord, double* dist) {
242 dist1 = startpoint.distanceTo(coord);
243 dist2 = endpoint.distanceTo(coord);
249 nearerPoint = &endpoint;
254 nearerPoint = &startpoint;
262 Vector RS_Arc::getNearestPointOnEntity(const Vector& coord,
263 bool onEntity, double* dist, RS_Entity** entity) {
270 double angle = (coord-data.center).angle();
271 if (onEntity==false || RS_Math::isAngleBetween(angle,
272 data.angle1, data.angle2, isReversed())) {
273 vec.setPolar(data.radius, angle);
277 *dist = fabs((vec-data.center).magnitude()-data.radius);
285 Vector RS_Arc::getNearestCenter(const Vector& coord,
288 *dist = coord.distanceTo(data.center);
295 Vector RS_Arc::getNearestMiddle(const Vector& coord,
298 Vector ret = getMiddlepoint();
301 *dist = coord.distanceTo(ret);
308 Vector RS_Arc::getNearestDist(double distance,
312 if (data.radius<1.0e-6) {
314 *dist = RS_MAXDOUBLE;
316 return Vector(false);
321 double aDist = distance / data.radius;
324 a1 = data.angle1 - aDist;
325 a2 = data.angle2 + aDist;
327 a1 = data.angle1 + aDist;
328 a2 = data.angle2 - aDist;
331 p1.setPolar(data.radius, a1);
333 p2.setPolar(data.radius, a2);
339 dist1 = p1.distanceTo(coord);
340 dist2 = p2.distanceTo(coord);
360 Vector RS_Arc::getNearestDist(double distance,
363 if (data.radius<1.0e-6) {
364 return Vector(false);
369 double aDist = distance / data.radius;
373 a = data.angle1 - aDist;
375 a = data.angle2 + aDist;
379 a = data.angle1 + aDist;
381 a = data.angle2 - aDist;
385 p.setPolar(data.radius, a);
393 double RS_Arc::getDistanceToPoint(const Vector& coord,
401 // check endpoints first:
402 double dist = coord.distanceTo(getStartpoint());
406 dist = coord.distanceTo(getEndpoint());
411 if (RS_Math::isAngleBetween(data.center.angleTo(coord),
412 data.angle1, data.angle2,
415 return fabs((coord-data.center).magnitude() - data.radius);
423 void RS_Arc::moveStartpoint(const Vector& pos) {
424 // polyline arcs: move point not angle:
425 //if (parent!=NULL && parent->rtti()==RS2::EntityPolyline) {
426 double bulge = getBulge();
427 createFrom2PBulge(pos, getEndpoint(), bulge);
430 // normal arc: move angle1
432 data.angle1 = data.center.angleTo(pos);
433 calculateEndpoints();
440 void RS_Arc::moveEndpoint(const Vector& pos) {
441 // polyline arcs: move point not angle:
442 //if (parent!=NULL && parent->rtti()==RS2::EntityPolyline) {
443 double bulge = getBulge();
444 createFrom2PBulge(getStartpoint(), pos, bulge);
447 // normal arc: move angle1
449 data.angle2 = data.center.angleTo(pos);
450 calculateEndpoints();
457 void RS_Arc::trimStartpoint(const Vector& pos) {
458 data.angle1 = data.center.angleTo(pos);
459 calculateEndpoints();
465 void RS_Arc::trimEndpoint(const Vector& pos) {
466 data.angle2 = data.center.angleTo(pos);
467 calculateEndpoints();
472 RS2::Ending RS_Arc::getTrimPoint(const Vector& coord,
473 const Vector& trimPoint) {
475 double angEl = data.center.angleTo(trimPoint);
476 double angM = data.center.angleTo(coord);
478 if (RS_Math::getAngleDifference(angM, angEl)>M_PI) {
480 return RS2::EndingEnd;
482 return RS2::EndingStart;
486 return RS2::EndingStart;
488 return RS2::EndingEnd;
494 void RS_Arc::reverse() {
495 double a = data.angle1;
496 data.angle1 = data.angle2;
498 data.reversed = !data.reversed;
499 calculateEndpoints();
504 void RS_Arc::move(Vector offset) {
505 data.center.move(offset);
506 calculateEndpoints();
512 void RS_Arc::rotate(Vector center, double angle) {
513 RS_DEBUG->print("RS_Arc::rotate");
514 data.center.rotate(center, angle);
515 data.angle1 = RS_Math::correctAngle(data.angle1+angle);
516 data.angle2 = RS_Math::correctAngle(data.angle2+angle);
517 calculateEndpoints();
519 RS_DEBUG->print("RS_Arc::rotate: OK");
524 void RS_Arc::scale(Vector center, Vector factor) {
525 // negative scaling: mirroring
527 mirror(data.center, data.center + Vector(0.0, 1.0));
531 mirror(data.center, data.center + Vector(1.0, 0.0));
535 data.center.scale(center, factor);
536 data.radius *= factor.x;
537 if (data.radius<0.0) {
540 calculateEndpoints();
546 void RS_Arc::mirror(Vector axisPoint1, Vector axisPoint2) {
547 data.center.mirror(axisPoint1, axisPoint2);
548 data.reversed = (!data.reversed);
550 startpoint.mirror(axisPoint1, axisPoint2);
551 endpoint.mirror(axisPoint1, axisPoint2);
553 data.angle1 = data.center.angleTo(startpoint);
554 data.angle2 = data.center.angleTo(endpoint);
558 vec.setPolar(1.0, data.angle1);
559 vec.mirror(Vector(0.0,0.0), axisPoint2-axisPoint1);
560 data.angle1 = vec.angle();
562 vec.setPolar(1.0, data.angle2);
563 vec.mirror(Vector(0.0,0.0), axisPoint2-axisPoint1);
564 data.angle2 = vec.angle();
566 calculateEndpoints();
572 void RS_Arc::moveRef(const Vector& ref, const Vector& offset) {
573 if (ref.distanceTo(startpoint)<1.0e-4) {
574 moveStartpoint(startpoint+offset);
576 if (ref.distanceTo(endpoint)<1.0e-4) {
577 moveEndpoint(endpoint+offset);
583 void RS_Arc::stretch(Vector firstCorner,
587 if (getMin().isInWindow(firstCorner, secondCorner) &&
588 getMax().isInWindow(firstCorner, secondCorner)) {
593 if (getStartpoint().isInWindow(firstCorner,
595 moveStartpoint(getStartpoint() + offset);
597 if (getEndpoint().isInWindow(firstCorner,
599 moveEndpoint(getEndpoint() + offset);
604 //void RS_Arc::draw(RS_Painter* painter, RS_GraphicView* view,
605 void RS_Arc::draw(PaintInterface * painter, RS_GraphicView * view, double /*patternOffset*/)
607 if (painter == NULL || view == NULL)
610 //double styleFactor = getStyleFactor();
612 // simple style-less lines
613 if (getPen().getLineType()==RS2::SolidLine ||
615 view->getDrawingMode()==RS2::ModePreview) {
617 painter->drawArc(view->toGui(getCenter()),
618 getRadius() * view->getFactor().x,
619 getAngle1(), getAngle2(),
622 double styleFactor = getStyleFactor(view);
623 if (styleFactor<0.0) {
624 painter->drawArc(view->toGui(getCenter()),
625 getRadius() * view->getFactor().x,
626 getAngle1(), getAngle2(),
632 RS_LineTypePattern* pat;
634 pat = &patternSelected;
636 pat = view->getPattern(getPen().getLineType());
643 if (getRadius()<1.0e-6) {
647 // Pen to draw pattern is always solid:
648 RS_Pen pen = painter->getPen();
649 pen.setLineType(RS2::SolidLine);
650 painter->setPen(pen);
662 double* da; // array of distances in x.
663 int i; // index counter
665 double length = getAngleLength();
667 // create scaled pattern:
668 da = new double[pat->num];
670 for (i=0; i<pat->num; ++i) {
671 da[i] = fabs(pat->pattern[i] * styleFactor) / getRadius();
678 //double cx = getCenter().x * factor.x + offsetX;
679 //double cy = - a->getCenter().y * factor.y + getHeight() - offsetY;
680 Vector cp = view->toGui(getCenter());
681 double r = getRadius() * view->getFactor().x;
684 if (pat->pattern[i] > 0.0) {
685 if (tot+da[i]<length) {
686 painter->drawArc(cp, r,
691 painter->drawArc(cp, r,
714 * @return Middle point of the entity.
716 Vector RS_Arc::getMiddlepoint() const {
721 a = data.angle1 - getAngleLength()/2.0;
723 a = data.angle1 + getAngleLength()/2.0;
725 ret.setPolar(data.radius, a);
734 * @return Angle length in rad.
736 double RS_Arc::getAngleLength() const {
740 if (data.angle1<data.angle2) {
741 ret = data.angle1+2*M_PI-data.angle2;
743 ret = data.angle1-data.angle2;
746 if (data.angle2<data.angle1) {
747 ret = data.angle2+2*M_PI-data.angle1;
749 ret = data.angle2-data.angle1;
754 if (fabs(ret)<1.0e-6) {
764 * @return Length of the arc.
766 double RS_Arc::getLength() {
767 return getAngleLength()*data.radius;
773 * Gets the arc's bulge (tangens of angle length divided by 4).
775 double RS_Arc::getBulge() const {
776 double bulge = tan(fabs(getAngleLength())/4.0);
786 * Dumps the point's data to stdout.
788 std::ostream& operator << (std::ostream& os, const RS_Arc& a) {
789 os << " Arc: " << a.data << "\n";