elliptical-arc.cpp revision 763176bacabff6cd0f23bf6be4c6c721c48c526e
5255N/A * Krzysztof KosiĆski <tweenk.pl@gmail.com> 5255N/A * Copyright 2008-2009 Authors 5255N/A * This library is free software; you can redistribute it and/or 5255N/A * modify it either under the terms of the GNU Lesser General Public 5255N/A * License version 2.1 as published by the Free Software Foundation 5255N/A * (the "LGPL") or, at your option, under the terms of the Mozilla 5255N/A * Public License Version 1.1 (the "MPL"). If you do not alter this 5255N/A * notice, a recipient may use your version of this file under either 5255N/A * You should have received a copy of the LGPL along with this library 5255N/A * in the file COPYING-LGPL-2.1; if not, write to the Free Software 5255N/A * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 5255N/A * You should have received a copy of the MPL along with this library 5255N/A * in the file COPYING-MPL-1.1 5255N/A * The contents of this file are subject to the Mozilla Public License 5255N/A * Version 1.1 (the "License"); you may not use this file except in 5255N/A * compliance with the License. You may obtain a copy of the License at 5255N/A * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY 5255N/A * OF ANY KIND, either express or implied. See the LGPL or the MPL for 5255N/A * the specific language governing rights and limitations. 5255N/A * @brief Elliptical arc curve 5255N/A * Elliptical arc is a curve taking the shape of a section of an ellipse. 5255N/A * The arc function has two forms: the regular one, mapping the unit interval to points 5255N/A * on 2D plane (the linear domain), and a second form that maps some interval 5255N/A * \f$A \subseteq [0,2\pi)\f$ to the same points (the angular domain). The interval \f$A\f$ 5255N/A * determines which part of the ellipse forms the arc. The arc is said to contain an angle 5255N/A * if its angular domain includes that angle (and therefore it is defined for that angle). 5255N/A * The angular domain considers each ellipse to be 5255N/A * a rotated, scaled and translated unit circle: 0 corresponds to \f$(1,0)\f$ on the unit circle, 5255N/A * \f$\pi/2\f$ corresponds to \f$(0,1)\f$, \f$\pi\f$ to \f$(-1,0)\f$ and \f$3\pi/2\f$ 5255N/A * to \f$(0,-1)\f$. After the angle is mapped to a point from a unit circle, the point is 5255N/A * transformed using a matrix of this form 5255N/A * \f[ M = \left[ \begin{array}{ccc} 5255N/A r_X \cos(\theta) & -r_Y \sin(\theta) & 0 \\ 5255N/A r_X \sin(\theta) & r_Y \cos(\theta) & 0 \\ 5255N/A c_X & c_Y & 1 \end{array} \right] \f] 5255N/A * where \f$r_X, r_Y\f$ are the X and Y rays of the ellipse, \f$\theta\f$ is its angle of rotation, 5255N/A * and \f$c_X, c_Y\f$ the coordinates of the ellipse's center - thus mapping the angle 5255N/A * to some point on the ellipse. Note that for example the point at angluar coordinate 0, 5255N/A * the center and the point at angular coordinate \f$\pi/4\f$ do not necessarily 5255N/A * create an angle of \f$\pi/4\f$ radians; it is only the case if both axes of the ellipse 5255N/A * are of the same length (i.e. it is a circle). 5255N/A * @image html ellipse-angular-coordinates.png "An illustration of the angular domain" 5255N/A * Each arc is defined by five variables: The initial and final point, the ellipse's rays, 5255N/A * and the ellipse's rotation. Each set of those parameters corresponds to four different arcs, 5255N/A * with two of them larger than half an ellipse and two of them turning clockwise while traveling 5255N/A * from initial to final point. The two flags disambiguate between them: "large arc flag" selects 5255N/A * the bigger arc, while the "sweep flag" selects the clockwise arc. 5255N/A * @image html elliptical-arc-flags.png "The four possible arcs and the meaning of flags" 5255N/A for (
unsigned i = 0; i <
4; ++i) {
5255N/A "s = (v - center(X)) / ( -ray(Y) * std::sin(_rot_angle) ); " 5255N/A "s should be contained in [-1,1]",
5255N/A "s = (v - center(X)) / ( ray(X) * std::cos(_rot_angle) ); " 5255N/A "s should be contained in [-1,1]" 5255N/A "s = (v - center(X)) / ( ray(Y) * std::cos(_rot_angle) ); " 5255N/A "s should be contained in [-1,1]",
5255N/A "s = (v - center(X)) / ( ray(X) * std::sin(_rot_angle) ); " 5255N/A "s should be contained in [-1,1]" 5255N/A //std::cerr << "s = " << rad_to_deg(s); 5255N/A //std::cerr << " -> t: " << s << std::endl; 5255N/A //std::cerr << "a = " << a << std::endl; 5255N/A //std::cerr << "b = " << b << std::endl; 5255N/A //std::cerr << "c = " << c << std::endl; 5255N/A //std::cerr << "delta = " << delta << std::endl; 5255N/A //std::cerr << "s = " << rad_to_deg(sol[i]); 5255N/A //std::cerr << " -> t: " << sol[i] << std::endl; 5255N/A// D(E(t,C),t) = E(t+PI/2,O), where C is the ellipse center 5255N/A// the derivative doesn't rotate the ellipse but there is a translation 5255N/A// of the parameter t by an angle of PI/2 so the ellipse points are shifted 5255N/A// of such an angle in the cw direction 5255N/A unsigned int nn = n+
1;
// nn represents the size of the result vector. 5255N/A for (
unsigned int i = 0; i < m; ++i )
5255N/A for (
unsigned int i =
1; i < m; ++i )
5255N/A for (
unsigned int j = 0; j <
4; ++j )
5255N/A for (
unsigned int i = 0; i < m; ++i )
5255N/A// the arc is the same but traversed in the opposite direction 5255N/A // TODO: implement case r != 0 5255N/A// Point np = ray(X) * unit_vector(r); 5255N/A// std::vector<double> solX = roots(np[X],X); 5255N/A// std::vector<double> solY = roots(np[Y],Y); 5255N/A// if ( are_near(solX[0], solY[0]) || are_near(solX[0], solY[1])) 5255N/A// if ( !(t < from || t > to) ) 5255N/A // solve the equation <D(E(t),t)|E(t)-p> == 0 5255N/A // that provides min and max distance points 5255N/A // on the ellipse E wrt the point p 5255N/A // after the substitutions: 5255N/A // cos(t) = (1 - s^2) / (1 + s^2) 5255N/A // we get a 4th degree equation in s 5255N/A * ry s^4 ((-cy + py) Cos[Phi] + (cx - px) Sin[Phi]) + 5255N/A * ry ((cy - py) Cos[Phi] + (-cx + px) Sin[Phi]) + 5255N/A * 2 s^3 (rx^2 - ry^2 + (-cx + px) rx Cos[Phi] + (-cy + py) rx Sin[Phi]) + 5255N/A * 2 s (-rx^2 + ry^2 + (-cx + px) rx Cos[Phi] + (-cy + py) rx Sin[Phi]) // for ( unsigned int i = 0; i < 5; ++i ) // std::cerr << "c[" << i << "] = " << coeff[i] << std::endl; // gsl_poly_complex_solve raises an error // if the leading coefficient is zero // when s -> Infinity then <D(E)|E-p> -> 0 iff coeff[4] == 0 // so we add M_PI to the solutions being lim arctan(s) = PI when s->Infinity // we need to test extreme points too * NOTE: this implementation follows Standard SVG 1.1 implementation guidelines * for elliptical arc curves. See Appendix F.6. // TODO move this to SVGElipticalArc? {
// but initialPoint != finalPoint "there is no ellipse that satisfies the given constraints: " "ray(X) == 0 && ray(Y) == 0 but initialPoint != finalPoint" "there is no ellipse that satisfies the given constraints: " "and slope(initialPoint - finalPoint) != rotation_angle " "and != rotation_angle + PI" "there is no ellipse that satisfies the given constraints: " "ray(Y) == 0 and distance(initialPoint, finalPoint) > 2*ray(X)" "there is infinite ellipses that satisfy the given constraints: " "ray(Y) == 0 and distance(initialPoint, finalPoint) < 2*ray(X)" "there is no ellipse that satisfies the given constraints: " "and slope(initialPoint - finalPoint) != rotation_angle + PI/2 " "and != rotation_angle + (3/2)*PI" "there is no ellipse that satisfies the given constraints: " "ray(X) == 0 and distance(initialPoint, finalPoint) > 2*ray(Y)" "there is infinite ellipses that satisfy the given constraints: " "ray(X) == 0 and distance(initialPoint, finalPoint) < 2*ray(Y)" "there is no ellipse that satisfies the given constraints" // the interval of parametrization has to be [0,1] // order = 4 seems to be enough to get a perfect looking elliptical arc // ensure that endpoints remain exact for (
int d = 0 ; d <
2 ; d++ ) {
c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :