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