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 "rs_graphicview.h"
19 #include "rs_information.h"
20 #include "paintintf.h"
25 RS_Ellipse::RS_Ellipse(RS_EntityContainer * parent, const RS_EllipseData & d):
26 RS_AtomicEntity(parent), data(d)
28 //calculateEndpoints();
33 * Recalculates the endpoints using the angles and the radius.
36 void RS_Ellipse::calculateEndpoints() {
37 double angle = data.majorP.angle();
38 double radius1 = getMajorRadius();
39 double radius2 = getMinorRadius();
41 startpoint.set(data.center.x + cos(data.angle1) * radius1,
42 data.center.y + sin(data.angle1) * radius2);
43 startpoint.rotate(data.center, angle);
44 endpoint.set(data.center.x + cos(data.angle2) * radius1,
45 data.center.y + sin(data.angle2) * radius2);
46 endpoint.rotate(data.center, angle);
52 * Calculates the boundary box of this ellipse.
54 * @todo Fix that - the algorithm used is really bad / slow.
56 void RS_Ellipse::calculateBorders() {
57 RS_DEBUG->print("RS_Ellipse::calculateBorders");
59 double radius1 = getMajorRadius();
60 double radius2 = getMinorRadius();
61 double angle = getAngle();
62 double a1 = ((!isReversed()) ? data.angle1 : data.angle2);
63 double a2 = ((!isReversed()) ? data.angle2 : data.angle1);
64 Vector startpoint = getStartpoint();
65 Vector endpoint = getEndpoint();
67 double minX = std::min(startpoint.x, endpoint.x);
68 double minY = std::min(startpoint.y, endpoint.y);
69 double maxX = std::max(startpoint.x, endpoint.x);
70 double maxY = std::max(startpoint.y, endpoint.y);
72 // kind of a brute force. TODO: exact calculation
76 vp.set(data.center.x + radius1 * cos(a),
77 data.center.y + radius2 * sin(a));
78 vp.rotate(data.center, angle);
80 minX = std::min(minX, vp.x);
81 minY = std::min(minY, vp.y);
82 maxX = std::max(maxX, vp.x);
83 maxY = std::max(maxY, vp.y);
86 } while (RS_Math::isAngleBetween(RS_Math::correctAngle(a), a1, a2, false) &&
92 RS_DEBUG->print("RS_Ellipse::calculateBorders: OK");
97 VectorSolutions RS_Ellipse::getRefPoints() {
98 VectorSolutions ret(getStartpoint(), getEndpoint(), data.center);
104 Vector RS_Ellipse::getNearestEndpoint(const Vector& coord, double* dist) {
107 Vector startpoint = getStartpoint();
108 Vector endpoint = getEndpoint();
110 dist1 = startpoint.distanceTo(coord);
111 dist2 = endpoint.distanceTo(coord);
117 nearerPoint = endpoint;
122 nearerPoint = startpoint;
130 Vector RS_Ellipse::getNearestPointOnEntity(const Vector& coord,
131 bool onEntity, double* dist, RS_Entity** entity) {
133 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity");
140 double ang = getAngle();
142 Vector normalized = (coord - data.center).rotate(-ang);
144 double dU = normalized.x;
145 double dV = normalized.y;
146 double dA = getMajorRadius();
147 double dB = getMinorRadius();
148 double dEpsilon = 1.0e-8;
155 bool majorSwap = false;
173 double dT = dB*(dV - dB);
177 for (i = 0; i < iMax; i++) {
178 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: i: %d", i);
179 double dTpASqr = dT + dA*dA;
180 double dTpBSqr = dT + dB*dB;
181 double dInvTpASqr = 1.0/dTpASqr;
182 double dInvTpBSqr = 1.0/dTpBSqr;
183 double dXDivA = dA*dU*dInvTpASqr;
184 double dYDivB = dB*dV*dInvTpBSqr;
185 double dXDivASqr = dXDivA*dXDivA;
186 double dYDivBSqr = dYDivB*dYDivB;
187 double dF = dXDivASqr + dYDivBSqr - 1.0;
188 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: dF: %f", dF);
189 if ( fabs(dF) < dEpsilon ) {
190 // F(t0) is close enough to zero, terminate the iteration:
194 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: rdX,rdY 1: %f,%f", rdX, rdY);
197 double dFDer = 2.0*(dXDivASqr*dInvTpASqr + dYDivBSqr*dInvTpBSqr);
198 double dRatio = dF/dFDer;
199 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: dRatio: %f", dRatio);
200 if ( fabs(dRatio) < dEpsilon ) {
201 // t1-t0 is close enough to zero, terminate the iteration:
205 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: rdX,rdY 2: %f,%f", rdX, rdY);
211 // failed to converge:
212 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: failed");
213 dDistance = RS_MAXDOUBLE;
216 double dDelta0 = rdX - dU;
217 double dDelta1 = rdY - dV;
218 dDistance = sqrt(dDelta0*dDelta0 + dDelta1*dDelta1);
219 ret = Vector(rdX, rdY);
220 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: rdX,rdY 2: %f,%f", rdX, rdY);
221 RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: ret: %f,%f", ret.x, ret.y);
228 *dist = RS_MAXDOUBLE;
241 ret = (ret.rotate(ang) + data.center);
244 double a1 = data.center.angleTo(getStartpoint());
245 double a2 = data.center.angleTo(getEndpoint());
246 double a = data.center.angleTo(ret);
247 if (!RS_Math::isAngleBetween(a, a1, a2, data.reversed)) {
259 * @param tolerance Tolerance.
261 * @retval true if the given point is on this entity.
262 * @retval false otherwise
264 bool RS_Ellipse::isPointOnEntity(const Vector& coord,
266 double dist = getDistanceToPoint(coord, NULL, RS2::ResolveNone);
267 return (dist<=tolerance);
272 Vector RS_Ellipse::getNearestCenter(const Vector& coord,
275 *dist = coord.distanceTo(data.center);
283 * @todo Implement this.
285 Vector RS_Ellipse::getNearestMiddle(const Vector& /*coord*/,
288 *dist = RS_MAXDOUBLE;
290 return Vector(false);
295 Vector RS_Ellipse::getNearestDist(double /*distance*/,
296 const Vector& /*coord*/,
299 *dist = RS_MAXDOUBLE;
301 return Vector(false);
306 double RS_Ellipse::getDistanceToPoint(const Vector& coord,
308 RS2::ResolveLevel, double /*solidDist*/) {
309 double dist = RS_MAXDOUBLE;
310 getNearestPointOnEntity(coord, true, &dist, entity);
318 void RS_Ellipse::move(Vector offset) {
319 data.center.move(offset);
320 //calculateEndpoints();
326 void RS_Ellipse::rotate(Vector center, double angle) {
327 data.center.rotate(center, angle);
328 data.majorP.rotate(angle);
329 //calculateEndpoints();
335 void RS_Ellipse::moveStartpoint(const Vector& pos) {
336 data.angle1 = getEllipseAngle(pos);
337 //data.angle1 = data.center.angleTo(pos);
338 //calculateEndpoints();
344 void RS_Ellipse::moveEndpoint(const Vector& pos) {
345 data.angle2 = getEllipseAngle(pos);
346 //data.angle2 = data.center.angleTo(pos);
347 //calculateEndpoints();
352 RS2::Ending RS_Ellipse::getTrimPoint(const Vector& coord,
353 const Vector& trimPoint) {
355 double angEl = getEllipseAngle(trimPoint);
356 double angM = getEllipseAngle(coord);
358 if (RS_Math::getAngleDifference(angM, angEl)>M_PI) {
359 //if (data.reversed) {
360 // return RS2::EndingEnd;
363 return RS2::EndingStart;
367 //if (data.reversed) {
368 // return RS2::EndingStart;
371 return RS2::EndingEnd;
376 double RS_Ellipse::getEllipseAngle(const Vector& pos) {
378 m.rotate(data.center, -data.majorP.angle());
379 Vector v = m-data.center;
380 v.scale(Vector(1.0, 1.0/data.ratio));
386 void RS_Ellipse::scale(Vector center, Vector factor) {
387 data.center.scale(center, factor);
388 data.majorP.scale(factor);
389 //calculateEndpoints();
395 * @todo deal with angles correctly
397 void RS_Ellipse::mirror(Vector axisPoint1, Vector axisPoint2) {
398 Vector mp = data.center + data.majorP;
400 data.center.mirror(axisPoint1, axisPoint2);
401 mp.mirror(axisPoint1, axisPoint2);
403 data.majorP = mp - data.center;
405 double a = axisPoint1.angleTo(axisPoint2);
408 vec.setPolar(1.0, data.angle1);
409 vec.mirror(Vector(0.0,0.0), axisPoint2-axisPoint1);
410 data.angle1 = vec.angle() - 2*a;
412 vec.setPolar(1.0, data.angle2);
413 vec.mirror(Vector(0.0,0.0), axisPoint2-axisPoint1);
414 data.angle2 = vec.angle() - 2*a;
416 data.reversed = (!data.reversed);
418 //calculateEndpoints();
424 void RS_Ellipse::moveRef(const Vector& ref, const Vector& offset) {
425 Vector startpoint = getStartpoint();
426 Vector endpoint = getEndpoint();
428 if (ref.distanceTo(startpoint)<1.0e-4) {
429 moveStartpoint(startpoint+offset);
431 if (ref.distanceTo(endpoint)<1.0e-4) {
432 moveEndpoint(endpoint+offset);
437 //void RS_Ellipse::draw(RS_Painter* painter, RS_GraphicView* view, double /*patternOffset*/) {
438 void RS_Ellipse::draw(PaintInterface * painter, RS_GraphicView * view, double /*patternOffset*/)
440 if (painter == NULL || view == NULL)
443 if (getPen().getLineType()==RS2::SolidLine || isSelected()
444 || view->getDrawingMode()==RS2::ModePreview)
446 painter->drawEllipse(view->toGui(getCenter()),
447 getMajorRadius() * view->getFactor().x,
448 getMinorRadius() * view->getFactor().x,
449 getAngle(), getAngle1(), getAngle2(), isReversed());
453 double styleFactor = getStyleFactor(view);
455 if (styleFactor < 0.0)
457 painter->drawEllipse(view->toGui(getCenter()),
458 getMajorRadius() * view->getFactor().x,
459 getMinorRadius() * view->getFactor().x,
460 getAngle(), getAngle1(), getAngle2(), isReversed());
465 RS_LineTypePattern * pat;
469 pat = &patternSelected;
473 pat = view->getPattern(getPen().getLineType());
481 // Pen to draw pattern is always solid:
482 RS_Pen pen = painter->getPen();
483 pen.setLineType(RS2::SolidLine);
484 painter->setPen(pen);
486 double * da; // array of distances in x.
487 int i; // index counter
489 double length = getAngleLength();
492 da = new double[pat->num];
497 double curA = getAngle1();
499 Vector cp = view->toGui(getCenter());
500 double r1 = getMajorRadius() * view->getFactor().x;
501 double r2 = getMinorRadius() * view->getFactor().x;
505 curR = sqrt(RS_Math::pow(getMinorRadius() * cos(curA), 2.0)
506 + RS_Math::pow(getMajorRadius() * sin(curA), 2.0));
510 da[i] = fabs(pat->pattern[i] * styleFactor) / curR;
512 if (pat->pattern[i] * styleFactor > 0.0)
514 if (tot+fabs(da[i])<length)
516 painter->drawEllipse(cp, r1, r2, getAngle(), curA, curA + da[i], false);
520 painter->drawEllipse(cp, r1, r2, getAngle(), curA, getAngle2(), false);
543 * Dumps the point's data to stdout.
545 std::ostream& operator << (std::ostream& os, const RS_Ellipse& a) {
546 os << " Ellipse: " << a.data << "\n";