]> Shamusworld >> Repos - architektonas/blob - src/base/rs_ellipse.cpp
Changed RS_Graphic to Drawing; this is less confusing as a drawing is
[architektonas] / src / base / rs_ellipse.cpp
1 // rs_ellipse.cpp
2 //
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
7 //
8 // JLH = James L. Hammons <jlhamm@acm.org>
9 //
10 // Who  When        What
11 // ---  ----------  -----------------------------------------------------------
12 // JLH  05/28/2010  Added this text. :-)
13 //
14
15 #include "rs_ellipse.h"
16
17 #include "drawing.h"
18 #include "rs_graphicview.h"
19 #include "rs_information.h"
20 #include "paintintf.h"
21
22 /**
23  * Constructor.
24  */
25 RS_Ellipse::RS_Ellipse(RS_EntityContainer * parent, const RS_EllipseData & d):
26         RS_AtomicEntity(parent), data(d)
27 {
28         //calculateEndpoints();
29         calculateBorders();
30 }
31
32 /**
33  * Recalculates the endpoints using the angles and the radius.
34  */
35  /*
36 void RS_Ellipse::calculateEndpoints() {
37     double angle = data.majorP.angle();
38     double radius1 = getMajorRadius();
39     double radius2 = getMinorRadius();
40
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);
47 }
48 */
49
50
51 /**
52  * Calculates the boundary box of this ellipse.
53  *
54  * @todo Fix that - the algorithm used is really bad / slow.
55  */
56 void RS_Ellipse::calculateBorders() {
57         RS_DEBUG->print("RS_Ellipse::calculateBorders");
58
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();
66
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);
71
72     // kind of a brute force. TODO: exact calculation
73     Vector vp;
74         double a = a1;
75         do {
76         vp.set(data.center.x + radius1 * cos(a),
77                data.center.y + radius2 * sin(a));
78         vp.rotate(data.center, angle);
79
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);
84
85                 a += 0.03;
86     } while (RS_Math::isAngleBetween(RS_Math::correctAngle(a), a1, a2, false) &&
87                         a<4*M_PI);
88
89
90     minV.set(minX, minY);
91     maxV.set(maxX, maxY);
92         RS_DEBUG->print("RS_Ellipse::calculateBorders: OK");
93 }
94
95
96
97 VectorSolutions RS_Ellipse::getRefPoints() {
98     VectorSolutions ret(getStartpoint(), getEndpoint(), data.center);
99     return ret;
100 }
101
102
103
104 Vector RS_Ellipse::getNearestEndpoint(const Vector& coord, double* dist) {
105     double dist1, dist2;
106     Vector nearerPoint;
107         Vector startpoint = getStartpoint();
108         Vector endpoint = getEndpoint();
109
110     dist1 = startpoint.distanceTo(coord);
111     dist2 = endpoint.distanceTo(coord);
112
113     if (dist2<dist1) {
114         if (dist!=NULL) {
115             *dist = dist2;
116         }
117         nearerPoint = endpoint;
118     } else {
119         if (dist!=NULL) {
120             *dist = dist1;
121         }
122         nearerPoint = startpoint;
123     }
124
125     return nearerPoint;
126 }
127
128
129
130 Vector RS_Ellipse::getNearestPointOnEntity(const Vector& coord,
131         bool onEntity, double* dist, RS_Entity** entity) {
132
133     RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity");
134
135     Vector ret(false);
136
137     if (entity!=NULL) {
138         *entity = this;
139     }
140     double ang = getAngle();
141
142     Vector normalized = (coord - data.center).rotate(-ang);
143
144     double dU = normalized.x;
145     double dV = normalized.y;
146     double dA = getMajorRadius();
147     double dB = getMinorRadius();
148     double dEpsilon = 1.0e-8;
149     int iMax = 32;
150     int riIFinal = 0;
151     double rdX = 0.0;
152     double rdY = 0.0;
153     double dDistance;
154     bool swap = false;
155     bool majorSwap = false;
156
157     if (dA<dB) {
158         double dum = dA;
159         dA = dB;
160         dB = dum;
161         dum = dU;
162         dU = dV;
163         dV = dum;
164         majorSwap = true;
165     }
166
167     if (dV<0.0) {
168         dV*=-1.0;
169         swap = true;
170     }
171
172     // initial guess
173     double dT = dB*(dV - dB);
174
175     // Newton s method
176     int i;
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:
191             rdX = dXDivA*dA;
192             rdY = dYDivB*dB;
193             riIFinal = i;
194             RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: rdX,rdY 1: %f,%f", rdX, rdY);
195             break;
196         }
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:
202             rdX = dXDivA*dA;
203             rdY = dYDivB*dB;
204             riIFinal = i;
205             RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: rdX,rdY 2: %f,%f", rdX, rdY);
206             break;
207         }
208         dT += dRatio;
209     }
210     if ( i == iMax ) {
211         // failed to converge:
212         RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: failed");
213         dDistance = RS_MAXDOUBLE;
214     }
215     else {
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);
222     }
223
224     if (dist!=NULL) {
225         if (ret.valid) {
226             *dist = dDistance;
227         } else {
228             *dist = RS_MAXDOUBLE;
229         }
230     }
231
232     if (ret.valid) {
233         if (swap) {
234             ret.y*=-1.0;
235         }
236         if (majorSwap) {
237             double dum = ret.x;
238             ret.x = ret.y;
239             ret.y = dum;
240         }
241         ret = (ret.rotate(ang) + data.center);
242
243         if (onEntity) {
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)) {
248                 ret = Vector(false);
249             }
250         }
251     }
252
253     return ret;
254 }
255
256
257
258 /**
259  * @param tolerance Tolerance.
260  *
261  * @retval true if the given point is on this entity.
262  * @retval false otherwise
263  */
264 bool RS_Ellipse::isPointOnEntity(const Vector& coord,
265                                 double tolerance) {
266     double dist = getDistanceToPoint(coord, NULL, RS2::ResolveNone);
267     return (dist<=tolerance);
268 }
269
270
271
272 Vector RS_Ellipse::getNearestCenter(const Vector& coord,
273                                        double* dist) {
274     if (dist!=NULL) {
275         *dist = coord.distanceTo(data.center);
276     }
277     return data.center;
278 }
279
280
281
282 /**
283  * @todo Implement this.
284  */
285 Vector RS_Ellipse::getNearestMiddle(const Vector& /*coord*/,
286                                        double* dist) {
287     if (dist!=NULL) {
288         *dist = RS_MAXDOUBLE;
289     }
290     return Vector(false);
291 }
292
293
294
295 Vector RS_Ellipse::getNearestDist(double /*distance*/,
296                                      const Vector& /*coord*/,
297                                      double* dist) {
298     if (dist!=NULL) {
299         *dist = RS_MAXDOUBLE;
300     }
301     return Vector(false);
302 }
303
304
305
306 double RS_Ellipse::getDistanceToPoint(const Vector& coord,
307                                       RS_Entity** entity,
308                                       RS2::ResolveLevel, double /*solidDist*/) {
309     double dist = RS_MAXDOUBLE;
310     getNearestPointOnEntity(coord, true, &dist, entity);
311
312     return dist;
313
314 }
315
316
317
318 void RS_Ellipse::move(Vector offset) {
319     data.center.move(offset);
320     //calculateEndpoints();
321     calculateBorders();
322 }
323
324
325
326 void RS_Ellipse::rotate(Vector center, double angle) {
327     data.center.rotate(center, angle);
328     data.majorP.rotate(angle);
329     //calculateEndpoints();
330     calculateBorders();
331 }
332
333
334
335 void RS_Ellipse::moveStartpoint(const Vector& pos) {
336         data.angle1 = getEllipseAngle(pos);
337         //data.angle1 = data.center.angleTo(pos);
338         //calculateEndpoints();
339         calculateBorders();
340 }
341
342
343
344 void RS_Ellipse::moveEndpoint(const Vector& pos) {
345         data.angle2 = getEllipseAngle(pos);
346         //data.angle2 = data.center.angleTo(pos);
347         //calculateEndpoints();
348         calculateBorders();
349 }
350
351
352 RS2::Ending RS_Ellipse::getTrimPoint(const Vector& coord,
353                 const Vector& trimPoint) {
354
355         double angEl = getEllipseAngle(trimPoint);
356         double angM = getEllipseAngle(coord);
357
358         if (RS_Math::getAngleDifference(angM, angEl)>M_PI) {
359                 //if (data.reversed) {
360                 //      return RS2::EndingEnd;
361                 //}
362                 //else {
363                         return RS2::EndingStart;
364                 //}
365         }
366         else {
367                 //if (data.reversed) {
368                 //      return RS2::EndingStart;
369                 //}
370                 //else {
371                         return RS2::EndingEnd;
372                 //}
373         }
374 }
375
376 double RS_Ellipse::getEllipseAngle(const Vector& pos) {
377         Vector m = 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));
381         return v.angle();
382 }
383
384
385
386 void RS_Ellipse::scale(Vector center, Vector factor) {
387     data.center.scale(center, factor);
388     data.majorP.scale(factor);
389     //calculateEndpoints();
390     calculateBorders();
391 }
392
393
394 /**
395  * @todo deal with angles correctly
396  */
397 void RS_Ellipse::mirror(Vector axisPoint1, Vector axisPoint2) {
398     Vector mp = data.center + data.majorP;
399
400     data.center.mirror(axisPoint1, axisPoint2);
401     mp.mirror(axisPoint1, axisPoint2);
402
403     data.majorP = mp - data.center;
404
405     double a = axisPoint1.angleTo(axisPoint2);
406
407     Vector vec;
408     vec.setPolar(1.0, data.angle1);
409     vec.mirror(Vector(0.0,0.0), axisPoint2-axisPoint1);
410     data.angle1 = vec.angle() - 2*a;
411
412     vec.setPolar(1.0, data.angle2);
413     vec.mirror(Vector(0.0,0.0), axisPoint2-axisPoint1);
414     data.angle2 = vec.angle() - 2*a;
415
416     data.reversed = (!data.reversed);
417
418     //calculateEndpoints();
419     calculateBorders();
420 }
421
422
423
424 void RS_Ellipse::moveRef(const Vector& ref, const Vector& offset) {
425         Vector startpoint = getStartpoint();
426         Vector endpoint = getEndpoint();
427
428     if (ref.distanceTo(startpoint)<1.0e-4) {
429         moveStartpoint(startpoint+offset);
430     }
431     if (ref.distanceTo(endpoint)<1.0e-4) {
432         moveEndpoint(endpoint+offset);
433     }
434 }
435
436
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*/)
439 {
440         if (painter == NULL || view == NULL)
441                 return;
442
443         if (getPen().getLineType()==RS2::SolidLine || isSelected()
444                 || view->getDrawingMode()==RS2::ModePreview)
445         {
446                 painter->drawEllipse(view->toGui(getCenter()),
447                         getMajorRadius() * view->getFactor().x,
448                         getMinorRadius() * view->getFactor().x,
449                         getAngle(), getAngle1(), getAngle2(), isReversed());
450         }
451         else
452         {
453                 double styleFactor = getStyleFactor(view);
454
455                 if (styleFactor < 0.0)
456                 {
457                         painter->drawEllipse(view->toGui(getCenter()),
458                                 getMajorRadius() * view->getFactor().x,
459                                 getMinorRadius() * view->getFactor().x,
460                                 getAngle(), getAngle1(), getAngle2(), isReversed());
461                         return;
462                 }
463
464                 // Pattern:
465                 RS_LineTypePattern * pat;
466
467                 if (isSelected())
468                 {
469                         pat = &patternSelected;
470                 }
471                 else
472                 {
473                         pat = view->getPattern(getPen().getLineType());
474                 }
475
476                 if (pat == NULL)
477                 {
478                         return;
479                 }
480
481                 // Pen to draw pattern is always solid:
482                 RS_Pen pen = painter->getPen();
483                 pen.setLineType(RS2::SolidLine);
484                 painter->setPen(pen);
485
486                 double * da;     // array of distances in x.
487                 int i;          // index counter
488
489                 double length = getAngleLength();
490
491                 // create pattern:
492                 da = new double[pat->num];
493
494                 double tot = 0.0;
495                 i = 0;
496                 bool done = false;
497                 double curA = getAngle1();
498                 double curR;
499                 Vector cp = view->toGui(getCenter());
500                 double r1 = getMajorRadius() * view->getFactor().x;
501                 double r2 = getMinorRadius() * view->getFactor().x;
502
503                 do
504                 {
505                         curR = sqrt(RS_Math::pow(getMinorRadius() * cos(curA), 2.0)
506                                 + RS_Math::pow(getMajorRadius() * sin(curA), 2.0));
507
508                         if (curR > 1.0e-6)
509                         {
510                                 da[i] = fabs(pat->pattern[i] * styleFactor) / curR;
511
512                                 if (pat->pattern[i] * styleFactor > 0.0)
513                                 {
514                                         if (tot+fabs(da[i])<length)
515                                         {
516                                                 painter->drawEllipse(cp, r1, r2, getAngle(), curA, curA + da[i], false);
517                                         }
518                                         else
519                                         {
520                                                 painter->drawEllipse(cp, r1, r2, getAngle(), curA, getAngle2(), false);
521                                         }
522                                 }
523                         }
524
525                         curA+=da[i];
526                         tot+=fabs(da[i]);
527                         done=tot>length;
528
529                         i++;
530
531                         if (i >= pat->num)
532                         {
533                                 i=0;
534                         }
535                 }
536                 while(!done);
537
538                 delete[] da;
539         }
540 }
541
542 /**
543  * Dumps the point's data to stdout.
544  */
545 std::ostream& operator << (std::ostream& os, const RS_Ellipse& a) {
546     os << " Ellipse: " << a.data << "\n";
547     return os;
548 }
549