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. :-)
20 #include "graphicview.h"
21 #include "information.h"
22 #include "linetypepattern.h"
23 #include "paintinterface.h"
28 RS_Ellipse::RS_Ellipse(RS_EntityContainer * parent, const RS_EllipseData & d):
29 RS_AtomicEntity(parent), data(d)
31 //calculateEndpoints();
35 /*virtual*/ RS_Ellipse::~RS_Ellipse()
39 /*virtual*/ RS_Entity * RS_Ellipse::clone()
41 RS_Ellipse * e = new RS_Ellipse(*this);
46 /** @return RS2::EntityEllipse */
47 /*virtual*/ RS2::EntityType RS_Ellipse::rtti() const
49 return RS2::EntityEllipse;
53 * @return Start point of the entity.
55 /*virtual*/ Vector RS_Ellipse::getStartpoint() const
58 p.set(data.center.x + cos(data.angle1) * getMajorRadius(),
59 data.center.y + sin(data.angle1) * getMinorRadius());
60 p.rotate(data.center, getAngle());
65 * @return End point of the entity.
67 /*virtual*/ Vector RS_Ellipse::getEndpoint() const
70 p.set(data.center.x + cos(data.angle2) * getMajorRadius(),
71 data.center.y + sin(data.angle2) * getMinorRadius());
72 p.rotate(data.center, getAngle());
76 void RS_Ellipse::moveStartpoint(const Vector & pos)
78 data.angle1 = getEllipseAngle(pos);
79 //data.angle1 = data.center.angleTo(pos);
80 //calculateEndpoints();
84 void RS_Ellipse::moveEndpoint(const Vector & pos)
86 data.angle2 = getEllipseAngle(pos);
87 //data.angle2 = data.center.angleTo(pos);
88 //calculateEndpoints();
92 RS2::Ending RS_Ellipse::getTrimPoint(const Vector & coord, const Vector & trimPoint)
94 double angEl = getEllipseAngle(trimPoint);
95 double angM = getEllipseAngle(coord);
97 if (RS_Math::getAngleDifference(angM, angEl) > M_PI)
98 //if (data.reversed) {
99 // return RS2::EndingEnd;
102 return RS2::EndingStart;
105 //if (data.reversed) {
106 // return RS2::EndingStart;
109 return RS2::EndingEnd;
113 double RS_Ellipse::getEllipseAngle(const Vector & pos)
116 m.rotate(data.center, -data.majorP.angle());
117 Vector v = m - data.center;
118 v.scale(Vector(1.0, 1.0 / data.ratio));
122 /** @return Copy of data that defines the ellipse. **/
123 RS_EllipseData RS_Ellipse::getData()
128 VectorSolutions RS_Ellipse::getRefPoints()
130 VectorSolutions ret(getStartpoint(), getEndpoint(), data.center);
135 * @retval true if the arc is reversed (clockwise),
136 * @retval false otherwise
138 bool RS_Ellipse::isReversed() const
140 return data.reversed;
143 /** sets the reversed status. */
144 void RS_Ellipse::setReversed(bool r)
149 /** @return The rotation angle of this ellipse */
150 double RS_Ellipse::getAngle() const
152 return data.majorP.angle();
155 /** @return The start angle of this arc */
156 double RS_Ellipse::getAngle1()
161 /** Sets new start angle. */
162 void RS_Ellipse::setAngle1(double a1)
167 /** @return The end angle of this arc */
168 double RS_Ellipse::getAngle2()
173 /** Sets new end angle. */
174 void RS_Ellipse::setAngle2(double a2)
179 /** @return The center point (x) of this arc */
180 Vector RS_Ellipse::getCenter()
185 /** Sets new center. */
186 void RS_Ellipse::setCenter(const Vector & c)
191 /** @return The endpoint of the major axis (relative to center). */
192 Vector RS_Ellipse::getMajorP()
197 /** Sets new major point (relative to center). */
198 void RS_Ellipse::setMajorP(const Vector & p)
203 /** @return The ratio of minor to major axis */
204 double RS_Ellipse::getRatio()
209 /** Sets new ratio. */
210 void RS_Ellipse::setRatio(double r)
216 * @return Angle length in rad.
218 /*virtual*/ double RS_Ellipse::getAngleLength() const
221 return data.angle1 - data.angle2;
223 return data.angle2 - data.angle1;
226 /** @return The major radius of this ellipse. Same as getRadius() */
227 double RS_Ellipse::getMajorRadius() const
229 return data.majorP.magnitude();
232 /** @return The minor radius of this ellipse */
233 double RS_Ellipse::getMinorRadius() const
235 return data.majorP.magnitude() * data.ratio;
239 * Recalculates the endpoints using the angles and the radius.
242 void RS_Ellipse::calculateEndpoints() {
243 double angle = data.majorP.angle();
244 double radius1 = getMajorRadius();
245 double radius2 = getMinorRadius();
247 startpoint.set(data.center.x + cos(data.angle1) * radius1,
248 data.center.y + sin(data.angle1) * radius2);
249 startpoint.rotate(data.center, angle);
250 endpoint.set(data.center.x + cos(data.angle2) * radius1,
251 data.center.y + sin(data.angle2) * radius2);
252 endpoint.rotate(data.center, angle);
257 * Calculates the boundary box of this ellipse.
259 * @todo Fix that - the algorithm used is really bad / slow.
261 void RS_Ellipse::calculateBorders()
263 RS_DEBUG->print("RS_Ellipse::calculateBorders");
265 double radius1 = getMajorRadius();
266 double radius2 = getMinorRadius();
267 double angle = getAngle();
268 double a1 = ((!isReversed()) ? data.angle1 : data.angle2);
269 double a2 = ((!isReversed()) ? data.angle2 : data.angle1);
270 Vector startpoint = getStartpoint();
271 Vector endpoint = getEndpoint();
273 double minX = std::min(startpoint.x, endpoint.x);
274 double minY = std::min(startpoint.y, endpoint.y);
275 double maxX = std::max(startpoint.x, endpoint.x);
276 double maxY = std::max(startpoint.y, endpoint.y);
278 // kind of a brute force. TODO: exact calculation
284 vp.set(data.center.x + radius1 * cos(a),
285 data.center.y + radius2 * sin(a));
286 vp.rotate(data.center, angle);
288 minX = std::min(minX, vp.x);
289 minY = std::min(minY, vp.y);
290 maxX = std::max(maxX, vp.x);
291 maxY = std::max(maxY, vp.y);
295 while (RS_Math::isAngleBetween(RS_Math::correctAngle(a), a1, a2, false)
298 minV.set(minX, minY);
299 maxV.set(maxX, maxY);
300 RS_DEBUG->print("RS_Ellipse::calculateBorders: OK");
303 Vector RS_Ellipse::getNearestEndpoint(const Vector & coord, double * dist)
307 Vector startpoint = getStartpoint();
308 Vector endpoint = getEndpoint();
310 dist1 = startpoint.distanceTo(coord);
311 dist2 = endpoint.distanceTo(coord);
317 nearerPoint = endpoint;
323 nearerPoint = startpoint;
329 Vector RS_Ellipse::getNearestPointOnEntity(const Vector & coord, bool onEntity, double * dist, RS_Entity * * entity)
331 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity");
337 double ang = getAngle();
339 Vector normalized = (coord - data.center).rotate(-ang);
341 double dU = normalized.x;
342 double dV = normalized.y;
343 double dA = getMajorRadius();
344 double dB = getMinorRadius();
345 double dEpsilon = 1.0e-8;
352 bool majorSwap = false;
372 double dT = dB * (dV - dB);
377 for (i = 0; i < iMax; i++)
379 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: i: %d", i);
380 double dTpASqr = dT + dA * dA;
381 double dTpBSqr = dT + dB * dB;
382 double dInvTpASqr = 1.0 / dTpASqr;
383 double dInvTpBSqr = 1.0 / dTpBSqr;
384 double dXDivA = dA * dU * dInvTpASqr;
385 double dYDivB = dB * dV * dInvTpBSqr;
386 double dXDivASqr = dXDivA * dXDivA;
387 double dYDivBSqr = dYDivB * dYDivB;
388 double dF = dXDivASqr + dYDivBSqr - 1.0;
389 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: dF: %f", dF);
391 if (fabs(dF) < dEpsilon)
393 // F(t0) is close enough to zero, terminate the iteration:
397 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: rdX,rdY 1: %f,%f", rdX, rdY);
400 double dFDer = 2.0 * (dXDivASqr * dInvTpASqr + dYDivBSqr * dInvTpBSqr);
401 double dRatio = dF / dFDer;
402 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: dRatio: %f", dRatio);
404 if (fabs(dRatio) < dEpsilon)
406 // t1-t0 is close enough to zero, terminate the iteration:
410 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: rdX,rdY 2: %f,%f", rdX, rdY);
418 // failed to converge:
419 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: failed");
420 dDistance = RS_MAXDOUBLE;
424 double dDelta0 = rdX - dU;
425 double dDelta1 = rdY - dV;
426 dDistance = sqrt(dDelta0 * dDelta0 + dDelta1 * dDelta1);
427 ret = Vector(rdX, rdY);
428 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: rdX,rdY 2: %f,%f", rdX, rdY);
429 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: ret: %f,%f", ret.x, ret.y);
437 *dist = RS_MAXDOUBLE;
452 ret = (ret.rotate(ang) + data.center);
456 double a1 = data.center.angleTo(getStartpoint());
457 double a2 = data.center.angleTo(getEndpoint());
458 double a = data.center.angleTo(ret);
460 if (!RS_Math::isAngleBetween(a, a1, a2, data.reversed))
469 * @param tolerance Tolerance.
471 * @retval true if the given point is on this entity.
472 * @retval false otherwise
474 bool RS_Ellipse::isPointOnEntity(const Vector & coord, double tolerance)
476 double dist = getDistanceToPoint(coord, NULL, RS2::ResolveNone);
477 return (dist <= tolerance);
480 Vector RS_Ellipse::getNearestCenter(const Vector & coord, double * dist)
483 *dist = coord.distanceTo(data.center);
488 * @todo Implement this.
490 Vector RS_Ellipse::getNearestMiddle(const Vector & /*coord*/, double * dist)
493 *dist = RS_MAXDOUBLE;
494 return Vector(false);
497 Vector RS_Ellipse::getNearestDist(double /*distance*/, const Vector & /*coord*/, double * dist)
500 *dist = RS_MAXDOUBLE;
501 return Vector(false);
504 double RS_Ellipse::getDistanceToPoint(const Vector & coord, RS_Entity * * entity, RS2::ResolveLevel, double /*solidDist*/)
506 double dist = RS_MAXDOUBLE;
507 getNearestPointOnEntity(coord, true, &dist, entity);
512 void RS_Ellipse::move(Vector offset)
514 data.center.move(offset);
515 //calculateEndpoints();
519 void RS_Ellipse::rotate(Vector center, double angle)
521 data.center.rotate(center, angle);
522 data.majorP.rotate(angle);
523 //calculateEndpoints();
527 void RS_Ellipse::scale(Vector center, Vector factor)
529 data.center.scale(center, factor);
530 data.majorP.scale(factor);
531 //calculateEndpoints();
536 * @todo deal with angles correctly
538 void RS_Ellipse::mirror(Vector axisPoint1, Vector axisPoint2)
540 Vector mp = data.center + data.majorP;
542 data.center.mirror(axisPoint1, axisPoint2);
543 mp.mirror(axisPoint1, axisPoint2);
545 data.majorP = mp - data.center;
547 double a = axisPoint1.angleTo(axisPoint2);
550 vec.setPolar(1.0, data.angle1);
551 vec.mirror(Vector(0.0, 0.0), axisPoint2 - axisPoint1);
552 data.angle1 = vec.angle() - 2 * a;
554 vec.setPolar(1.0, data.angle2);
555 vec.mirror(Vector(0.0, 0.0), axisPoint2 - axisPoint1);
556 data.angle2 = vec.angle() - 2 * a;
558 data.reversed = (!data.reversed);
560 //calculateEndpoints();
564 void RS_Ellipse::moveRef(const Vector & ref, const Vector & offset)
566 Vector startpoint = getStartpoint();
567 Vector endpoint = getEndpoint();
569 if (ref.distanceTo(startpoint) < 1.0e-4)
570 moveStartpoint(startpoint + offset);
573 if (ref.distanceTo(endpoint) < 1.0e-4)
574 moveEndpoint(endpoint + offset);
577 void RS_Ellipse::draw(PaintInterface * painter, GraphicView * view, double /*patternOffset*/)
579 if (!painter || !view)
582 if (getPen().getLineType() == RS2::SolidLine || isSelected()
583 || view->getDrawingMode() == RS2::ModePreview)
584 painter->drawEllipse(view->toGui(getCenter()),
585 getMajorRadius() * view->getFactor().x,
586 getMinorRadius() * view->getFactor().x,
587 getAngle(), getAngle1(), getAngle2(), isReversed());
590 double styleFactor = getStyleFactor(view);
592 if (styleFactor < 0.0)
594 painter->drawEllipse(view->toGui(getCenter()),
595 getMajorRadius() * view->getFactor().x,
596 getMinorRadius() * view->getFactor().x,
597 getAngle(), getAngle1(), getAngle2(), isReversed());
602 RS_LineTypePattern * pat;
605 pat = &patternSelected;
607 pat = view->getPattern(getPen().getLineType());
612 // Pen to draw pattern is always solid:
613 RS_Pen pen = painter->getPen();
614 pen.setLineType(RS2::SolidLine);
615 painter->setPen(pen);
617 double * da; // array of distances in x.
618 int i; // index counter
620 double length = getAngleLength();
623 da = new double[pat->num];
628 double curA = getAngle1();
630 Vector cp = view->toGui(getCenter());
631 double r1 = getMajorRadius() * view->getFactor().x;
632 double r2 = getMinorRadius() * view->getFactor().x;
636 curR = sqrt(RS_Math::pow(getMinorRadius() * cos(curA), 2.0)
637 + RS_Math::pow(getMajorRadius() * sin(curA), 2.0));
641 da[i] = fabs(pat->pattern[i] * styleFactor) / curR;
643 if (pat->pattern[i] * styleFactor > 0.0)
645 if (tot + fabs(da[i]) < length)
646 painter->drawEllipse(cp, r1, r2, getAngle(), curA, curA + da[i], false);
648 painter->drawEllipse(cp, r1, r2, getAngle(), curA, getAngle2(), false);
668 * Dumps the point's data to stdout.
670 std::ostream & operator<<(std::ostream & os, const RS_Ellipse & a)
672 os << " Ellipse: " << a.data << "\n";