コード例 #1
0
        void PoseGraph::clearGraph (void)
        {
            /// Clear out data associated with the graph
            _graph.clear();
            _nextVertexId = VertexId(1);
            _nextEdgeId = EdgeId(1);
            _vertexMap.clear();
            _edgeMap.clear();
            //graphCleared();

        }
コード例 #2
0
ファイル: delaunay.cpp プロジェクト: omco/geode
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);
}
コード例 #3
0
ファイル: delaunay.cpp プロジェクト: omco/geode
// Retriangulate a cavity formed when a constraint edge is inserted, following Shewchuck and Brown.
// The cavity is defined by a counterclockwise list of vertices v[0] to v[m-1] as in Shewchuck and Brown, Figure 5.
static void cavity_delaunay(MutableTriangleTopology& parent_mesh, RawField<const EV,VertexId> X,
                            RawArray<const VertexId> cavity, Random& random) {
  // Since the algorithm generates meshes which may be inconsistent with the outer mesh, and the cavity
  // array may have duplicate vertices, we use a temporary mesh and then copy the triangles over when done.
  // In the temporary, vertices are indexed consecutively from 0 to m-1.
  const int m = cavity.size();
  assert(m >= 3);
  const auto mesh = new_<MutableTriangleTopology>();
  Field<Perturbed2,VertexId> Xc(m,uninit);
  for (const int i : range(m))
    Xc.flat[i] = Perturbed2(cavity[i].id,X[cavity[i]]);
  mesh->add_vertices(m);
  const auto xs = Xc.flat[0],
             xe = Xc.flat[m-1];

  // Set up data structures for prev, next, pi in the paper
  const Field<VertexId,VertexId> prev(m,uninit),
                                 next(m,uninit);
  for (int i=0;i<m-1;i++) {
    next.flat[i] = VertexId(i+1);
    prev.flat[i+1] = VertexId(i);
  }
  const Array<VertexId> pi_(m-2,uninit);
  for (int i=1;i<=m-2;i++)
    pi_[i-1] = VertexId(i);
  #define PI(i) pi_[(i)-1]

  // Randomly shuffle [1,m-2], subject to vertices closer to xs-xe than both their neighbors occurring later
  for (int i=m-2;i>=2;i--) {
    int j;
    for (;;) {
      j = random.uniform<int>(0,i)+1;
      const auto pj = PI(j);
      if (!(   segment_directions_oriented(xe,xs,Xc[pj],Xc[prev[pj]])
            && segment_directions_oriented(xe,xs,Xc[pj],Xc[next[pj]])))
        break;
    }
    swap(PI(i),PI(j));
    // Remove PI(i) from the list
    const auto pi = PI(i);
    next[prev[pi]] = next[pi];
    prev[next[pi]] = prev[pi];
  }

  // Add the first triangle
  mesh->add_face(vec(VertexId(0),PI(1),VertexId(m-1)));

  // Add remaining triangles, flipping to ensure Delaunay
  const Field<bool,VertexId> marked(m);
  Array<HalfedgeId> fan;
  bool used_chew = false;
  for (int i=2;i<m-1;i++) {
    const auto pi = PI(i);
    insert_cavity_vertex(mesh,Xc,marked,pi,next[pi],prev[pi]);
    if (marked[pi]) {
      used_chew = true;
      marked[pi] = false;
      // Retriangulate the fans of triangles that have all three vertices marked
      auto e = mesh->reverse(mesh->halfedge(pi));
      auto v = mesh->src(e);
      bool mv = marked[v];
      marked[v] = false;
      fan.clear();
      do {
        const auto h = mesh->prev(e);
        e = mesh->reverse(mesh->next(e));
        v = mesh->src(e);
        const bool mv2 = marked[v];
        marked[v] = false;
        if (mv) {
          if (mv2)
            fan.append(h);
          if (!mv2 || mesh->is_boundary(e)) {
            chew_fan(mesh,Xc,pi,fan,random);
            fan.clear();
          }
        }
        mv = mv2;
      } while (!mesh->is_boundary(e));
    }
  }
  #undef PI

  // If we ran Chew's algorithm, validate the output.  I haven't tested this
  // case enough to be confident of its correctness.
  if (used_chew)
    assert_delaunay("Failure in extreme special case.  If this triggers, please email [email protected]: ",
                    mesh,Xc,Tuple<>(),false,false);

  // Copy triangles from temporary mesh to real mesh
  for (const auto f : mesh->faces()) {
    const auto v = mesh->vertices(f);
    parent_mesh.add_face(vec(cavity[v.x.id],cavity[v.y.id],cavity[v.z.id]));
  }
}
コード例 #4
0
ファイル: delaunay.cpp プロジェクト: omco/geode
// This routine assumes the sentinel points have already been added, and processes points in order
GEODE_NEVER_INLINE static Ref<MutableTriangleTopology> deterministic_exact_delaunay(RawField<const Perturbed2,VertexId> X, const bool validate) {

  const int n = X.size()-3;
  const auto mesh = new_<MutableTriangleTopology>();
  IntervalScope scope;

  // Initialize the mesh to a Delaunay triangle containing the sentinels at infinity.
  mesh->add_vertices(n+3);
  mesh->add_face(vec(VertexId(n+0),VertexId(n+1),VertexId(n+2)));
  if (self_check) {
    mesh->assert_consistent();
    assert_delaunay("self check 0: ",mesh,X);
  }

  // The randomized incremental construction algorithm uses the history of the triangles
  // as the acceleration structure.  Specifically, we maintain a BSP tree where the nodes
  // are edge tests (are we only the left or right of an edge) and the leaves are triangles.
  // There are two operations that modify this tree:
  //
  // 1. Split: A face is split into three by the insertion of an interior vertex.
  // 2. Flip: An edge is flipped, turning two triangles into two different triangles.
  //
  // The three starts out empty, since one triangle needs zero tests.
  Array<Node> bsp; // All BSP nodes including leaves
  bsp.preallocate(3*n); // The minimum number of possible BSP nodes
  Field<Vector<int,2>,FaceId> face_to_bsp; // Map from FaceId to up to two BSP leaf points (2*node+(right?0:1))
  face_to_bsp.flat.preallocate(2*n+1); // The exact maximum number of faces
  face_to_bsp.flat.append_assuming_enough_space(vec(0,-1)); // By the time we call set_links, node 0 will be valid
  if (self_check)
    check_bsp(*mesh,bsp,face_to_bsp,X);

  // Allocate a stack to simulate recursion when flipping non-Delaunay edges.
  // Invariant: if edge is on the stack, the other edges of face(edge) are Delaunay.
  // Since halfedge ids change during edge flips in a corner mesh, we store half edges as directed vertex pairs.
  Array<Tuple<HalfedgeId,Vector<VertexId,2>>> stack;
  stack.preallocate(8);

  // Insert all vertices into the mesh in random order, maintaining the Delaunay property
  for (const auto i : range(n)) {
    const VertexId v(i);
    check_interrupts();

    // Search through the BSP tree to find the containing triangle
    const auto f0 = bsp_search(bsp,X,v);
    const auto vs = mesh->vertices(f0);

    // Split the face by inserting the new vertex and update the BSP tree accordingly.
    mesh->split_face(f0,v);
    const auto e0 = mesh->halfedge(v),
               e1 = mesh->left(e0),
               e2 = mesh->right(e0);
    assert(mesh->dst(e0)==vs.x);
    const auto f1 = mesh->face(e1),
               f2 = mesh->face(e2);
    const int base = bsp.extend(3,uninit);
    set_links(bsp,face_to_bsp[f0],base);
    bsp[base+0] = Node(vec(v,vs.x),base+2,base+1);
    bsp[base+1] = Node(vec(v,vs.y),~f0.id,~f1.id);
    bsp[base+2] = Node(vec(v,vs.z),~f1.id,~f2.id);
    face_to_bsp[f0] = vec(2*(base+1)+0,-1);
    face_to_bsp.flat.append_assuming_enough_space(vec(2*(base+1)+1,2*(base+2)+0));
    face_to_bsp.flat.append_assuming_enough_space(vec(2*(base+2)+1,-1));
    if (self_check) {
      mesh->assert_consistent();
      check_bsp(*mesh,bsp,face_to_bsp,X);
    }

    // Fix all non-Delaunay edges
    stack.copy(vec(tuple(mesh->next(e0),vec(vs.x,vs.y)),
                   tuple(mesh->next(e1),vec(vs.y,vs.z)),
                   tuple(mesh->next(e2),vec(vs.z,vs.x))));
    if (self_check)
      assert_delaunay("self check 1: ",mesh,X,Tuple<>(),true);
    while (stack.size()) {
      const auto evs = stack.pop();
      auto e = mesh->vertices(evs.x)==evs.y ? evs.x : mesh->halfedge(evs.y.x,evs.y.y);
      if (e.valid() && !is_delaunay(*mesh,X,e)) {
        // Our mesh is linearly embedded in the plane, so edge flips are always safe
        assert(mesh->is_flip_safe(e));
        e = mesh->unsafe_flip_edge(e);
        GEODE_ASSERT(is_delaunay(*mesh,X,e));
        // Update the BSP tree for the triangle flip
        const auto f0 = mesh->face(e),
                   f1 = mesh->face(mesh->reverse(e));
        const int node = bsp.append(Node(mesh->vertices(e),~f1.id,~f0.id));
        set_links(bsp,face_to_bsp[f0],node);
        set_links(bsp,face_to_bsp[f1],node);
        face_to_bsp[f0] = vec(2*node+1,-1);
        face_to_bsp[f1] = vec(2*node+0,-1);
        if (self_check) {
          mesh->assert_consistent();
          check_bsp(*mesh,bsp,face_to_bsp,X);
        }
        // Recurse to successor edges to e
        const auto e0 = mesh->next(e),
                   e1 = mesh->prev(mesh->reverse(e));
        stack.extend(vec(tuple(e0,mesh->vertices(e0)),
                         tuple(e1,mesh->vertices(e1))));
        if (self_check)
          assert_delaunay("self check 2: ",mesh,X,Tuple<>(),true);
      }
    }
    if (self_check) {
      mesh->assert_consistent();
      assert_delaunay("self check 3: ",mesh,X);
    }
  }

  // Remove sentinels
  for (int i=0;i<3;i++)
    mesh->erase_last_vertex_with_reordering();

  // If desired, check that the final mesh is Delaunay
  if (validate)
    assert_delaunay("delaunay validate: ",mesh,X);

  // Return the mesh with the sentinels removed
  return mesh;
}