Esempio n. 1
0
GEODE_NEVER_INLINE static void add_constraint_edges(MutableTriangleTopology& mesh, RawField<const EV,VertexId> X,
                                                    RawArray<const Vector<int,2>> edges, const bool validate) {
  if (!edges.size())
    return;
  IntervalScope scope;
  Hashtable<Vector<VertexId,2>> constrained;
  Array<VertexId> left_cavity, right_cavity; // List of vertices for both cavities
  const auto random = new_<Random>(key+7);
  for (int i=0;i<edges.size();i++) {
    // Randomly choose an edge to ensure optimal time complexity
    const auto edge = edges[int(random_permute(edges.size(),key+5,i))].sorted();
    auto v0 = VertexId(edge.x),
         v1 = VertexId(edge.y);
    const auto vs = vec(v0,v1);
    GEODE_ASSERT(mesh.valid(v0) && mesh.valid(v1));

    {
      // Check if the edge already exists in the triangulation.  To ensure optimal complexity,
      // we loop around both vertices interleaved so that our time is O(min(degree(v0),degree(v1))).
      const auto s0 = mesh.halfedge(v0),
                 s1 = mesh.halfedge(v1);
      {
        auto e0 = s0,
             e1 = s1;
        do {
          if (mesh.dst(e0)==v1 || mesh.dst(e1)==v0)
            goto success; // The edge already exists, so there's nothing to be done.
          e0 = mesh.left(e0);
          e1 = mesh.left(e1);
        } while (e0!=s0 && e1!=s1);
      }

      // Find a triangle touching v0 or v1 containing part of the v0-v1 segment.
      // As above, we loop around both vertices interleaved.
      auto e0 = s0;
      {
        auto e1 = s1;
        if (mesh.is_boundary(e0)) e0 = mesh.left(e0);
        if (mesh.is_boundary(e1)) e1 = mesh.left(e1);
        const auto x0 = Perturbed2(v0.id,X[v0]),
                   x1 = Perturbed2(v1.id,X[v1]);
        const auto e0d = mesh.dst(e0),
                   e1d = mesh.dst(e1);
        bool e0o = triangle_oriented(x0,Perturbed2(e0d.id,X[e0d]),x1),
             e1o = triangle_oriented(x1,Perturbed2(e1d.id,X[e1d]),x0);
        for (;;) { // No need to check for an end condition, since we're guaranteed to terminate
          const auto n0 = mesh.left(e0),
                     n1 = mesh.left(e1);
          const auto n0d = mesh.dst(n0),
                     n1d = mesh.dst(n1);
          const bool n0o = triangle_oriented(x0,Perturbed2(n0d.id,X[n0d]),x1),
                     n1o = triangle_oriented(x1,Perturbed2(n1d.id,X[n1d]),x0);
          if (e0o && !n0o)
            break;
          if (e1o && !n1o) {
            // Swap v0 with v1 and e0 with e1 so that our ray starts at v0
            swap(v0,v1);
            swap(e0,e1);
            break;
          }
          e0 = n0;
          e1 = n1;
          e0o = n0o;
          e1o = n1o;
        }
      }

      // If we only need to walk one step, the retriangulation is a single edge flip
      auto cut = mesh.reverse(mesh.next(e0));
      if (mesh.dst(mesh.next(cut))==v1) {
        if (constrained.contains(vec(mesh.src(cut),mesh.dst(cut)).sorted()))
          throw DelaunayConstraintConflict(vec(v0.id,v1.id),vec(mesh.src(cut).id,mesh.dst(cut).id));
        cut = mesh.flip_edge(cut);
        goto success;
      }

      // Walk from v0 to v1, collecting the two cavities.
      const auto x0 = Perturbed2(v0.id,X[v0]),
                 x1 = Perturbed2(v1.id,X[v1]);
      right_cavity.copy(vec(v0,mesh.dst(cut)));
      left_cavity .copy(vec(v0,mesh.src(cut)));
      mesh.erase(mesh.face(e0));
      for (;;) {
        if (constrained.contains(vec(mesh.src(cut),mesh.dst(cut)).sorted()))
          throw DelaunayConstraintConflict(vec(v0.id,v1.id),vec(mesh.src(cut).id,mesh.dst(cut).id));
        const auto n = mesh.reverse(mesh.next(cut)),
                   p = mesh.reverse(mesh.prev(cut));
        const auto v = mesh.src(n);
        mesh.erase(mesh.face(cut));
        if (v == v1) {
          left_cavity.append(v);
          right_cavity.append(v);
          break;
        } else if (triangle_oriented(x0,x1,Perturbed2(v.id,X[v]))) {
          left_cavity.append(v);
          cut = n;
        } else {
          right_cavity.append(v);
          cut = p;
        }
      }

      // Retriangulate both cavities
      left_cavity.reverse();
      cavity_delaunay(mesh,X,left_cavity,random),
      cavity_delaunay(mesh,X,right_cavity,random);
    }
    success:
    constrained.set(vs);
  }

  // If desired, check that the final mesh is constrained Delaunay
  if (validate)
    assert_delaunay("constrained delaunay validate: ",mesh,X,constrained);
}
Esempio n. 2
0
// Delaunay retriangulate a triangle fan
static void chew_fan(MutableTriangleTopology& parent_mesh, RawField<const Perturbed2,VertexId> X,
                     const VertexId u, RawArray<HalfedgeId> fan, Random& random) {
  chew_fan_count_ += 1;
#ifndef NDEBUG
  for (const auto e : fan)
    assert(parent_mesh.opposite(e)==u);
  for (int i=0;i<fan.size()-1;i++)
    GEODE_ASSERT(parent_mesh.src(fan[i])==parent_mesh.dst(fan[i+1]));
#endif
  const int n = fan.size();
  if (n < 2)
    return;
  chew_fan_count_ += 1024*n;

  // Collect vertices
  const Field<VertexId,VertexId> vertices(n+2,uninit);
  vertices.flat[0] = u;
  vertices.flat[1] = parent_mesh.src(fan[n-1]);
  for (int i=0;i<n;i++)
    vertices.flat[i+2] = parent_mesh.dst(fan[n-1-i]);

  // Delete original vertices
  for (const auto e : fan)
    parent_mesh.erase(parent_mesh.face(e));

  // Make the vertices into a doubly linked list
  const Field<VertexId,VertexId> prev(n+2,uninit),
                                 next(n+2,uninit);
  prev.flat[0].id = n+1;
  next.flat[n+1].id = 0;
  for (int i=0;i<n+1;i++) {
    prev.flat[i+1].id = i;
    next.flat[i].id = i+1;
  }

  // Randomly shuffle the vertices, then pulling elements off the linked list in reverse order of our final shuffle.
  const Array<VertexId> pi(n+2,uninit);
  for (int i=0;i<n+2;i++)
    pi[i].id = i;
  random.shuffle(pi);
  for (int i=n+1;i>=0;i--) {
    const auto j = pi[i];
    prev[next[j]] = prev[j];
    next[prev[j]] = next[j];
  }

  // Make a new singleton mesh
  const auto mesh = new_<MutableTriangleTopology>();
  mesh->add_vertices(n+2);
  small_sort(pi[0],pi[1],pi[2]);
  mesh->add_face(vec(pi[0],pi[1],pi[2]));

  // Insert remaining vertices
  Array<HalfedgeId> work;
  for (int i=3;i<n+2;i++) {
    const auto j = pi[i];
    const auto f = mesh->add_face(vec(j,next[j],prev[j]));
    work.append(mesh->reverse(mesh->opposite(f,j)));
    while (work.size()) {
      auto e = work.pop();
      if (   !mesh->is_boundary(e)
          && incircle(X[vertices[mesh->src(e)]],
                      X[vertices[mesh->dst(e)]],
                      X[vertices[mesh->opposite(e)]],
                      X[vertices[mesh->opposite(mesh->reverse(e))]])) {
        work.append(mesh->reverse(mesh->next(e)));
        work.append(mesh->reverse(mesh->prev(e)));
        e = mesh->unsafe_flip_edge(e);
      }
    }
  }

  // Copy triangles back to parent
  for (const auto f : mesh->faces()) {
    const auto vs = mesh->vertices(f);
    parent_mesh.add_face(vec(vertices[vs.x],vertices[vs.y],vertices[vs.z]));
  }
}