]> Shamusworld >> Repos - architektonas/blob - src/base/rs_arc.cpp
Initial import
[architektonas] / src / base / rs_arc.cpp
1 // rs_arc.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_arc.h"
16
17 #include "rs_constructionline.h"
18 #include "rs_linetypepattern.h"
19 #include "rs_information.h"
20 #include "rs_math.h"
21 #include "rs_graphicview.h"
22 #include "paintintf.h"
23
24 /**
25  * Default constructor.
26  */
27 RS_Arc::RS_Arc(RS_EntityContainer* parent,
28                const RS_ArcData& d)
29         : RS_AtomicEntity(parent), data(d) {
30     calculateEndpoints();
31     calculateBorders();
32 }
33
34
35
36 /**
37  * Creates this arc from 3 given points which define the arc line.
38  *
39  * @param p1 1st point.
40  * @param p2 2nd point.
41  * @param p3 3rd point.
42  */
43 bool RS_Arc::createFrom3P(const Vector& p1, const Vector& p2,
44                           const Vector& p3) {
45     if (p1.distanceTo(p2)>RS_TOLERANCE &&
46             p2.distanceTo(p3)>RS_TOLERANCE &&
47             p3.distanceTo(p1)>RS_TOLERANCE) {
48
49         // middle points between 3 points:
50         Vector mp1, mp2;
51         Vector dir1, dir2;
52         double a1, a2;
53
54         // intersection of two middle lines
55         mp1 = (p1 + p2)/2.0;
56         a1 = p1.angleTo(p2) + M_PI/2.0;
57         dir1.setPolar(100.0, a1);
58         mp2 = (p2 + p3)/2.0;
59         a2 = p2.angleTo(p3) + M_PI/2.0;
60         dir2.setPolar(100.0, a2);
61
62         RS_ConstructionLineData d1(mp1, mp1 + dir1);
63         RS_ConstructionLineData d2(mp2, mp2 + dir2);
64         RS_ConstructionLine midLine1(NULL, d1);
65         RS_ConstructionLine midLine2(NULL, d2);
66
67         VectorSolutions sol =
68             RS_Information::getIntersection(&midLine1, &midLine2);
69
70         data.center = sol.get(0);
71         data.radius = data.center.distanceTo(p3);
72         data.angle1 = data.center.angleTo(p1);
73         data.angle2 = data.center.angleTo(p3);
74         data.reversed = RS_Math::isAngleBetween(data.center.angleTo(p2),
75                                                 data.angle1, data.angle2, true);
76
77         if (sol.get(0).valid && data.radius<1.0e14 &&
78                 data.radius>RS_TOLERANCE) {
79             calculateEndpoints();
80             calculateBorders();
81             return true;
82         } else {
83             RS_DEBUG->print("RS_Arc::createFrom3P(): "
84                             "Cannot create an arc with inf radius.");
85             return false;
86         }
87     } else {
88         RS_DEBUG->print("RS_Arc::createFrom3P(): "
89                         "Cannot create an arc with radius 0.0.");
90         return false;
91     }
92 }
93
94
95
96 /**
97  * Creates an arc from its startpoint, endpoint, start direction (angle)
98  * and radius.
99  *
100  * @retval true Successfully created arc
101  * @retval false Cannot creats arc (radius to small or endpoint to far away)
102  */
103 bool RS_Arc::createFrom2PDirectionRadius(const Vector& startPoint,
104         const Vector& endPoint,
105         double direction1, double radius) {
106
107     Vector ortho;
108     ortho.setPolar(radius, direction1 + M_PI/2.0);
109     Vector center1 = startPoint + ortho;
110     Vector center2 = startPoint - ortho;
111
112     if (center1.distanceTo(endPoint) < center2.distanceTo(endPoint)) {
113         data.center = center1;
114     } else {
115         data.center = center2;
116     }
117
118     data.radius = radius;
119     data.angle1 = data.center.angleTo(startPoint);
120     data.angle2 = data.center.angleTo(endPoint);
121     data.reversed = false;
122
123     double diff = RS_Math::correctAngle(getDirection1()-direction1);
124     if (fabs(diff-M_PI)<1.0e-1) {
125         data.reversed = true;
126     }
127
128     calculateEndpoints();
129     calculateBorders();
130
131     return true;
132 }
133
134
135
136 /**
137  * Creates an arc from its startpoint, endpoint and bulge.
138  */
139 bool RS_Arc::createFrom2PBulge(const Vector& startPoint, const Vector& endPoint,
140                                double bulge) {
141     data.reversed = (bulge<0.0);
142     double alpha = atan(bulge)*4.0;
143
144     Vector middle = (startPoint+endPoint)/2.0;
145     double dist = startPoint.distanceTo(endPoint)/2.0;
146
147     // alpha can't be 0.0 at this point
148     data.radius = fabs(dist / sin(alpha/2.0));
149
150     double wu = fabs(RS_Math::pow(data.radius, 2.0) - RS_Math::pow(dist, 2.0));
151     double h = sqrt(wu);
152     double angle = startPoint.angleTo(endPoint);
153
154     if (bulge>0.0) {
155         angle+=M_PI/2.0;
156     } else {
157         angle-=M_PI/2.0;
158     }
159
160     if (fabs(alpha)>M_PI) {
161         h*=-1.0;
162     }
163
164     data.center.setPolar(h, angle);
165     data.center+=middle;
166     data.angle1 = data.center.angleTo(startPoint);
167     data.angle2 = data.center.angleTo(endPoint);
168
169     calculateEndpoints();
170     calculateBorders();
171
172         return true;
173 }
174
175
176
177 /**
178  * Recalculates the endpoints using the angles and the radius.
179  */
180 void RS_Arc::calculateEndpoints() {
181     startpoint.set(data.center.x + cos(data.angle1) * data.radius,
182                    data.center.y + sin(data.angle1) * data.radius);
183     endpoint.set(data.center.x + cos(data.angle2) * data.radius,
184                  data.center.y + sin(data.angle2) * data.radius);
185 }
186
187
188 void RS_Arc::calculateBorders() {
189     double minX = std::min(startpoint.x, endpoint.x);
190     double minY = std::min(startpoint.y, endpoint.y);
191     double maxX = std::max(startpoint.x, endpoint.x);
192     double maxY = std::max(startpoint.y, endpoint.y);
193
194     double a1 = !isReversed() ? data.angle1 : data.angle2;
195     double a2 = !isReversed() ? data.angle2 : data.angle1;
196
197     // check for left limit:
198     if ((a1<M_PI && a2>M_PI) ||
199             (a1>a2-1.0e-12 && a2>M_PI) ||
200             (a1>a2-1.0e-12 && a1<M_PI) ) {
201
202         minX = std::min(data.center.x - data.radius, minX);
203     }
204
205     // check for right limit:
206     if (a1 > a2-1.0e-12) {
207         maxX = std::max(data.center.x + data.radius, maxX);
208     }
209
210     // check for bottom limit:
211     if ((a1<(M_PI_2*3) && a2>(M_PI_2*3)) ||
212             (a1>a2-1.0e-12    && a2>(M_PI_2*3)) ||
213             (a1>a2-1.0e-12    && a1<(M_PI_2*3)) ) {
214
215         minY = std::min(data.center.y - data.radius, minY);
216     }
217
218     // check for top limit:
219     if ((a1<M_PI_2 && a2>M_PI_2) ||
220             (a1>a2-1.0e-12   && a2>M_PI_2) ||
221             (a1>a2-1.0e-12   && a1<M_PI_2) ) {
222
223         maxY = std::max(data.center.y + data.radius, maxY);
224     }
225
226     minV.set(minX, minY);
227     maxV.set(maxX, maxY);
228 }
229
230
231
232 VectorSolutions RS_Arc::getRefPoints() {
233     VectorSolutions ret(startpoint, endpoint, data.center);
234     return ret;
235 }
236
237
238 Vector RS_Arc::getNearestEndpoint(const Vector& coord, double* dist) {
239     double dist1, dist2;
240     Vector* nearerPoint;
241
242     dist1 = startpoint.distanceTo(coord);
243     dist2 = endpoint.distanceTo(coord);
244
245     if (dist2<dist1) {
246         if (dist!=NULL) {
247             *dist = dist2;
248         }
249         nearerPoint = &endpoint;
250     } else {
251         if (dist!=NULL) {
252             *dist = dist1;
253         }
254         nearerPoint = &startpoint;
255     }
256
257     return *nearerPoint;
258 }
259
260
261
262 Vector RS_Arc::getNearestPointOnEntity(const Vector& coord,
263         bool onEntity, double* dist, RS_Entity** entity) {
264
265     Vector vec(false);
266     if (entity!=NULL) {
267         *entity = this;
268     }
269
270     double angle = (coord-data.center).angle();
271     if (onEntity==false || RS_Math::isAngleBetween(angle,
272             data.angle1, data.angle2, isReversed())) {
273         vec.setPolar(data.radius, angle);
274         vec+=data.center;
275     }
276     if (dist!=NULL) {
277         *dist = fabs((vec-data.center).magnitude()-data.radius);
278     }
279
280     return vec;
281 }
282
283
284
285 Vector RS_Arc::getNearestCenter(const Vector& coord,
286                                    double* dist) {
287     if (dist!=NULL) {
288         *dist = coord.distanceTo(data.center);
289     }
290     return data.center;
291 }
292
293
294
295 Vector RS_Arc::getNearestMiddle(const Vector& coord,
296                                    double* dist) {
297
298     Vector ret = getMiddlepoint();
299
300     if (dist!=NULL) {
301         *dist = coord.distanceTo(ret);
302     }
303     return ret;
304 }
305
306
307
308 Vector RS_Arc::getNearestDist(double distance,
309                                  const Vector& coord,
310                                  double* dist) {
311
312     if (data.radius<1.0e-6) {
313         if (dist!=NULL) {
314             *dist = RS_MAXDOUBLE;
315         }
316         return Vector(false);
317     }
318
319     double a1, a2;
320     Vector p1, p2;
321     double aDist = distance / data.radius;
322
323     if (isReversed()) {
324         a1 = data.angle1 - aDist;
325         a2 = data.angle2 + aDist;
326     } else {
327         a1 = data.angle1 + aDist;
328         a2 = data.angle2 - aDist;
329     }
330
331     p1.setPolar(data.radius, a1);
332     p1 += data.center;
333     p2.setPolar(data.radius, a2);
334     p2 += data.center;
335
336     double dist1, dist2;
337     Vector* nearerPoint;
338
339     dist1 = p1.distanceTo(coord);
340     dist2 = p2.distanceTo(coord);
341
342     if (dist2<dist1) {
343         if (dist!=NULL) {
344             *dist = dist2;
345         }
346         nearerPoint = &p2;
347     } else {
348         if (dist!=NULL) {
349             *dist = dist1;
350         }
351         nearerPoint = &p1;
352     }
353
354     return *nearerPoint;
355 }
356
357
358
359
360 Vector RS_Arc::getNearestDist(double distance,
361                                  bool startp) {
362
363     if (data.radius<1.0e-6) {
364         return Vector(false);
365     }
366
367     double a;
368     Vector p;
369     double aDist = distance / data.radius;
370
371     if (isReversed()) {
372         if (startp) {
373             a = data.angle1 - aDist;
374         } else {
375             a = data.angle2 + aDist;
376         }
377     } else {
378         if (startp) {
379             a = data.angle1 + aDist;
380         } else {
381             a = data.angle2 - aDist;
382         }
383     }
384
385     p.setPolar(data.radius, a);
386     p += data.center;
387
388     return p;
389 }
390
391
392
393 double RS_Arc::getDistanceToPoint(const Vector& coord,
394                                   RS_Entity** entity,
395                                   RS2::ResolveLevel,
396                                   double) {
397     if (entity!=NULL) {
398         *entity = this;
399     }
400
401     // check endpoints first:
402     double dist = coord.distanceTo(getStartpoint());
403     if (dist<1.0e-4) {
404         return dist;
405     }
406     dist = coord.distanceTo(getEndpoint());
407     if (dist<1.0e-4) {
408         return dist;
409     }
410
411     if (RS_Math::isAngleBetween(data.center.angleTo(coord),
412                                 data.angle1, data.angle2,
413                                 isReversed())) {
414
415         return fabs((coord-data.center).magnitude() - data.radius);
416     } else {
417         return RS_MAXDOUBLE;
418     }
419 }
420
421
422
423 void RS_Arc::moveStartpoint(const Vector& pos) {
424     // polyline arcs: move point not angle:
425     //if (parent!=NULL && parent->rtti()==RS2::EntityPolyline) {
426                 double bulge = getBulge();
427                 createFrom2PBulge(pos, getEndpoint(), bulge);
428     //}
429
430         // normal arc: move angle1
431         /*else {
432         data.angle1 = data.center.angleTo(pos);
433         calculateEndpoints();
434         calculateBorders();
435         }*/
436 }
437
438
439
440 void RS_Arc::moveEndpoint(const Vector& pos) {
441     // polyline arcs: move point not angle:
442     //if (parent!=NULL && parent->rtti()==RS2::EntityPolyline) {
443                 double bulge = getBulge();
444                 createFrom2PBulge(getStartpoint(), pos, bulge);
445     //}
446
447         // normal arc: move angle1
448         /*else {
449         data.angle2 = data.center.angleTo(pos);
450             calculateEndpoints();
451         calculateBorders();
452         }*/
453 }
454
455
456
457 void RS_Arc::trimStartpoint(const Vector& pos) {
458         data.angle1 = data.center.angleTo(pos);
459         calculateEndpoints();
460         calculateBorders();
461 }
462
463
464
465 void RS_Arc::trimEndpoint(const Vector& pos) {
466         data.angle2 = data.center.angleTo(pos);
467             calculateEndpoints();
468         calculateBorders();
469 }
470
471
472 RS2::Ending RS_Arc::getTrimPoint(const Vector& coord,
473                                  const Vector& trimPoint) {
474
475     double angEl = data.center.angleTo(trimPoint);
476     double angM = data.center.angleTo(coord);
477
478     if (RS_Math::getAngleDifference(angM, angEl)>M_PI) {
479         if (data.reversed) {
480             return RS2::EndingEnd;
481         } else {
482             return RS2::EndingStart;
483         }
484     } else {
485         if (data.reversed) {
486             return RS2::EndingStart;
487         } else {
488             return RS2::EndingEnd;
489         }
490     }
491 }
492
493
494 void RS_Arc::reverse() {
495     double a = data.angle1;
496     data.angle1 = data.angle2;
497     data.angle2 = a;
498     data.reversed = !data.reversed;
499     calculateEndpoints();
500     calculateBorders();
501 }
502
503
504 void RS_Arc::move(Vector offset) {
505     data.center.move(offset);
506     calculateEndpoints();
507     calculateBorders();
508 }
509
510
511
512 void RS_Arc::rotate(Vector center, double angle) {
513     RS_DEBUG->print("RS_Arc::rotate");
514     data.center.rotate(center, angle);
515     data.angle1 = RS_Math::correctAngle(data.angle1+angle);
516     data.angle2 = RS_Math::correctAngle(data.angle2+angle);
517     calculateEndpoints();
518     calculateBorders();
519     RS_DEBUG->print("RS_Arc::rotate: OK");
520 }
521
522
523
524 void RS_Arc::scale(Vector center, Vector factor) {
525     // negative scaling: mirroring
526     if (factor.x<0.0) {
527         mirror(data.center, data.center + Vector(0.0, 1.0));
528         //factor.x*=-1;
529     }
530     if (factor.y<0.0) {
531         mirror(data.center, data.center + Vector(1.0, 0.0));
532         //factor.y*=-1;
533     }
534
535     data.center.scale(center, factor);
536     data.radius *= factor.x;
537     if (data.radius<0.0) {
538         data.radius*=-1.0;
539     }
540     calculateEndpoints();
541     calculateBorders();
542 }
543
544
545
546 void RS_Arc::mirror(Vector axisPoint1, Vector axisPoint2) {
547     data.center.mirror(axisPoint1, axisPoint2);
548     data.reversed = (!data.reversed);
549     /*
550     startpoint.mirror(axisPoint1, axisPoint2);
551     endpoint.mirror(axisPoint1, axisPoint2);
552
553     data.angle1 = data.center.angleTo(startpoint);
554     data.angle2 = data.center.angleTo(endpoint);
555     */
556
557     Vector vec;
558     vec.setPolar(1.0, data.angle1);
559     vec.mirror(Vector(0.0,0.0), axisPoint2-axisPoint1);
560     data.angle1 = vec.angle();
561
562     vec.setPolar(1.0, data.angle2);
563     vec.mirror(Vector(0.0,0.0), axisPoint2-axisPoint1);
564     data.angle2 = vec.angle();
565
566     calculateEndpoints();
567     calculateBorders();
568 }
569
570
571
572 void RS_Arc::moveRef(const Vector& ref, const Vector& offset) {
573     if (ref.distanceTo(startpoint)<1.0e-4) {
574         moveStartpoint(startpoint+offset);
575     }
576     if (ref.distanceTo(endpoint)<1.0e-4) {
577         moveEndpoint(endpoint+offset);
578     }
579 }
580
581
582
583 void RS_Arc::stretch(Vector firstCorner,
584                       Vector secondCorner,
585                       Vector offset) {
586
587     if (getMin().isInWindow(firstCorner, secondCorner) &&
588             getMax().isInWindow(firstCorner, secondCorner)) {
589
590         move(offset);
591     }
592         else {
593             if (getStartpoint().isInWindow(firstCorner,
594                                        secondCorner)) {
595                 moveStartpoint(getStartpoint() + offset);
596             }
597             if (getEndpoint().isInWindow(firstCorner,
598                                          secondCorner)) {
599                 moveEndpoint(getEndpoint() + offset);
600             }
601         }
602 }
603
604 //void RS_Arc::draw(RS_Painter* painter, RS_GraphicView* view,
605 void RS_Arc::draw(PaintInterface * painter, RS_GraphicView * view, double /*patternOffset*/)
606 {
607     if (painter == NULL || view == NULL)
608         return;
609
610     //double styleFactor = getStyleFactor();
611
612     // simple style-less lines
613     if (getPen().getLineType()==RS2::SolidLine ||
614             isSelected() ||
615             view->getDrawingMode()==RS2::ModePreview) {
616
617         painter->drawArc(view->toGui(getCenter()),
618                          getRadius() * view->getFactor().x,
619                          getAngle1(), getAngle2(),
620                          isReversed());
621     } else {
622         double styleFactor = getStyleFactor(view);
623                 if (styleFactor<0.0) {
624                 painter->drawArc(view->toGui(getCenter()),
625                          getRadius() * view->getFactor().x,
626                          getAngle1(), getAngle2(),
627                          isReversed());
628                         return;
629                 }
630
631         // Pattern:
632         RS_LineTypePattern* pat;
633         if (isSelected()) {
634             pat = &patternSelected;
635         } else {
636             pat = view->getPattern(getPen().getLineType());
637         }
638
639         if (pat==NULL) {
640             return;
641         }
642
643         if (getRadius()<1.0e-6) {
644             return;
645         }
646
647         // Pen to draw pattern is always solid:
648         RS_Pen pen = painter->getPen();
649         pen.setLineType(RS2::SolidLine);
650         painter->setPen(pen);
651
652         double a1;
653         double a2;
654         if (data.reversed) {
655             a2 = getAngle1();
656             a1 = getAngle2();
657         } else {
658             a1 = getAngle1();
659             a2 = getAngle2();
660         }
661
662         double* da;     // array of distances in x.
663         int i;          // index counter
664
665         double length = getAngleLength();
666
667         // create scaled pattern:
668         da = new double[pat->num];
669
670         for (i=0; i<pat->num; ++i) {
671             da[i] = fabs(pat->pattern[i] * styleFactor) / getRadius();
672         }
673
674         double tot=0.0;
675         i=0;
676         bool done = false;
677         double curA = a1;
678         //double cx = getCenter().x * factor.x + offsetX;
679         //double cy = - a->getCenter().y * factor.y + getHeight() - offsetY;
680         Vector cp = view->toGui(getCenter());
681         double r = getRadius() * view->getFactor().x;
682
683         do {
684             if (pat->pattern[i] > 0.0) {
685                 if (tot+da[i]<length) {
686                     painter->drawArc(cp, r,
687                                      curA,
688                                      curA + da[i],
689                                      false);
690                 } else {
691                     painter->drawArc(cp, r,
692                                      curA,
693                                      a2,
694                                      false);
695                 }
696             }
697             curA+=da[i];
698             tot+=da[i];
699             done=tot>length;
700
701             i++;
702             if (i>=pat->num) {
703                 i=0;
704             }
705         } while(!done);
706
707         delete[] da;
708     }
709 }
710
711
712
713 /**
714  * @return Middle point of the entity.
715  */
716 Vector RS_Arc::getMiddlepoint() const {
717     double a;
718     Vector ret;
719
720     if (isReversed()) {
721         a = data.angle1 - getAngleLength()/2.0;
722     } else {
723         a = data.angle1 + getAngleLength()/2.0;
724     }
725     ret.setPolar(data.radius, a);
726     ret+=data.center;
727
728     return ret;
729 }
730
731
732
733 /**
734  * @return Angle length in rad.
735  */
736 double RS_Arc::getAngleLength() const {
737     double ret = 0.0;
738
739     if (isReversed()) {
740         if (data.angle1<data.angle2) {
741             ret = data.angle1+2*M_PI-data.angle2;
742         } else {
743             ret = data.angle1-data.angle2;
744         }
745     } else {
746         if (data.angle2<data.angle1) {
747             ret = data.angle2+2*M_PI-data.angle1;
748         } else {
749             ret = data.angle2-data.angle1;
750         }
751     }
752
753     // full circle:
754     if (fabs(ret)<1.0e-6) {
755         ret = 2*M_PI;
756     }
757
758     return ret;
759 }
760
761
762
763 /**
764  * @return Length of the arc.
765  */
766 double RS_Arc::getLength() {
767     return getAngleLength()*data.radius;
768 }
769
770
771
772 /**
773  * Gets the arc's bulge (tangens of angle length divided by 4).
774  */
775 double RS_Arc::getBulge() const {
776     double bulge = tan(fabs(getAngleLength())/4.0);
777     if (isReversed()) {
778         bulge*=-1;
779     }
780     return bulge;
781 }
782
783
784
785 /**
786  * Dumps the point's data to stdout.
787  */
788 std::ostream& operator << (std::ostream& os, const RS_Arc& a) {
789     os << " Arc: " << a.data << "\n";
790     return os;
791 }
792