1 /****************************************************************************
2 **
3 ** This file is part of the LibreCAD project, a 2D CAD program
4 **
5 ** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
6 ** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
7 **
8 **
9 ** This file may be distributed and/or modified under the terms of the
10 ** GNU General Public License version 2 as published by the Free Software
11 ** Foundation and appearing in the file gpl-2.0.txt included in the
12 ** packaging of this file.
13 **
14 ** This program is distributed in the hope that it will be useful,
15 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ** GNU General Public License for more details.
18 **
19 ** You should have received a copy of the GNU General Public License
20 ** along with this program; if not, write to the Free Software
21 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 **
23 ** This copyright notice MUST APPEAR in all copies of the script!
24 **
25 **********************************************************************/
26 
27 #include <cmath>
28 #include "rs_arc.h"
29 
30 #include "rs_line.h"
31 #include "rs_constructionline.h"
32 #include "rs_linetypepattern.h"
33 #include "rs_information.h"
34 #include "rs_math.h"
35 #include "rs_graphicview.h"
36 #include "rs_painter.h"
37 #include "lc_quadratic.h"
38 #include "rs_painterqt.h"
39 #include "rs_debug.h"
40 #include "lc_rect.h"
41 
42 #ifdef EMU_C99
43 #include "emu_c99.h"
44 #endif
45 
RS_ArcData(const RS_Vector & _center,double _radius,double _angle1,double _angle2,bool _reversed)46 RS_ArcData::RS_ArcData(const RS_Vector& _center,
47 					   double _radius,
48 					   double _angle1, double _angle2,
49 					   bool _reversed):
50 	center(_center)
51   ,radius(_radius)
52   ,angle1(_angle1)
53   ,angle2(_angle2)
54   ,reversed(_reversed)
55 {
56 }
57 
reset()58 void RS_ArcData::reset() {
59 	center = RS_Vector(false);
60 	radius = 0.0;
61 	angle1 = 0.0;
62 	angle2 = 0.0;
63 	reversed = false;
64 }
65 
isValid() const66 bool RS_ArcData::isValid() const{
67 	return (center.valid && radius>RS_TOLERANCE &&
68 			fabs(remainder(angle1-angle2, 2.*M_PI))>RS_TOLERANCE_ANGLE);
69 }
70 
operator <<(std::ostream & os,const RS_ArcData & ad)71 std::ostream& operator << (std::ostream& os, const RS_ArcData& ad) {
72 	os << "(" << ad.center <<
73 		  "/" << ad.radius <<
74 		  " " << ad.angle1 <<
75 		  "," << ad.angle2 <<
76 		  ")";
77 	return os;
78 }
79 /**
80  * Default constructor.
81  */
RS_Arc(RS_EntityContainer * parent,const RS_ArcData & d)82 RS_Arc::RS_Arc(RS_EntityContainer* parent,
83                const RS_ArcData& d)
84     : RS_AtomicEntity(parent), data(d) {
85     calculateBorders();
86 }
87 
clone() const88 RS_Entity* RS_Arc::clone() const {
89 	RS_Arc* a = new RS_Arc(*this);
90 	a->initId();
91 	return a;
92 }
93 
94 /**
95  * Creates this arc from 3 given points which define the arc line.
96  *
97  * @param p1 1st point.
98  * @param p2 2nd point.
99  * @param p3 3rd point.
100  */
createFrom3P(const RS_Vector & p1,const RS_Vector & p2,const RS_Vector & p3)101 bool RS_Arc::createFrom3P(const RS_Vector& p1, const RS_Vector& p2,
102                           const RS_Vector& p3) {
103         RS_Vector vra=p2 - p1;
104         RS_Vector vrb=p3 - p1;
105         double ra2=vra.squared()*0.5;
106         double rb2=vrb.squared()*0.5;
107         double crossp=vra.x * vrb.y - vra.y * vrb.x;
108         if (fabs(crossp)< RS_TOLERANCE2) {
109                 RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Arc::createFrom3P(): "
110                         "Cannot create a arc with radius 0.0.");
111                 return false;
112         }
113         crossp=1./crossp;
114         data.center.set((ra2*vrb.y - rb2*vra.y)*crossp,(rb2*vra.x - ra2*vrb.x)*crossp);
115         data.radius=data.center.magnitude();
116         data.center += p1;
117         data.angle1=data.center.angleTo(p1);
118         data.angle2=data.center.angleTo(p3);
119         data.reversed = RS_Math::isAngleBetween(data.center.angleTo(p2),
120                                                 data.angle1, data.angle2, true);
121         return true;
122 }
123 
124 
125 /**
126  * Creates an arc from its startpoint, endpoint, start direction (angle)
127  * and radius.
128  *
129  * @retval true Successfully created arc
130  * @retval false Cannot create arc (radius to small or endpoint to far away)
131  */
createFrom2PDirectionRadius(const RS_Vector & startPoint,const RS_Vector & endPoint,double direction1,double radius)132 bool RS_Arc::createFrom2PDirectionRadius(const RS_Vector& startPoint,
133         const RS_Vector& endPoint,
134         double direction1, double radius) {
135 
136 	RS_Vector ortho = RS_Vector::polar(radius, direction1 + M_PI_2);
137     RS_Vector center1 = startPoint + ortho;
138     RS_Vector center2 = startPoint - ortho;
139 
140     if (center1.distanceTo(endPoint) < center2.distanceTo(endPoint)) {
141         data.center = center1;
142     } else {
143         data.center = center2;
144     }
145 
146     data.radius = radius;
147     data.angle1 = data.center.angleTo(startPoint);
148     data.angle2 = data.center.angleTo(endPoint);
149     data.reversed = false;
150 
151     double diff = RS_Math::correctAngle(getDirection1()-direction1);
152     if (fabs(diff-M_PI)<1.0e-1) {
153         data.reversed = true;
154     }
155     calculateBorders();
156 
157     return true;
158 }
159 
160 /**
161  * Creates an arc from its startpoint, endpoint, start direction (angle)
162  * and angle length.
163  *
164  * @retval true Successfully created arc
165  * @retval false Cannot create arc (radius to small or endpoint to far away)
166  */
createFrom2PDirectionAngle(const RS_Vector & startPoint,const RS_Vector & endPoint,double direction1,double angleLength)167 bool RS_Arc::createFrom2PDirectionAngle(const RS_Vector& startPoint,
168                                         const RS_Vector& endPoint,
169 										double direction1, double angleLength) {
170 	if (angleLength <= RS_TOLERANCE_ANGLE || angleLength > 2. * M_PI - RS_TOLERANCE_ANGLE) return false;
171 	RS_Line l0 {nullptr, startPoint, startPoint - RS_Vector{direction1}};
172 	double const halfA = 0.5 * angleLength;
173 	l0.rotate(startPoint, halfA);
174 
175 	double d0;
176 	RS_Vector vEnd0 = l0.getNearestPointOnEntity(endPoint, false, &d0);
177 	RS_Line l1 = l0;
178 	l1.rotate(startPoint, -angleLength);
179 	double d1;
180 	RS_Vector vEnd1 = l1.getNearestPointOnEntity(endPoint, false, &d1);
181 	if (d1 < d0) {
182 		vEnd0 = vEnd1;
183 		l0 = l1;
184 	}
185 
186 	l0.rotate((startPoint + vEnd0) * 0.5, M_PI_2);
187 
188 	l1 = RS_Line{nullptr, startPoint, startPoint + RS_Vector{direction1 + M_PI_2}};
189 
190 	auto const sol = RS_Information::getIntersection(&l0, &l1, false);
191 	if (sol.size()==0) return false;
192 
193 	data.center = sol.at(0);
194 
195 	data.radius = data.center.distanceTo(startPoint);
196     data.angle1 = data.center.angleTo(startPoint);
197     data.reversed = false;
198 
199     double diff = RS_Math::correctAngle(getDirection1()-direction1);
200     if (fabs(diff-M_PI)<1.0e-1) {
201     data.angle2 = RS_Math::correctAngle(data.angle1 -angleLength);
202         data.reversed = true;
203     }else{
204     data.angle2 = RS_Math::correctAngle(data.angle1 +angleLength);
205     }
206     calculateBorders();
207 
208     return true;
209 }
210 
211 
212 
213 /**
214  * Creates an arc from its startpoint, endpoint and bulge.
215  */
createFrom2PBulge(const RS_Vector & startPoint,const RS_Vector & endPoint,double bulge)216 bool RS_Arc::createFrom2PBulge(const RS_Vector& startPoint, const RS_Vector& endPoint,
217                                double bulge) {
218     data.reversed = (bulge<0.0);
219     double alpha = atan(bulge)*4.0;
220 
221     RS_Vector middle = (startPoint+endPoint)/2.0;
222     double dist = startPoint.distanceTo(endPoint)/2.0;
223 
224     // alpha can't be 0.0 at this point
225     data.radius = fabs(dist / sin(alpha/2.0));
226 
227     double wu = fabs(RS_Math::pow(data.radius, 2.0) - RS_Math::pow(dist, 2.0));
228     double h = sqrt(wu);
229     double angle = startPoint.angleTo(endPoint);
230 
231     if (bulge>0.0) {
232 		angle+=M_PI_2;
233     } else {
234 		angle-=M_PI_2;
235     }
236 
237     if (fabs(alpha)>M_PI) {
238         h*=-1.0;
239     }
240 
241     data.center.setPolar(h, angle);
242     data.center+=middle;
243     data.angle1 = data.center.angleTo(startPoint);
244     data.angle2 = data.center.angleTo(endPoint);
245 
246     calculateBorders();
247 
248     return true;
249 }
250 
calculateBorders()251 void RS_Arc::calculateBorders() {
252 	RS_Vector const startpoint = data.center + RS_Vector::polar(data.radius, data.angle1);
253 	RS_Vector const endpoint = data.center + RS_Vector::polar(data.radius, data.angle2);
254 	LC_Rect const rect{startpoint, endpoint};
255 
256 	double minX = rect.lowerLeftCorner().x;
257 	double minY = rect.lowerLeftCorner().y;
258 	double maxX = rect.upperRightCorner().x;
259 	double maxY = rect.upperRightCorner().y;
260 
261     double a1 = isReversed() ? data.angle2 : data.angle1;
262     double a2 = isReversed() ? data.angle1 : data.angle2;
263     if ( RS_Math::isAngleBetween(0.5*M_PI,a1,a2,false) ) {
264         maxY = data.center.y + data.radius;
265     }
266     if ( RS_Math::isAngleBetween(1.5*M_PI,a1,a2,false) ) {
267         minY = data.center.y - data.radius;
268     }
269     if ( RS_Math::isAngleBetween(M_PI,a1,a2,false) ) {
270         minX = data.center.x - data.radius;
271     }
272     if ( RS_Math::isAngleBetween(0.,a1,a2,false) ) {
273         maxX = data.center.x + data.radius;
274     }
275 
276     minV.set(minX, minY);
277     maxV.set(maxX, maxY);
278 }
279 
280 
getStartpoint() const281 RS_Vector RS_Arc::getStartpoint() const
282 {
283 	return data.center + RS_Vector::polar(data.radius, data.angle1);
284 }
285 
286 /** @return End point of the entity. */
getEndpoint() const287 RS_Vector RS_Arc::getEndpoint() const
288 {
289 	return data.center + RS_Vector::polar(data.radius, data.angle2);
290 }
291 
getRefPoints() const292 RS_VectorSolutions RS_Arc::getRefPoints() const
293 {
294 	//order: start, end, center
295 	return {getStartpoint(), getEndpoint(), data.center};
296 }
297 
getDirection1() const298 double RS_Arc::getDirection1() const {
299 	if (!data.reversed) {
300 		return RS_Math::correctAngle(data.angle1+M_PI_2);
301 	}
302 	else {
303 		return RS_Math::correctAngle(data.angle1-M_PI_2);
304 	}
305 }
306 /**
307  * @return Direction 2. The angle at which the arc starts at
308  * the endpoint.
309  */
getDirection2() const310 double RS_Arc::getDirection2() const {
311 	if (!data.reversed) {
312 		return RS_Math::correctAngle(data.angle2-M_PI_2);
313 	}
314 	else {
315 		return RS_Math::correctAngle(data.angle2+M_PI_2);
316 	}
317 }
318 
getNearestEndpoint(const RS_Vector & coord,double * dist) const319 RS_Vector RS_Arc::getNearestEndpoint(const RS_Vector& coord, double* dist) const{
320     double dist1, dist2;
321 
322 	auto const startpoint = getStartpoint();
323 	auto const endpoint = getEndpoint();
324 
325 	dist1 = coord.squaredTo(startpoint);
326 	dist2 = coord.squaredTo(endpoint);
327 
328     if (dist2<dist1) {
329 		if (dist)
330             *dist = sqrt(dist2);
331 
332          return endpoint;
333     } else {
334 		if (dist)
335             *dist = sqrt(dist1);
336 
337         return startpoint;
338     }
339 
340 }
341 
342 
343 /**
344   *find the tangential points from a given point, i.e., the tangent lines should pass
345   * the given point and tangential points
346   *
347   *Author: Dongxu Li
348   */
getTangentPoint(const RS_Vector & point) const349 RS_VectorSolutions RS_Arc::getTangentPoint(const RS_Vector& point) const {
350     RS_VectorSolutions ret;
351     double r2(getRadius()*getRadius());
352     if(r2<RS_TOLERANCE2) return ret; //circle too small
353     RS_Vector vp(point-getCenter());
354     double c2(vp.squared());
355     if(c2<r2-getRadius()*2.*RS_TOLERANCE) {
356         //inside point, no tangential point
357         return ret;
358     }
359     if(c2>r2+getRadius()*2.*RS_TOLERANCE) {
360         //external point
361         RS_Vector vp1(-vp.y,vp.x);
362         vp1*=getRadius()*sqrt(c2-r2)/c2;
363         vp *= r2/c2;
364         vp += getCenter();
365         if(vp1.squared()>RS_TOLERANCE2) {
366             ret.push_back(vp+vp1);
367             ret.push_back(vp-vp1);
368             return ret;
369         }
370     }
371     ret.push_back(point);
372     return ret;
373 }
374 
getTangentDirection(const RS_Vector & point) const375 RS_Vector RS_Arc::getTangentDirection(const RS_Vector& point) const {
376     RS_Vector vp(point-getCenter());
377 //    double c2(vp.squared());
378 //    if(c2<r2-getRadius()*2.*RS_TOLERANCE) {
379 //        //inside point, no tangential point
380 //        return RS_Vector(false);
381 //    }
382     return RS_Vector(-vp.y,vp.x);
383 
384 }
385 
getNearestPointOnEntity(const RS_Vector & coord,bool onEntity,double * dist,RS_Entity ** entity) const386 RS_Vector RS_Arc::getNearestPointOnEntity(const RS_Vector& coord,
387 		bool onEntity, double* dist, RS_Entity** entity) const{
388 
389     RS_Vector vec(false);
390 	if (entity) {
391         *entity = const_cast<RS_Arc*>(this);
392     }
393 
394     double angle = (coord-data.center).angle();
395     if ( ! onEntity || RS_Math::isAngleBetween(angle,
396             data.angle1, data.angle2, isReversed())) {
397         vec.setPolar(data.radius, angle);
398         vec+=data.center;
399     } else {
400             return vec=getNearestEndpoint(coord, dist);
401     }
402 	if (dist) {
403         *dist = vec.distanceTo(coord);
404 //        RS_DEBUG->print(RS_Debug::D_ERROR, "distance to (%g, %g)=%g\n", coord.x,coord.y,*dist);
405     }
406 
407     return vec;
408 }
409 
410 
411 
getNearestCenter(const RS_Vector & coord,double * dist) const412 RS_Vector RS_Arc::getNearestCenter(const RS_Vector& coord,
413 								   double* dist) const{
414 	if (dist) {
415         *dist = coord.distanceTo(data.center);
416     }
417     return data.center;
418 }
419 
420 /*
421  * get the nearest equidistant middle points
422  * @coord, coordinate
423  * @middlePoints, number of equidistant middle points
424  *
425  */
426 
getNearestMiddle(const RS_Vector & coord,double * dist,int middlePoints) const427 RS_Vector RS_Arc::getNearestMiddle(const RS_Vector& coord,
428                                    double* dist,
429                                    int middlePoints
430                                    )const {
431 #ifndef EMU_C99
432     using std::isnormal;
433 #endif
434 
435     RS_DEBUG->print("RS_Arc::getNearestMiddle(): begin\n");
436         double amin=getAngle1();
437         double amax=getAngle2();
438         //std::cout<<"RS_Arc::getNearestMiddle(): middlePoints="<<middlePoints<<std::endl;
439 		if( !(isnormal(amin) || isnormal(amax))){
440                 //whole circle, no middle point
441 				if(dist) {
442                         *dist=RS_MAXDOUBLE;
443                 }
444                 return RS_Vector(false);
445         }
446         if(isReversed()) {
447                 std::swap(amin,amax);
448         }
449         double da=fmod(amax-amin+2.*M_PI, 2.*M_PI);
450         if ( da < RS_TOLERANCE ) {
451                 da= 2.*M_PI; // whole circle
452         }
453         RS_Vector vp(getNearestPointOnEntity(coord,true,dist));
454         double angle=getCenter().angleTo(vp);
455         int counts=middlePoints+1;
456         int i( static_cast<int>(fmod(angle-amin+2.*M_PI,2.*M_PI)/da*counts+0.5));
457         if(!i) i++; // remove end points
458         if(i==counts) i--;
459         angle=amin + da*(double(i)/double(counts));
460         vp.setPolar(getRadius(), angle);
461         vp.move(getCenter());
462 
463 	if (dist) {
464         *dist = vp.distanceTo(coord);
465     }
466     RS_DEBUG->print("RS_Arc::getNearestMiddle(): end\n");
467     return vp;
468 }
469 
getNearestDist(double distance,const RS_Vector & coord,double * dist) const470 RS_Vector RS_Arc::getNearestDist(double distance,
471                                  const RS_Vector& coord,
472 								 double* dist) const{
473 
474     if (data.radius<RS_TOLERANCE) {
475 		if (dist)
476             *dist = RS_MAXDOUBLE;
477 
478 		return {};
479     }
480 
481     double aDist = distance / data.radius;
482     if (isReversed()) aDist= -aDist;
483     double a;
484 
485 	if(coord.distanceTo(getStartpoint()) < coord.distanceTo(getEndpoint()))
486         a=getAngle1() + aDist;
487 	else
488         a=getAngle2() - aDist;
489 
490 	RS_Vector ret = RS_Vector::polar(data.radius, a);
491     ret += getCenter();
492 
493     return ret;
494 }
495 
496 
497 
498 
getNearestDist(double distance,bool startp) const499 RS_Vector RS_Arc::getNearestDist(double distance,
500 								 bool startp) const{
501 
502     if (data.radius<RS_TOLERANCE) {
503 		return {};
504     }
505 
506     double a;
507     double aDist = distance / data.radius;
508 
509     if (isReversed()) {
510         if (startp) {
511             a = data.angle1 - aDist;
512         } else {
513             a = data.angle2 + aDist;
514         }
515     } else {
516         if (startp) {
517             a = data.angle1 + aDist;
518         } else {
519             a = data.angle2 - aDist;
520         }
521     }
522 
523 	RS_Vector p = RS_Vector::polar(data.radius, a);
524     p += data.center;
525 
526     return p;
527 }
528 
529 
getNearestOrthTan(const RS_Vector & coord,const RS_Line & normal,bool onEntity) const530 RS_Vector RS_Arc::getNearestOrthTan(const RS_Vector& coord,
531                     const RS_Line& normal,
532 					bool onEntity ) const
533 {
534         if ( !coord.valid ) {
535                 return RS_Vector(false);
536         }
537         double angle=normal.getAngle1();
538 		RS_Vector vp = RS_Vector::polar(getRadius(),angle);
539 		std::vector<RS_Vector> sol;
540 		for(int i=0; i <= 1; i++){
541 			if(!onEntity ||
542 					RS_Math::isAngleBetween(angle,getAngle1(),getAngle2(),isReversed())) {
543 				if (i)
544 					sol.push_back(- vp);
545 				else
546 					sol.push_back(vp);
547 			}
548 			angle=RS_Math::correctAngle(angle+M_PI);
549 		}
550 		switch(sol.size()) {
551                 case 0:
552                         return RS_Vector(false);
553                 case 2:
554                         if( RS_Vector::dotP(sol[1],coord-getCenter())>0.) {
555                                 vp=sol[1];
556                                 break;
557                         }
558                         // fall-through
559                 default:
560                         vp=sol[0];
561                         break;
562         }
563         return getCenter()+vp;
564 }
565 
moveStartpoint(const RS_Vector & pos)566 void RS_Arc::moveStartpoint(const RS_Vector& pos) {
567     // polyline arcs: move point not angle:
568 	//if (parent && parent->rtti()==RS2::EntityPolyline) {
569     double bulge = getBulge();
570 	if(fabs(bulge - M_PI_2)<RS_TOLERANCE_ANGLE) return;
571 
572     createFrom2PBulge(pos, getEndpoint(), bulge);
573     correctAngles(); // make sure angleLength is no more than 2*M_PI
574     //}
575 }
576 
577 
578 
moveEndpoint(const RS_Vector & pos)579 void RS_Arc::moveEndpoint(const RS_Vector& pos) {
580     // polyline arcs: move point not angle:
581 	//if (parent && parent->rtti()==RS2::EntityPolyline) {
582     double bulge = getBulge();
583     createFrom2PBulge(getStartpoint(), pos, bulge);
584     correctAngles(); // make sure angleLength is no more than 2*M_PI
585     //}
586 }
587 
588 /**
589   * this function creates offset
590   *@coord, position indicates the direction of offset
591   *@distance, distance of offset
592   * return true, if success, otherwise, false
593   *
594   *Author: Dongxu Li
595   */
offset(const RS_Vector & coord,const double & distance)596 bool RS_Arc::offset(const RS_Vector& coord, const double& distance) {
597     double r0(coord.distanceTo(getCenter()));
598     if(r0 > getRadius()){
599         //external
600         r0 = getRadius()+ fabs(distance);
601     }else{
602         r0 = getRadius()- fabs(distance);
603         if(r0<RS_TOLERANCE) {
604             return false;
605         }
606     }
607     setRadius(r0);
608     calculateBorders();
609     return true;
610 }
offsetTwoSides(const double & distance) const611 std::vector<RS_Entity* > RS_Arc::offsetTwoSides(const double& distance) const
612 {
613 	std::vector<RS_Entity*> ret(0,nullptr);
614 	ret.push_back(new RS_Arc(nullptr,RS_ArcData(getCenter(),getRadius()+distance,getAngle1(),getAngle2(),isReversed())));
615     if(getRadius()>distance)
616 	ret.push_back(new RS_Arc(nullptr,RS_ArcData(getCenter(),getRadius()-distance,getAngle1(),getAngle2(),isReversed())));
617     return ret;
618 }
619 
620 /**
621       * implementations must revert the direction of an atomic entity
622       */
revertDirection()623 void RS_Arc::revertDirection(){
624     std::swap(data.angle1,data.angle2);
625     data.reversed = ! data.reversed;
626 }
627 
628 /**
629  * make sure angleLength() is not more than 2*M_PI
630  */
correctAngles()631 void RS_Arc::correctAngles() {
632         double *pa1= & data.angle1;
633         double *pa2= & data.angle2;
634         if (isReversed()) std::swap(pa1,pa2);
635         *pa2 = *pa1 + fmod(*pa2 - *pa1, 2.*M_PI);
636         if ( fabs(getAngleLength()) < RS_TOLERANCE_ANGLE ) *pa2 += 2.*M_PI;
637 }
638 
trimStartpoint(const RS_Vector & pos)639 void RS_Arc::trimStartpoint(const RS_Vector& pos) {
640     data.angle1 = data.center.angleTo(pos);
641     correctAngles(); // make sure angleLength is no more than 2*M_PI
642     calculateBorders();
643 }
644 
645 
646 
trimEndpoint(const RS_Vector & pos)647 void RS_Arc::trimEndpoint(const RS_Vector& pos) {
648     data.angle2 = data.center.angleTo(pos);
649     correctAngles(); // make sure angleLength is no more than 2*M_PI
650     calculateBorders();
651 }
652 
653 /**
654   *@ trimCoord, mouse point
655   *@  trimPoint, trim to this intersection point
656   */
getTrimPoint(const RS_Vector & trimCoord,const RS_Vector &)657 RS2::Ending RS_Arc::getTrimPoint(const RS_Vector& trimCoord,
658                                  const RS_Vector& /*trimPoint*/) {
659 
660     //double angEl = data.center.angleTo(trimPoint);
661     double angMouse = data.center.angleTo(trimCoord);
662 //    double angTrim = data.center.angleTo(trimPoint);
663     if( fabs(remainder(angMouse-data.angle1, 2.*M_PI))< fabs(remainder(angMouse-data.angle2, 2.*M_PI)))
664         return RS2::EndingStart;
665     else
666         return RS2::EndingEnd;
667 
668 //    if( RS_Math::isAngleBetween(angMouse , data.angle1, angTrim, isReversed())) {
669 
670 //        return RS2::EndingEnd;
671 //    } else {
672 
673 //        return RS2::EndingStart;
674 //    }
675 }
676 
prepareTrim(const RS_Vector & trimCoord,const RS_VectorSolutions & trimSol)677 RS_Vector RS_Arc::prepareTrim(const RS_Vector& trimCoord,
678                               const RS_VectorSolutions& trimSol) {
679     //special trimming for ellipse arc
680             RS_DEBUG->print("RS_Ellipse::prepareTrim()");
681         if( ! trimSol.hasValid() ) return (RS_Vector(false));
682         if( trimSol.getNumber() == 1 ) return (trimSol.get(0));
683         double am=getArcAngle(trimCoord);
684 		std::vector<double> ias;
685         double ia(0.),ia2(0.);
686         RS_Vector is,is2;
687 		for(size_t ii=0; ii<trimSol.getNumber(); ++ii) { //find closest according ellipse angle
688 			ias.push_back(getArcAngle(trimSol.get(ii)));
689             if( !ii ||  fabs( remainder( ias[ii] - am, 2*M_PI)) < fabs( remainder( ia -am, 2*M_PI)) ) {
690                 ia = ias[ii];
691                 is = trimSol.get(ii);
692             }
693         }
694         std::sort(ias.begin(),ias.end());
695 		for(size_t ii=0; ii<trimSol.getNumber(); ++ii) { //find segment to include trimCoord
696             if ( ! RS_Math::isSameDirection(ia,ias[ii],RS_TOLERANCE)) continue;
697             if( RS_Math::isAngleBetween(am,ias[(ii+trimSol.getNumber()-1)% trimSol.getNumber()],ia,false))  {
698                 ia2=ias[(ii+trimSol.getNumber()-1)% trimSol.getNumber()];
699             } else {
700                 ia2=ias[(ii+1)% trimSol.getNumber()];
701             }
702             break;
703         }
704 		for(const RS_Vector& vp: trimSol) { //find segment to include trimCoord
705 			if ( ! RS_Math::isSameDirection(ia2,getArcAngle(vp),RS_TOLERANCE)) continue;
706 			is2=vp;
707             break;
708         }
709 //        if(RS_Math::isSameDirection(getAngle1(),getAngle2(),RS_TOLERANCE_ANGLE)
710 //                ||  RS_Math::isSameDirection(ia2,ia,RS_TOLERANCE) ) {
711 //            //whole ellipse
712 //            if( !RS_Math::isAngleBetween(am,ia,ia2,isReversed())) {
713 //                std::swap(ia,ia2);
714 //                std::swap(is,is2);
715 //            }
716 //            setAngle1(ia);
717 //            setAngle2(ia2);
718 //            double da1=fabs(remainder(getAngle1()-am,2*M_PI));
719 //            double da2=fabs(remainder(getAngle2()-am,2*M_PI));
720 //            if(da2<da1) {
721 //                std::swap(is,is2);
722 //            }
723 
724 //        } else {
725             double dia=fabs(remainder(ia-am,2*M_PI));
726             double dia2=fabs(remainder(ia2-am,2*M_PI));
727             double ai_min=std::min(dia,dia2);
728             double da1=fabs(remainder(getAngle1()-am,2*M_PI));
729             double da2=fabs(remainder(getAngle2()-am,2*M_PI));
730             double da_min=std::min(da1,da2);
731             if( da_min < ai_min ) {
732                 //trimming one end of arc
733                 bool irev= RS_Math::isAngleBetween(am,ia2,ia, isReversed()) ;
734                 if ( RS_Math::isAngleBetween(ia,getAngle1(),getAngle2(), isReversed()) &&
735                         RS_Math::isAngleBetween(ia2,getAngle1(),getAngle2(), isReversed()) ) { //
736                     if(irev) {
737                         setAngle2(ia);
738                         setAngle1(ia2);
739                     } else {
740                         setAngle1(ia);
741                         setAngle2(ia2);
742                     }
743                     da1=fabs(remainder(getAngle1()-am,2*M_PI));
744                     da2=fabs(remainder(getAngle2()-am,2*M_PI));
745                 }
746                 if( ((da1 < da2) && (RS_Math::isAngleBetween(ia2,ia,getAngle1(),isReversed()))) ||
747                         ((da1 > da2) && (RS_Math::isAngleBetween(ia2,getAngle2(),ia,isReversed())))
748                   ) {
749                     std::swap(is,is2);
750                     //std::cout<<"reset: angle1="<<getAngle1()<<" angle2="<<getAngle2()<<" am="<< am<<" is="<<getArcAngle(is)<<" ia2="<<ia2<<std::endl;
751                 }
752             } else {
753                 //choose intersection as new end
754                 if( dia > dia2) {
755                     std::swap(is,is2);
756                     std::swap(ia,ia2);
757                 }
758                 if(RS_Math::isAngleBetween(ia,getAngle1(),getAngle2(),isReversed())) {
759                     if(RS_Math::isAngleBetween(am,getAngle1(),ia,isReversed())) {
760                         setAngle2(ia);
761                     } else {
762                         setAngle1(ia);
763                     }
764                 }
765             }
766 //        }
767         return is;
768 }
769 
770 
771 
reverse()772 void RS_Arc::reverse() {
773     std::swap(data.angle1,data.angle2);
774     data.reversed = !data.reversed;
775 //    calculateBorders();
776 }
777 
778 
move(const RS_Vector & offset)779 void RS_Arc::move(const RS_Vector& offset) {
780     data.center.move(offset);
781     moveBorders(offset);
782 }
783 
784 
785 
rotate(const RS_Vector & center,const double & angle)786 void RS_Arc::rotate(const RS_Vector& center, const double& angle) {
787     RS_DEBUG->print("RS_Arc::rotate");
788     data.center.rotate(center, angle);
789     data.angle1 = RS_Math::correctAngle(data.angle1+angle);
790     data.angle2 = RS_Math::correctAngle(data.angle2+angle);
791     calculateBorders();
792     RS_DEBUG->print("RS_Arc::rotate: OK");
793 }
794 
rotate(const RS_Vector & center,const RS_Vector & angleVector)795 void RS_Arc::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
796     RS_DEBUG->print("RS_Arc::rotate");
797     data.center.rotate(center, angleVector);
798     double angle(angleVector.angle());
799     data.angle1 = RS_Math::correctAngle(data.angle1+angle);
800     data.angle2 = RS_Math::correctAngle(data.angle2+angle);
801     calculateBorders();
802     RS_DEBUG->print("RS_Arc::rotate: OK");
803 }
804 
805 
806 
scale(const RS_Vector & center,const RS_Vector & factor)807 void RS_Arc::scale(const RS_Vector& center, const RS_Vector& factor) {
808     // negative scaling: mirroring
809     if (factor.x<0.0) {
810         mirror(data.center, data.center + RS_Vector(0.0, 1.0));
811         //factor.x*=-1;
812     }
813     if (factor.y<0.0) {
814         mirror(data.center, data.center + RS_Vector(1.0, 0.0));
815         //factor.y*=-1;
816     }
817 
818     data.center.scale(center, factor);
819     data.radius *= factor.x;
820     data.radius = fabs( data.radius );
821     //todo, does this handle negative factors properly?
822 	calculateBorders();
823 }
824 
825 
826 
mirror(const RS_Vector & axisPoint1,const RS_Vector & axisPoint2)827 void RS_Arc::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
828     data.center.mirror(axisPoint1, axisPoint2);
829     setReversed( ! isReversed() );
830     double a= (axisPoint2 - axisPoint1).angle()*2;
831     setAngle1(RS_Math::correctAngle(a - getAngle1()));
832     setAngle2(RS_Math::correctAngle(a - getAngle2()));
833     correctAngles(); // make sure angleLength is no more than 2*M_PI
834     calculateBorders();
835 }
836 
837 
838 
moveRef(const RS_Vector & ref,const RS_Vector & offset)839 void RS_Arc::moveRef(const RS_Vector& ref, const RS_Vector& offset)
840 {
841 	//avoid moving start/end points for full circle arcs
842 	//as start/end points coincident
843 	if (fabs(fabs(getAngleLength()-M_PI)-M_PI) < RS_TOLERANCE_ANGLE){
844         move(offset);
845         return;
846 	}
847 	auto const refs = getRefPoints();
848 	double dMin;
849 	size_t index;
850 	RS_Vector const vp = refs.getClosest(ref, &dMin, &index);
851 	if (dMin >= 1.0e-4)
852 		return;
853 
854 	//reference points must be by the order: start, end, center
855 	switch (index) {
856 	case 0:
857 		moveStartpoint(vp + offset);
858 		return;
859 	case 1:
860 		moveEndpoint(vp + offset);
861 		return;
862 	default:
863 		move(offset);
864 	}
865 
866     correctAngles(); // make sure angleLength is no more than 2*M_PI
867 }
868 
869 
870 
stretch(const RS_Vector & firstCorner,const RS_Vector & secondCorner,const RS_Vector & offset)871 void RS_Arc::stretch(const RS_Vector& firstCorner,
872                      const RS_Vector& secondCorner,
873                      const RS_Vector& offset) {
874 
875     if (getMin().isInWindow(firstCorner, secondCorner) &&
876             getMax().isInWindow(firstCorner, secondCorner)) {
877 
878         move(offset);
879     }
880     else {
881         if (getStartpoint().isInWindow(firstCorner,
882                                        secondCorner)) {
883             moveStartpoint(getStartpoint() + offset);
884         }
885         if (getEndpoint().isInWindow(firstCorner,
886                                      secondCorner)) {
887             moveEndpoint(getEndpoint() + offset);
888         }
889     }
890     correctAngles(); // make sure angleLength is no more than 2*M_PI
891 }
892 
893 /** find the visible part of the arc, and call drawVisible() to draw */
draw(RS_Painter * painter,RS_GraphicView * view,double & patternOffset)894 void RS_Arc::draw(RS_Painter* painter, RS_GraphicView* view,
895                   double& patternOffset) {
896 	if (!( painter && view)) return;
897 
898     //only draw the visible portion of line
899     RS_Vector vpMin(view->toGraph(0,view->getHeight()));
900     RS_Vector vpMax(view->toGraph(view->getWidth(),0));
901     QPolygonF visualBox(QRectF(vpMin.x,vpMin.y,vpMax.x-vpMin.x, vpMax.y-vpMin.y));
902 
903     RS_Vector vpStart(isReversed()?getEndpoint():getStartpoint());
904     RS_Vector vpEnd(isReversed()?getStartpoint():getEndpoint());
905 
906 	std::vector<RS_Vector> vertex(0);
907     for(unsigned short i=0;i<4;i++){
908         const QPointF& vp(visualBox.at(i));
909 		vertex.push_back(RS_Vector(vp.x(),vp.y()));
910     }
911     /** angles at cross points */
912 	std::vector<double> crossPoints(0);
913 
914     double baseAngle=isReversed()?getAngle2():getAngle1();
915     for(unsigned short i=0;i<4;i++){
916 		RS_Line line{vertex.at(i),vertex.at((i+1)%4)};
917 		auto vpIts=RS_Information::getIntersection(
918                     static_cast<RS_Entity*>(this),
919                     &line,
920                     true);
921         if( vpIts.size()==0) continue;
922 		for(const RS_Vector& vp: vpIts){
923 			auto ap1=getTangentDirection(vp).angle();
924 			auto ap2=line.getTangentDirection(vp).angle();
925             //ignore tangent points, because the arc doesn't cross over
926             if( fabs( remainder(ap2 - ap1, M_PI) ) < RS_TOLERANCE_ANGLE) continue;
927             crossPoints.push_back(
928                         RS_Math::getAngleDifference(baseAngle, getCenter().angleTo(vp))
929                         );
930         }
931     }
932     if(vpStart.isInWindowOrdered(vpMin, vpMax)) crossPoints.push_back(0.);
933     if(vpEnd.isInWindowOrdered(vpMin, vpMax)) crossPoints.push_back(getAngleLength());
934 
935     //sorting
936     std::sort(crossPoints.begin(),crossPoints.end());
937     //draw visible
938     RS_Arc arc(*this);
939     arc.setPen(getPen());
940     arc.setSelected(isSelected());
941     arc.setReversed(false);
942 	for(size_t i=1;i<crossPoints.size();i+=2){
943 		arc.setAngle1(baseAngle+crossPoints[i-1]);
944 		arc.setAngle2(baseAngle+crossPoints[i]);
945         arc.drawVisible(painter,view,patternOffset);
946     }
947 
948 }
949 
950 /** directly draw the arc, assuming the whole arc is within visible window */
drawVisible(RS_Painter * painter,RS_GraphicView * view,double & patternOffset)951 void RS_Arc::drawVisible(RS_Painter* painter, RS_GraphicView* view,
952                   double& patternOffset) {
953 
954 	if (!( painter && view)) return;
955     //visible in graphic view
956     if(isVisibleInWindow(view)==false) return;
957 
958     RS_Vector cp=view->toGui(getCenter());
959     double ra=getRadius()*view->getFactor().x;
960     double length=getLength()*view->getFactor().x;
961     //double styleFactor = getStyleFactor();
962     patternOffset -= length;
963 
964     bool drawAsSelected = isSelected() && !(view->isPrinting() || view->isPrintPreview());
965 
966     // simple style-less lines
967     if ( !drawAsSelected && (
968              getPen().getLineType()==RS2::SolidLine ||
969              view->getDrawingMode()==RS2::ModePreview)) {
970         painter->drawArc(cp,
971                          ra,
972                          getAngle1(), getAngle2(),
973                          isReversed());
974         return;
975     }
976 //    double styleFactor = getStyleFactor(view);
977     //        if (styleFactor<0.0) {
978     //            painter->drawArc(cp,
979     //                             ra,
980     //                             getAngle1(), getAngle2(),
981     //                             isReversed());
982     //            return;
983     //        }
984 
985     // Pattern:
986     const RS_LineTypePattern* pat;
987     if (drawAsSelected)
988     {
989         pat = &RS_LineTypePattern::patternSelected;
990     } else {
991         pat = view->getPattern(getPen().getLineType());
992     }
993 
994 	if (!pat || ra<0.5) {//avoid division by zero from small ra
995 		RS_DEBUG->print("%s: Invalid line pattern or radius too small, drawing arc using solid line", __func__);
996         painter->drawArc(cp, ra,
997                          getAngle1(),getAngle2(),
998                          isReversed());
999         return;
1000     }
1001 
1002 //    patternOffset=remainder(patternOffset - length -0.5*pat->totalLength,pat->totalLength)+0.5*pat->totalLength;
1003 
1004     if (ra<RS_TOLERANCE_ANGLE){
1005         return;
1006     }
1007 
1008     // Pen to draw pattern is always solid:
1009     RS_Pen pen = painter->getPen();
1010     pen.setLineType(RS2::SolidLine);
1011     painter->setPen(pen);
1012 
1013 
1014 
1015     // create scaled pattern:
1016 	if(pat->num<=0) { //invalid pattern
1017 		RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Arc::draw(): invalid line pattern\n");
1018 		painter->drawArc(cp,
1019 						 ra,
1020 						 getAngle1(), getAngle2(),
1021 						 isReversed());
1022 		return;
1023 	}
1024 	std::vector<double> da(pat->num);
1025     double patternSegmentLength(pat->totalLength);
1026 	double ira=1./ra;
1027 	double dpmm=static_cast<RS_PainterQt*>(painter)->getDpmm();
1028 	for (size_t i=0; i<pat->num; i++){
1029 		//        da[j] = pat->pattern[i++] * styleFactor;
1030 		//fixme, stylefactor needed
1031 		da[i] =dpmm*(isReversed() ? -fabs(pat->pattern[i]):fabs(pat->pattern[i]));
1032 		if ( fabs(da[i]) < 1.) da[i] = copysign(1., da[i]);
1033 		da[i] *= ira;
1034 	}
1035 
1036     //    bool done = false;
1037     double total=remainder(patternOffset-0.5*patternSegmentLength,patternSegmentLength)-0.5*patternSegmentLength;
1038 
1039 	double a1{RS_Math::correctAngle(getAngle1())};
1040 	double a2{RS_Math::correctAngle(getAngle2())};
1041 
1042     if(isReversed()) {//always draw from a1 to a2, so, patternOffset is automatic
1043         if(a1<a2+RS_TOLERANCE_ANGLE) a2 -= 2.*M_PI;
1044         total = a1 - total*ira; //in angle
1045     }else{
1046         if(a2<a1+RS_TOLERANCE_ANGLE) a2 += 2.*M_PI;
1047         total = a1 + total*ira; //in angle
1048     }
1049     double limit(fabs(a1-a2));
1050     double t2;
1051 
1052 	for(int j=0; fabs(total-a1) < limit; j=(j+1)%pat->num) {
1053 		t2=total+da[j];
1054 
1055 		if(pat->pattern[j] > 0.0 && fabs(t2-a2) < limit) {
1056 			double a11=(fabs(total-a2) < limit)?total:a1;
1057 			double a21=(fabs(t2-a1) < limit)?t2:a2;
1058 			painter->drawArc(cp, ra, a11, a21, isReversed());
1059 		}
1060 
1061 		total=t2;
1062 	}
1063 }
1064 
1065 
1066 
1067 /**
1068  * @return Middle point of the entity.
1069  */
getMiddlePoint() const1070 RS_Vector RS_Arc::getMiddlePoint() const {
1071     double a=getAngle1();
1072     double b=getAngle2();
1073 
1074     if (isReversed()) {
1075         a =b+ RS_Math::correctAngle(a-b)*0.5;
1076     }else{
1077         a += RS_Math::correctAngle(b-a)*0.5;
1078     }
1079     RS_Vector ret(a);
1080     return getCenter() + ret*getRadius();
1081 }
1082 
1083 
1084 
1085 /**
1086  * @return Angle length in rad.
1087  */
getAngleLength() const1088 double RS_Arc::getAngleLength() const {
1089     double ret;
1090     double a=getAngle1();
1091     double b=getAngle2();
1092 
1093     if (isReversed()) std::swap(a,b);
1094     ret = RS_Math::correctAngle(b-a);
1095     // full circle:
1096     if (fabs(remainder(ret,2.*M_PI))<RS_TOLERANCE_ANGLE) {
1097         ret = 2*M_PI;
1098     }
1099 
1100     return ret;
1101 }
1102 
1103 
1104 
1105 /**
1106  * @return Length of the arc.
1107  */
getLength() const1108 double RS_Arc::getLength() const {
1109     return getAngleLength()*data.radius;
1110 }
1111 
1112 
1113 
1114 /**
1115  * Gets the arc's bulge (tangens of angle length divided by 4).
1116  */
getBulge() const1117 double RS_Arc::getBulge() const {
1118     double bulge = tan(fabs(getAngleLength())/4.0);
1119     if (isReversed()) {
1120         bulge*=-1;
1121     }
1122     return bulge;
1123 }
1124 
1125 /** return the equation of the entity
1126 for quadratic,
1127 
1128 return a vector contains:
1129 m0 x^2 + m1 xy + m2 y^2 + m3 x + m4 y + m5 =0
1130 
1131 for linear:
1132 m0 x + m1 y + m2 =0
1133 **/
getQuadratic() const1134 LC_Quadratic RS_Arc::getQuadratic() const
1135 {
1136     std::vector<double> ce(6,0.);
1137     ce[0]=1.;
1138     ce[2]=1.;
1139     ce[5]=-data.radius*data.radius;
1140     LC_Quadratic ret(ce);
1141     ret.move(data.center);
1142     return ret;
1143 }
1144 
1145 /**
1146  * @brief areaLineIntegral, line integral for contour area calculation by Green's Theorem
1147  * Contour Area =\oint x dy
1148  * @return line integral \oint x dy along the entity
1149  * \oint x dy = c_x r \sin t + \frac{1}{4}r^2\sin 2t +  \frac{1}{2}r^2 t
1150  */
areaLineIntegral() const1151 double RS_Arc::areaLineIntegral() const
1152 {
1153     const double& r=data.radius;
1154     const double& a0=data.angle1;
1155     const double& a1=data.angle2;
1156     const double r2=0.25*r*r;
1157     const double fStart=data.center.x*r*sin(a0)+r2*sin(a0+a0);
1158     const double fEnd=data.center.x*r*sin(a1)+r2*sin(a1+a1);
1159     return (isReversed()?fStart-fEnd:fEnd-fStart) + 2.*r2*getAngleLength();
1160 }
1161 
1162 /**
1163  * Dumps the point's data to stdout.
1164  */
operator <<(std::ostream & os,const RS_Arc & a)1165 std::ostream& operator << (std::ostream& os, const RS_Arc& a) {
1166     os << " Arc: " << a.data << "\n";
1167     return os;
1168 }
1169 
1170