Code

update to really latest 2geom
[inkscape.git] / src / 2geom / elliptical-arc.cpp
1 /*
2  * SVG Elliptical Arc Class
3  *
4  * Copyright 2008  Marco Cecchetti <mrcekets at gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it either under the terms of the GNU Lesser General Public
8  * License version 2.1 as published by the Free Software Foundation
9  * (the "LGPL") or, at your option, under the terms of the Mozilla
10  * Public License Version 1.1 (the "MPL"). If you do not alter this
11  * notice, a recipient may use your version of this file under either
12  * the MPL or the LGPL.
13  *
14  * You should have received a copy of the LGPL along with this library
15  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  * You should have received a copy of the MPL along with this library
18  * in the file COPYING-MPL-1.1
19  *
20  * The contents of this file are subject to the Mozilla Public License
21  * Version 1.1 (the "License"); you may not use this file except in
22  * compliance with the License. You may obtain a copy of the License at
23  * http://www.mozilla.org/MPL/
24  *
25  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27  * the specific language governing rights and limitations.
28  */
31 #include "path.h"
32 #include "angle.h"
34 #include "poly.h"
36 #include <cfloat>
41 namespace Geom
42 {
45 Rect EllipticalArc::boundsExact() const
46 {
47         std::vector<double> extremes(4);
48         double cosrot = std::cos(rotation_angle());
49         double sinrot = std::sin(rotation_angle());
50         extremes[0] = std::atan2( -ray(Y) * sinrot, ray(X) * cosrot );
51         extremes[1] = extremes[0] + M_PI;
52         if ( extremes[0] < 0 ) extremes[0] += 2*M_PI;   
53         extremes[2] = std::atan2( ray(Y) * cosrot, ray(X) * sinrot );
54         extremes[3] = extremes[2] + M_PI;
55         if ( extremes[2] < 0 ) extremes[2] += 2*M_PI;
56         
57         
58         std::vector<double>arc_extremes(4);
59         arc_extremes[0] = initialPoint()[X];
60         arc_extremes[1] = finalPoint()[X];
61         if ( arc_extremes[0] < arc_extremes[1] ) 
62                 std::swap(arc_extremes[0], arc_extremes[1]);
63         arc_extremes[2] = initialPoint()[Y];
64         arc_extremes[3] = finalPoint()[Y];
65         if ( arc_extremes[2] < arc_extremes[3] ) 
66                 std::swap(arc_extremes[2], arc_extremes[3]);
67         
68         
69         if ( start_angle() < end_angle() )
70         {
71                 if ( sweep_flag() )
72                 {
73                         for ( unsigned int i = 0; i < extremes.size(); ++i )
74                         {
75                                 if ( start_angle() < extremes[i] && extremes[i] < end_angle() )
76                                 {
77                                         arc_extremes[i] = pointAtAngle(extremes[i])[i >> 1];
78                                 }
79                         }
80                 }
81                 else
82                 {
83                         for ( unsigned int i = 0; i < extremes.size(); ++i )
84                         {
85                                 if ( start_angle() > extremes[i] || extremes[i] > end_angle() )
86                                 {
87                                         arc_extremes[i] = pointAtAngle(extremes[i])[i >> 1];
88                                 }
89                         }
90                 }
91         }
92         else
93         {
94                 if ( sweep_flag() )
95                 {
96                         for ( unsigned int i = 0; i < extremes.size(); ++i )
97                         {
98                                 if ( start_angle() < extremes[i] || extremes[i] < end_angle() )
99                                 {
100                                         arc_extremes[i] = pointAtAngle(extremes[i])[i >> 1];
101                                 }
102                         }
103                 }
104                 else
105                 {
106                         for ( unsigned int i = 0; i < extremes.size(); ++i )
107                         {
108                                 if ( start_angle() > extremes[i] && extremes[i] > end_angle() )
109                                 {
110                                         arc_extremes[i] = pointAtAngle(extremes[i])[i >> 1];
111                                 }
112                         }               
113                 }
114         }
115         
116         return Rect( Point(arc_extremes[1], arc_extremes[3]) , 
117                              Point(arc_extremes[0], arc_extremes[2]) );
122 std::vector<double> 
123 EllipticalArc::roots(double v, Dim2 d) const
125         if ( d > Y )
126         {
127                 THROW_RANGEERROR("dimention out of range");
128         }
129         
130         std::vector<double> sol;
131         if ( are_near(ray(X), 0) && are_near(ray(Y), 0) )
132         {
133                 if ( center(d) == v )
134                         sol.push_back(0);
135                 return sol;
136         }
137         
138         const char* msg[2][2] = 
139         {
140                 { "d == X; ray(X) == 0; "
141                   "s = (v - center(X)) / ( -ray(Y) * std::sin(rotation_angle()) ); "
142                   "s should be contained in [-1,1]",
143                   "d == X; ray(Y) == 0; "
144                   "s = (v - center(X)) / ( ray(X) * std::cos(rotation_angle()) ); "
145                   "s should be contained in [-1,1]"
146                 },
147                 { "d == Y; ray(X) == 0; "
148                   "s = (v - center(X)) / ( ray(Y) * std::cos(rotation_angle()) ); "
149                   "s should be contained in [-1,1]",
150                   "d == Y; ray(Y) == 0; "
151                   "s = (v - center(X)) / ( ray(X) * std::sin(rotation_angle()) ); "
152                   "s should be contained in [-1,1]"
153                 },
154         };        
155         
156         for ( unsigned int dim = 0; dim < 2; ++dim )
157         {
158                 if ( are_near(ray(dim), 0) )
159                 {
160                         
161                         if ( initialPoint()[d] == v && finalPoint()[d] == v )
162                         {
163                                 THROW_INFINITESOLUTIONS(0);
164                         }
165                         if ( (initialPoint()[d] < finalPoint()[d])
166                                  && (initialPoint()[d] > v || finalPoint()[d] < v) )
167                         {
168                                 return sol;
169                         }
170                         if ( (initialPoint()[d] > finalPoint()[d])
171                                  && (finalPoint()[d] > v || initialPoint()[d] < v) )
172                         {
173                                 return sol;
174                         }
175                         double ray_prj;
176                         switch(d)
177                         {
178                                 case X:         
179                                         switch(dim)
180                                         {       
181                                                 case X: ray_prj = -ray(Y) * std::sin(rotation_angle());
182                                                                 break;
183                                                 case Y: ray_prj = ray(X) * std::cos(rotation_angle());
184                                                                 break;
185                                         }
186                                         break;
187                                 case Y:
188                                         switch(dim)
189                                         {       
190                                                 case X: ray_prj = ray(Y) * std::cos(rotation_angle());
191                                                                 break;
192                                                 case Y: ray_prj = ray(X) * std::sin(rotation_angle());
193                                                                 break;
194                                         }
195                                         break;
196                         }
197                         
198                         double s = (v - center(d)) / ray_prj;
199                         if ( s < -1 || s > 1 )
200                         {
201                                 THROW_LOGICALERROR(msg[d][dim]);
202                         }
203                         switch(dim)
204                         {       
205                                 case X: 
206                                         s = std::asin(s); // return a value in [-PI/2,PI/2]
207                                         if ( logical_xor( sweep_flag(), are_near(start_angle(), M_PI/2) )  )
208                                         {
209                                                 if ( s < 0 ) s += 2*M_PI;
210                                         }
211                                         else
212                                         {
213                                                 s = M_PI - s;
214                                                 if (!(s < 2*M_PI) ) s -= 2*M_PI;
215                                         }
216                                         break;
217                                 case Y: 
218                                         s = std::acos(s); // return a value in [0,PI]
219                                         if ( logical_xor( sweep_flag(), are_near(start_angle(), 0) ) )
220                                         {
221                                                 s = 2*M_PI - s;
222                                                 if ( !(s < 2*M_PI) ) s -= 2*M_PI;
223                                         }
224                                         break;
225                         }
226                         
227                         //std::cerr << "s = " << rad_to_deg(s);
228                         s = map_to_01(s);
229                         //std::cerr << " -> t: " << s << std::endl;
230                         if ( !(s < 0 || s > 1) )
231                                 sol.push_back(s);
232                         return sol;
233                 }
234         }
235                 
236         double rotx, roty;
237         switch(d)
238         {
239                 case X: 
240                         rotx = std::cos(rotation_angle());
241                         roty = -std::sin(rotation_angle());
242                         break;
243                 case Y:
244                         rotx = std::sin(rotation_angle());
245                         roty = std::cos(rotation_angle());
246                         break;
247         }
248         double rxrotx = ray(X) * rotx;
249         double c_v = center(d) - v;
251         double a = -rxrotx + c_v;
252         double b = ray(Y) * roty;
253         double c = rxrotx + c_v;
254         //std::cerr << "a = " << a << std::endl;
255         //std::cerr << "b = " << b << std::endl;
256         //std::cerr << "c = " << c << std::endl;
257         
258         if ( are_near(a,0) )
259         {
260                 sol.push_back(M_PI);
261                 if ( !are_near(b,0) )
262                 {
263                         double s = 2 * std::atan(-c/(2*b));
264                         if ( s < 0 ) s += 2*M_PI;
265                         sol.push_back(s);
266                 }
267         }
268         else
269         {
270                 double delta = b * b - a * c;
271                 //std::cerr << "delta = " << delta << std::endl;
272                 if ( are_near(delta, 0) )
273                 {
274                         double s = 2 * std::atan(-b/a);
275                         if ( s < 0 ) s += 2*M_PI;
276                         sol.push_back(s);
277                 }
278                 else if ( delta > 0 )
279                 {               
280                         double sq = std::sqrt(delta);
281                         double s = 2 * std::atan( (-b - sq) / a );
282                         if ( s < 0 ) s += 2*M_PI;
283                         sol.push_back(s);
284                         s = 2 * std::atan( (-b + sq) / a );
285                         if ( s < 0 ) s += 2*M_PI;
286                         sol.push_back(s);
287                 }
288         }
289         
290         std::vector<double> arc_sol;
291         for (unsigned int i = 0; i < sol.size(); ++i )
292         {
293                 //std::cerr << "s = " << rad_to_deg(sol[i]);
294                 sol[i] = map_to_01(sol[i]);
295                 //std::cerr << " -> t: " << sol[i] << std::endl;
296                 if ( !(sol[i] < 0 || sol[i] > 1) )
297                         arc_sol.push_back(sol[i]);
298         }
299         return arc_sol;
300         
301         
302 //      return SBasisCurve(toSBasis()).roots(v, d);
305 // D(E(t,C),t) = E(t+PI/2,O)
306 Curve* EllipticalArc::derivative() const
308         EllipticalArc* result = new EllipticalArc(*this);
309         result->m_center[X] = result->m_center[Y] = 0;
310         result->m_start_angle += M_PI/2;
311         if( !( result->m_start_angle < 2*M_PI ) )
312         {
313                 result->m_start_angle -= 2*M_PI;
314         }
315         result->m_end_angle += M_PI/2;
316         if( !( result->m_end_angle < 2*M_PI ) )
317         {
318                 result->m_end_angle -= 2*M_PI;
319         }
320         result->m_initial_point = result->pointAtAngle( result->start_angle() );
321         result->m_final_point = result->pointAtAngle( result->end_angle() );
322         return result;
323         
326 std::vector<Point> 
327 EllipticalArc::pointAndDerivatives(Coord t, unsigned int n) const
329         std::vector<Point> result;
330         result.reserve(n);
331         double angle = map_unit_interval_on_circular_arc(t, start_angle(), 
332                                                                  end_angle(), sweep_flag());
333         EllipticalArc ea(*this);
334         ea.m_center = Point(0,0);
335         unsigned int m = std::min(n, 4u);
336         for ( unsigned int i = 0; i < m; ++i )
337         {
338                 result.push_back( ea.pointAtAngle(angle) );
339                 angle += M_PI/2;
340                 if ( !(angle < 2*M_PI) ) angle -= 2*M_PI;
341         }
342         m = n / 4;
343         for ( unsigned int i = 1; i < m; ++i )
344         {
345                 for ( unsigned int j = 0; j < 4; ++j )
346                         result.push_back( result[j] );
347         }
348         m = n - 4 * m;
349         for ( unsigned int i = 0; i < m; ++i )
350         {
351                 result.push_back( result[i] );
352         }
353         if ( !result.empty() ) // n != 0
354                 result[0] = pointAtAngle(angle);
355         return result;
358 D2<SBasis> EllipticalArc::toSBasis() const
360     // the interval of parametrization has to be [0,1]
361     Coord et = start_angle() + ( sweep_flag() ? sweep_angle() : -sweep_angle() );
362     Linear param(start_angle(), et);
363     Coord cos_rot_angle = std::cos(rotation_angle());
364     Coord sin_rot_angle = std::sin(rotation_angle());
365     // order = 4 seems to be enough to get a perfect looking elliptical arc
366     // should it be choosen in function of the arc length anyway ?
367     // or maybe a user settable parameter: toSBasis(unsigned int order) ?
368     SBasis arc_x = ray(X) * cos(param,4);
369     SBasis arc_y = ray(Y) * sin(param,4);
370     D2<SBasis> arc;
371     arc[0] = arc_x * cos_rot_angle - arc_y * sin_rot_angle + Linear(center(X),center(X));
372     arc[1] = arc_x * sin_rot_angle + arc_y * cos_rot_angle + Linear(center(Y),center(Y));
373     return arc;
377 bool EllipticalArc::containsAngle(Coord angle) const
379         if ( sweep_flag() )
380                 if ( start_angle() < end_angle() )
381                         return ( !( angle < start_angle() || angle > end_angle() ) );
382                 else
383                         return ( !( angle < start_angle() && angle > end_angle() ) );
384         else
385                 if ( start_angle() > end_angle() )
386                         return ( !( angle > start_angle() || angle < end_angle() ) );
387                 else
388                         return ( !( angle > start_angle() && angle < end_angle() ) );
392 double EllipticalArc::valueAtAngle(Coord t, Dim2 d) const
394     double sin_rot_angle = std::sin(rotation_angle());
395     double cos_rot_angle = std::cos(rotation_angle());
396     if ( d == X )
397     {
398         return    ray(X) * cos_rot_angle * std::cos(t) 
399                 - ray(Y) * sin_rot_angle * std::sin(t) 
400                 + center(X);
401     }
402     else if ( d == Y )
403     {
404         return    ray(X) * sin_rot_angle * std::cos(t) 
405                 + ray(Y) * cos_rot_angle * std::sin(t) 
406                 + center(Y);
407     }
408     THROW_RANGEERROR("dimension parameter out of range");
412 Curve* EllipticalArc::portion(double f, double t) const 
414         if (f < 0) f = 0;
415         if (f > 1) f = 1;
416         if (t < 0) t = 0;
417         if (t > 1) t = 1;
418         if ( are_near(f, t) )
419         {
420                 EllipticalArc* arc = new EllipticalArc();
421                 arc->m_center = arc->m_initial_point = arc->m_final_point = pointAt(f);
422                 arc->m_start_angle = arc->m_end_angle = m_start_angle;
423                 arc->m_rot_angle = m_rot_angle;
424                 arc->m_sweep = m_sweep;
425                 arc->m_large_arc = m_large_arc;
426         }
427     EllipticalArc* arc = new EllipticalArc( *this );
428     arc->m_initial_point = pointAt(f);
429     arc->m_final_point = pointAt(t);
430     double sa = sweep_flag() ? sweep_angle() : -sweep_angle();
431     arc->m_start_angle = m_start_angle + sa * f;
432     if ( !(arc->m_start_angle < 2*M_PI) )
433         arc->m_start_angle -= 2*M_PI;
434     if ( arc->m_start_angle < 0 )
435         arc->m_start_angle += 2*M_PI;
436     arc->m_end_angle = m_start_angle + sa * t;
437     if ( !(arc->m_end_angle < 2*M_PI) )
438         arc->m_end_angle -= 2*M_PI;
439     if ( arc->m_end_angle < 0 )
440         arc->m_end_angle += 2*M_PI;
441     if ( f > t ) arc->m_sweep = !sweep_flag();
442     if ( large_arc_flag() && (arc->sweep_angle() < M_PI) )
443         arc->m_large_arc = false;
444     return arc;
447 // NOTE: doesn't work with 360 deg arcs
448 void EllipticalArc::calculate_center_and_extreme_angles()
450     if ( are_near(initialPoint(), finalPoint()) )
451     {
452         if ( are_near(ray(X), 0) && are_near(ray(Y), 0) )
453         {
454                 m_start_angle = m_end_angle = 0;
455                 m_center = initialPoint();
456                 return;
457         }
458         else
459         {
460                 THROW_RANGEERROR("initial and final point are the same");
461         }
462     }
463         if ( are_near(ray(X), 0) && are_near(ray(Y), 0) )
464         { // but initialPoint != finalPoint
465                 THROW_RANGEERROR(
466                         "there is no ellipse that satisfies the given constraints: "
467                         "ray(X) == 0 && ray(Y) == 0 but initialPoint != finalPoint"
468                 );
469         }
470         if ( are_near(ray(Y), 0) )
471         {
472                 Point v = initialPoint() - finalPoint();
473                 if ( are_near(L2sq(v), 4*ray(X)*ray(X)) )
474                 {
475                         double angle = std::atan2(v[Y], v[X]);
476                         if (angle < 0) angle += 2*M_PI;
477                         if ( are_near( angle, rotation_angle() ) )
478                         {
479                                 m_start_angle = 0;
480                                 m_end_angle = M_PI;
481                                 m_center = v/2 + finalPoint();
482                                 return;
483                         }
484                         angle -= M_PI;
485                         if ( angle < 0 ) angle += 2*M_PI;
486                         if ( are_near( angle, rotation_angle() ) )
487                         {
488                                 m_start_angle = M_PI;
489                                 m_end_angle = 0;
490                                 m_center = v/2 + finalPoint();
491                                 return;
492                         }
493                         THROW_RANGEERROR(
494                                 "there is no ellipse that satisfies the given constraints: "
495                                 "ray(Y) == 0 "
496                                 "and slope(initialPoint - finalPoint) != rotation_angle "
497                                 "and != rotation_angle + PI"
498                         );
499                 }
500                 if ( L2sq(v) > 4*ray(X)*ray(X) )
501                 {
502                         THROW_RANGEERROR(
503                                 "there is no ellipse that satisfies the given constraints: "
504                                 "ray(Y) == 0 and distance(initialPoint, finalPoint) > 2*ray(X)"
505                         );
506                 }
507                 else
508                 {
509                         THROW_RANGEERROR(
510                                 "there is infinite ellipses that satisfy the given constraints: "
511                                 "ray(Y) == 0  and distance(initialPoint, finalPoint) < 2*ray(X)"
512                         );
513                 }
514                 
515         }
516         
517         if ( are_near(ray(X), 0) )
518         {
519                 Point v = initialPoint() - finalPoint();
520                 if ( are_near(L2sq(v), 4*ray(Y)*ray(Y)) )
521                 {
522                         double angle = std::atan2(v[Y], v[X]);
523                         if (angle < 0) angle += 2*M_PI;
524                         double rot_angle = rotation_angle() + M_PI/2;
525                         if ( !(rot_angle < 2*M_PI) ) rot_angle -= 2*M_PI;
526                         if ( are_near( angle, rot_angle ) )
527                         {
528                                 m_start_angle = M_PI/2;
529                                 m_end_angle = 3*M_PI/2;
530                                 m_center = v/2 + finalPoint();
531                                 return;
532                         }
533                         angle -= M_PI;
534                         if ( angle < 0 ) angle += 2*M_PI;
535                         if ( are_near( angle, rot_angle ) )
536                         {
537                                 m_start_angle = 3*M_PI/2;
538                                 m_end_angle = M_PI/2;
539                                 m_center = v/2 + finalPoint();
540                                 return;
541                         }
542                         THROW_RANGEERROR(
543                                 "there is no ellipse that satisfies the given constraints: "
544                                 "ray(X) == 0 "
545                                 "and slope(initialPoint - finalPoint) != rotation_angle + PI/2 "
546                                 "and != rotation_angle + (3/2)*PI"
547                         );
548                 }
549                 if ( L2sq(v) > 4*ray(Y)*ray(Y) )
550                 {
551                         THROW_RANGEERROR(
552                                 "there is no ellipse that satisfies the given constraints: "
553                                 "ray(X) == 0 and distance(initialPoint, finalPoint) > 2*ray(Y)"
554                         );
555                 }
556                 else
557                 {
558                         THROW_RANGEERROR(
559                                 "there is infinite ellipses that satisfy the given constraints: "
560                                 "ray(X) == 0  and distance(initialPoint, finalPoint) < 2*ray(Y)"
561                         );
562                 }
563                 
564         }
565         
566     double sin_rot_angle = std::sin(rotation_angle());
567     double cos_rot_angle = std::cos(rotation_angle());
569     Point sp = sweep_flag() ? initialPoint() : finalPoint();
570     Point ep = sweep_flag() ? finalPoint() : initialPoint();
572     Matrix m( ray(X) * cos_rot_angle, ray(X) * sin_rot_angle,
573              -ray(Y) * sin_rot_angle, ray(Y) * cos_rot_angle,
574               0,                      0 );
575     Matrix im = m.inverse();
576     Point sol = (ep - sp) * im;
577     double half_sum_angle = std::atan2(-sol[X], sol[Y]);
578     double half_diff_angle;
579     if ( are_near(std::fabs(half_sum_angle), M_PI/2) )
580     {
581         double anti_sgn_hsa = (half_sum_angle > 0) ? -1 : 1;
582         double arg = anti_sgn_hsa * sol[X] / 2;
583         // if |arg| is a little bit > 1 acos returns nan
584         if ( are_near(arg, 1) )
585             half_diff_angle = 0;
586         else if ( are_near(arg, -1) )
587             half_diff_angle = M_PI;
588         else
589         {
590                 if ( !(-1 < arg && arg < 1) )
591                         THROW_RANGEERROR(
592                                 "there is no ellipse that satisfies the given constraints"
593                         );
594             // assert( -1 < arg && arg < 1 );
595             // if it fails 
596             // => there is no ellipse that satisfies the given constraints
597             half_diff_angle = std::acos( arg );
598         }
600         half_diff_angle = M_PI/2 - half_diff_angle;
601     }
602     else
603     {
604         double  arg = sol[Y] / ( 2 * std::cos(half_sum_angle) );
605         // if |arg| is a little bit > 1 asin returns nan
606         if ( are_near(arg, 1) ) 
607             half_diff_angle = M_PI/2;
608         else if ( are_near(arg, -1) )
609             half_diff_angle = -M_PI/2;
610         else
611         {
612                 if ( !(-1 < arg && arg < 1) )
613                         THROW_RANGEERROR(
614                                 "there is no ellipse that satisfies the given constraints"
615                         );
616             // assert( -1 < arg && arg < 1 );
617             // if it fails 
618             // => there is no ellipse that satisfies the given constraints
619             half_diff_angle = std::asin( arg );
620         }
621     }
623     if (   ( m_large_arc && half_diff_angle > 0 ) 
624         || (!m_large_arc && half_diff_angle < 0 ) )
625     {
626         half_diff_angle = -half_diff_angle;
627     }
628     if ( half_sum_angle < 0 ) half_sum_angle += 2*M_PI;
629     if ( half_diff_angle < 0 ) half_diff_angle += M_PI;
630     
631     m_start_angle = half_sum_angle - half_diff_angle;
632     m_end_angle =  half_sum_angle + half_diff_angle;
633     // 0 <= m_start_angle, m_end_angle < 2PI
634     if ( m_start_angle < 0 ) m_start_angle += 2*M_PI;
635     if( !(m_end_angle < 2*M_PI) ) m_end_angle -= 2*M_PI;
636     sol[0] = std::cos(m_start_angle);
637     sol[1] = std::sin(m_start_angle);
638     m_center = sp - sol * m;
639     if ( !sweep_flag() )
640     {
641         double angle = m_start_angle;
642         m_start_angle = m_end_angle;
643         m_end_angle = angle;
644     }
647 Coord EllipticalArc::map_to_02PI(Coord t) const
649     if ( sweep_flag() )
650     {
651         Coord angle = start_angle() + sweep_angle() * t;
652         if ( !(angle < 2*M_PI) )
653             angle -= 2*M_PI;
654         return angle;
655     }
656     else
657     {
658         Coord angle = start_angle() - sweep_angle() * t;
659         if ( angle < 0 ) angle += 2*M_PI;
660         return angle;
661     }
664 Coord EllipticalArc::map_to_01(Coord angle) const 
666         return map_circular_arc_on_unit_interval(angle, start_angle(), 
667                                                          end_angle(), sweep_flag());
671 std::vector<double> EllipticalArc::
672 allNearestPoints( Point const& p, double from, double to ) const
674         if ( from > to ) std::swap(from, to);
675         if ( from < 0 || to > 1 )
676         {
677                 THROW_RANGEERROR("[from,to] interval out of range");
678         }
679         std::vector<double> result;
680         if ( ( are_near(ray(X), 0) && are_near(ray(Y), 0) )  || are_near(from, to) )
681         {
682                 result.push_back(from);
683                 return result;
684         }
685         else if ( are_near(ray(X), 0) || are_near(ray(Y), 0) )
686         {
687                 LineSegment seg(pointAt(from), pointAt(to));
688                 Point np = seg.pointAt( seg.nearestPoint(p) );
689                 if ( are_near(ray(Y), 0) )
690                 {
691                         if ( are_near(rotation_angle(), M_PI/2) 
692                                  || are_near(rotation_angle(), 3*M_PI/2) )
693                         {
694                                 result = roots(np[Y], Y);
695                         }
696                         else
697                         {
698                                 result = roots(np[X], X);
699                         }
700                 }
701                 else
702                 {
703                         if ( are_near(rotation_angle(), M_PI/2) 
704                                  || are_near(rotation_angle(), 3*M_PI/2) )
705                         {
706                                 result = roots(np[X], X);
707                         }
708                         else
709                         {
710                                 result = roots(np[Y], Y);
711                         }
712                 }
713                 return result;
714         }
715         else if ( are_near(ray(X), ray(Y)) )
716         {
717                 Point r = p - center();
718                 if ( are_near(r, Point(0,0)) )
719                 {
720                         THROW_INFINITESOLUTIONS(0);
721                 }
722                 // TODO: implement case r != 0
723 //              Point np = ray(X) * unit_vector(r);
724 //              std::vector<double> solX = roots(np[X],X);
725 //              std::vector<double> solY = roots(np[Y],Y);
726 //              double t;
727 //              if ( are_near(solX[0], solY[0]) || are_near(solX[0], solY[1]))
728 //              {
729 //                      t = solX[0];
730 //              }
731 //              else
732 //              {
733 //                      t = solX[1];
734 //              }
735 //              if ( !(t < from || t > to) )
736 //              {
737 //                      result.push_back(t);
738 //              }
739 //              else
740 //              {
741 //                      
742 //              }
743         }
744         
745         // solve the equation <D(E(t),t)|E(t)-p> == 0
746         // that provides min and max distance points 
747         // on the ellipse E wrt the point p
748         // after the substitutions: 
749         // cos(t) = (1 - s^2) / (1 + s^2)
750         // sin(t) = 2t / (1 + s^2)
751         // where s = tan(t/2)
752         // we get a 4th degree equation in s
753         /*
754          *      ry s^4 ((-cy + py) Cos[Phi] + (cx - px) Sin[Phi]) + 
755          *      ry ((cy - py) Cos[Phi] + (-cx + px) Sin[Phi]) + 
756          *      2 s^3 (rx^2 - ry^2 + (-cx + px) rx Cos[Phi] + (-cy + py) rx Sin[Phi]) + 
757          *      2 s (-rx^2 + ry^2 + (-cx + px) rx Cos[Phi] + (-cy + py) rx Sin[Phi])
758          */
760         Point p_c = p - center();
761         double rx2_ry2 = (ray(X) - ray(Y)) * (ray(X) + ray(Y));
762         double cosrot = std::cos( rotation_angle() );
763         double sinrot = std::sin( rotation_angle() );
764         double expr1 = ray(X) * (p_c[X] * cosrot + p_c[Y] * sinrot);
765         Poly coeff;
766         coeff.resize(5);
767         coeff[4] = ray(Y) * ( p_c[Y] * cosrot - p_c[X] * sinrot );
768         coeff[3] = 2 * ( rx2_ry2 + expr1 );
769         coeff[2] = 0;
770         coeff[1] = 2 * ( -rx2_ry2 + expr1 );
771         coeff[0] = -coeff[4];
772         
773 //      for ( unsigned int i = 0; i < 5; ++i )
774 //              std::cerr << "c[" << i << "] = " << coeff[i] << std::endl;
775         
776         std::vector<double> real_sol;
777         // gsl_poly_complex_solve raises an error 
778         // if the leading coefficient is zero
779         if ( are_near(coeff[4], 0) )  
780         {
781                 real_sol.push_back(0);
782                 if ( !are_near(coeff[3], 0) )
783                 {
784                         double sq = -coeff[1] / coeff[3];
785                         if ( sq > 0 )
786                         {
787                                 double s = std::sqrt(sq);
788                                 real_sol.push_back(s);
789                                 real_sol.push_back(-s);
790                         }
791                 }
792         }
793         else
794         {
795                 real_sol = solve_reals(coeff);
796         }
797 //      else
798 //      {
799 //              double sol[8];
800 //              gsl_poly_complex_workspace * w = gsl_poly_complex_workspace_alloc(5);
801 //              gsl_poly_complex_solve(coeff, 5, w, sol );
802 //              gsl_poly_complex_workspace_free(w);
803 //              
804 //              for ( unsigned int i = 0; i < 4; ++i )
805 //              {
806 //                      if ( sol[2*i+1] == 0 ) real_sol.push_back(sol[2*i]);
807 //              }
808 //      }
809                 
810         for ( unsigned int i = 0; i < real_sol.size(); ++i )
811         {
812                 real_sol[i] = 2 * std::atan(real_sol[i]);
813                 if ( real_sol[i] < 0 ) real_sol[i] += 2*M_PI;
814         }
815         // when s -> Infinity then <D(E)|E-p> -> 0 iff coeff[4] == 0
816         // so we add M_PI to the solutions being lim arctan(s) = PI when s->Infinity
817         if ( (real_sol.size() % 2) != 0 )
818         {
819                 real_sol.push_back(M_PI);
820         }
821         
822         double mindistsq1 = std::numeric_limits<double>::max();
823         double mindistsq2 = std::numeric_limits<double>::max();
824         double dsq;
825         unsigned int mi1, mi2;
826         for ( unsigned int i = 0; i < real_sol.size(); ++i )
827         {
828                 dsq = distanceSq(p, pointAtAngle(real_sol[i]));
829                 if ( mindistsq1 > dsq )
830                 {
831                         mindistsq2 = mindistsq1;
832                         mi2 = mi1;
833                         mindistsq1 = dsq;
834                         mi1 = i;
835                 }
836                 else if ( mindistsq2 > dsq )
837                 {
838                         mindistsq2 = dsq;
839                         mi2 = i;
840                 }
841         }
842         
843         double t = map_to_01( real_sol[mi1] );
844         if ( !(t < from || t > to) )
845         {
846                 result.push_back(t);
847         }
848         
849         bool second_sol = false; 
850         t = map_to_01( real_sol[mi2] );
851         if ( real_sol.size() == 4 && !(t < from || t > to) )
852         {
853         if ( result.empty() || are_near(mindistsq1, mindistsq2) )
854         {
855                 result.push_back(t);
856                 second_sol = true;
857         }
858         }
859         
860         // we need to test extreme points too
861         double dsq1 = distanceSq(p, pointAt(from));
862         double dsq2 = distanceSq(p, pointAt(to));
863         if ( second_sol )
864         {
865                 if ( mindistsq2 > dsq1 )
866                 {
867                         result.clear();
868                         result.push_back(from);
869                         mindistsq2 = dsq1;
870                 }
871                 else if ( are_near(mindistsq2, dsq) )
872                 {
873                         result.push_back(from);
874                 }
875                 if ( mindistsq2 > dsq2 )
876                 {
877                         result.clear();
878                         result.push_back(to);
879                 }
880                 else if ( are_near(mindistsq2, dsq2) )
881                 {
882                         result.push_back(to);
883                 }
884                 
885         }
886         else
887         {
888                 if ( result.empty() )
889                 {
890                         if ( are_near(dsq1, dsq2) )
891                         {
892                                 result.push_back(from);
893                                 result.push_back(to);
894                         }
895                         else if ( dsq2 > dsq1 )
896                         {
897                                 result.push_back(from);
898                         }
899                         else
900                         {
901                                 result.push_back(to);
902                         }
903                 }
904         }
905         
906         return result;
910 } // end namespace Geom
913 /*
914   Local Variables:
915   mode:c++
916   c-file-style:"stroustrup"
917   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
918   indent-tabs-mode:nil
919   fill-column:99
920   End:
921 */
922 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :