diff options
Diffstat (limited to 'include/2geom/symbolic/implicit.h')
-rw-r--r-- | include/2geom/symbolic/implicit.h | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/include/2geom/symbolic/implicit.h b/include/2geom/symbolic/implicit.h new file mode 100644 index 0000000..82d77cd --- /dev/null +++ b/include/2geom/symbolic/implicit.h @@ -0,0 +1,353 @@ +/* + * Routines to compute the implicit equation of a parametric polynomial curve + * + * Authors: + * Marco Cecchetti <mrcekets at gmail.com> + * + * Copyright 2008 authors + * + * This library is free software; you can redistribute it and/or + * 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 + * http://www.mozilla.org/MPL/ + * + * 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. + */ + + +#ifndef _GEOM_SL_IMPLICIT_H_ +#define _GEOM_SL_IMPLICIT_H_ + + + +#include <2geom/symbolic/multipoly.h> +#include <2geom/symbolic/matrix.h> + + +#include <2geom/exception.h> + +#include <array> + + +namespace Geom { namespace SL { + +typedef MultiPoly<1, double> MVPoly1; +typedef MultiPoly<2, double> MVPoly2; +typedef MultiPoly<3, double> MVPoly3; +typedef std::array<MVPoly1, 3> poly_vector_type; +typedef std::array<poly_vector_type, 2> basis_type; +typedef std::array<double, 3> coeff_vector_type; + +namespace detail { + +/* + * transform a univariate polynomial f(t) in a 3-variate polynomial + * p(t, x, y) = f(t) * x^i * y^j + */ +inline +void poly1_to_poly3(MVPoly3 & p3, MVPoly1 const& p1, size_t i, size_t j) +{ + multi_index_type I = make_multi_index(0, i, j); + for (; I[0] < p1.get_poly().size(); ++I[0]) + { + p3.coefficient(I, p1[I[0]]); + } +} + +/* + * evaluates the degree of a poly_vector_type, such a degree is defined as: + * deg({p[0](t), p[1](t), p[2](t)}) := {max(deg(p[i](t)), i = 0, 1, 2), k} + * here k is the index where the max is achieved, + * if deg(p[i](t)) == deg(p[j](t)) and i < j then k = i + */ +inline +std::pair<size_t, size_t> deg(poly_vector_type const& p) +{ + std::pair<size_t, size_t> d; + d.first = p[0].get_poly().real_degree(); + d.second = 0; + size_t k = p[1].get_poly().real_degree(); + if (d.first < k) + { + d.first = k; + d.second = 1; + } + k = p[2].get_poly().real_degree(); + if (d.first < k) + { + d.first = k; + d.second = 2; + } + return d; +} + +} // end namespace detail + + +/* + * A polynomial parametrization could be seen as 1-variety V in R^3, + * intersection of two surfaces x = f(t), y = g(t), this variety V has + * attached an ideal I in the ring of polynomials in t, x, y with coefficients + * on reals; a basis of generators for I is given by p(t, x, y) = x - f(t), + * q(t, x, y) = y - g(t); such a basis has the nice property that could be + * written as a couple of vectors of dim 3 with entries in R[t]; the original + * polinomials p and q can be obtained by doing a dot product between each + * vector and the vector {x, y, 1} + * As reference you can read the text book: + * Ideals, Varieties and Algorithms by Cox, Little, O'Shea + */ +inline +void make_initial_basis(basis_type& b, MVPoly1 const& p, MVPoly1 const& q) +{ + // first basis vector + b[0][0] = 1; + b[0][1] = 0; + b[0][2] = -p; + + // second basis vector + b[1][0] = 0; + b[1][1] = 1; + b[1][2] = -q; +} + +/* + * Starting from the initial basis for the ideal I is possible to make up + * a new basis, still showing off the nice property that each generator is + * a moving line that is a linear combination of x, y, 1 where the coefficients + * are polynomials in R[t], and moreover each generator is of minimal degree. + * Can be proved that given a polynomial parametrization f(t), g(t) + * we are able to make up a "micro" basis of generators p(t, x, y), q(t, x, y) + * for the ideal I such that the deg(p, t) = m <= n/2 and deg(q, t) = n - m, + * where n = max(deg(f(t)), deg(g(t))); this let us halve the order of + * the Bezout matrix. + * Reference: + * Zheng, Sederberg - A Direct Approach to Computing the micro-basis + * of a Planar Rational Curves + * Deng, Chen, Shen - Computing micro-Basis of Rational Curves and Surfaces + * Using Polynomial Matrix Factorization + */ +inline +void microbasis(basis_type& b, MVPoly1 const& p, MVPoly1 const& q) +{ + typedef std::pair<size_t, size_t> degree_pair_t; + + size_t n = std::max(p.get_poly().real_degree(), q.get_poly().real_degree()); + make_initial_basis(b, p, q); + degree_pair_t n0 = detail::deg(b[0]); + degree_pair_t n1 = detail::deg(b[1]); + size_t d; + double r0, r1; + //size_t iter = 0; + while ((n0.first + n1.first) > n)// && iter < 30) + { +// ++iter; +// std::cout << "iter = " << iter << std::endl; +// for (size_t i= 0; i < 2; ++i) +// for (size_t j= 0; j < 3; ++j) +// std::cout << b[i][j] << std::endl; +// std::cout << n0.first << ", " << n0.second << std::endl; +// std::cout << n1.first << ", " << n1.second << std::endl; +// std::cout << "-----" << std::endl; +// if (n0.first < n1.first) +// { +// d = n1.first - n0.first; +// r = b[1][n1.second][n1.first] / b[0][n1.second][n0.first]; +// for (size_t i = 0; i < b[0].size(); ++i) +// b[1][i] -= ((r * b[0][i]).get_poly() << d); +// b[1][n1.second][n1.first] = 0; +// n1 = detail::deg(b[1]); +// } +// else +// { +// d = n0.first - n1.first; +// r = b[0][n0.second][n0.first] / b[1][n0.second][n1.first]; +// for (size_t i = 0; i < b[0].size(); ++i) +// b[0][i] -= ((r * b[1][i]).get_poly() << d); +// b[0][n0.second][n0.first] = 0; +// n0 = detail::deg(b[0]); +// } + + // this version shouldn't suffer of ill-conditioning due to + // cancellation issue + if (n0.first < n1.first) + { + d = n1.first - n0.first; + r0 = b[0][n1.second][n0.first]; + r1 = b[1][n1.second][n1.first]; + for (size_t i = 0; i < b[0].size(); ++i) + { + b[1][i] *= r0; + b[1][i] -= ((r1 * b[0][i]).get_poly() << d); + // without the following division the modulus grows + // beyond the limit of the double type + b[1][i] /= r0; + } + n1 = detail::deg(b[1]); + } + else + { + d = n0.first - n1.first; + r0 = b[0][n1.second][n0.first]; + r1 = b[1][n1.second][n1.first]; + + for (size_t i = 0; i < b[0].size(); ++i) + { + b[0][i] *= r1; + b[0][i] -= ((r0 * b[1][i]).get_poly() << d); + b[0][i] /= r1; + } + n0 = detail::deg(b[0]); + } + + } +} + +/* + * computes the dot product: + * p(t, x, y) = {p0(t), p1(t), p2(t)} . {x, y, 1} + */ +inline +void basis_to_poly(MVPoly3 & p0, poly_vector_type const& v) +{ + MVPoly3 p1, p2; + detail::poly1_to_poly3(p0, v[0], 1,0); + detail::poly1_to_poly3(p1, v[1], 0,1); + detail::poly1_to_poly3(p2, v[2], 0,0); + p0 += p1; + p0 += p2; +} + + +/* + * Make up a Bezout matrix with two basis genarators as input. + * + * A Bezout matrix is the matrix related to the symmetric bilinear form + * (f,g) -> B[f,g] where B[f,g](s,t) = (f(t)*g(s) - f(s)*g(t))/(s-t) + * where f, g are polynomials, this function is called a bezoutian. + * Given a basis of generators {p(t, x, y), q(t, x, y)} for the ideal I + * related to our parametrization x = f(t), y = g(t), we are able to prove + * that the implicit equation of such polynomial parametrization can be + * evaluated computing the determinant of the Bezout matrix made up using + * the polinomial p and q as univariate polynomials in t with coefficients + * in R[x,y], so the resulting Bezout matrix will be a matrix with bivariate + * polynomials as entries. A Bezout matrix is always symmetric. + * Reference: + * Sederberg, Zheng - Algebraic Methods for Computer Aided Geometric Design + */ +Matrix<MVPoly2> +make_bezout_matrix (MVPoly3 const& p, MVPoly3 const& q) +{ + size_t pdeg = p.get_poly().real_degree(); + size_t qdeg = q.get_poly().real_degree(); + size_t n = std::max(pdeg, qdeg); + + Matrix<MVPoly2> BM(n, n); + //std::cerr << "rows, columns " << BM.rows() << " , " << BM.columns() << std::endl; + for (size_t i = n; i >= 1; --i) + { + for (size_t j = n; j >= i; --j) + { + size_t m = std::min(i, n + 1 - j); + //std::cerr << "m = " << m << std::endl; + for (size_t k = 1; k <= m; ++k) + { + //BM(i-1,j-1) += (p[j-1+k] * q[i-k] - p[i-k] * q[j-1+k]); + BM(n-i,n-j) += (p.coefficient(j-1+k) * q.coefficient(i-k) + - p.coefficient(i-k) * q.coefficient(j-1+k)); + } + } + } + + for (size_t i = 0; i < n; ++i) + { + for (size_t j = 0; j < i; ++j) + BM(j,i) = BM(i,j); + } + return BM; +} + +/* + * Make a matrix that represents a main minor (i.e. with the diagonal + * on the diagonal of the matrix to which it owns) of the Bezout matrix + * with order n-1 where n is the order of the Bezout matrix. + * The minor is obtained by removing the "h"-th row and the "h"-th column, + * and as the Bezout matrix is symmetric. + */ +Matrix<MVPoly2> +make_bezout_main_minor (MVPoly3 const& p, MVPoly3 const& q, size_t h) +{ + size_t pdeg = p.get_poly().real_degree(); + size_t qdeg = q.get_poly().real_degree(); + size_t n = std::max(pdeg, qdeg); + + Matrix<MVPoly2> BM(n-1, n-1); + size_t u = 0, v; + for (size_t i = 1; i <= n; ++i) + { + v = 0; + if (i == h) + { + u = 1; + continue; + } + for (size_t j = 1; j <= i; ++j) + { + if (j == h) + { + v = 1; + continue; + } + size_t m = std::min(i, n + 1 - j); + for (size_t k = 1; k <= m; ++k) + { + //BM(i-u-1,j-v-1) += (p[j-1+k] * q[i-k] - p[i-k] * q[j-1+k]); + BM(i-u-1,j-v-1) += (p.coefficient(j-1+k) * q.coefficient(i-k) + - p.coefficient(i-k) * q.coefficient(j-1+k)); + } + } + } + + --n; + for (size_t i = 0; i < n; ++i) + { + for (size_t j = 0; j < i; ++j) + BM(j,i) = BM(i,j); + } + return BM; +} + + +} /*end namespace Geom*/ } /*end namespace SL*/ + + + + +#endif // _GEOM_SL_IMPLICIT_H_ + + +/* + 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:fileencoding=utf-8:textwidth=99 : |