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(); }
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; }
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)); }
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)); } }
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)); } }
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); }
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); }
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)); } }
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]; } }
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; }
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; }
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; }
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; }
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; }
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; }
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); }
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); }
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(); }
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); }
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(); } }
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); }
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()); }
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; }
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); } }