void find_intersections(std::vector<std::pair<double, double> > &xs, D2<SBasis> const & A, D2<SBasis> const & B) { vector<Point> BezA, BezB; sbasis_to_bezier(BezA, A); sbasis_to_bezier(BezB, B); xs.clear(); find_intersections_bezier_clipping(xs, BezA, BezB); }
/** * Compute the Hausdorf distance from A to B only. */ double hausdorfl(D2<SBasis>& A, D2<SBasis> const& B, double m_precision, double *a_t, double* b_t) { std::vector< std::pair<double, double> > xs; std::vector<Point> Az, Bz; sbasis_to_bezier (Az, A); sbasis_to_bezier (Bz, B); find_collinear_normal(xs, Az, Bz, m_precision); double h_dist = 0, h_a_t = 0, h_b_t = 0; double dist = 0; Point Ax = A.at0(); double t = Geom::nearest_point(Ax, B); dist = Geom::distance(Ax, B(t)); if (dist > h_dist) { h_a_t = 0; h_b_t = t; h_dist = dist; } Ax = A.at1(); t = Geom::nearest_point(Ax, B); dist = Geom::distance(Ax, B(t)); if (dist > h_dist) { h_a_t = 1; h_b_t = t; h_dist = dist; } for (size_t i = 0; i < xs.size(); ++i) { Point At = A(xs[i].first); Point Bu = B(xs[i].second); double distAtBu = Geom::distance(At, Bu); t = Geom::nearest_point(At, B); dist = Geom::distance(At, B(t)); //FIXME: we might miss it due to floating point precision... if (dist >= distAtBu-.1 && distAtBu > h_dist) { h_a_t = xs[i].first; h_b_t = xs[i].second; h_dist = distAtBu; } } if(a_t) *a_t = h_a_t; if(b_t) *b_t = h_b_t; return h_dist; }
void Curve::feed(PathSink &sink, bool moveto_initial) const { std::vector<Point> pts; sbasis_to_bezier(pts, toSBasis(), 2); //TODO: use something better! if (moveto_initial) { sink.moveTo(initialPoint()); } sink.curveTo(pts[0], pts[1], pts[2]); }
/** Changes the basis of p to be Bernstein. \param p the D2 Symmetric basis polynomial \returns the D2 Bernstein basis polynomial sz is always the polynomial degree, i. e. the Bezier order */ void sbasis_to_bezier (std::vector<Point> & bz, D2<SBasis> const& sb, size_t sz) { Bezier bzx, bzy; if(sz == 0) { sz = std::max(sb[X].size(), sb[Y].size())*2; } sbasis_to_bezier(bzx, sb[X], sz); sbasis_to_bezier(bzy, sb[Y], sz); assert(bzx.size() == bzy.size()); size_t n = (bzx.size() >= bzy.size()) ? bzx.size() : bzy.size(); bz.resize(n, Point(0,0)); for (size_t i = 0; i < bzx.size(); ++i) { bz[i][X] = bzx[i]; } for (size_t i = 0; i < bzy.size(); ++i) { bz[i][Y] = bzy[i]; } }
void find_self_intersections(std::vector<std::pair<double, double> > &xs, D2<SBasis> const & A) { vector<double> dr = roots(derivative(A[X])); { vector<double> dyr = roots(derivative(A[Y])); dr.insert(dr.begin(), dyr.begin(), dyr.end()); } dr.push_back(0); dr.push_back(1); // We want to be sure that we have no empty segments sort(dr.begin(), dr.end()); vector<double>::iterator new_end = unique(dr.begin(), dr.end()); dr.resize( new_end - dr.begin() ); vector<vector<Point> > pieces; { vector<Point> in, l, r; sbasis_to_bezier(in, A); for(unsigned i = 0; i < dr.size()-1; i++) { split(in, (dr[i+1]-dr[i]) / (1 - dr[i]), l, r); pieces.push_back(l); in = r; } } for(unsigned i = 0; i < dr.size()-1; i++) { for(unsigned j = i+1; j < dr.size()-1; j++) { std::vector<std::pair<double, double> > section; find_intersections( section, pieces[i], pieces[j]); for(unsigned k = 0; k < section.size(); k++) { double l = section[k].first; double r = section[k].second; // XXX: This condition will prune out false positives, but it might create some false negatives. Todo: Confirm it is correct. if(j == i+1) //if((l == 1) && (r == 0)) if( ( l > 1-1e-4 ) && (r < 1e-4) )//FIXME: what precision should be used here??? continue; xs.push_back(std::make_pair((1-l)*dr[i] + l*dr[i+1], (1-r)*dr[j] + r*dr[j+1])); } } } // Because i is in order, xs should be roughly already in order? //sort(xs.begin(), xs.end()); //unique(xs.begin(), xs.end()); }
/** Make a path from a d2 sbasis. \param p the d2 Symmetric basis polynomial \returns a Path If only_cubicbeziers is true, the resulting path may only contain CubicBezier curves. */ void build_from_sbasis(Geom::PathBuilder &pb, D2<SBasis> const &B, double tol, bool only_cubicbeziers) { if (!B.isFinite()) { THROW_EXCEPTION("assertion failed: B.isFinite()"); } if(tail_error(B, 2) < tol || sbasis_size(B) == 2) { // nearly cubic enough if( !only_cubicbeziers && (sbasis_size(B) <= 1) ) { pb.lineTo(B.at1()); } else { std::vector<Geom::Point> bez; sbasis_to_bezier(bez, B, 4); pb.curveTo(bez[1], bez[2], bez[3]); } } else { build_from_sbasis(pb, compose(B, Linear(0, 0.5)), tol, only_cubicbeziers); build_from_sbasis(pb, compose(B, Linear(0.5, 1)), tol, only_cubicbeziers); } }
/* * This version works by inverting a reasonable upper bound on the error term after subdividing the * curve at $a$. We keep biting off pieces until there is no more curve left. * * Derivation: The tail of the power series is $a_ks^k + a_{k+1}s^{k+1} + \ldots = e$. A * subdivision at $a$ results in a tail error of $e*A^k, A = (1-a)a$. Let this be the desired * tolerance tol $= e*A^k$ and invert getting $A = e^{1/k}$ and $a = 1/2 - \sqrt{1/4 - A}$ */ void subpath_from_sbasis_incremental(Geom::OldPathSetBuilder &pb, D2<SBasis> B, double tol, bool initial) { const unsigned k = 2; // cubic bezier double te = B.tail_error(k); assert(B[0].IS_FINITE()); assert(B[1].IS_FINITE()); //std::cout << "tol = " << tol << std::endl; while(1) { double A = std::sqrt(tol/te); // pow(te, 1./k) double a = A; if(A < 1) { A = std::min(A, 0.25); a = 0.5 - std::sqrt(0.25 - A); // quadratic formula if(a > 1) a = 1; // clamp to the end of the segment } else a = 1; assert(a > 0); //std::cout << "te = " << te << std::endl; //std::cout << "A = " << A << "; a=" << a << std::endl; D2<SBasis> Bs = compose(B, Linear(0, a)); assert(Bs.tail_error(k)); std::vector<Geom::Point> bez = sbasis_to_bezier(Bs, 2); reverse(bez.begin(), bez.end()); if (initial) { pb.start_subpath(bez[0]); initial = false; } pb.push_cubic(bez[1], bez[2], bez[3]); // move to next piece of curve if(a >= 1) break; B = compose(B, Linear(a, 1)); te = B.tail_error(k); } }
std::vector<double> roots(SBasis const & s) { if(s.size() == 0) return std::vector<double>(); return sbasis_to_bezier(s).roots(); }