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. :-)
15 #include "rs_ellipse.h"
18 #include "graphicview.h"
19 #include "rs_information.h"
20 #include "rs_linetypepattern.h"
21 #include "paintintf.h"
26 RS_Ellipse::RS_Ellipse(RS_EntityContainer * parent, const RS_EllipseData & d):
27 RS_AtomicEntity(parent), data(d)
29 //calculateEndpoints();
33 /*virtual*/ RS_Ellipse::~RS_Ellipse()
37 /*virtual*/ RS_Entity * RS_Ellipse::clone()
39 RS_Ellipse * e = new RS_Ellipse(*this);
44 /** @return RS2::EntityEllipse */
45 /*virtual*/ RS2::EntityType RS_Ellipse::rtti() const
47 return RS2::EntityEllipse;
51 * @return Start point of the entity.
53 /*virtual*/ Vector RS_Ellipse::getStartpoint() const
56 p.set(data.center.x + cos(data.angle1) * getMajorRadius(),
57 data.center.y + sin(data.angle1) * getMinorRadius());
58 p.rotate(data.center, getAngle());
63 * @return End point of the entity.
65 /*virtual*/ Vector RS_Ellipse::getEndpoint() const
68 p.set(data.center.x + cos(data.angle2) * getMajorRadius(),
69 data.center.y + sin(data.angle2) * getMinorRadius());
70 p.rotate(data.center, getAngle());
74 void RS_Ellipse::moveStartpoint(const Vector & pos)
76 data.angle1 = getEllipseAngle(pos);
77 //data.angle1 = data.center.angleTo(pos);
78 //calculateEndpoints();
82 void RS_Ellipse::moveEndpoint(const Vector & pos)
84 data.angle2 = getEllipseAngle(pos);
85 //data.angle2 = data.center.angleTo(pos);
86 //calculateEndpoints();
90 RS2::Ending RS_Ellipse::getTrimPoint(const Vector & coord, const Vector & trimPoint)
92 double angEl = getEllipseAngle(trimPoint);
93 double angM = getEllipseAngle(coord);
95 if (RS_Math::getAngleDifference(angM, angEl) > M_PI)
96 //if (data.reversed) {
97 // return RS2::EndingEnd;
100 return RS2::EndingStart;
103 //if (data.reversed) {
104 // return RS2::EndingStart;
107 return RS2::EndingEnd;
111 double RS_Ellipse::getEllipseAngle(const Vector & pos)
114 m.rotate(data.center, -data.majorP.angle());
115 Vector v = m - data.center;
116 v.scale(Vector(1.0, 1.0 / data.ratio));
120 /** @return Copy of data that defines the ellipse. **/
121 RS_EllipseData RS_Ellipse::getData()
126 VectorSolutions RS_Ellipse::getRefPoints()
128 VectorSolutions ret(getStartpoint(), getEndpoint(), data.center);
133 * @retval true if the arc is reversed (clockwise),
134 * @retval false otherwise
136 bool RS_Ellipse::isReversed() const
138 return data.reversed;
141 /** sets the reversed status. */
142 void RS_Ellipse::setReversed(bool r)
147 /** @return The rotation angle of this ellipse */
148 double RS_Ellipse::getAngle() const
150 return data.majorP.angle();
153 /** @return The start angle of this arc */
154 double RS_Ellipse::getAngle1()
159 /** Sets new start angle. */
160 void RS_Ellipse::setAngle1(double a1)
165 /** @return The end angle of this arc */
166 double RS_Ellipse::getAngle2()
171 /** Sets new end angle. */
172 void RS_Ellipse::setAngle2(double a2)
177 /** @return The center point (x) of this arc */
178 Vector RS_Ellipse::getCenter()
183 /** Sets new center. */
184 void RS_Ellipse::setCenter(const Vector & c)
189 /** @return The endpoint of the major axis (relative to center). */
190 Vector RS_Ellipse::getMajorP()
195 /** Sets new major point (relative to center). */
196 void RS_Ellipse::setMajorP(const Vector & p)
201 /** @return The ratio of minor to major axis */
202 double RS_Ellipse::getRatio()
207 /** Sets new ratio. */
208 void RS_Ellipse::setRatio(double r)
214 * @return Angle length in rad.
216 /*virtual*/ double RS_Ellipse::getAngleLength() const
219 return data.angle1 - data.angle2;
221 return data.angle2 - data.angle1;
224 /** @return The major radius of this ellipse. Same as getRadius() */
225 double RS_Ellipse::getMajorRadius() const
227 return data.majorP.magnitude();
230 /** @return The minor radius of this ellipse */
231 double RS_Ellipse::getMinorRadius() const
233 return data.majorP.magnitude() * data.ratio;
237 * Recalculates the endpoints using the angles and the radius.
240 void RS_Ellipse::calculateEndpoints() {
241 double angle = data.majorP.angle();
242 double radius1 = getMajorRadius();
243 double radius2 = getMinorRadius();
245 startpoint.set(data.center.x + cos(data.angle1) * radius1,
246 data.center.y + sin(data.angle1) * radius2);
247 startpoint.rotate(data.center, angle);
248 endpoint.set(data.center.x + cos(data.angle2) * radius1,
249 data.center.y + sin(data.angle2) * radius2);
250 endpoint.rotate(data.center, angle);
255 * Calculates the boundary box of this ellipse.
257 * @todo Fix that - the algorithm used is really bad / slow.
259 void RS_Ellipse::calculateBorders()
261 RS_DEBUG->print("RS_Ellipse::calculateBorders");
263 double radius1 = getMajorRadius();
264 double radius2 = getMinorRadius();
265 double angle = getAngle();
266 double a1 = ((!isReversed()) ? data.angle1 : data.angle2);
267 double a2 = ((!isReversed()) ? data.angle2 : data.angle1);
268 Vector startpoint = getStartpoint();
269 Vector endpoint = getEndpoint();
271 double minX = std::min(startpoint.x, endpoint.x);
272 double minY = std::min(startpoint.y, endpoint.y);
273 double maxX = std::max(startpoint.x, endpoint.x);
274 double maxY = std::max(startpoint.y, endpoint.y);
276 // kind of a brute force. TODO: exact calculation
282 vp.set(data.center.x + radius1 * cos(a),
283 data.center.y + radius2 * sin(a));
284 vp.rotate(data.center, angle);
286 minX = std::min(minX, vp.x);
287 minY = std::min(minY, vp.y);
288 maxX = std::max(maxX, vp.x);
289 maxY = std::max(maxY, vp.y);
293 while (RS_Math::isAngleBetween(RS_Math::correctAngle(a), a1, a2, false)
296 minV.set(minX, minY);
297 maxV.set(maxX, maxY);
298 RS_DEBUG->print("RS_Ellipse::calculateBorders: OK");
301 Vector RS_Ellipse::getNearestEndpoint(const Vector & coord, double * dist)
305 Vector startpoint = getStartpoint();
306 Vector endpoint = getEndpoint();
308 dist1 = startpoint.distanceTo(coord);
309 dist2 = endpoint.distanceTo(coord);
315 nearerPoint = endpoint;
321 nearerPoint = startpoint;
327 Vector RS_Ellipse::getNearestPointOnEntity(const Vector & coord, bool onEntity, double * dist, RS_Entity * * entity)
329 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity");
335 double ang = getAngle();
337 Vector normalized = (coord - data.center).rotate(-ang);
339 double dU = normalized.x;
340 double dV = normalized.y;
341 double dA = getMajorRadius();
342 double dB = getMinorRadius();
343 double dEpsilon = 1.0e-8;
350 bool majorSwap = false;
370 double dT = dB * (dV - dB);
375 for (i = 0; i < iMax; i++)
377 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: i: %d", i);
378 double dTpASqr = dT + dA * dA;
379 double dTpBSqr = dT + dB * dB;
380 double dInvTpASqr = 1.0 / dTpASqr;
381 double dInvTpBSqr = 1.0 / dTpBSqr;
382 double dXDivA = dA * dU * dInvTpASqr;
383 double dYDivB = dB * dV * dInvTpBSqr;
384 double dXDivASqr = dXDivA * dXDivA;
385 double dYDivBSqr = dYDivB * dYDivB;
386 double dF = dXDivASqr + dYDivBSqr - 1.0;
387 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: dF: %f", dF);
389 if (fabs(dF) < dEpsilon)
391 // F(t0) is close enough to zero, terminate the iteration:
395 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: rdX,rdY 1: %f,%f", rdX, rdY);
398 double dFDer = 2.0 * (dXDivASqr * dInvTpASqr + dYDivBSqr * dInvTpBSqr);
399 double dRatio = dF / dFDer;
400 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: dRatio: %f", dRatio);
402 if (fabs(dRatio) < dEpsilon)
404 // t1-t0 is close enough to zero, terminate the iteration:
408 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: rdX,rdY 2: %f,%f", rdX, rdY);
416 // failed to converge:
417 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: failed");
418 dDistance = RS_MAXDOUBLE;
422 double dDelta0 = rdX - dU;
423 double dDelta1 = rdY - dV;
424 dDistance = sqrt(dDelta0 * dDelta0 + dDelta1 * dDelta1);
425 ret = Vector(rdX, rdY);
426 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: rdX,rdY 2: %f,%f", rdX, rdY);
427 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: ret: %f,%f", ret.x, ret.y);
435 *dist = RS_MAXDOUBLE;
450 ret = (ret.rotate(ang) + data.center);
454 double a1 = data.center.angleTo(getStartpoint());
455 double a2 = data.center.angleTo(getEndpoint());
456 double a = data.center.angleTo(ret);
458 if (!RS_Math::isAngleBetween(a, a1, a2, data.reversed))
467 * @param tolerance Tolerance.
469 * @retval true if the given point is on this entity.
470 * @retval false otherwise
472 bool RS_Ellipse::isPointOnEntity(const Vector & coord, double tolerance)
474 double dist = getDistanceToPoint(coord, NULL, RS2::ResolveNone);
475 return (dist <= tolerance);
478 Vector RS_Ellipse::getNearestCenter(const Vector & coord, double * dist)
481 *dist = coord.distanceTo(data.center);
486 * @todo Implement this.
488 Vector RS_Ellipse::getNearestMiddle(const Vector & /*coord*/, double * dist)
491 *dist = RS_MAXDOUBLE;
492 return Vector(false);
495 Vector RS_Ellipse::getNearestDist(double /*distance*/, const Vector & /*coord*/, double * dist)
498 *dist = RS_MAXDOUBLE;
499 return Vector(false);
502 double RS_Ellipse::getDistanceToPoint(const Vector & coord, RS_Entity * * entity, RS2::ResolveLevel, double /*solidDist*/)
504 double dist = RS_MAXDOUBLE;
505 getNearestPointOnEntity(coord, true, &dist, entity);
510 void RS_Ellipse::move(Vector offset)
512 data.center.move(offset);
513 //calculateEndpoints();
517 void RS_Ellipse::rotate(Vector center, double angle)
519 data.center.rotate(center, angle);
520 data.majorP.rotate(angle);
521 //calculateEndpoints();
525 void RS_Ellipse::scale(Vector center, Vector factor)
527 data.center.scale(center, factor);
528 data.majorP.scale(factor);
529 //calculateEndpoints();
534 * @todo deal with angles correctly
536 void RS_Ellipse::mirror(Vector axisPoint1, Vector axisPoint2)
538 Vector mp = data.center + data.majorP;
540 data.center.mirror(axisPoint1, axisPoint2);
541 mp.mirror(axisPoint1, axisPoint2);
543 data.majorP = mp - data.center;
545 double a = axisPoint1.angleTo(axisPoint2);
548 vec.setPolar(1.0, data.angle1);
549 vec.mirror(Vector(0.0, 0.0), axisPoint2 - axisPoint1);
550 data.angle1 = vec.angle() - 2 * a;
552 vec.setPolar(1.0, data.angle2);
553 vec.mirror(Vector(0.0, 0.0), axisPoint2 - axisPoint1);
554 data.angle2 = vec.angle() - 2 * a;
556 data.reversed = (!data.reversed);
558 //calculateEndpoints();
562 void RS_Ellipse::moveRef(const Vector & ref, const Vector & offset)
564 Vector startpoint = getStartpoint();
565 Vector endpoint = getEndpoint();
567 if (ref.distanceTo(startpoint) < 1.0e-4)
568 moveStartpoint(startpoint + offset);
571 if (ref.distanceTo(endpoint) < 1.0e-4)
572 moveEndpoint(endpoint + offset);
575 void RS_Ellipse::draw(PaintInterface * painter, GraphicView * view, double /*patternOffset*/)
577 if (!painter || !view)
580 if (getPen().getLineType() == RS2::SolidLine || isSelected()
581 || view->getDrawingMode() == RS2::ModePreview)
582 painter->drawEllipse(view->toGui(getCenter()),
583 getMajorRadius() * view->getFactor().x,
584 getMinorRadius() * view->getFactor().x,
585 getAngle(), getAngle1(), getAngle2(), isReversed());
588 double styleFactor = getStyleFactor(view);
590 if (styleFactor < 0.0)
592 painter->drawEllipse(view->toGui(getCenter()),
593 getMajorRadius() * view->getFactor().x,
594 getMinorRadius() * view->getFactor().x,
595 getAngle(), getAngle1(), getAngle2(), isReversed());
600 RS_LineTypePattern * pat;
603 pat = &patternSelected;
605 pat = view->getPattern(getPen().getLineType());
610 // Pen to draw pattern is always solid:
611 RS_Pen pen = painter->getPen();
612 pen.setLineType(RS2::SolidLine);
613 painter->setPen(pen);
615 double * da; // array of distances in x.
616 int i; // index counter
618 double length = getAngleLength();
621 da = new double[pat->num];
626 double curA = getAngle1();
628 Vector cp = view->toGui(getCenter());
629 double r1 = getMajorRadius() * view->getFactor().x;
630 double r2 = getMinorRadius() * view->getFactor().x;
634 curR = sqrt(RS_Math::pow(getMinorRadius() * cos(curA), 2.0)
635 + RS_Math::pow(getMajorRadius() * sin(curA), 2.0));
639 da[i] = fabs(pat->pattern[i] * styleFactor) / curR;
641 if (pat->pattern[i] * styleFactor > 0.0)
643 if (tot + fabs(da[i]) < length)
644 painter->drawEllipse(cp, r1, r2, getAngle(), curA, curA + da[i], false);
646 painter->drawEllipse(cp, r1, r2, getAngle(), curA, getAngle2(), false);
666 * Dumps the point's data to stdout.
668 std::ostream & operator<<(std::ostream & os, const RS_Ellipse & a)
670 os << " Ellipse: " << a.data << "\n";