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 Ellipse::Ellipse(EntityContainer * parent, const EllipseData & d):
29 AtomicEntity(parent), data(d)
31 //calculateEndpoints();
35 /*virtual*/ Ellipse::~Ellipse()
39 /*virtual*/ Entity * Ellipse::clone()
41 Ellipse * e = new Ellipse(*this);
46 /** @return RS2::EntityEllipse */
47 /*virtual*/ RS2::EntityType Ellipse::rtti() const
49 return RS2::EntityEllipse;
53 * @return Start point of the entity.
55 /*virtual*/ Vector 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 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 Ellipse::moveStartpoint(const Vector & pos)
78 data.angle1 = getEllipseAngle(pos);
79 //data.angle1 = data.center.angleTo(pos);
80 //calculateEndpoints();
84 void Ellipse::moveEndpoint(const Vector & pos)
86 data.angle2 = getEllipseAngle(pos);
87 //data.angle2 = data.center.angleTo(pos);
88 //calculateEndpoints();
92 RS2::Ending Ellipse::getTrimPoint(const Vector & coord, const Vector & trimPoint)
94 double angEl = getEllipseAngle(trimPoint);
95 double angM = getEllipseAngle(coord);
97 if (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 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 EllipseData Ellipse::getData()
128 VectorSolutions Ellipse::getRefPoints()
130 VectorSolutions ret(getStartpoint(), getEndpoint(), data.center);
135 * @retval true if the arc is reversed (clockwise),
136 * @retval false otherwise
138 bool Ellipse::isReversed() const
140 return data.reversed;
143 /** sets the reversed status. */
144 void Ellipse::setReversed(bool r)
149 /** @return The rotation angle of this ellipse */
150 double Ellipse::getAngle() const
152 return data.majorP.angle();
155 /** @return The start angle of this arc */
156 double Ellipse::getAngle1()
161 /** Sets new start angle. */
162 void Ellipse::setAngle1(double a1)
167 /** @return The end angle of this arc */
168 double Ellipse::getAngle2()
173 /** Sets new end angle. */
174 void Ellipse::setAngle2(double a2)
179 /** @return The center point (x) of this arc */
180 Vector Ellipse::getCenter()
185 /** Sets new center. */
186 void Ellipse::setCenter(const Vector & c)
191 /** @return The endpoint of the major axis (relative to center). */
192 Vector Ellipse::getMajorP()
197 /** Sets new major point (relative to center). */
198 void Ellipse::setMajorP(const Vector & p)
203 /** @return The ratio of minor to major axis */
204 double Ellipse::getRatio()
209 /** Sets new ratio. */
210 void Ellipse::setRatio(double r)
216 * @return Angle length in rad.
218 /*virtual*/ double 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 Ellipse::getMajorRadius() const
229 return data.majorP.magnitude();
232 /** @return The minor radius of this ellipse */
233 double Ellipse::getMinorRadius() const
235 return data.majorP.magnitude() * data.ratio;
239 * Recalculates the endpoints using the angles and the radius.
242 void 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 Ellipse::calculateBorders()
263 DEBUG->print("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 (Math::isAngleBetween(Math::correctAngle(a), a1, a2, false)
298 minV.set(minX, minY);
299 maxV.set(maxX, maxY);
300 DEBUG->print("Ellipse::calculateBorders: OK");
303 Vector 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 Ellipse::getNearestPointOnEntity(const Vector & coord, bool onEntity, double * dist, Entity * * entity)
331 DEBUG->print("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 DEBUG->print("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 DEBUG->print("Ellipse::getNearestPointOnEntity: dF: %f", dF);
391 if (fabs(dF) < dEpsilon)
393 // F(t0) is close enough to zero, terminate the iteration:
397 DEBUG->print("Ellipse::getNearestPointOnEntity: rdX,rdY 1: %f,%f", rdX, rdY);
400 double dFDer = 2.0 * (dXDivASqr * dInvTpASqr + dYDivBSqr * dInvTpBSqr);
401 double dRatio = dF / dFDer;
402 DEBUG->print("Ellipse::getNearestPointOnEntity: dRatio: %f", dRatio);
404 if (fabs(dRatio) < dEpsilon)
406 // t1-t0 is close enough to zero, terminate the iteration:
410 DEBUG->print("Ellipse::getNearestPointOnEntity: rdX,rdY 2: %f,%f", rdX, rdY);
418 // failed to converge:
419 DEBUG->print("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 DEBUG->print("Ellipse::getNearestPointOnEntity: rdX,rdY 2: %f,%f", rdX, rdY);
429 DEBUG->print("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 (!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 Ellipse::isPointOnEntity(const Vector & coord, double tolerance)
476 double dist = getDistanceToPoint(coord, NULL, RS2::ResolveNone);
477 return (dist <= tolerance);
480 Vector Ellipse::getNearestCenter(const Vector & coord, double * dist)
483 *dist = coord.distanceTo(data.center);
488 * @todo Implement this.
490 Vector Ellipse::getNearestMiddle(const Vector & /*coord*/, double * dist)
493 *dist = RS_MAXDOUBLE;
494 return Vector(false);
497 Vector Ellipse::getNearestDist(double /*distance*/, const Vector & /*coord*/, double * dist)
500 *dist = RS_MAXDOUBLE;
501 return Vector(false);
504 double Ellipse::getDistanceToPoint(const Vector & coord, Entity * * entity, RS2::ResolveLevel, double /*solidDist*/)
506 double dist = RS_MAXDOUBLE;
507 getNearestPointOnEntity(coord, true, &dist, entity);
512 void Ellipse::move(Vector offset)
514 data.center.move(offset);
515 //calculateEndpoints();
519 void Ellipse::rotate(Vector center, double angle)
521 data.center.rotate(center, angle);
522 data.majorP.rotate(angle);
523 //calculateEndpoints();
527 void 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 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 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 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 LineTypePattern * pat;
605 pat = &patternSelected;
607 pat = view->getPattern(getPen().getLineType());
612 // Pen to draw pattern is always solid:
613 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(Math::pow(getMinorRadius() * cos(curA), 2.0)
637 + 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 Ellipse & a)
672 os << " Ellipse: " << a.data << "\n";