Nested<CircleArc> canonicalize_circle_arcs(Nested<const CircleArc> polys) { // Find the minimal point in each polygon under lexicographic order Array<int> mins(polys.size()); for (int p=0;p<polys.size();p++) { const auto poly = polys[p]; for (int i=1;i<poly.size();i++) if (lex_less(poly[i].x,poly[mins[p]].x)) mins[p] = i; } // Sort the polygons struct Order { Nested<const CircleArc> polys; RawArray<const int> mins; Order(Nested<const CircleArc> polys, RawArray<const int> mins) : polys(polys), mins(mins) {} bool operator()(int i,int j) const { return lex_less(polys(i,mins[i]).x,polys(j,mins[j]).x); } }; Array<int> order = arange(polys.size()).copy(); sort(order,Order(polys,mins)); // Copy into new array Nested<CircleArc> new_polys(polys.sizes().subset(order).copy(),uninit); for (int p=0;p<polys.size();p++) { const int base = mins[order[p]]; const auto poly = polys[order[p]]; const auto new_poly = new_polys[p]; for (int i=0;i<poly.size();i++) new_poly[i] = poly[(i+base)%poly.size()]; } return new_polys; }
Ref<SparseMatrix> TriangleSubdivision::loop_matrix() const { if (loop_matrix_) return ref(loop_matrix_); // Build matrix of Loop subdivision weights Hashtable<Vector<int,2>,T> A; int offset = coarse_mesh->nodes(); RawArray<const Vector<int,2> > segments = coarse_mesh->segment_soup()->elements; Nested<const int> neighbors = coarse_mesh->sorted_neighbors(); Nested<const int> boundary_neighbors = coarse_mesh->boundary_mesh()->neighbors(); unordered_set<int> corners_set(corners.begin(),corners.end()); // Fill in node weights for (int i=0;i<offset;i++){ if (!neighbors.valid(i) || !neighbors.size(i) || corners_set.count(i) || (boundary_neighbors.valid(i) && boundary_neighbors.size(i) && boundary_neighbors.size(i)!=2)) A.set(vec(i,i),1); else if (boundary_neighbors.valid(i) && boundary_neighbors.size(i)==2) { // Regular boundary node A.set(vec(i,i),(T).75); for (int a=0;a<2;a++) A.set(vec(i,boundary_neighbors(i,a)),(T).125); } else { // Interior node RawArray<const int> ni = neighbors[i]; T alpha = new_loop_alpha(ni.size()); A.set(vec(i,i),alpha); T other = (1-alpha)/ni.size(); for (int j : ni) A.set(vec(i,j),other); } } // Fill in edge values for (int s=0;s<segments.size();s++) { int i = offset+s; Vector<int,2> e = segments[s]; RawArray<const int> n[2] = {neighbors.valid(e[0])?neighbors[e[0]]:RawArray<const int>(), neighbors.valid(e[1])?neighbors[e[1]]:RawArray<const int>()}; if (boundary_neighbors.valid(e[0]) && boundary_neighbors.valid(e[1]) && boundary_neighbors.size(e[0]) && boundary_neighbors.size(e[1]) && boundary_neighbors[e[0]].contains(e[1])) // Boundary edge for (int a=0;a<2;a++) A.set(vec(i,e[a]),(T).5); else if (n[0].size()==6 && n[1].size()==6) { // Edge between regular vertices int j = n[0].find(e[1]); int c[2] = {n[0][(j-1+n[0].size())%n[0].size()], n[0][(j+1)%n[0].size()]}; for (int a=0;a<2;a++) { A.set(vec(i,e[a]),(T).375); A.set(vec(i,c[a]),(T).125); } } else { // Edge between one or two irregular vertices T factor = n[0].size()!=6 && n[1].size()!=6 ?.5:1; for (int k=0;k<2;k++) if (n[k].size()!=6) { A[vec(i,e[k])] += factor*(1-new_loop_beta(n[k].size())); int start = n[k].find(e[1-k]); for (int j=0;j<n[k].size();j++) A[vec(i,n[k][(start+j)%n[k].size()])] += factor*new_loop_weight(n[k].size(),j); } } } // Finalize construction loop_matrix_ = new_<SparseMatrix>(A,vec(offset+segments.size(),offset)); return ref(loop_matrix_); }
static void random_circle_quantize_test(int seed) { auto r = new_<Random>(seed); { // First check that we can split without hitting any asserts const auto sizes = vec(1.e-3,1.e1,1.e3,1.e7); Nested<CircleArc, false> arcs; arcs.append(make_circle(Vec2(0,0),Vec2(1,0))); for(const auto& s : sizes) { for(int i = 0; i < 200; ++i) { arcs.append(make_circle(s*r->unit_ball<Vec2>(),s*r->unit_ball<Vec2>())); } } // Take the union and make sure we don't hit any asserts circle_arc_union(arcs); } { // Build a bunch of arcs that don't touch const auto log_options = vec(1.e-3,1.e-1,1.e1,1.e3); const auto max_bounds = Box<Vec2>(Vec2(0.,0.)).thickened(1.e1 * log_options.max()); const real spacing = 1e-5*max_bounds.sizes().max(); const real max_x = max_bounds.max.x; real curr_x = max_bounds.min.x; Nested<CircleArc, false> arcs; for(int i = 0; i < 50; ++i) { const real remaining = max_x - curr_x; if(remaining < spacing) break; const real log_choice = log_options[r->uniform<int>(0, log_options.size())]; real next_r = r->uniform<real>(0., min(log_choice, remaining)); arcs.append(make_circle(Vec2(curr_x, 0.),Vec2(curr_x+next_r, 0.))); curr_x += next_r + spacing; } // Take the union and make sure we don't hit any asserts auto unioned = circle_arc_union(arcs); // If range of sizes is very large, some arcs could be filtered out if they are smaller than quantization threshold... GEODE_ASSERT(unioned.size() <= arcs.size()); } }
Nested<EV> exact_split_polygons(Nested<const EV> polys, const int depth) { IntervalScope scope; RawArray<const EV> X = polys.flat; // We index segments by the index of their first point in X. For convenience, we make an array to keep track of wraparounds. Array<int> next = (arange(X.size())+1).copy(); for (int i=0;i<polys.size();i++) { GEODE_ASSERT(polys.size(i)>=3,"Degenerate polygons are not allowed"); next[polys.offsets[i+1]-1] = polys.offsets[i]; } // Compute all nontrivial intersections between segments struct Pairs { const BoxTree<EV>& tree; RawArray<const int> next; RawArray<const EV> X; Array<Vector<int,2>> pairs; Pairs(const BoxTree<EV>& tree, RawArray<const int> next, RawArray<const EV> X) : tree(tree), next(next), X(X) {} bool cull(const int n) const { return false; } bool cull(const int n0, const int box1) const { return false; } void leaf(const int n) const { assert(tree.prims(n).size()==1); } void leaf(const int n0, const int n1) { assert(tree.prims(n0).size()==1 && tree.prims(n1).size()==1); const int i0 = tree.prims(n0)[0], i1 = next[i0], j0 = tree.prims(n1)[0], j1 = next[j0]; if (!(i0==j0 || i0==j1 || i1==j0 || i1==j1)) { const auto a0 = Perturbed2(i0,X[i0]), a1 = Perturbed2(i1,X[i1]), b0 = Perturbed2(j0,X[j0]), b1 = Perturbed2(j1,X[j1]); if (segments_intersect(a0,a1,b0,b1)) pairs.append(vec(i0,j0)); } } }; const auto tree = new_<BoxTree<EV>>(segment_boxes(next,X),1); Pairs pairs(tree,next,X); double_traverse(*tree,pairs); // Group intersections by segment. Each pair is added twice: once for each order. Array<int> counts(X.size()); for (auto pair : pairs.pairs) { counts[pair.x]++; counts[pair.y]++; } Nested<int> others(counts,uninit); for (auto pair : pairs.pairs) { others(pair.x,--counts[pair.x]) = pair.y; others(pair.y,--counts[pair.y]) = pair.x; } pairs.pairs.clean_memory(); counts.clean_memory(); // Walk all original polygons, recording which subsegments occur in the final result Hashtable<Vector<int,2>,int> graph; // If (i,j) -> k, the output contains the portion of segment j from ij to jk for (const int p : range(polys.size())) { const auto poly = range(polys.offsets[p],polys.offsets[p+1]); // Compute the depth of the first point in the polygon by firing a ray along the positive x axis. struct Depth { const BoxTree<EV>& tree; RawArray<const int> next; RawArray<const EV> X; const Perturbed2 start; int depth; Depth(const BoxTree<EV>& tree, RawArray<const int> next, RawArray<const EV> X, const int prev, const int i) : tree(tree), next(next), X(X) , start(i,X[i]) // If we intersect no other segments, the depth depends on the orientation of direction = (1,0) relative to segments prev and i , depth(-!local_outwards_x_axis(Perturbed2(prev,X[prev]),start,Perturbed2(next[i],X[next[i]]))) {} bool cull(const int n) const { const auto box = tree.boxes(n); return box.max.x<start.value().x || box.max.y<start.value().y || box.min.y>start.value().y; } void leaf(const int n) { assert(tree.prims(n).size()==1); const int i0 = tree.prims(n)[0], i1 = next[i0]; if (start.seed()!=i0 && start.seed()!=i1) { const auto a0 = Perturbed2(i0,X[i0]), a1 = Perturbed2(i1,X[i1]); const bool above0 = upwards(start,a0), above1 = upwards(start,a1); if (above0!=above1 && above1==triangle_oriented(a0,a1,start)) depth += above1 ? 1 : -1; } } }; Depth ray(tree,next,X,poly.back(),poly[0]); single_traverse(*tree,ray); // Walk around the polygon, recording all subsegments at the desired depth int delta = ray.depth-depth; int prev = poly.back(); for (const int i : poly) { const int j = next[i]; const Vector<Perturbed2,2> segment(Perturbed2(i,X[i]),Perturbed2(j,X[j])); const auto other = others[i]; // Sort intersections along this segment if (other.size() > 1) { struct PairOrder { RawArray<const int> next; RawArray<const EV> X; const Vector<Perturbed2,2> segment; PairOrder(RawArray<const int> next, RawArray<const EV> X, const Vector<Perturbed2,2>& segment) : next(next), X(X), segment(segment) {} bool operator()(const int j, const int k) const { if (j==k) return false; const int jn = next[j], kn = next[k]; return segment_intersections_ordered(segment.x,segment.y, Perturbed2(j,X[j]),Perturbed2(jn,X[jn]), Perturbed2(k,X[k]),Perturbed2(kn,X[kn])); } }; sort(other,PairOrder(next,X,segment)); } // Walk through each intersection of this segment, updating delta as we go and remembering the subsegment if it has the right depth for (const int o : other) { if (!delta) graph.set(vec(prev,i),o); const int on = next[o]; delta += segment_directions_oriented(segment.x,segment.y,Perturbed2(o,X[o]),Perturbed2(on,X[on])) ? -1 : 1; prev = o; } if (!delta) graph.set(vec(prev,i),next[i]); // Advance to the next segment prev = i; } } // Walk the graph to produce output polygons Hashtable<Vector<int,2>> seen; Nested<EV,false> output; for (const auto& start : graph) if (seen.set(start.x)) { auto ij = start.x; for (;;) { const int i = ij.x, j = ij.y, in = next[i], jn = next[j]; output.flat.append(j==next[i] ? X[j] : segment_segment_intersection(Perturbed2(i,X[i]),Perturbed2(in,X[in]),Perturbed2(j,X[j]),Perturbed2(jn,X[jn]))); ij = vec(j,graph.get(ij)); if (ij == start.x) break; seen.set(ij); } output.offsets.append(output.flat.size()); } return output; }