Ejemplo n.º 1
0
void SimpleInflator::generate_joint(
        const MatrixFr& pts, const VectorI& source_ids, size_t vertex_index) {
    const size_t dim = m_wire_network->get_dim();
    ConvexHullEngine::Ptr convex_hull = ConvexHullEngine::create(dim, "qhull");
    convex_hull->run(pts);

    MatrixFr vertices = convex_hull->get_vertices();
    MatrixIr faces = convex_hull->get_faces();
    VectorI  index_map = convex_hull->get_index_map();

    if (dim == 2) {
        // Need to triangulate the loop.
        const size_t num_vertices = vertices.rows();
        TriangleWrapper tri(vertices, faces);
        tri.run(std::numeric_limits<Float>::max(), false, false, false);
        vertices = tri.get_vertices();
        faces = tri.get_faces();
        assert(vertices.rows() == num_vertices);
    }

    m_vertex_list.push_back(vertices);
    const size_t num_faces = faces.rows();
    for (size_t i=0; i<num_faces; i++) {
        const auto& face = faces.row(i);
        if (dim == 3) {
            auto ori_indices = map_indices(face, index_map);
            if (belong_to_the_same_loop(ori_indices, source_ids)) continue;
        }
        m_face_list.push_back(face.array() + m_num_vertex_accumulated);
        m_face_source_list.push_back(vertex_index+1);
    }

    m_num_vertex_accumulated += vertices.rows();
}
Ejemplo n.º 2
0
GeoMeshPtr GeogramMeshUtils::raw_to_geomesh(
        const MatrixFr& vertices, const MatrixIr& faces) {
    const size_t dim = vertices.cols();
    const size_t vertex_per_face = faces.cols();
    const size_t num_vertices = vertices.rows();
    const size_t num_faces = faces.rows();

    if (vertex_per_face != 3) {
        throw NotImplementedError("Converting non-triangle mesh to "
                "Geogram mesh is not yet implemented");
    }

    auto geo_mesh = std::make_shared<GeoMesh>(dim, false);
    geo_mesh->vertices.clear();
    geo_mesh->vertices.create_vertices(num_vertices);
    geo_mesh->facets.clear();
    geo_mesh->facets.create_triangles(num_faces);

    for (size_t i=0; i<num_vertices; i++) {
        auto& p = geo_mesh->vertices.point(i);
        for (size_t j=0; j<dim; j++) {
            p[j] = vertices(i,j);
        }
    }

    for (size_t i=0; i<num_faces; i++) {
        geo_mesh->facets.set_vertex(i, 0, faces(i,0));
        geo_mesh->facets.set_vertex(i, 1, faces(i,1));
        geo_mesh->facets.set_vertex(i, 2, faces(i,2));
    }

    return geo_mesh;
}
Ejemplo n.º 3
0
    CarveMeshPtr create_mesh(const MatrixFr& vertices, const MatrixIr& faces) {
        const size_t num_vertices = vertices.rows();
        const size_t num_faces = faces.rows();

        if (vertices.cols() != 3) {
            throw NotImplementedError("Only 3D mesh is supported.");
        }
        if (faces.cols() != 3) {
            throw NotImplementedError("Only triangle mesh is supported.");
        }

        std::vector<CarveVector> points;

        for (size_t i=0; i<num_vertices; i++) {
            const auto& v = vertices.row(i);
            CarveVector p;
            p.v[0] = v[0];
            p.v[1] = v[1];
            p.v[2] = v[2];
            points.push_back(p);
        }

        std::vector<int> raw_faces;
        raw_faces.reserve(num_faces * 4);
        for (size_t i=0; i<num_faces; i++) {
            raw_faces.push_back(3);
            raw_faces.push_back(faces(i,0));
            raw_faces.push_back(faces(i,1));
            raw_faces.push_back(faces(i,2));
        }

        return CarveMeshPtr(new CarveMesh(points, num_faces, raw_faces));
    }
Ejemplo n.º 4
0
void SimpleInflator::generate_end_loops() {
    const size_t num_edges = m_wire_network->get_num_edges();

    const MatrixFr vertices = m_wire_network->get_vertices();
    const MatrixIr edges = m_wire_network->get_edges();
    const MatrixFr edge_thickness = get_edge_thickness();
    for (size_t i=0; i<num_edges; i++) {
        const VectorI& edge = edges.row(i);
        const VectorF& v1 = vertices.row(edge[0]);
        const VectorF& v2 = vertices.row(edge[1]);
        Float edge_len = (v2 - v1).norm();
        MatrixFr loop_1 = m_profile->place(v1, v2,
                m_end_loop_offsets[edge[0]],
                edge_thickness(i, 0),
                m_rel_correction, m_abs_correction, m_correction_cap,
                m_spread_const);
        assert(loop_is_valid(loop_1, v1, v2));
        MatrixFr loop_2 = m_profile->place(v1, v2,
                edge_len - m_end_loop_offsets[edge[1]],
                edge_thickness(i, 1),
                m_rel_correction, m_abs_correction, m_correction_cap,
                m_spread_const);
        assert(loop_is_valid(loop_2, v1, v2));
        m_end_loops.push_back(std::make_pair(loop_1, loop_2));
    }
}
Ejemplo n.º 5
0
SelfIntersection::SelfIntersection(
        const MatrixFr& vertices, const MatrixIr& faces)
: m_faces(faces) {
    const size_t num_vertices = vertices.rows();
    const size_t dim = vertices.cols();
    const size_t num_faces = faces.rows();
    const size_t vertex_per_face = faces.cols();

    if (dim != 3) {
        throw NotImplementedError(
                "Self intersection check only support 3D");
    }
    if (vertex_per_face != 3) {
        throw NotImplementedError(
                "Self intersection check only works with triangles");
    }

    m_points.resize(num_vertices);
    for (size_t i=0; i<num_vertices; i++) {
        m_points[i] = Point_3(
                vertices(i,0),
                vertices(i,1),
                vertices(i,2));
    }
}
Ejemplo n.º 6
0
    void extract_mesh(const C3t3& c3t3,
            MatrixFr& vertices, MatrixIr& faces, MatrixIr& voxels) {
        const Tr& tr = c3t3.triangulation();
        size_t num_vertices = tr.number_of_vertices();
        size_t num_faces = c3t3.number_of_facets_in_complex();
        size_t num_voxels = c3t3.number_of_cells_in_complex();

        vertices.resize(num_vertices, 3);
        faces.resize(num_faces, 3);
        voxels.resize(num_voxels, 4);

        std::map<Tr::Vertex_handle, int> V;
        size_t inum = 0;
        for(auto vit = tr.finite_vertices_begin();
                vit != tr.finite_vertices_end(); ++vit) {
            V[vit] = inum;
            const auto& p = vit->point();
            vertices.row(inum) = Vector3F(p.x(), p.y(), p.z()).transpose();
            assert(inum < num_vertices);
            inum++;
        }
        assert(inum == num_vertices);

        size_t face_count = 0;
        for(auto fit = c3t3.facets_in_complex_begin();
                fit != c3t3.facets_in_complex_end(); ++fit) {
            assert(face_count < num_faces);
            for (int i=0; i<3; i++) {
                if (i != fit->second) {
                    const auto& vh = (*fit).first->vertex(i);
                    assert(V.find(vh) != V.end());
                    const int vid = V[vh];
                    faces(face_count, i) = vid;
                }
            }
            face_count++;
        }
        assert(face_count == num_faces);

        size_t voxel_count = 0;
        for(auto cit = c3t3.cells_in_complex_begin() ;
                cit != c3t3.cells_in_complex_end(); ++cit ) {
            assert(voxel_count < num_voxels);
            for (int i=0; i<4; i++) {
                assert(V.find(cit->vertex(i)) != V.end());
                const size_t vid = V[cit->vertex(i)];
                voxels(voxel_count, i) = vid;
            }
            voxel_count++;
        }
        assert(voxel_count == num_voxels);
    }
Ejemplo n.º 7
0
Boundary::Ptr Boundary::extract_surface_boundary_raw(
        MatrixFr& vertices, MatrixIr& faces) {
    VectorF flattened_vertices = Eigen::Map<VectorF>(vertices.data(),
            vertices.rows() * vertices.cols());
    VectorI flattened_faces = Eigen::Map<VectorI>(faces.data(),
            faces.rows() * faces.cols());
    VectorI voxels = VectorI::Zero(0);

    MeshFactory factory;
    Mesh::Ptr mesh = factory.load_data(flattened_vertices, flattened_faces,
            voxels, vertices.cols(), faces.cols(), 0).create();

    return extract_surface_boundary(*mesh);
}
Ejemplo n.º 8
0
    void reorientate_triangles(const MatrixFr& vertices, MatrixIr& faces,
            const VectorF& n) {
        assert(vertices.cols() == 3);
        assert(faces.cols() == 3);

        const VectorI& f = faces.row(0);
        const Vector3F& v0 = vertices.row(f[0]);
        const Vector3F& v1 = vertices.row(f[1]);
        const Vector3F& v2 = vertices.row(f[2]);

        Float projected_area = (v1-v0).cross(v2-v0).dot(n);
        if (projected_area < 0) {
            faces.col(2).swap(faces.col(1));
        }
    }
Ejemplo n.º 9
0
    void extract_data(CarveMeshPtr mesh, MatrixFr& vertices, MatrixIr& faces) {
        typedef CarveMesh::vertex_t CarveVertex;
        const size_t num_vertices = mesh->vertex_storage.size();
        vertices.resize(num_vertices, 3);
        for (size_t i=0; i<num_vertices; i++) {
            const auto& v = mesh->vertex_storage[i];
            vertices(i,0) = v.v.x;
            vertices(i,1) = v.v.y;
            vertices(i,2) = v.v.z;
        }

        const size_t num_faces = mesh->faceEnd() - mesh->faceBegin();
        faces.resize(num_faces, 3);
        for (auto itr=mesh->faceBegin(); itr != mesh->faceEnd(); itr++) {
            std::vector<CarveVertex* > vts;
            (*itr)->getVertices(vts);
            assert(vts.size() == 3);

            // WARNING:
            // Here is my guess on how to extract vertex index.
            // Carve's documentation is not clear on how to do this.  
            const size_t fid = itr - mesh->faceBegin();
            faces(fid, 0) = vts[0] - &mesh->vertex_storage[0];
            faces(fid, 1) = vts[1] - &mesh->vertex_storage[0];
            faces(fid, 2) = vts[2] - &mesh->vertex_storage[0];
        }
    }
Ejemplo n.º 10
0
 void create_box(const VectorF& bbox_min, const VectorF& bbox_max,
         MatrixFr& box_vertices, MatrixIr& box_faces) {
     box_vertices.resize(8, 3);
     box_faces.resize(12, 3);
     box_vertices << bbox_min[0], bbox_min[1], bbox_min[2],
                     bbox_max[0], bbox_min[1], bbox_min[2],
                     bbox_max[0], bbox_max[1], bbox_min[2],
                     bbox_min[0], bbox_max[1], bbox_min[2],
                     bbox_min[0], bbox_min[1], bbox_max[2],
                     bbox_max[0], bbox_min[1], bbox_max[2],
                     bbox_max[0], bbox_max[1], bbox_max[2],
                     bbox_min[0], bbox_max[1], bbox_max[2];
     box_faces << 1, 2, 5,
                  5, 2, 6,
                  3, 4, 7,
                  3, 0, 4,
                  2, 3, 6,
                  3, 7, 6,
                  0, 1, 5,
                  0, 5, 4,
                  4, 5, 6,
                  4, 6, 7,
                  0, 3, 2,
                  0, 2, 1;
 }
Ejemplo n.º 11
0
    EdgeMap compute_edge_map(const MatrixIr& faces) {
        assert(faces.cols() == 3);
        EdgeMap result;
        const size_t num_faces = faces.rows();
        for (size_t i=0; i<num_faces; i++) {
            const Vector3I& f = faces.row(i);
            Triplet e0(f[1], f[2]);
            Triplet e1(f[2], f[0]);
            Triplet e2(f[0], f[1]);

            result.insert(e0, i);
            result.insert(e1, i);
            result.insert(e2, i);
        }

        return result;
    }
Ejemplo n.º 12
0
WireNetwork::Ptr MixedMeshTiler::tile() {
    const size_t num_cells = get_num_cells();
    auto transforms = get_tiling_operators();
    auto vars_array = extract_attributes(m_mesh);
    VectorI pattern_id = m_mesh->get_attribute("pattern_id").cast<int>();
    assert(pattern_id.size() == num_cells);

    m_tiled_vertices.clear();
    m_tiled_edges.clear();
    m_tiled_thicknesses.clear();
    m_tiled_offsets.clear();

    size_t v_count = 0;
    auto transform_itr = transforms.begin();
    for (size_t i=0; i<num_cells; i++) {
        set_active_wire_network(pattern_id[i]);
        scale_to_unit_box();
        append_vertices(*transform_itr);
        append_edges(v_count);
        append_thicknesses(vars_array[i]);
        append_offsets(vars_array[i], *transform_itr);

        v_count += m_unit_wire_network->get_num_vertices();
        transform_itr++;
    }

    MatrixFr vertices = vstack(m_tiled_vertices);
    MatrixIr edges = vstack(m_tiled_edges);
    MatrixFr thicknesses = vstack(m_tiled_thicknesses);
    MatrixFr offsets = vstack(m_tiled_offsets);
    assert(edges.minCoeff() >= 0);
    assert(edges.maxCoeff() < vertices.rows());

    WireNetwork::Ptr tiled_network =
        WireNetwork::create_raw(vertices, edges);

    tiled_network->add_attribute("thickness",
            m_target_type == ParameterCommon::VERTEX);
    tiled_network->set_attribute("thickness", thicknesses);
    tiled_network->add_attribute("vertex_offset", true);
    tiled_network->set_attribute("vertex_offset", offsets);

    clean_up(*tiled_network);
    return tiled_network;
}
Ejemplo n.º 13
0
 Polyhedron generate_polyhedron(
         const MatrixFr& vertices, const MatrixIr& faces) {
     Polyhedron P;
     PolyhedronBuilder<HalfedgeDS> triangle(vertices, faces);
     P.delegate(triangle);
     assert(vertices.rows() == P.size_of_vertices());
     assert(faces.rows() == P.size_of_facets());
     return P;
 }
Ejemplo n.º 14
0
    std::vector<bool> create_duplication_mask(const MatrixIr& edges) {
        const size_t num_edges = edges.rows();

        std::unordered_set<Triplet, hash> unique_set;
        std::vector<bool> mask(num_edges, false);

        for (size_t i=0; i<num_edges; i++) {
            const auto& edge = edges.row(i);
            Triplet key(edge[0], edge[1]);
            auto itr = unique_set.find(key);
            if (itr == unique_set.end()) {
                unique_set.insert(key);
            } else {
                mask[i] = true;
            }
        }

        return mask;
    }
Ejemplo n.º 15
0
 std::vector<Box> get_triangle_bboxes(
         const SelfIntersection::Points& pts, const MatrixIr& faces) {
     const size_t num_faces = faces.rows();
     std::vector<Box> boxes;
     boxes.reserve(num_faces);
     for (size_t i=0; i<num_faces; i++) {
         const Vector3I f = faces.row(i);
         const std::vector<SelfIntersection::Point_3> corners{
             pts[f[0]], pts[f[1]], pts[f[2]]
         };
         if (CGAL::collinear(pts[f[0]], pts[f[1]], pts[f[2]])) {
             // Triangle is degenerated.
             continue;
         }
         boxes.emplace_back(CGAL::bbox_3(corners.begin(), corners.end()));
         boxes.back().set_id(i);
     }
     return boxes;
 }
Ejemplo n.º 16
0
    void save_mesh(const std::string& filename,
            const MatrixFr& vertices, const MatrixIr& faces) {
        auto flattened_vertices = MatrixUtils::flatten<VectorF>(vertices);
        auto flattened_faces = MatrixUtils::flatten<VectorI>(faces);
        VectorI voxels = VectorI::Zero(0);

        MeshWriter::Ptr writer = MeshWriter::create(filename);
        writer->write(flattened_vertices, flattened_faces, voxels,
                vertices.cols(), faces.cols(), 0);
    }
Ejemplo n.º 17
0
BoundaryRemesher::BoundaryRemesher(const MatrixFr& vertices, const MatrixIr& faces)
    : m_vertices(vertices), m_faces(faces) {
        if (vertices.cols() != 3) {
            throw NotImplementedError(
                    "Only 3D meshes are supported for remeshing");
        }
        if (faces.cols() != 3) {
            throw NotImplementedError(
                    "Only triangle meshes are supported for remeshing");
        }
        assert_faces_are_valid(m_faces);
    }
Ejemplo n.º 18
0
    void triangulate(MatrixFr vertices, MatrixIr edges,
            MatrixFr& output_vertices, MatrixIr& output_faces, Float max_area) {
        assert(edges.rows() >= 3);
        MeshCleaner cleaner;
        cleaner.remove_isolated_vertices(vertices, edges);
        cleaner.remove_duplicated_vertices(vertices, edges, 1e-12);
        assert(vertices.rows() >= 3);

        TriangleWrapper triangle(vertices, edges);
        triangle.run(max_area, false, true, true);

        output_vertices = triangle.get_vertices();
        output_faces = triangle.get_faces();
    }
Ejemplo n.º 19
0
Boundary::Ptr Boundary::extract_volume_boundary_raw(
        MatrixFr& vertices, MatrixIr& voxels) {
    VectorF flattened_vertices = Eigen::Map<VectorF>(vertices.data(),
            vertices.rows() * vertices.cols());
    VectorI faces = VectorI::Zero(0);
    VectorI flattened_voxels = Eigen::Map<VectorI>(voxels.data(),
            voxels.rows() * voxels.cols());

    size_t vertex_per_voxel = voxels.cols();
    size_t vertex_per_face=0;
    if (vertex_per_voxel == 4) vertex_per_face = 3;
    else if (vertex_per_voxel == 8) vertex_per_face = 4;
    else {
        throw RuntimeError("Unknown voxel type.");
    }

    MeshFactory factory;
    Mesh::Ptr mesh = factory.load_data(flattened_vertices, faces,
            flattened_voxels, vertices.cols(), vertex_per_face,
            vertex_per_voxel).create();

    return extract_volume_boundary(*mesh);
}
Ejemplo n.º 20
0
void SimpleInflator::connect_end_loops() {
    const size_t dim = m_wire_network->get_dim();
    const Float ave_thickness = m_thickness.sum() / m_thickness.size();
    const auto& edge_lengths = m_wire_network->get_attribute("edge_length");
    const size_t num_edges = m_wire_network->get_num_edges();
    const size_t loop_size = m_profile->size();

    const MatrixIr connecting_faces = generate_faces_connecting_loops(
            loop_size, dim != 2);
    const size_t num_connecting_faces = connecting_faces.rows();

    for (size_t i=0; i<num_edges; i++) {
        Float edge_length = edge_lengths(i, 0);
        const auto& end_loops = m_end_loops[i];
        const size_t num_segments = std::max(1.0,
                std::round(edge_length / ave_thickness));
        MatrixFr pts((num_segments+1)*loop_size, dim);

        for (size_t j=0; j<num_segments+1; j++) {
            Float alpha = Float(j) / Float(num_segments);
            pts.block(j*loop_size, 0, loop_size, dim) =
                end_loops.first * (1.0 - alpha) + end_loops.second * alpha;
        }

        MatrixIr faces(num_connecting_faces * num_segments, 3);
        for (size_t j=0; j<num_segments; j++) {
            faces.block(j*num_connecting_faces, 0, num_connecting_faces, 3) =
                connecting_faces.array() + j*loop_size;
        }

        m_vertex_list.push_back(pts);
        m_face_list.push_back(faces.array() + m_num_vertex_accumulated);
        m_face_source_list.push_back(int(i)*(-1)-1);
        m_num_vertex_accumulated += pts.rows();
    }
}
Ejemplo n.º 21
0
void InflatorEngine::save_mesh(const std::string& filename,
        const MatrixFr& vertices, const MatrixIr& faces, VectorF debug) {
    VectorF flattened_vertices(vertices.rows() * vertices.cols());
    std::copy(vertices.data(), vertices.data() + vertices.rows() *
            vertices.cols(), flattened_vertices.data());
    VectorI flattened_faces(faces.rows() * faces.cols());
    std::copy(faces.data(), faces.data() + faces.rows() * faces.cols(),
            flattened_faces.data());
    VectorI voxels = VectorI::Zero(0);

    Mesh::Ptr mesh = MeshFactory().load_data(
            flattened_vertices, flattened_faces, voxels,
            vertices.cols(), faces.cols(), 0).create_shared();
    mesh->add_attribute("debug");
    mesh->set_attribute("debug", debug);

    MeshWriter::Ptr writer = MeshWriter::create(filename);
    writer->with_attribute("debug");
    writer->write_mesh(*mesh);
}
Ejemplo n.º 22
0
 void assert_faces_are_valid(const MatrixIr& faces) {
     assert((faces.col(0).array() != faces.col(1).array()).all());
     assert((faces.col(1).array() != faces.col(2).array()).all());
     assert((faces.col(2).array() != faces.col(1).array()).all());
 }
Ejemplo n.º 23
0
bool MeshValidation::face_source_is_valid(
        const MatrixFr& vertices,
        const MatrixIr& faces,
        const VectorI& face_sources) {
    const Float EPS = 1e-6;
    const size_t num_vertices = vertices.rows();
    const size_t num_faces = faces.rows();

    Vector3F bbox_min = vertices.colwise().minCoeff();
    Vector3F bbox_max = vertices.colwise().maxCoeff();

    bool result = true;
    for (size_t i=0; i<num_faces; i++) {
        const Vector3I& f = faces.row(i);
        const Vector3F& v0 = vertices.row(f[0]);
        const Vector3F& v1 = vertices.row(f[1]);
        const Vector3F& v2 = vertices.row(f[2]);

        if (fabs(v0[0] - bbox_min[0]) < EPS &&
            fabs(v1[0] - bbox_min[0]) < EPS &&
            fabs(v2[0] - bbox_min[0]) < EPS) {
            result = result && (face_sources[i] == 0);
            continue;
        }

        if (fabs(v0[1] - bbox_min[1]) < EPS &&
            fabs(v1[1] - bbox_min[1]) < EPS &&
            fabs(v2[1] - bbox_min[1]) < EPS) {
            result = result && (face_sources[i] == 0);
            continue;
        }

        if (fabs(v0[2] - bbox_min[2]) < EPS &&
            fabs(v1[2] - bbox_min[2]) < EPS &&
            fabs(v2[2] - bbox_min[2]) < EPS) {
            result = result && (face_sources[i] == 0);
            continue;
        }

        if (fabs(v0[0] - bbox_max[0]) < EPS &&
            fabs(v1[0] - bbox_max[0]) < EPS &&
            fabs(v2[0] - bbox_max[0]) < EPS) {
            result = result && (face_sources[i] == 0);
            continue;
        }

        if (fabs(v0[1] - bbox_max[1]) < EPS &&
            fabs(v1[1] - bbox_max[1]) < EPS &&
            fabs(v2[1] - bbox_max[1]) < EPS) {
            result = result && (face_sources[i] == 0);
            continue;
        }

        if (fabs(v0[2] - bbox_max[2]) < EPS &&
            fabs(v1[2] - bbox_max[2]) < EPS &&
            fabs(v2[2] - bbox_max[2]) < EPS) {
            result = result && (face_sources[i] == 0);
            continue;
        }
        result = result && (face_sources[i] != 0);
        if (!result) {
            std::cout << i << ":  ";
            std::cout << face_sources[i] << std::endl;
            std::cout << v0.transpose() << std::endl;
            std::cout << v1.transpose() << std::endl;
            std::cout << v2.transpose() << std::endl;
            return result;
        }
    }
    return result;
}
Ejemplo n.º 24
0
void LongEdgeRemoval::triangulate_chain(
        std::vector<VectorI>& faces,
        const std::vector<size_t>& chain,
        size_t v0_idx, size_t v1_idx, size_t v2_idx) {
    const size_t chain_size = chain.size();
    auto next = [&](size_t i) { return (i+1) % chain_size; };
    auto prev = [&](size_t i) { return (i+chain_size-1) % chain_size; };
    auto length = [&](size_t vi, size_t vj) {
        return (m_vertices.row(vi) - m_vertices.row(vj)).norm();
    };

    MatrixIr visited = MatrixIr::Zero(chain_size, 3);
    visited(v0_idx, 0) = 1;
    visited(v1_idx, 1) = 1;
    visited(v2_idx, 2) = 1;
    MatrixIr candidates(3, 6);
    candidates << v0_idx, next(v0_idx), prev(v0_idx), 0, 0, 0,
                  v1_idx, next(v1_idx), prev(v1_idx), 0, 0, 0,
                  v2_idx, next(v2_idx), prev(v2_idx), 0, 0, 0;
    MatrixFr candidate_lengths(3, 2);
    const Float NOT_USED = std::numeric_limits<Float>::max();
    candidate_lengths
        << length(chain[candidates(0, 1)], chain[candidates(0, 2)]),
           NOT_USED,
           length(chain[candidates(1, 1)], chain[candidates(1, 2)]),
           NOT_USED,
           length(chain[candidates(2, 1)], chain[candidates(2, 2)]),
           NOT_USED;

    auto index_comp = [&](size_t i, size_t j) {
        // Use greater than operator so the queue is a min heap.
        return candidate_lengths.row(i).minCoeff() >
            candidate_lengths.row(j).minCoeff();
    };
    std::priority_queue<size_t, std::vector<size_t>, decltype(index_comp)>
        Q(index_comp);
    Q.push(0);
    Q.push(1);
    Q.push(2);

    while (!Q.empty()) {
        size_t idx = Q.top();
        Q.pop();
        size_t selection;
        if (candidate_lengths(idx, 0) != NOT_USED &&
                candidate_lengths(idx, 0) <= candidate_lengths(idx, 1)) {
            selection = 0;
        } else if (candidate_lengths(idx, 1) != NOT_USED &&
                candidate_lengths(idx, 1) < candidate_lengths(idx, 0)){
            selection = 1;
        } else {
            continue;
        }
        size_t base_v = candidates(idx,  selection * 3 + 0);
        size_t right_v = candidates(idx, selection * 3 + 1);
        size_t left_v = candidates(idx,  selection * 3 + 2);
        assert(visited(base_v, idx) >= 1);
        if (visited.row(base_v).sum() > 1 ||
                visited(right_v, idx) > 1 ||
                visited(left_v, idx) > 1) {
            candidate_lengths(idx, selection) = NOT_USED;
            Q.push(idx);
            continue;
        }

        visited(right_v, idx) = 1;
        visited(left_v, idx) = 1;
        visited(base_v, idx) = 2;
        faces.push_back(Vector3I(chain[base_v], chain[right_v], chain[left_v]));

        if (visited.row(right_v).sum() == 1) {
            size_t right_to_right = next(right_v);
            Float edge_len = length(chain[left_v], chain[right_to_right]);
            candidate_lengths(idx, 0) = edge_len;
            candidates.block(idx, 0, 1, 3) =
                Vector3I(right_v, right_to_right, left_v).transpose();
        } else {
            candidate_lengths(idx, 0) = NOT_USED;
        }
        if (visited.row(left_v).sum() == 1) {
            size_t left_to_left = prev(left_v);
            Float edge_len = length(chain[right_v], chain[left_to_left]);
            candidate_lengths(idx, 1) = edge_len;
            candidates.block(idx, 3, 1, 3) =
                Vector3I(left_v, right_v, left_to_left).transpose();
        } else {
            candidate_lengths(idx, 1) = NOT_USED;
        }
        Q.push(idx);
    }
    auto visited_sum = (visited.array() > 0).rowwise().count().eval();
    if ((visited_sum.array() > 1).count() == 3) {
        Vector3I face;
        size_t count = 0;
        for (size_t i=0; i<chain_size; i++) {
            if (visited_sum[i] > 1) {
                assert(count < 3);
                face[count] = chain[i];
                count++;
            }
        }
        faces.push_back(face);
    }
}