sbasis-roots.cpp revision 6492fcbc5fbe9a07962f989247731b6435fd72b9
/** root finding for sbasis functions.
* Copyright 2006 N Hurst
* Copyright 2007 JF Barraud
*
* It is more efficient to find roots of f(t) = c_0, c_1, ... all at once, rather than iterating.
*
* multi-roots using bernstein method, one approach would be:
sort c
take median and find roots of that
whenever a segment lies entirely on one side of the median,
find the median of the half and recurse.
in essence we are implementing quicksort on a continuous function
* the gsl poly roots finder is faster than bernstein too, but we don't use it for 3 reasons:
a) it requires convertion to poly, which is numerically unstable
b) it requires gsl (which is currently not a dependency, and would bring in a whole slew of unrelated stuff)
c) it finds all roots, even complex ones. We don't want to accidently treat a nearly real root as a real root
From memory gsl poly roots was about 10 times faster than bernstein in the case where all the roots
are in [0,1] for polys of order 5. I spent some time working out whether eigenvalue root finding
could be done directly in sbasis space, but the maths was too hard for me. -- njh
jfbarraud: eigenvalue root finding could be done directly in sbasis space ?
njh: I don't know, I think it should. You would make a matrix whose characteristic polynomial was
correct, but do it by putting the sbasis terms in the right spots in the matrix. normal eigenvalue
root finding makes a matrix that is a diagonal + a row along the top. This matrix has the property
that its characteristic poly is just the poly whose coefficients are along the top row.
Now an sbasis function is a linear combination of the poly coeffs. So it seems to me that you
should be able to put the sbasis coeffs directly into a matrix in the right spots so that the
characteristic poly is the sbasis. You'll still have problems b) and c).
We might be able to lift an eigenvalue solver and include that directly into 2geom. Eigenvalues
also allow you to find intersections of multiple curves but require solving n*m x n*m matrices.
**/
#include <cmath>
#include <map>
#include "sbasis.h"
#include "sbasis-to-bezier.h"
#include "solver.h"
using namespace std;
namespace Geom{
}
return result;
}
double a=sb[j][0];
double b=sb[j][1];
double v, t = 0;
v = res[0];
if (v<0) t = ((b-a)/v+1)*0.5;
if (v>=0 || t<0 || t>1) {
}else{
}
v = res[1];
if (v>0) t = ((b-a)/v+1)*0.5;
if (v<=0 || t<0 || t>1) {
}else{
}
}
return res;
}
double a=sb[j][0];
double b=sb[j][1];
double t = 0;
}else{
}
}else{
}
}
return res;
}
//-- multi_roots ------------------------------------
// goal: solve f(t)=c for several c at once.
/* algo: -compute f at both ends of the given segment [a,b].
-compute bounds m<df(t)<M for df on the segment.
let c and C be the levels below and above f(a):
going from f(a) down to c with slope m takes at least time (f(a)-c)/m
going from f(a) up to C with slope M takes at least time (C-f(a))/M
From this we conclude there are no roots before a'=a+min((f(a)-c)/m,(C-f(a))/M).
Do the same for b: compute some b' such that there are no roots in (b',b].
-if [a',b'] is not empty, repeat the process with [a',(a'+b')/2] and [(a'+b')/2,b'].
unfortunately, extra care is needed about rounding errors, and also to avoid the repetition of roots,
making things tricky and unpleasant...
*/
//TODO: Make sure the code is "rounding-errors proof" and take care about repetition of roots!
}
static void multi_roots_internal(SBasis const &f,
double htol,
double vtol,
double a,
double fa,
double b,
double fb){
if (f.size()==0){
int idx;
}
return;
}
////usefull?
// if (f.size()==1){
// int idxa=upper_level(levels,fa);
// int idxb=upper_level(levels,fb);
// if (fa==fb){
// if (fa==levels[idxa]){
// roots[a]=idxa;
// roots[b]=idxa;
// }
// return;
// }
// int idx_min=std::min(idxa,idxb);
// int idx_max=std::max(idxa,idxb);
// if (idx_max==levels.size()) idx_max-=1;
// for(int i=idx_min;i<=idx_max; i++){
// double t=a+(b-a)*(levels[i]-fa)/(fb-fa);
// if(a<t&&t<b) roots[t]=i;
// }
// return;
// }
if ((b-a)<htol){
//TODO: use different tol for t and f ?
//TODO: unsigned idx ? (remove int casts when fixed)
}
return;
}
//first times when a level (higher or lower) can be reached from a or b.
//ta_hi=ta_lo=a;
}else{
}
//tb_hi=tb_lo=b;
}else{
}
//hum, rounding errors frighten me! so I add this +tol...
}else{
//we do not want to count it twice (from the left and from the right)
}
}
}
double htol,
double vtol,
double a,
double b){
return(roots);
}
//-------------------------------------
#if 0
double Laguerre_internal(SBasis const & p,
double x0,
double tol,
bool & quad_root) {
double a = 2*tol;
double n = p.size();
quad_root = false;
while(a > tol) {
//std::cout << "xk = " << xk << std::endl;
Linear d(0), f(0);
for(int j = p.size()-2; j >= 0; j--) {
f = xk*f + d;
d = xk*d + b;
b = xk*b + p[j];
}
double px = b;
return xk;
//if(std::norm(px) < tol*tol)
// return xk;
double G = d / px;
double H = G*G - f / px;
//std::cout << "G = " << G << "H = " << H;
double radicand = (n - 1)*(n*H-G*G);
//assert(radicand.real() > 0);
if(radicand < 0)
quad_root = true;
//std::cout << "radicand = " << radicand << std::endl;
if(G.real() < 0) // here we try to maximise the denominator avoiding cancellation
else
//std::cout << "a = " << a << std::endl;
a = n / (a + G);
//std::cout << "a = " << a << std::endl;
xk -= a;
}
//std::cout << "xk = " << xk << std::endl;
return xk;
}
#endif
void subdiv_sbasis(SBasis const & s,
return; // no roots here
double t = s[0][0] / (s[0][0] - s[0][1]);
return;
}
}
// It is faster to use the bernstein root finder for small degree polynomials (<100?.
double d = s[0][0] - s[0][1];
if(d != 0) {
double r = s[0][0] / d;
if(0 <= r and r <= 1)
}
return res;
}
switch(s.size()) {
case 0:
case 1:
return roots1(s);
default:
return sbasis_to_bezier(s).roots();
}
}
};
/*
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 :