svg-elliptical-arc.cpp revision fc69b99d41121cdaab54d55eb2efe2dc3c6ac358
/*
* SVG Elliptical Arc Class
*
* Copyright 2008 Marco Cecchetti <mrcekets at gmail.com>
*
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*/
#include <cfloat>
#include <limits>
namespace Geom
{
{
if (isDegenerate() && is_svg_compliant())
return chord().boundsExact();
arc_extremes[0] = initialPoint()[X];
if ( start_angle() < end_angle() )
{
if ( sweep_flag() )
{
{
{
}
}
}
else
{
{
{
}
}
}
}
else
{
if ( sweep_flag() )
{
{
{
}
}
}
else
{
{
{
}
}
}
}
}
{
if ( d == X )
{
+ center(X);
}
else if ( d == Y )
{
+ center(Y);
}
THROW_RANGEERROR("dimension parameter out of range");
}
{
if ( d > Y )
{
THROW_RANGEERROR("dimention out of range");
}
if (isDegenerate() && is_svg_compliant())
{
}
else
{
{
if ( center(d) == v )
return sol;
}
{
{ "d == X; ray(X) == 0; "
"s = (v - center(X)) / ( -ray(Y) * std::sin(rotation_angle()) ); "
"s should be contained in [-1,1]",
"d == X; ray(Y) == 0; "
"s = (v - center(X)) / ( ray(X) * std::cos(rotation_angle()) ); "
"s should be contained in [-1,1]"
},
{ "d == Y; ray(X) == 0; "
"s = (v - center(X)) / ( ray(Y) * std::cos(rotation_angle()) ); "
"s should be contained in [-1,1]",
"d == Y; ray(Y) == 0; "
"s = (v - center(X)) / ( ray(X) * std::sin(rotation_angle()) ); "
"s should be contained in [-1,1]"
},
};
{
{
if ( initialPoint()[d] == v && finalPoint()[d] == v )
{
}
if ( (initialPoint()[d] < finalPoint()[d])
&& (initialPoint()[d] > v || finalPoint()[d] < v) )
{
return sol;
}
if ( (initialPoint()[d] > finalPoint()[d])
&& (finalPoint()[d] > v || initialPoint()[d] < v) )
{
return sol;
}
double ray_prj;
switch(d)
{
case X:
switch(dim)
{
break;
break;
}
break;
case Y:
switch(dim)
{
break;
break;
}
break;
}
if ( s < -1 || s > 1 )
{
}
switch(dim)
{
case X:
{
if ( s < 0 ) s += 2*M_PI;
}
else
{
s = M_PI - s;
}
break;
case Y:
{
s = 2*M_PI - s;
}
break;
}
//std::cerr << "s = " << rad_to_deg(s);
s = map_to_01(s);
//std::cerr << " -> t: " << s << std::endl;
if ( !(s < 0 || s > 1) )
return sol;
}
}
}
switch(d)
{
case X:
break;
case Y:
break;
}
//std::cerr << "a = " << a << std::endl;
//std::cerr << "b = " << b << std::endl;
//std::cerr << "c = " << c << std::endl;
if ( are_near(a,0) )
{
if ( !are_near(b,0) )
{
if ( s < 0 ) s += 2*M_PI;
}
}
else
{
double delta = b * b - a * c;
//std::cerr << "delta = " << delta << std::endl;
{
if ( s < 0 ) s += 2*M_PI;
}
else if ( delta > 0 )
{
if ( s < 0 ) s += 2*M_PI;
if ( s < 0 ) s += 2*M_PI;
}
}
{
//std::cerr << "s = " << rad_to_deg(sol[i]);
//std::cerr << " -> t: " << sol[i] << std::endl;
}
return arc_sol;
}
// D(E(t,C),t) = E(t+PI/2,O)
{
if (isDegenerate() && is_svg_compliant())
return chord().derivative();
{
}
{
}
return result;
}
{
if (isDegenerate() && is_svg_compliant())
return chord().pointAndDerivatives(t, n);
end_angle(), sweep_flag());
SVGEllipticalArc ea(*this);
for ( unsigned int i = 0; i < m; ++i )
{
}
m = nn / 4;
for ( unsigned int i = 1; i < m; ++i )
{
for ( unsigned int j = 0; j < 4; ++j )
}
m = nn - 4 * m;
for ( unsigned int i = 0; i < m; ++i )
{
}
return result;
}
{
if ( sweep_flag() )
if ( start_angle() < end_angle() )
else
else
if ( start_angle() > end_angle() )
else
}
{
if (f < 0) f = 0;
if (f > 1) f = 1;
if (t < 0) t = 0;
if (t > 1) t = 1;
if ( are_near(f, t) )
{
}
if ( arc->m_start_angle < 0 )
if ( arc->m_end_angle < 0 )
arc->m_large_arc = false;
return arc;
}
{
if (isDegenerate() && is_svg_compliant())
{
return result;
}
{
THROW_RANGEERROR("[from,to] interval out of range");
}
{
return result;
}
{
{
{
}
else
{
}
}
else
{
{
}
else
{
}
}
return result;
}
{
{
}
// TODO: implement case r != 0
// Point np = ray(X) * unit_vector(r);
// std::vector<double> solX = roots(np[X],X);
// std::vector<double> solY = roots(np[Y],Y);
// double t;
// if ( are_near(solX[0], solY[0]) || are_near(solX[0], solY[1]))
// {
// t = solX[0];
// }
// else
// {
// t = solX[1];
// }
// if ( !(t < from || t > to) )
// {
// result.push_back(t);
// }
// else
// {
//
// }
}
// solve the equation <D(E(t),t)|E(t)-p> == 0
// that provides min and max distance points
// on the ellipse E wrt the point p
// after the substitutions:
// cos(t) = (1 - s^2) / (1 + s^2)
// sin(t) = 2t / (1 + s^2)
// where s = tan(t/2)
// we get a 4th degree equation in s
/*
* ry s^4 ((-cy + py) Cos[Phi] + (cx - px) Sin[Phi]) +
* ry ((cy - py) Cos[Phi] + (-cx + px) Sin[Phi]) +
* 2 s^3 (rx^2 - ry^2 + (-cx + px) rx Cos[Phi] + (-cy + py) rx Sin[Phi]) +
* 2 s (-rx^2 + ry^2 + (-cx + px) rx Cos[Phi] + (-cy + py) rx Sin[Phi])
*/
coeff[2] = 0;
// 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
{
{
if ( sq > 0 )
{
}
}
}
else
{
}
{
}
// 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
{
}
double dsq;
{
if ( mindistsq1 > dsq )
{
mindistsq1 = dsq;
mi1 = i;
}
else if ( mindistsq2 > dsq )
{
mindistsq2 = dsq;
mi2 = i;
}
}
{
}
bool second_sol = false;
{
{
second_sol = true;
}
}
// we need to test extreme points too
if ( second_sol )
{
if ( mindistsq2 > dsq1 )
{
mindistsq2 = dsq1;
}
{
}
if ( mindistsq2 > dsq2 )
{
}
{
}
}
else
{
{
{
}
{
}
else
{
}
}
}
return result;
}
/*
* NOTE: this implementation follows Standard SVG 1.1 implementation guidelines
* for elliptical arc curves. See Appendix F.6.
*/
{
if (is_svg_compliant())
{
if ( initialPoint() == finalPoint() )
{
m_center = initialPoint();
m_large_arc = m_sweep = false;
return;
}
{
m_ry = 0;
m_start_angle = 0;
m_end_angle = M_PI;
m_large_arc = false;
m_sweep = false;
return;
}
}
else
{
{
{
m_start_angle = m_end_angle = 0;
m_center = initialPoint();
return;
}
else
{
THROW_RANGEERROR("initial and final point are the same");
}
}
{ // but initialPoint != finalPoint
"there is no ellipse that satisfies the given constraints: "
"ray(X) == 0 && ray(Y) == 0 but initialPoint != finalPoint"
);
}
{
{
{
m_start_angle = 0;
m_end_angle = M_PI;
return;
}
{
m_end_angle = 0;
return;
}
"there is no ellipse that satisfies the given constraints: "
"ray(Y) == 0 "
"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)"
);
}
else
{
"there is infinite ellipses that satisfy the given constraints: "
"ray(Y) == 0 and distance(initialPoint, finalPoint) < 2*ray(X)"
);
}
}
{
{
{
return;
}
{
return;
}
"there is no ellipse that satisfies the given constraints: "
"ray(X) == 0 "
"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)"
);
}
else
{
"there is infinite ellipses that satisfy the given constraints: "
"ray(X) == 0 and distance(initialPoint, finalPoint) < 2*ray(Y)"
);
}
}
}
0, 0 );
Point p = (d / 2) * m;
Point c(0,0);
if (rad > 1)
{
rad -= 1;
m[1] = -m[1];
m[2] = -m[2];
}
{
}
else
{
"there is no ellipse that satisfies the given constraints"
);
}
Point v(1, 0);
}
{
if (isDegenerate() && is_svg_compliant())
// the interval of parametrization has to be [0,1]
// order = 4 seems to be enough to get a perfect looking elliptical arc
// should it be choosen in function of the arc length anyway ?
// or maybe a user settable parameter: toSBasis(unsigned int order) ?
// ensure that endpoints remain exact
for ( int d = 0 ; d < 2 ; d++ ) {
arc[d][0][0] = initialPoint()[d];
}
return arc;
}
{
// return SBasisCurve(toSBasis()).transformed(m);
inner_point * m,
finalPoint() * m,
is_svg_compliant() );
}
{
if ( sweep_flag() )
{
angle += sweep_angle() * t;
}
else
{
angle -= sweep_angle() * t;
}
return angle;
}
{
end_angle(), sweep_flag());
}
namespace detail
{
struct ellipse_equation
{
ellipse_equation(double a, double b, double c, double d, double e, double f)
: A(a), B(b), C(c), D(d), E(e), F(f)
{
}
double operator()(double x, double y) const
{
// A * x * x + B * x * y + C * y * y + D * x + E * y + F;
return (A * x + B * y + D) * x + (C * y + E) * y + F;
}
double operator()(Point const& p) const
{
return (*this)(p[X], p[Y]);
}
{
return unit_vector(n);
}
{
return normal(p[X], p[Y]);
}
double A, B, C, D, E, F;
};
}
curve_type const& _curve,
unsigned int _total_samples,
double _tolerance )
svg_compliant(true)
{
}
bool
{
//angle_err *= angle_err;
}
bool
check_bound(double A, double B, double C, double D, double E, double F)
{
// check error magnitude
{
return false;
}
{
return false;
}
// std::cerr << "e1x = " << e1x << std::endl;
// std::cerr << "e1y = " << e1y << std::endl;
// std::cerr << "e2 = " << e2 << std::endl;
for ( unsigned int k = 1; k < last; ++k )
{
{
return false;
}
}
return true;
}
void make_elliptical_arc::fit()
{
for (unsigned int k = 0; k < N; ++k)
{
p[k] = curve( k / partitions );
}
}
bool make_elliptical_arc::make_elliptiarc()
{
Ellipse e;
try
{
}
catch(LogicalError exc)
{
return false;
}
if (svg_compliant_flag())
{
}
else
{
try
{
}
catch(RangeError exc)
{
return false;
}
}
)
)
{
return false;
}
return true;
}
} // end namespace Geom
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :