76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński/**
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * @file
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * @brief Root finding for sbasis functions.
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński *//*
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * Authors:
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * Nathan Hurst <njh@njhurst.com>
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * JF Barraud
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * Copyright 2006-2007 Authors
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński *
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * This library is free software; you can redistribute it and/or
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * modify it either under the terms of the GNU Lesser General Public
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * License version 2.1 as published by the Free Software Foundation
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * (the "LGPL") or, at your option, under the terms of the Mozilla
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * Public License Version 1.1 (the "MPL"). If you do not alter this
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * notice, a recipient may use your version of this file under either
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * the MPL or the LGPL.
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen *
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * You should have received a copy of the LGPL along with this library
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * in the file COPYING-LGPL-2.1; if not, write to the Free Software
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * You should have received a copy of the MPL along with this library
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * in the file COPYING-MPL-1.1
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński *
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * The contents of this file are subject to the Mozilla Public License
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * Version 1.1 (the "License"); you may not use this file except in
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * compliance with the License. You may obtain a copy of the License at
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * http://www.mozilla.org/MPL/
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński *
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * OF ANY KIND, either express or implied. See the LGPL or the MPL for
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński * the specific language governing rights and limitations.
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński *
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński */
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński /*
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen * It is more efficient to find roots of f(t) = c_0, c_1, ... all at once, rather than iterating.
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen *
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen * Todo/think about:
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen * multi-roots using bernstein method, one approach would be:
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen sort c
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen take median and find roots of that
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen whenever a segment lies entirely on one side of the median,
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen find the median of the half and recurse.
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen in essence we are implementing quicksort on a continuous function
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen * the gsl poly roots finder is faster than bernstein too, but we don't use it for 3 reasons:
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen a) it requires convertion to poly, which is numerically unstable
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen b) it requires gsl (which is currently not a dependency, and would bring in a whole slew of unrelated stuff)
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen c) it finds all roots, even complex ones. We don't want to accidently treat a nearly real root as a real root
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelenFrom memory gsl poly roots was about 10 times faster than bernstein in the case where all the roots
981b809bc6ed10a21e89444d9447e5475801874fjohanengelenare in [0,1] for polys of order 5. I spent some time working out whether eigenvalue root finding
981b809bc6ed10a21e89444d9447e5475801874fjohanengelencould be done directly in sbasis space, but the maths was too hard for me. -- njh
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelenjfbarraud: eigenvalue root finding could be done directly in sbasis space ?
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelennjh: I don't know, I think it should. You would make a matrix whose characteristic polynomial was
981b809bc6ed10a21e89444d9447e5475801874fjohanengelencorrect, but do it by putting the sbasis terms in the right spots in the matrix. normal eigenvalue
981b809bc6ed10a21e89444d9447e5475801874fjohanengelenroot finding makes a matrix that is a diagonal + a row along the top. This matrix has the property
981b809bc6ed10a21e89444d9447e5475801874fjohanengelenthat its characteristic poly is just the poly whose coefficients are along the top row.
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelenNow an sbasis function is a linear combination of the poly coeffs. So it seems to me that you
981b809bc6ed10a21e89444d9447e5475801874fjohanengelenshould be able to put the sbasis coeffs directly into a matrix in the right spots so that the
981b809bc6ed10a21e89444d9447e5475801874fjohanengelencharacteristic poly is the sbasis. You'll still have problems b) and c).
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelenWe might be able to lift an eigenvalue solver and include that directly into 2geom. Eigenvalues
981b809bc6ed10a21e89444d9447e5475801874fjohanengelenalso allow you to find intersections of multiple curves but require solving n*m x n*m matrices.
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen **/
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen#include <cmath>
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen#include <map>
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
8001ba81cb851b38d86650a2fef5817facffb763johanengelen#include <2geom/sbasis.h>
8001ba81cb851b38d86650a2fef5817facffb763johanengelen#include <2geom/sbasis-to-bezier.h>
8001ba81cb851b38d86650a2fef5817facffb763johanengelen#include <2geom/solver.h>
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelenusing namespace std;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelennamespace Geom{
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould/** Find the smallest interval that bounds a
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould \param a sbasis function
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould \returns inteval
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould*/
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould#ifdef USE_SBASIS_OF
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed GouldOptInterval bounds_exact(SBasisOf<double> const &a) {
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould Interval result = Interval(a.at0(), a.at1());
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould SBasisOf<double> df = derivative(a);
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould vector<double>extrema = roots(df);
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould for (unsigned i=0; i<extrema.size(); i++){
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould result.extendTo(a(extrema[i]));
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould }
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould return result;
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould}
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould#else
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed GouldOptInterval bounds_exact(SBasis const &a) {
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen Interval result = Interval(a.at0(), a.at1());
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen SBasis df = derivative(a);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen vector<double>extrema = roots(df);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen for (unsigned i=0; i<extrema.size(); i++){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen result.expandTo(a(extrema[i]));
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen return result;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen}
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould#endif
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould/** Find a small interval that bounds a
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould \param a sbasis function
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould \returns inteval
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould*/
1f37e9d97c3bb8cf02b2cc80af8dcfc9aba7c7b4johanengelen// I have no idea how this works, some clever bounding argument by jfb.
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould#ifdef USE_SBASIS_OF
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed GouldOptInterval bounds_fast(const SBasisOf<double> &sb, int order) {
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould#else
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed GouldOptInterval bounds_fast(const SBasis &sb, int order) {
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould#endif
1f37e9d97c3bb8cf02b2cc80af8dcfc9aba7c7b4johanengelen Interval res(0,0); // an empty sbasis is 0.
1f37e9d97c3bb8cf02b2cc80af8dcfc9aba7c7b4johanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen for(int j = sb.size()-1; j>=order; j--) {
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double a=sb[j][0];
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double b=sb[j][1];
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
29684a16b6c92bee28a94fdc2607bcc143950fa8johanengelen double v, t = 0;
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński v = res.min();
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (v<0) t = ((b-a)/v+1)*0.5;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (v>=0 || t<0 || t>1) {
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński res.setMin(std::min(a,b));
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński } else {
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński res.setMin(lerp(t, a+v*t, b));
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński v = res.max();
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (v>0) t = ((b-a)/v+1)*0.5;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (v<=0 || t<0 || t>1) {
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński res.setMax(std::max(a,b));
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }else{
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński res.setMax(lerp(t, a+v*t, b));
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }
ccb552a7e79ca66f285ad9ccf933f956cc45dc06Johan B. C. Engelen if (order>0) res*=std::pow(.25,order);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen return res;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen}
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould/** Find a small interval that bounds a(t) for t in i to order order
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould \param sb sbasis function
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould \param i domain interval
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould \param order number of terms
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould \return interval
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould*/
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould#ifdef USE_SBASIS_OF
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed GouldOptInterval bounds_local(const SBasisOf<double> &sb, const OptInterval &i, int order) {
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould#else
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed GouldOptInterval bounds_local(const SBasis &sb, const OptInterval &i, int order) {
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould#endif
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould double t0=i->min(), t1=i->max(), lo=0., hi=0.;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen for(int j = sb.size()-1; j>=order; j--) {
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double a=sb[j][0];
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double b=sb[j][1];
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
29684a16b6c92bee28a94fdc2607bcc143950fa8johanengelen double t = 0;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (lo<0) t = ((b-a)/lo+1)*0.5;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (lo>=0 || t<t0 || t>t1) {
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen lo = std::min(a*(1-t0)+b*t0+lo*t0*(1-t0),a*(1-t1)+b*t1+lo*t1*(1-t1));
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }else{
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen lo = lerp(t, a+lo*t, b);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (hi>0) t = ((b-a)/hi+1)*0.5;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (hi<=0 || t<t0 || t>t1) {
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen hi = std::max(a*(1-t0)+b*t0+hi*t0*(1-t0),a*(1-t1)+b*t1+hi*t1*(1-t1));
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }else{
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen hi = lerp(t, a+hi*t, b);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen Interval res = Interval(lo,hi);
ccb552a7e79ca66f285ad9ccf933f956cc45dc06Johan B. C. Engelen if (order>0) res*=std::pow(.25,order);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen return res;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen}
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen//-- multi_roots ------------------------------------
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// goal: solve f(t)=c for several c at once.
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen/* algo: -compute f at both ends of the given segment [a,b].
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen -compute bounds m<df(t)<M for df on the segment.
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen let c and C be the levels below and above f(a):
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen going from f(a) down to c with slope m takes at least time (f(a)-c)/m
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen going from f(a) up to C with slope M takes at least time (C-f(a))/M
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen From this we conclude there are no roots before a'=a+min((f(a)-c)/m,(C-f(a))/M).
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen Do the same for b: compute some b' such that there are no roots in (b',b].
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen -if [a',b'] is not empty, repeat the process with [a',(a'+b')/2] and [(a'+b')/2,b'].
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen unfortunately, extra care is needed about rounding errors, and also to avoid the repetition of roots,
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen making things tricky and unpleasant...
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen*/
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen//TODO: Make sure the code is "rounding-errors proof" and take care about repetition of roots!
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelenstatic int upper_level(vector<double> const &levels,double x,double tol=0.){
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen return(upper_bound(levels.begin(),levels.end(),x-tol)-levels.begin());
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen}
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould#ifdef USE_SBASIS_OF
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gouldstatic void multi_roots_internal(SBasis const &f,
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould SBasis const &df,
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould#else
981b809bc6ed10a21e89444d9447e5475801874fjohanengelenstatic void multi_roots_internal(SBasis const &f,
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen SBasis const &df,
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould#endif
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen std::vector<double> const &levels,
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen std::vector<std::vector<double> > &roots,
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double htol,
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double vtol,
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double a,
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double fa,
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double b,
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double fb){
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen
f055ef73fb003d7cfda819eb4cd73d3341893a14Krzysztof Kosiński if (f.isZero(0)){
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen int idx;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen idx=upper_level(levels,0,vtol);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (idx<(int)levels.size()&&fabs(levels.at(idx))<=vtol){
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen roots[idx].push_back(a);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen roots[idx].push_back(b);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen return;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen////usefull?
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// if (f.size()==1){
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// int idxa=upper_level(levels,fa);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// int idxb=upper_level(levels,fb);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// if (fa==fb){
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// if (fa==levels[idxa]){
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// roots[a]=idxa;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// roots[b]=idxa;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// }
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// return;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// }
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// int idx_min=std::min(idxa,idxb);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// int idx_max=std::max(idxa,idxb);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// if (idx_max==levels.size()) idx_max-=1;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// for(int i=idx_min;i<=idx_max; i++){
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// double t=a+(b-a)*(levels[i]-fa)/(fb-fa);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// if(a<t&&t<b) roots[t]=i;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// }
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// return;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// }
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if ((b-a)<htol){
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen //TODO: use different tol for t and f ?
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen //TODO: unsigned idx ? (remove int casts when fixed)
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen int idx=std::min(upper_level(levels,fa,vtol),upper_level(levels,fb,vtol));
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (idx==(int)levels.size()) idx-=1;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double c=levels.at(idx);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if((fa-c)*(fb-c)<=0||fabs(fa-c)<vtol||fabs(fb-c)<vtol){
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen roots[idx].push_back((a+b)/2);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen return;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen int idxa=upper_level(levels,fa,vtol);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen int idxb=upper_level(levels,fb,vtol);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould Interval bs = *bounds_local(df,Interval(a,b));
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen //first times when a level (higher or lower) can be reached from a or b.
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double ta_hi,tb_hi,ta_lo,tb_lo;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen ta_hi=ta_lo=b+1;//default values => no root there.
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen tb_hi=tb_lo=a-1;//default values => no root there.
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (idxa<(int)levels.size() && fabs(fa-levels.at(idxa))<vtol){//a can be considered a root.
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen //ta_hi=ta_lo=a;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen roots[idxa].push_back(a);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen ta_hi=ta_lo=a+htol;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }else{
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (bs.max()>0 && idxa<(int)levels.size())
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen ta_hi=a+(levels.at(idxa )-fa)/bs.max();
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (bs.min()<0 && idxa>0)
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen ta_lo=a+(levels.at(idxa-1)-fa)/bs.min();
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (idxb<(int)levels.size() && fabs(fb-levels.at(idxb))<vtol){//b can be considered a root.
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen //tb_hi=tb_lo=b;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen roots[idxb].push_back(b);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen tb_hi=tb_lo=b-htol;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }else{
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (bs.min()<0 && idxb<(int)levels.size())
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen tb_hi=b+(levels.at(idxb )-fb)/bs.min();
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (bs.max()>0 && idxb>0)
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen tb_lo=b+(levels.at(idxb-1)-fb)/bs.max();
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double t0,t1;
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen t0=std::min(ta_hi,ta_lo);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen t1=std::max(tb_hi,tb_lo);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen //hum, rounding errors frighten me! so I add this +tol...
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (t0>t1+htol) return;//no root here.
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (fabs(t1-t0)<htol){
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen multi_roots_internal(f,df,levels,roots,htol,vtol,t0,f(t0),t1,f(t1));
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }else{
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double t,t_left,t_right,ft,ft_left,ft_right;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen t_left =t_right =t =(t0+t1)/2;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen ft_left=ft_right=ft=f(t);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen int idx=upper_level(levels,ft,vtol);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if (idx<(int)levels.size() && fabs(ft-levels.at(idx))<vtol){//t can be considered a root.
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen roots[idx].push_back(t);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen //we do not want to count it twice (from the left and from the right)
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen t_left =t-htol/2;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen t_right=t+htol/2;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen ft_left =f(t_left);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen ft_right=f(t_right);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen multi_roots_internal(f,df,levels,roots,htol,vtol,t0 ,f(t0) ,t_left,ft_left);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen multi_roots_internal(f,df,levels,roots,htol,vtol,t_right,ft_right,t1 ,f(t1) );
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen}
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould/** Solve f(t)=c for several c at once.
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould \param f sbasis function
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould \param levels vector of 'y' values
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould \param htol, vtol
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould \param a, b left and right bounds
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould \returns a vector of vectors, one for each y giving roots
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed GouldEffectively computes:
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gouldresults = roots(f(y_i)) for all y_i
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould* algo: -compute f at both ends of the given segment [a,b].
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould -compute bounds m<df(t)<M for df on the segment.
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould let c and C be the levels below and above f(a):
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould going from f(a) down to c with slope m takes at least time (f(a)-c)/m
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould going from f(a) up to C with slope M takes at least time (C-f(a))/M
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould From this we conclude there are no roots before a'=a+min((f(a)-c)/m,(C-f(a))/M).
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould Do the same for b: compute some b' such that there are no roots in (b',b].
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould -if [a',b'] is not empty, repeat the process with [a',(a'+b')/2] and [(a'+b')/2,b'].
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould unfortunately, extra care is needed about rounding errors, and also to avoid the repetition of roots,
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould making things tricky and unpleasant...
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed GouldTODO: Make sure the code is "rounding-errors proof" and take care about repetition of roots!
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould*/
981b809bc6ed10a21e89444d9447e5475801874fjohanengelenstd::vector<std::vector<double> > multi_roots(SBasis const &f,
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen std::vector<double> const &levels,
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double htol,
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double vtol,
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double a,
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double b){
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen std::vector<std::vector<double> > roots(levels.size(), std::vector<double>());
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen SBasis df=derivative(f);
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen multi_roots_internal(f,df,levels,roots,htol,vtol,a,f(a),b,f(b));
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen return(roots);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen}
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelenstatic bool compareIntervalMin( Interval I, Interval J ){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen return I.min()<J.min();
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen}
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelenstatic bool compareIntervalMax( Interval I, Interval J ){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen return I.max()<J.max();
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen}
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen//find the first interval whose max is >= x
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelenstatic unsigned upper_level(vector<Interval> const &levels, double x ){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen return( lower_bound( levels.begin(), levels.end(), Interval(x,x), compareIntervalMax) - levels.begin() );
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen}
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelenstatic std::vector<Interval> fuseContiguous(std::vector<Interval> const &sets, double tol=0.){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen std::vector<Interval> result;
ea8dd7683dd12883474f6cf9b5f424f8ed831166Kris if (sets.empty() ) return result;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen result.push_back( sets.front() );
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen for (unsigned i=1; i < sets.size(); i++ ){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( result.back().max() + tol >= sets[i].min() ){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen result.back().unionWith( sets[i] );
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }else{
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen result.push_back( sets[i] );
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen return result;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen}
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen/** level_sets internal method.
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen* algorithm: (~adaptation of Newton method versus 'accroissements finis')
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen -compute f at both ends of the given segment [a,b].
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen -compute bounds m<df(t)<M for df on the segment.
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen Suppose f(a) is between two 'levels' c and C. Then
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen f wont enter c before a + (f(a)-c.max())/m
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen f wont enter C before a + (C.min()-f(a))/M
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen From this we conclude nothing happens before a'=a+min((f(a)-c.max())/m,(C.min()-f(a))/M).
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen We do the same for b: compute some b' such that nothing happens in (b',b].
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen -if [a',b'] is not empty, repeat the process with [a',(a'+b')/2] and [(a'+b')/2,b'].
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen If f(a) or f(b) belongs to some 'level' C, then use the same argument to find a' or b' such
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen that f remains in C on [a,a'] or [b',b]. In case f is monotonic, we also know f won't enter another
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen level before or after some time, allowing us to restrict the search a little more.
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen unfortunately, extra care is needed about rounding errors, and also to avoid the repetition of roots,
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen making things tricky and unpleasant...
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen*/
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelenstatic void level_sets_internal(SBasis const &f,
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen SBasis const &df,
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen std::vector<Interval> const &levels,
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen std::vector<std::vector<Interval> > &solsets,
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen double a,
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen double fa,
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen double b,
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen double fb,
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen double tol=1e-5){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
f055ef73fb003d7cfda819eb4cd73d3341893a14Krzysztof Kosiński if (f.isZero(0)){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen unsigned idx;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen idx=upper_level( levels, 0. );
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if (idx<levels.size() && levels[idx].contains(0.)){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen solsets[idx].push_back( Interval(a,b) ) ;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen return;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen unsigned idxa=upper_level(levels,fa);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen unsigned idxb=upper_level(levels,fb);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen Interval bs = *bounds_local(df,Interval(a,b));
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen //first times when a level (higher or lower) can be reached from a or b.
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen double ta_hi; // f remains below next level for t<ta_hi
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen double ta_lo; // f remains above prev level for t<ta_lo
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen double tb_hi; // f remains below next level for t>tb_hi
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen double tb_lo; // f remains above next level for t>tb_lo
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen ta_hi=ta_lo=b+1;//default values => no root there.
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen tb_hi=tb_lo=a-1;//default values => no root there.
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen //--- if f(a) belongs to a level.-------
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( idxa < levels.size() && levels[idxa].contains( fa ) ){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen //find the first time when we may exit this level.
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen ta_lo = a + ( levels[idxa].min() - fa)/bs.min();
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen ta_hi = a + ( levels[idxa].max() - fa)/bs.max();
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( ta_lo < a || ta_lo > b ) ta_lo = b;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( ta_hi < a || ta_hi > b ) ta_hi = b;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen //move to that time for the next iteration.
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen solsets[idxa].push_back( Interval( a, std::min( ta_lo, ta_hi ) ) );
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }else{
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen //--- if f(b) does not belong to a level.-------
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( idxa == 0 ){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen ta_lo = b;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }else{
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen ta_lo = a + ( levels[idxa-1].max() - fa)/bs.min();
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( ta_lo < a ) ta_lo = b;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( idxa == levels.size() ){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen ta_hi = b;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }else{
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen ta_hi = a + ( levels[idxa].min() - fa)/bs.max();
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( ta_hi < a ) ta_hi = b;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen //--- if f(b) belongs to a level.-------
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if (idxb<levels.size() && levels.at(idxb).contains(fb)){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen //find the first time from b when we may exit this level.
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen tb_lo = b + ( levels[idxb].min() - fb ) / bs.max();
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen tb_hi = b + ( levels[idxb].max() - fb ) / bs.min();
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( tb_lo > b || tb_lo < a ) tb_lo = a;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( tb_hi > b || tb_hi < a ) tb_hi = a;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen //move to that time for the next iteration.
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen solsets[idxb].push_back( Interval( std::max( tb_lo, tb_hi ), b) );
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }else{
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen //--- if f(b) does not belong to a level.-------
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( idxb == 0 ){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen tb_lo = a;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }else{
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen tb_lo = b + ( levels[idxb-1].max() - fb)/bs.max();
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( tb_lo > b ) tb_lo = a;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( idxb == levels.size() ){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen tb_hi = a;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }else{
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen tb_hi = b + ( levels[idxb].min() - fb)/bs.min();
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( tb_hi > b ) tb_hi = a;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( bs.min() < 0 && idxb < levels.size() )
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen tb_hi = b + ( levels[idxb ].min() - fb ) / bs.min();
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( bs.max() > 0 && idxb > 0 )
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen tb_lo = b + ( levels[idxb-1].max() - fb ) / bs.max();
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen //let [t0,t1] be the next interval where to search.
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen double t0=std::min(ta_hi,ta_lo);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen double t1=std::max(tb_hi,tb_lo);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if (t0>=t1) return;//no root here.
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen //if the interval is smaller than our resolution:
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen //pretend f simultaneously meets all the levels between f(t0) and f(t1)...
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( t1 - t0 <= tol ){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen Interval f_t0t1 ( f(t0), f(t1) );
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen unsigned idxmin = std::min(idxa, idxb);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen unsigned idxmax = std::max(idxa, idxb);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen //push [t0,t1] into all crossed level. Cheat to avoid overlapping intervals on different levels?
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( idxmax > idxmin ){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen for (unsigned idx = idxmin; idx < idxmax; idx++){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen solsets[idx].push_back( Interval( t0, t1 ) );
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( idxmax < levels.size() && f_t0t1.intersects( levels[idxmax] ) ){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen solsets[idxmax].push_back( Interval( t0, t1 ) );
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen return;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen //To make sure we finally exit the level jump at least by tol:
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen t0 = std::min( std::max( t0, a + tol ), b );
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen t1 = std::max( std::min( t1, b - tol ), a );
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen double t =(t0+t1)/2;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen double ft=f(t);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen level_sets_internal( f, df, levels, solsets, t0, f(t0), t, ft );
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen level_sets_internal( f, df, levels, solsets, t, ft, t1, f(t1) );
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen}
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelenstd::vector<std::vector<Interval> > level_sets(SBasis const &f,
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen std::vector<Interval> const &levels,
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen double a, double b, double tol){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen std::vector<std::vector<Interval> > solsets(levels.size(), std::vector<Interval>());
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen SBasis df=derivative(f);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen level_sets_internal(f,df,levels,solsets,a,f(a),b,f(b),tol);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen // Fuse overlapping intervals...
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen for (unsigned i=0; i<solsets.size(); i++){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if ( solsets[i].size() == 0 ) continue;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen std::sort( solsets[i].begin(), solsets[i].end(), compareIntervalMin );
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen solsets[i] = fuseContiguous( solsets[i], tol );
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen return solsets;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen}
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelenstd::vector<Interval> level_set (SBasis const &f, double level, double vtol, double a, double b, double tol){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen Interval fat_level( level - vtol, level + vtol );
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen return level_set(f, fat_level, a, b, tol);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen}
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelenstd::vector<Interval> level_set (SBasis const &f, Interval const &level, double a, double b, double tol){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen std::vector<Interval> levels(1,level);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen return level_sets(f,levels, a, b, tol).front() ;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen}
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelenstd::vector<std::vector<Interval> > level_sets (SBasis const &f, std::vector<double> const &levels, double vtol, double a, double b, double tol){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen std::vector<Interval> fat_levels( levels.size(), Interval());
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen for (unsigned i = 0; i < levels.size(); i++){
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen fat_levels[i] = Interval( levels[i]-vtol, levels[i]+vtol);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen return level_sets(f, fat_levels, a, b, tol);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen}
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen//-------------------------------------
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen//-------------------------------------
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelenvoid subdiv_sbasis(SBasis const & s,
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen std::vector<double> & roots,
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double left, double right) {
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould OptInterval bs = bounds_fast(s);
6c3e745a94ef6b25a4ef9f018d350a7535aa45afTed Gould if(!bs || bs->min() > 0 || bs->max() < 0)
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen return; // no roots here
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen if(s.tailError(1) < 1e-7) {
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double t = s[0][0] / (s[0][0] - s[0][1]);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen roots.push_back(left*(1-t) + t*right);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen return;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen }
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen double middle = (left + right)/2;
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen subdiv_sbasis(compose(s, Linear(0, 0.5)), roots, left, middle);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen subdiv_sbasis(compose(s, Linear(0.5, 1.)), roots, middle, right);
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen}
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen// It is faster to use the bernstein root finder for small degree polynomials (<100?.
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
6492fcbc5fbe9a07962f989247731b6435fd72b9johanengelenstd::vector<double> roots1(SBasis const & s) {
6492fcbc5fbe9a07962f989247731b6435fd72b9johanengelen std::vector<double> res;
6492fcbc5fbe9a07962f989247731b6435fd72b9johanengelen double d = s[0][0] - s[0][1];
6492fcbc5fbe9a07962f989247731b6435fd72b9johanengelen if(d != 0) {
6492fcbc5fbe9a07962f989247731b6435fd72b9johanengelen double r = s[0][0] / d;
98a704c566ce5a750d76d5fc9675ccc804ac65f5johanengelen if(0 <= r && r <= 1)
6492fcbc5fbe9a07962f989247731b6435fd72b9johanengelen res.push_back(r);
6492fcbc5fbe9a07962f989247731b6435fd72b9johanengelen }
6492fcbc5fbe9a07962f989247731b6435fd72b9johanengelen return res;
6492fcbc5fbe9a07962f989247731b6435fd72b9johanengelen}
6492fcbc5fbe9a07962f989247731b6435fd72b9johanengelen
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelenstd::vector<double> roots1(SBasis const & s, Interval const ivl) {
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen std::vector<double> res;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen double d = s[0][0] - s[0][1];
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if(d != 0) {
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen double r = s[0][0] / d;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen if(ivl.contains(r))
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen res.push_back(r);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen return res;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen}
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould/** Find all t s.t s(t) = 0
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould \param a sbasis function
76addc201c409e81eaaa73fe27cc0f79c4db097cKrzysztof Kosiński \see Bezier::roots
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould \returns vector of zeros (roots)
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould
01d27eab5fca2dcb8e883011f8be77ae6b78a11cTed Gould*/
981b809bc6ed10a21e89444d9447e5475801874fjohanengelenstd::vector<double> roots(SBasis const & s) {
6492fcbc5fbe9a07962f989247731b6435fd72b9johanengelen switch(s.size()) {
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen case 0:
f055ef73fb003d7cfda819eb4cd73d3341893a14Krzysztof Kosiński assert(false);
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen return std::vector<double>();
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen case 1:
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen return roots1(s);
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen default:
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen {
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen Bezier bz;
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen sbasis_to_bezier(bz, s);
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen return bz.roots();
6bc0b25077dcb0cce5dea357de5bab735babe891johanengelen }
6492fcbc5fbe9a07962f989247731b6435fd72b9johanengelen }
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen}
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelenstd::vector<double> roots(SBasis const & s, Interval const ivl) {
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen switch(s.size()) {
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen case 0:
f055ef73fb003d7cfda819eb4cd73d3341893a14Krzysztof Kosiński assert(false);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen return std::vector<double>();
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen case 1:
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen return roots1(s, ivl);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen default:
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen {
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen Bezier bz;
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen sbasis_to_bezier(bz, s);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen return bz.roots(ivl);
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen }
d37634d73670180f99a3e0ea583621373d90ec4fJohan Engelen}
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen};
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen/*
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen Local Variables:
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen mode:c++
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen c-file-style:"stroustrup"
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen indent-tabs-mode:nil
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen fill-column:99
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen End:
981b809bc6ed10a21e89444d9447e5475801874fjohanengelen*/
a4030d5ca449e7e384bc699cd249ee704faaeab0Chris Morgan// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :