diff options
Diffstat (limited to 'src/2geom/path-intersection.cpp')
| -rw-r--r-- | src/2geom/path-intersection.cpp | 178 |
1 files changed, 138 insertions, 40 deletions
diff --git a/src/2geom/path-intersection.cpp b/src/2geom/path-intersection.cpp index bfb1fb96c..b2d5ceabb 100644 --- a/src/2geom/path-intersection.cpp +++ b/src/2geom/path-intersection.cpp @@ -4,8 +4,11 @@ //for path_direction: #include <2geom/sbasis-geometric.h> +#include <2geom/line.h> +#ifdef HAVE_GSL #include <gsl/gsl_vector.h> #include <gsl/gsl_multiroots.h> +#endif namespace Geom { @@ -199,6 +202,7 @@ static double EpsilonOf(double value) } #endif +#ifdef HAVE_GSL struct rparams { Curve const &A; Curve const &B; @@ -219,45 +223,91 @@ intersect_polish_f (const gsl_vector * x, void *params, return GSL_SUCCESS; } +#endif static void intersect_polish_root (Curve const &A, double &s, Curve const &B, double &t) { int status; size_t iter = 0; + std::vector<Point> as, bs; + as = A.pointAndDerivatives(s, 2); + bs = B.pointAndDerivatives(t, 2); + Point F = as[0] - bs[0]; + double best = dot(F, F); + + for(int i = 0; i < 4; i++) { + + /** + we want to solve + J*(x1 - x0) = f(x0) + + |dA(s)[0] -dB(t)[0]| (X1 - X0) = A(s) - B(t) + |dA(s)[1] -dB(t)[1]| + **/ + + // We're using the standard transformation matricies, which is numerically rather poor. Much better to solve the equation using elimination. + + Matrix jack(as[1][0], as[1][1], + -bs[1][0], -bs[1][1], + 0, 0); + Point soln = (F)*jack.inverse(); + double ns = s - soln[0]; + double nt = t - soln[1]; + + if (ns<0) ns=0; + else if (ns>1) ns=1; + if (nt<0) nt=0; + else if (nt>1) nt=1; + + as = A.pointAndDerivatives(ns, 2); + bs = B.pointAndDerivatives(nt, 2); + F = as[0] - bs[0]; + double trial = dot(F, F); + if (trial > best*0.1) // we have standards, you know + // At this point we could do a line search + break; + best = trial; + s = ns; + t = nt; + } + +#ifdef HAVE_GSL + if(0) { // the GSL version is more accurate, but taints this with GPL + const size_t n = 2; + struct rparams p = {A, B}; + gsl_multiroot_function f = {&intersect_polish_f, n, &p}; - const size_t n = 2; - struct rparams p = {A, B}; - gsl_multiroot_function f = {&intersect_polish_f, n, &p}; - - double x_init[2] = {s, t}; - gsl_vector *x = gsl_vector_alloc (n); + double x_init[2] = {s, t}; + gsl_vector *x = gsl_vector_alloc (n); - gsl_vector_set (x, 0, x_init[0]); - gsl_vector_set (x, 1, x_init[1]); + gsl_vector_set (x, 0, x_init[0]); + gsl_vector_set (x, 1, x_init[1]); - const gsl_multiroot_fsolver_type *T = gsl_multiroot_fsolver_hybrids; - gsl_multiroot_fsolver *sol = gsl_multiroot_fsolver_alloc (T, 2); - gsl_multiroot_fsolver_set (sol, &f, x); + const gsl_multiroot_fsolver_type *T = gsl_multiroot_fsolver_hybrids; + gsl_multiroot_fsolver *sol = gsl_multiroot_fsolver_alloc (T, 2); + gsl_multiroot_fsolver_set (sol, &f, x); - do - { - iter++; - status = gsl_multiroot_fsolver_iterate (sol); + do + { + iter++; + status = gsl_multiroot_fsolver_iterate (sol); - if (status) /* check if solver is stuck */ - break; + if (status) /* check if solver is stuck */ + break; - status = - gsl_multiroot_test_residual (sol->f, 1e-12); - } - while (status == GSL_CONTINUE && iter < 1000); + status = + gsl_multiroot_test_residual (sol->f, 1e-12); + } + while (status == GSL_CONTINUE && iter < 1000); - s = gsl_vector_get (sol->x, 0); - t = gsl_vector_get (sol->x, 1); + s = gsl_vector_get (sol->x, 0); + t = gsl_vector_get (sol->x, 1); - gsl_multiroot_fsolver_free (sol); - gsl_vector_free (x); + gsl_multiroot_fsolver_free (sol); + gsl_vector_free (x); + } +#endif } /** @@ -267,7 +317,7 @@ intersect_polish_root (Curve const &A, double &s, */ void pair_intersect(Curve const & A, double Al, double Ah, Curve const & B, double Bl, double Bh, - Crossings &ret, unsigned depth=0) { + Crossings &ret, unsigned depth = 0) { // std::cout << depth << "(" << Al << ", " << Ah << ")\n"; OptRect Ar = A.boundsLocal(Interval(Al, Ah)); if (!Ar) return; @@ -305,6 +355,13 @@ void pair_intersect(Curve const & A, double Al, double Ah, ret, depth+1); } +Crossings pair_intersect(Curve const & A, Interval const &Ad, + Curve const & B, Interval const &Bd) { + Crossings ret; + pair_intersect(A, Ad.min(), Ad.max(), B, Bd.min(), Bd.max(), ret); + return ret; +} + /** A simple wrapper around pair_intersect */ Crossings SimpleCrosser::crossings(Curve const &a, Curve const &b) { Crossings ret; @@ -312,6 +369,53 @@ Crossings SimpleCrosser::crossings(Curve const &a, Curve const &b) { return ret; } + +//same as below but curves not paths +void mono_intersect(Curve const &A, double Al, double Ah, + Curve const &B, double Bl, double Bh, + Crossings &ret, double tol = 0.1, unsigned depth = 0) { + if( Al >= Ah || Bl >= Bh) return; + //std::cout << " " << depth << "[" << Al << ", " << Ah << "]" << "[" << Bl << ", " << Bh << "]"; + + Point A0 = A.pointAt(Al), A1 = A.pointAt(Ah), + B0 = B.pointAt(Bl), B1 = B.pointAt(Bh); + //inline code that this implies? (without rect/interval construction) + Rect Ar = Rect(A0, A1), Br = Rect(B0, B1); + if(!Ar.intersects(Br) || A0 == A1 || B0 == B1) return; + + if(depth > 12 || (Ar.maxExtent() < tol && Ar.maxExtent() < tol)) { + double tA, tB, c; + if(linear_intersect(A.pointAt(Al), A.pointAt(Ah), + B.pointAt(Bl), B.pointAt(Bh), + tA, tB, c)) { + tA = tA * (Ah - Al) + Al; + tB = tB * (Bh - Bl) + Bl; + intersect_polish_root(A, tA, + B, tB); + if(depth % 2) + ret.push_back(Crossing(tB, tA, c < 0)); + else + ret.push_back(Crossing(tA, tB, c > 0)); + return; + } + } + if(depth > 12) return; + double mid = (Bl + Bh)/2; + mono_intersect(B, Bl, mid, + A, Al, Ah, + ret, tol, depth+1); + mono_intersect(B, mid, Bh, + A, Al, Ah, + ret, tol, depth+1); +} + +Crossings mono_intersect(Curve const & A, Interval const &Ad, + Curve const & B, Interval const &Bd) { + Crossings ret; + mono_intersect(A, Ad.min(), Ad.max(), B, Bd.min(), Bd.max(), ret); + return ret; +} + /** * Takes two paths and time ranges on them, with the invariant that the * paths are monotonic on the range. Splits A when the linear intersection @@ -327,11 +431,10 @@ void mono_pair(Path const &A, double Al, double Ah, Point A0 = A.pointAt(Al), A1 = A.pointAt(Ah), B0 = B.pointAt(Bl), B1 = B.pointAt(Bh); //inline code that this implies? (without rect/interval construction) - if(!Rect(A0, A1).intersects(Rect(B0, B1)) || A0 == A1 || B0 == B1) return; - - //Checks the general linearity of the function - //if((depth > 12) || (A.boundsLocal(Interval(Al, Ah), 1).maxExtent() < 0.1 - // && B.boundsLocal(Interval(Bl, Bh), 1).maxExtent() < 0.1)) { + Rect Ar = Rect(A0, A1), Br = Rect(B0, B1); + if(!Ar.intersects(Br) || A0 == A1 || B0 == B1) return; + + if(depth > 12 || (Ar.maxExtent() < 0.1 && Ar.maxExtent() < 0.1)) { double tA, tB, c; if(linear_intersect(A0, A1, B0, B1, tA, tB, c)) { @@ -343,7 +446,7 @@ void mono_pair(Path const &A, double Al, double Ah, ret.push_back(Crossing(tA, tB, c > 0)); return; } - //} + } if(depth > 12) return; double mid = (Bl + Bh)/2; mono_pair(B, Bl, mid, @@ -356,8 +459,10 @@ void mono_pair(Path const &A, double Al, double Ah, /** This returns the times when the x or y derivative is 0 in the curve. */ std::vector<double> curve_mono_splits(Curve const &d) { + Curve* deriv = d.derivative(); std::vector<double> rs = d.roots(0, X); append(rs, d.roots(0, Y)); + delete deriv; std::sort(rs.begin(), rs.end()); return rs; } @@ -378,17 +483,10 @@ std::vector<double> offset_doubles(std::vector<double> const &x, double offs) { std::vector<double> path_mono_splits(Path const &p) { std::vector<double> ret; if(p.empty()) return ret; - ret.push_back(0); - - Curve* deriv = p[0].derivative(); - append(ret, curve_mono_splits(*deriv)); - delete deriv; bool pdx=2, pdy=2; //Previous derivative direction for(unsigned i = 0; i <= p.size(); i++) { - deriv = p[i].derivative(); - std::vector<double> spl = offset_doubles(curve_mono_splits(*deriv), i); - delete deriv; + std::vector<double> spl = offset_doubles(curve_mono_splits(p[i]), i); bool dx = p[i].initialPoint()[X] > (spl.empty()? p[i].finalPoint()[X] : p.valueAt(spl.front(), X)); bool dy = p[i].initialPoint()[Y] > (spl.empty()? p[i].finalPoint()[Y] : |
