Beispiel #1
0
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_);
}
Beispiel #3
0
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());
  }
}
Beispiel #4
0
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;
}