Crossings self_crossings(Path const &p) { Crossings ret; std::vector<std::vector<unsigned> > cull = sweep_bounds(bounds(p)); for(unsigned i = 0; i < cull.size(); i++) { Crossings res = curve_self_crossings(p[i]); offset_crossings(res, i, i); append(ret, res); for(unsigned jx = 0; jx < cull[i].size(); jx++) { unsigned j = cull[i][jx]; res.clear(); pair_intersect(p[i], 0, 1, p[j], 0, 1, res); //if(fabs(int(i)-j) == 1 || fabs(int(i)-j) == p.size()-1) { Crossings res2; for(unsigned k = 0; k < res.size(); k++) { if(res[k].ta != 0 && res[k].ta != 1 && res[k].tb != 0 && res[k].tb != 1) { res2.push_back(res[k]); } } res = res2; //} offset_crossings(res, i, j); append(ret, res); } } 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 * doesn't exist or is inaccurate. Uses the fact that it is monotonic to * do very fast local bounds. */ void mono_pair(Path const &A, double Al, double Ah, Path const &B, double Bl, double Bh, Crossings &ret, double /*tol*/, 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) 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)) { double tA, tB, c; if(linear_intersect(A0, A1, B0, B1, tA, tB, c)) { tA = tA * (Ah - Al) + Al; tB = tB * (Bh - Bl) + Bl; 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_pair(B, Bl, mid, A, Al, Ah, ret, depth+1); mono_pair(B, mid, Bh, A, Al, Ah, ret, depth+1); }
CrossingSet reverse_tb(CrossingSet const &cr, unsigned split, std::vector<double> max) { CrossingSet ret; for(unsigned i = 0; i < cr.size(); i++) { Crossings res = reverse_tb(cr[i], split, max); if(i >= split) std::reverse(res.begin(), res.end()); ret.push_back(res); } return ret; }
Crossings reverse_ta(Crossings const &cr, std::vector<double> max) { Crossings ret; for(Crossings::const_iterator i = cr.begin(); i != cr.end(); ++i) { double mx = max[i->a]; ret.push_back(Crossing(i->ta > mx+0.01 ? (1 - (i->ta - mx) + mx) : mx - i->ta, i->tb, !i->dir)); } return ret; }
Crossings reverse_tb(Crossings const &cr, unsigned split, std::vector<double> max) { Crossings ret; for(Crossings::const_iterator i = cr.begin(); i != cr.end(); ++i) { double mx = max[i->b - split]; std::cout << i->b << "\n"; ret.push_back(Crossing(i->ta, i->tb > mx+0.01 ? (1 - (i->tb - mx) + mx) : mx - i->tb, !i->dir)); } return ret; }
void merge_crossings(Crossings &a, Crossings &b, unsigned i) { Crossings n; sort_crossings(b, i); n.resize(a.size() + b.size()); std::merge(a.begin(), a.end(), b.begin(), b.end(), n.begin(), CrossingOrder(i)); a = n; }
void mark_crossings(cairo_t *cr, Shape const &a, Shape const &b) { const Regions ac = a.getContent(); Crossings c = crossings(Path(a[0]), Path(b[0])); //for(unsigned j = 0; j < cc.size(); j++) { //Crossings c = cc[j]; for(Crossings::iterator i = c.begin(); i != c.end(); ++i) { draw_cross(cr, Path(ac[i->a]).pointAt(i->ta)); cairo_stroke(cr); //draw_text(cr, ac[i->a].getBoundary().pointAt(i->ta), i->dir ? "T" : "F"); } //} }
Edges edges(Path const &p, Crossings const &cr, unsigned ix) { Edges ret = Edges(); EndPoint prev; for(unsigned i = 0; i <= cr.size(); i++) { double t = cr[i == cr.size() ? 0 : i].getTime(ix); Point pnt = p.pointAt(t); Point normal = p.pointAt(t+0.01) - pnt; normal.normalize(); std::cout << pnt << "\n"; EndPoint cur(pnt, normal, t); if(i == 0) { prev = cur; continue; } ret.push_back(Edge(prev, cur, ix, false)); ret.push_back(Edge(prev, cur, ix, true)); prev = cur; } return ret; }
/** * This is the main routine of "MonoCrosser", and implements a monotonic strategy on multiple curves. * Finds crossings between two sets of paths, yielding a CrossingSet. [0, a.size()) of the return correspond * to the sorted crossings of a with paths of b. The rest of the return, [a.size(), a.size() + b.size()], * corresponds to the sorted crossings of b with paths of a. * * This function does two sweeps, one on the bounds of each path, and after that cull, one on the curves within. * This leads to a certain amount of code complexity, however, most of that is factored into the above functions */ CrossingSet MonoCrosser::crossings(std::vector<Path> const &a, std::vector<Path> const &b) { if(b.empty()) return CrossingSet(a.size(), Crossings()); CrossingSet results(a.size() + b.size(), Crossings()); if(a.empty()) return results; std::vector<std::vector<double> > splits_a = paths_mono_splits(a), splits_b = paths_mono_splits(b); std::vector<std::vector<Rect> > bounds_a = split_bounds(a, splits_a), bounds_b = split_bounds(b, splits_b); std::vector<Rect> bounds_a_union, bounds_b_union; for(unsigned i = 0; i < bounds_a.size(); i++) bounds_a_union.push_back(union_list(bounds_a[i])); for(unsigned i = 0; i < bounds_b.size(); i++) bounds_b_union.push_back(union_list(bounds_b[i])); std::vector<std::vector<unsigned> > cull = sweep_bounds(bounds_a_union, bounds_b_union); Crossings n; for(unsigned i = 0; i < cull.size(); i++) { for(unsigned jx = 0; jx < cull[i].size(); jx++) { unsigned j = cull[i][jx]; unsigned jc = j + a.size(); Crossings res; //Sweep of the monotonic portions std::vector<std::vector<unsigned> > cull2 = sweep_bounds(bounds_a[i], bounds_b[j]); for(unsigned k = 0; k < cull2.size(); k++) { for(unsigned lx = 0; lx < cull2[k].size(); lx++) { unsigned l = cull2[k][lx]; mono_pair(a[i], splits_a[i][k-1], splits_a[i][k], b[j], splits_b[j][l-1], splits_b[j][l], res, .1); } } for(unsigned k = 0; k < res.size(); k++) { res[k].a = i; res[k].b = jc; } merge_crossings(results[i], res, i); merge_crossings(results[i], res, jc); } } return results; }
/** * This uses the local bounds functions of curves to generically intersect two. * It passes in the curves, time intervals, and keeps track of depth, while * returning the results through the Crossings parameter. */ void pair_intersect(Curve const & A, double Al, double Ah, Curve const & B, double Bl, double Bh, Crossings &ret, unsigned depth=0) { // std::cout << depth << "(" << Al << ", " << Ah << ")\n"; OptRect Ar = A.boundsLocal(Interval(Al, Ah)); if (!Ar) return; OptRect Br = B.boundsLocal(Interval(Bl, Bh)); if (!Br) return; if(! Ar->intersects(*Br)) 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)) { 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; pair_intersect(B, Bl, mid, A, Al, Ah, ret, depth+1); pair_intersect(B, mid, Bh, A, Al, Ah, ret, depth+1); }
//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); }
CrossingSet crossings_among(std::vector<Path> const &p) { CrossingSet results(p.size(), Crossings()); if(p.empty()) return results; SimpleCrosser cc; std::vector<std::vector<unsigned> > cull = sweep_bounds(bounds(p)); for(unsigned i = 0; i < cull.size(); i++) { Crossings res = self_crossings(p[i]); for(unsigned k = 0; k < res.size(); k++) { res[k].a = res[k].b = i; } merge_crossings(results[i], res, i); flip_crossings(res); merge_crossings(results[i], res, i); for(unsigned jx = 0; jx < cull[i].size(); jx++) { unsigned j = cull[i][jx]; Crossings res = cc.crossings(p[i], p[j]); for(unsigned k = 0; k < res.size(); k++) { res[k].a = i; res[k].b = j; } merge_crossings(results[i], res, i); merge_crossings(results[j], res, j); } } return results; }
void offset_crossings(Crossings &cr, double a, double b) { for(unsigned i = 0; i < cr.size(); i++) { cr[i].ta += a; cr[i].tb += b; } }
void flip_crossings(Crossings &crs) { for(unsigned i = 0; i < crs.size(); i++) crs[i] = Crossing(crs[i].tb, crs[i].ta, crs[i].b, crs[i].a, !crs[i].dir); }