mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 12:41:20 -06:00 
			
		
		
		
	Merge branch 'tm_its_refactor_3'
This commit is contained in:
		
						commit
						cd19756a1d
					
				
					 45 changed files with 2334 additions and 932 deletions
				
			
		|  | @ -1,5 +1,6 @@ | |||
| #add_subdirectory(slasupporttree) | ||||
| #add_subdirectory(openvdb) | ||||
| add_subdirectory(meshboolean) | ||||
| add_subdirectory(opencsg) | ||||
| # add_subdirectory(meshboolean) | ||||
| add_subdirectory(its_neighbor_index) | ||||
| # add_subdirectory(opencsg) | ||||
| #add_subdirectory(aabb-evaluation) | ||||
							
								
								
									
										7
									
								
								sandboxes/its_neighbor_index/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								sandboxes/its_neighbor_index/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| add_executable(its_neighbor_index main.cpp ItsNeighborIndex.cpp ItsNeighborIndex.hpp) | ||||
| 
 | ||||
| target_link_libraries(its_neighbor_index libslic3r admesh) | ||||
| 
 | ||||
| if (WIN32) | ||||
|     prusaslicer_copy_dlls(its_neighbor_index) | ||||
| endif() | ||||
							
								
								
									
										613
									
								
								sandboxes/its_neighbor_index/ItsNeighborIndex.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										613
									
								
								sandboxes/its_neighbor_index/ItsNeighborIndex.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,613 @@ | |||
| #include <iostream> | ||||
| #include <vector> | ||||
| #include <unordered_map> | ||||
| #include <map> | ||||
| 
 | ||||
| #include "ItsNeighborIndex.hpp" | ||||
| #include "libslic3r/Execution/ExecutionTBB.hpp" | ||||
| #include "libslic3r/Execution/ExecutionSeq.hpp" | ||||
| 
 | ||||
| #include "tbb/parallel_sort.h" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| FaceNeighborIndex its_create_neighbors_index_1(const indexed_triangle_set &its) | ||||
| { | ||||
|     // Just to be clear what type of object are we referencing
 | ||||
|     using FaceID = size_t; | ||||
|     using VertexID = uint64_t; | ||||
|     using EdgeID = uint64_t; | ||||
| 
 | ||||
|     constexpr auto UNASSIGNED = std::numeric_limits<FaceID>::max(); | ||||
| 
 | ||||
|     struct Edge // Will contain IDs of the two facets touching this edge
 | ||||
|     { | ||||
|         FaceID first, second; | ||||
|         Edge() : first{UNASSIGNED}, second{UNASSIGNED} {} | ||||
|         void   assign(FaceID fid) | ||||
|         { | ||||
|             first == UNASSIGNED ? first = fid : second = fid; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     // All vertex IDs will fit into this number of bits. (Used for hashing)
 | ||||
|     const int max_vertex_id_bits = std::ceil(std::log2(its.vertices.size())); | ||||
|     assert(max_vertex_id_bits <= 32); | ||||
| 
 | ||||
|     std::unordered_map< EdgeID, Edge> edge_index; | ||||
| 
 | ||||
|     // Edge id is constructed by concatenating two vertex ids, starting with
 | ||||
|     // the lowest in MSB
 | ||||
|     auto hash = [max_vertex_id_bits] (VertexID a, VertexID b) { | ||||
|         if (a > b) std::swap(a, b); | ||||
|         return (a << max_vertex_id_bits) + b; | ||||
|     }; | ||||
| 
 | ||||
|     // Go through all edges of all facets and mark the facets touching each edge
 | ||||
|     for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) { | ||||
|         const Vec3i &face = its.indices[face_id]; | ||||
| 
 | ||||
|         EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)), | ||||
|                e3 = hash(face(2), face(0)); | ||||
| 
 | ||||
|         edge_index[e1].assign(face_id); | ||||
|         edge_index[e2].assign(face_id); | ||||
|         edge_index[e3].assign(face_id); | ||||
|     } | ||||
| 
 | ||||
|     FaceNeighborIndex index(its.indices.size()); | ||||
| 
 | ||||
|     // Now collect the neighbors for each facet into the final index
 | ||||
|     for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) { | ||||
|         const Vec3i &face = its.indices[face_id]; | ||||
| 
 | ||||
|         EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)), | ||||
|                e3 = hash(face(2), face(0)); | ||||
| 
 | ||||
|         const Edge &neighs1 = edge_index[e1]; | ||||
|         const Edge &neighs2 = edge_index[e2]; | ||||
|         const Edge &neighs3 = edge_index[e3]; | ||||
| 
 | ||||
|         std::array<size_t, 3> &neighs = index[face_id]; | ||||
|         neighs[0] = neighs1.first == face_id ? neighs1.second : neighs1.first; | ||||
|         neighs[1] = neighs2.first == face_id ? neighs2.second : neighs2.first; | ||||
|         neighs[2] = neighs3.first == face_id ? neighs3.second : neighs3.first; | ||||
|     } | ||||
| 
 | ||||
|     return index; | ||||
| } | ||||
| 
 | ||||
| std::vector<Vec3i> its_create_neighbors_index_2(const indexed_triangle_set &its) | ||||
| { | ||||
|     std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1)); | ||||
| 
 | ||||
|     // Create a mapping from triangle edge into face.
 | ||||
|     struct EdgeToFace { | ||||
|         // Index of the 1st vertex of the triangle edge. vertex_low <= vertex_high.
 | ||||
|         int  vertex_low; | ||||
|         // Index of the 2nd vertex of the triangle edge.
 | ||||
|         int  vertex_high; | ||||
|         // Index of a triangular face.
 | ||||
|         int  face; | ||||
|         // Index of edge in the face, starting with 1. Negative indices if the edge was stored reverse in (vertex_low, vertex_high).
 | ||||
|         int  face_edge; | ||||
|         bool operator==(const EdgeToFace &other) const { return vertex_low == other.vertex_low && vertex_high == other.vertex_high; } | ||||
|         bool operator<(const EdgeToFace &other) const { return vertex_low < other.vertex_low || (vertex_low == other.vertex_low && vertex_high < other.vertex_high); } | ||||
|     }; | ||||
|     std::vector<EdgeToFace> edges_map; | ||||
|     edges_map.assign(its.indices.size() * 3, EdgeToFace()); | ||||
|     for (uint32_t facet_idx = 0; facet_idx < its.indices.size(); ++ facet_idx) | ||||
|         for (int i = 0; i < 3; ++ i) { | ||||
|             EdgeToFace &e2f = edges_map[facet_idx * 3 + i]; | ||||
|             e2f.vertex_low  = its.indices[facet_idx][i]; | ||||
|             e2f.vertex_high = its.indices[facet_idx][(i + 1) % 3]; | ||||
|             e2f.face        = facet_idx; | ||||
|             // 1 based indexing, to be always strictly positive.
 | ||||
|             e2f.face_edge   = i + 1; | ||||
|             if (e2f.vertex_low > e2f.vertex_high) { | ||||
|                 // Sort the vertices
 | ||||
|                 std::swap(e2f.vertex_low, e2f.vertex_high); | ||||
|                 // and make the face_edge negative to indicate a flipped edge.
 | ||||
|                 e2f.face_edge = - e2f.face_edge; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     std::sort(edges_map.begin(), edges_map.end()); | ||||
| 
 | ||||
|     // Assign a unique common edge id to touching triangle edges.
 | ||||
|     int num_edges = 0; | ||||
|     for (size_t i = 0; i < edges_map.size(); ++ i) { | ||||
|         EdgeToFace &edge_i = edges_map[i]; | ||||
|         if (edge_i.face == -1) | ||||
|             // This edge has been connected to some neighbor already.
 | ||||
|             continue; | ||||
|         // Unconnected edge. Find its neighbor with the correct orientation.
 | ||||
|         size_t j; | ||||
|         bool found = false; | ||||
|         for (j = i + 1; j < edges_map.size() && edge_i == edges_map[j]; ++ j) | ||||
|             if (edge_i.face_edge * edges_map[j].face_edge < 0 && edges_map[j].face != -1) { | ||||
|                 // Faces touching with opposite oriented edges and none of the edges is connected yet.
 | ||||
|                 found = true; | ||||
|                 break; | ||||
|             } | ||||
|         if (! found) { | ||||
|             //FIXME Vojtech: Trying to find an edge with equal orientation. This smells.
 | ||||
|             // admesh can assign the same edge ID to more than two facets (which is
 | ||||
|             // still topologically correct), so we have to search for a duplicate of
 | ||||
|             // this edge too in case it was already seen in this orientation
 | ||||
|             for (j = i + 1; j < edges_map.size() && edge_i == edges_map[j]; ++ j) | ||||
|                 if (edges_map[j].face != -1) { | ||||
|                     // Faces touching with equally oriented edges and none of the edges is connected yet.
 | ||||
|                     found = true; | ||||
|                     break; | ||||
|                 } | ||||
|         } | ||||
|         // Assign an edge index to the 1st face.
 | ||||
|         //        out[edge_i.face](std::abs(edge_i.face_edge) - 1) = num_edges;
 | ||||
|         if (found) { | ||||
|             EdgeToFace &edge_j = edges_map[j]; | ||||
|             out[edge_i.face](std::abs(edge_i.face_edge) - 1) = edge_j.face; | ||||
|             out[edge_j.face](std::abs(edge_j.face_edge) - 1) = edge_i.face; | ||||
|             // Mark the edge as connected.
 | ||||
|             edge_j.face = -1; | ||||
|         } | ||||
|         ++ num_edges; | ||||
|     } | ||||
| 
 | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| std::vector<Vec3i> its_create_neighbors_index_3(const indexed_triangle_set &its) | ||||
| { | ||||
|     std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1)); | ||||
| 
 | ||||
|     // Create a mapping from triangle edge into face.
 | ||||
|     struct EdgeToFace { | ||||
|         // Index of the 1st vertex of the triangle edge. vertex_low <= vertex_high.
 | ||||
|         int  vertex_low; | ||||
|         // Index of the 2nd vertex of the triangle edge.
 | ||||
|         int  vertex_high; | ||||
|         // Index of a triangular face.
 | ||||
|         int  face; | ||||
|         // Index of edge in the face, starting with 1. Negative indices if the edge was stored reverse in (vertex_low, vertex_high).
 | ||||
|         int  face_edge; | ||||
|         bool operator==(const EdgeToFace &other) const { return vertex_low == other.vertex_low && vertex_high == other.vertex_high; } | ||||
|         bool operator<(const EdgeToFace &other) const { return vertex_low < other.vertex_low || (vertex_low == other.vertex_low && vertex_high < other.vertex_high); } | ||||
|     }; | ||||
|     std::vector<EdgeToFace> edges_map; | ||||
|     edges_map.assign(its.indices.size() * 3, EdgeToFace()); | ||||
|     for (uint32_t facet_idx = 0; facet_idx < its.indices.size(); ++ facet_idx) | ||||
|         for (int i = 0; i < 3; ++ i) { | ||||
|             EdgeToFace &e2f = edges_map[facet_idx * 3 + i]; | ||||
|             e2f.vertex_low  = its.indices[facet_idx][i]; | ||||
|             e2f.vertex_high = its.indices[facet_idx][(i + 1) % 3]; | ||||
|             e2f.face        = facet_idx; | ||||
|             // 1 based indexing, to be always strictly positive.
 | ||||
|             e2f.face_edge   = i + 1; | ||||
|             if (e2f.vertex_low > e2f.vertex_high) { | ||||
|                 // Sort the vertices
 | ||||
|                 std::swap(e2f.vertex_low, e2f.vertex_high); | ||||
|                 // and make the face_edge negative to indicate a flipped edge.
 | ||||
|                 e2f.face_edge = - e2f.face_edge; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     tbb::parallel_sort(edges_map.begin(), edges_map.end()); | ||||
| 
 | ||||
|     // Assign a unique common edge id to touching triangle edges.
 | ||||
|     int num_edges = 0; | ||||
|     for (size_t i = 0; i < edges_map.size(); ++ i) { | ||||
|         EdgeToFace &edge_i = edges_map[i]; | ||||
|         if (edge_i.face == -1) | ||||
|             // This edge has been connected to some neighbor already.
 | ||||
|             continue; | ||||
|         // Unconnected edge. Find its neighbor with the correct orientation.
 | ||||
|         size_t j; | ||||
|         bool found = false; | ||||
|         for (j = i + 1; j < edges_map.size() && edge_i == edges_map[j]; ++ j) | ||||
|             if (edge_i.face_edge * edges_map[j].face_edge < 0 && edges_map[j].face != -1) { | ||||
|                 // Faces touching with opposite oriented edges and none of the edges is connected yet.
 | ||||
|                 found = true; | ||||
|                 break; | ||||
|             } | ||||
|         if (! found) { | ||||
|             //FIXME Vojtech: Trying to find an edge with equal orientation. This smells.
 | ||||
|             // admesh can assign the same edge ID to more than two facets (which is
 | ||||
|             // still topologically correct), so we have to search for a duplicate of
 | ||||
|             // this edge too in case it was already seen in this orientation
 | ||||
|             for (j = i + 1; j < edges_map.size() && edge_i == edges_map[j]; ++ j) | ||||
|                 if (edges_map[j].face != -1) { | ||||
|                     // Faces touching with equally oriented edges and none of the edges is connected yet.
 | ||||
|                     found = true; | ||||
|                     break; | ||||
|                 } | ||||
|         } | ||||
|         // Assign an edge index to the 1st face.
 | ||||
|         //        out[edge_i.face](std::abs(edge_i.face_edge) - 1) = num_edges;
 | ||||
|         if (found) { | ||||
|             EdgeToFace &edge_j = edges_map[j]; | ||||
|             out[edge_i.face](std::abs(edge_i.face_edge) - 1) = edge_j.face; | ||||
|             out[edge_j.face](std::abs(edge_j.face_edge) - 1) = edge_i.face; | ||||
|             // Mark the edge as connected.
 | ||||
|             edge_j.face = -1; | ||||
|         } | ||||
|         ++ num_edges; | ||||
|     } | ||||
| 
 | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| FaceNeighborIndex its_create_neighbors_index_4(const indexed_triangle_set &its) | ||||
| { | ||||
|     // Just to be clear what type of object are we referencing
 | ||||
|     using FaceID = size_t; | ||||
|     using VertexID = uint64_t; | ||||
|     using EdgeID = uint64_t; | ||||
| 
 | ||||
|     constexpr auto UNASSIGNED = std::numeric_limits<FaceID>::max(); | ||||
| 
 | ||||
|     struct Edge // Will contain IDs of the two facets touching this edge
 | ||||
|     { | ||||
|         FaceID first, second; | ||||
|         Edge() : first{UNASSIGNED}, second{UNASSIGNED} {} | ||||
|         void   assign(FaceID fid) | ||||
|         { | ||||
|             first == UNASSIGNED ? first = fid : second = fid; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     Benchmark bm; | ||||
|     bm.start(); | ||||
| 
 | ||||
|     // All vertex IDs will fit into this number of bits. (Used for hashing)
 | ||||
|     //    const int max_vertex_id_bits = std::ceil(std::log2(its.vertices.size()));
 | ||||
|     //    assert(max_vertex_id_bits <= 32);
 | ||||
| 
 | ||||
|     const uint64_t Vn  = its.vertices.size(); | ||||
|     //    const uint64_t Fn  = 3 * its.indices.size();
 | ||||
|     //    double MaxQ = double(Vn) * (Vn + 1) / Fn;
 | ||||
|     //    const uint64_t Nq = MaxQ < 0 ? 0 : std::ceil(std::log2(MaxQ));
 | ||||
|     //    const uint64_t Nr = std::ceil(std::log2(std::min(Vn * (Vn + 1), Fn)));
 | ||||
|     //    const uint64_t Nfn = std::ceil(std::log2(Fn));
 | ||||
| 
 | ||||
|     ////    const uint64_t max_edge_ids = (uint64_t(1) << (Nq + Nr));
 | ||||
|     //    const uint64_t max_edge_ids = MaxQ * Fn + (std::min(Vn * (Vn + 1), Fn)); //(uint64_t(1) << Nfn);
 | ||||
|     const uint64_t Fn  = 3 * its.indices.size(); | ||||
|     std::vector< Edge > edge_index(3 * Fn); | ||||
| 
 | ||||
|     // Edge id is constructed by concatenating two vertex ids, starting with
 | ||||
|     // the lowest in MSB
 | ||||
|     auto hash = [Vn, Fn /*, Nr*/] (VertexID a, VertexID b) { | ||||
|         if (a > b) std::swap(a, b); | ||||
| 
 | ||||
|         uint64_t C = Vn * a + b; | ||||
|         uint64_t Q = C / Fn, R = C % Fn; | ||||
| 
 | ||||
|         return Q * Fn + R; | ||||
|     }; | ||||
| 
 | ||||
|     // Go through all edges of all facets and mark the facets touching each edge
 | ||||
|     for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) { | ||||
|         const Vec3i &face = its.indices[face_id]; | ||||
| 
 | ||||
|         EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)), | ||||
|                e3 = hash(face(2), face(0)); | ||||
| 
 | ||||
|         edge_index[e1].assign(face_id); | ||||
|         edge_index[e2].assign(face_id); | ||||
|         edge_index[e3].assign(face_id); | ||||
|     } | ||||
| 
 | ||||
|     FaceNeighborIndex index(its.indices.size()); | ||||
| 
 | ||||
|     // Now collect the neighbors for each facet into the final index
 | ||||
|     for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) { | ||||
|         const Vec3i &face = its.indices[face_id]; | ||||
| 
 | ||||
|         EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)), | ||||
|                e3 = hash(face(2), face(0)); | ||||
| 
 | ||||
|         const Edge &neighs1 = edge_index[e1]; | ||||
|         const Edge &neighs2 = edge_index[e2]; | ||||
|         const Edge &neighs3 = edge_index[e3]; | ||||
| 
 | ||||
|         std::array<size_t, 3> &neighs = index[face_id]; | ||||
|         neighs[0] = neighs1.first == face_id ? neighs1.second : neighs1.first; | ||||
|         neighs[1] = neighs2.first == face_id ? neighs2.second : neighs2.first; | ||||
|         neighs[2] = neighs3.first == face_id ? neighs3.second : neighs3.first; | ||||
|     } | ||||
| 
 | ||||
|     bm.stop(); | ||||
| 
 | ||||
|     std::cout << "Creating neighbor index took: " << bm.getElapsedSec() << " seconds." << std::endl; | ||||
| 
 | ||||
|     return index; | ||||
| } | ||||
| 
 | ||||
| // Create an index of faces belonging to each vertex. The returned vector can
 | ||||
| // be indexed with vertex indices and contains a list of face indices for each
 | ||||
| // vertex.
 | ||||
| std::vector<std::vector<size_t>> create_vertex_faces_index(const indexed_triangle_set &its) | ||||
| { | ||||
|     std::vector<std::vector<size_t>> index; | ||||
| 
 | ||||
|     if (! its.vertices.empty()) { | ||||
|         size_t res = its.indices.size() / its.vertices.size(); | ||||
|         index.assign(its.vertices.size(), reserve_vector<size_t>(res)); | ||||
|         for (size_t fi = 0; fi < its.indices.size(); ++fi) { | ||||
|             auto &face = its.indices[fi]; | ||||
|             index[face(0)].emplace_back(fi); | ||||
|             index[face(1)].emplace_back(fi); | ||||
|             index[face(2)].emplace_back(fi); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return index; | ||||
| } | ||||
| 
 | ||||
| //static int get_vertex_index(size_t vertex_index, const stl_triangle_vertex_indices &triangle_indices) {
 | ||||
| //    if (vertex_index == triangle_indices[0]) return 0;
 | ||||
| //    if (vertex_index == triangle_indices[1]) return 1;
 | ||||
| //    if (vertex_index == triangle_indices[2]) return 2;
 | ||||
| //    return -1;
 | ||||
| //}
 | ||||
| 
 | ||||
| //static Vec2crd get_edge_indices(int edge_index, const stl_triangle_vertex_indices &triangle_indices)
 | ||||
| //{
 | ||||
| //    int next_edge_index = (edge_index == 2) ? 0 : edge_index + 1;
 | ||||
| //    coord_t vi0             = triangle_indices[edge_index];
 | ||||
| //    coord_t vi1             = triangle_indices[next_edge_index];
 | ||||
| //    return Vec2crd(vi0, vi1);
 | ||||
| //}
 | ||||
| 
 | ||||
| static std::vector<std::vector<size_t>> create_vertex_faces_index( | ||||
|     const std::vector<stl_triangle_vertex_indices>& indices, size_t count_vertices) | ||||
| { | ||||
|     if (count_vertices == 0) return {}; | ||||
|     std::vector<std::vector<size_t>> index; | ||||
|     size_t res = indices.size() / count_vertices; | ||||
|     index.assign(count_vertices, reserve_vector<size_t>(res)); | ||||
|     for (size_t fi = 0; fi < indices.size(); ++fi) { | ||||
|         auto &face = indices[fi]; | ||||
|         index[face(0)].emplace_back(fi); | ||||
|         index[face(1)].emplace_back(fi); | ||||
|         index[face(2)].emplace_back(fi); | ||||
|     } | ||||
|     return index; | ||||
| } | ||||
| 
 | ||||
| std::vector<Vec3crd> its_create_neighbors_index_5(const indexed_triangle_set &its) | ||||
| { | ||||
|     const std::vector<stl_triangle_vertex_indices> &indices = its.indices; | ||||
|     size_t vertices_size = its.vertices.size(); | ||||
| 
 | ||||
|     if (indices.empty() || vertices_size == 0) return {}; | ||||
|     std::vector<std::vector<size_t>> vertex_triangles = create_vertex_faces_index(indices, vertices_size); | ||||
|     coord_t              no_value = -1; | ||||
|     std::vector<Vec3crd> neighbors(indices.size(), Vec3crd(no_value, no_value, no_value)); | ||||
|     for (const stl_triangle_vertex_indices& triangle_indices : indices) { | ||||
|         coord_t index = &triangle_indices - &indices.front(); | ||||
|         Vec3crd& neighbor = neighbors[index]; | ||||
|         for (int edge_index = 0; edge_index < 3; ++edge_index) { | ||||
|             // check if done
 | ||||
|             coord_t& neighbor_edge = neighbor[edge_index]; | ||||
|             if (neighbor_edge != no_value) continue; | ||||
|             Vec2crd edge_indices = get_edge_indices(edge_index, triangle_indices); | ||||
|             // IMPROVE: use same vector for 2 sides of triangle
 | ||||
|             const std::vector<size_t> &faces = vertex_triangles[edge_indices[0]]; | ||||
|             for (const size_t &face : faces) { | ||||
|                 if (face <= index) continue; | ||||
|                 const stl_triangle_vertex_indices &face_indices = indices[face]; | ||||
|                 int vertex_index = get_vertex_index(edge_indices[1], face_indices); | ||||
|                 // NOT Contain second vertex?
 | ||||
|                 if (vertex_index < 0) continue; | ||||
|                 // Has NOT oposit direction?
 | ||||
|                 if (edge_indices[0] != face_indices[(vertex_index + 1) % 3]) continue; | ||||
|                 neighbor_edge = face; | ||||
|                 neighbors[face][vertex_index] = index; | ||||
|                 break; | ||||
|             } | ||||
|             // must be paired
 | ||||
|             assert(neighbor_edge != no_value); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return neighbors; | ||||
| } | ||||
| 
 | ||||
| std::vector<std::array<size_t, 3>> its_create_neighbors_index_6(const indexed_triangle_set &its) | ||||
| { | ||||
|     constexpr auto UNASSIGNED_EDGE = std::numeric_limits<uint64_t>::max(); | ||||
|     constexpr auto UNASSIGNED_FACE = std::numeric_limits<size_t>::max(); | ||||
|     struct Edge | ||||
|     { | ||||
|         uint64_t id      = UNASSIGNED_EDGE; | ||||
|         size_t   face_id = UNASSIGNED_FACE; | ||||
|         bool operator < (const Edge &e) const { return id < e.id; } | ||||
|     }; | ||||
| 
 | ||||
|     const size_t facenum = its.indices.size(); | ||||
| 
 | ||||
|     // All vertex IDs will fit into this number of bits. (Used for hashing)
 | ||||
|     const int max_vertex_id_bits = std::ceil(std::log2(its.vertices.size())); | ||||
|     assert(max_vertex_id_bits <= 32); | ||||
| 
 | ||||
|     // Edge id is constructed by concatenating two vertex ids, starting with
 | ||||
|     // the lowest in MSB
 | ||||
|     auto hash = [max_vertex_id_bits] (uint64_t a, uint64_t b) { | ||||
|         if (a > b) std::swap(a, b); | ||||
|         return (a << max_vertex_id_bits) + b; | ||||
|     }; | ||||
| 
 | ||||
|     std::vector<Edge> edge_map(3 * facenum); | ||||
| 
 | ||||
|     // Go through all edges of all facets and mark the facets touching each edge
 | ||||
|     for (size_t face_id = 0; face_id < facenum; ++face_id) { | ||||
|         const Vec3i &face = its.indices[face_id]; | ||||
| 
 | ||||
|         edge_map[face_id * 3] = {hash(face(0), face(1)), face_id}; | ||||
|         edge_map[face_id * 3 + 1] = {hash(face(1), face(2)), face_id}; | ||||
|         edge_map[face_id * 3 + 2] = {hash(face(2), face(0)), face_id}; | ||||
|     } | ||||
| 
 | ||||
|     std::sort(edge_map.begin(), edge_map.end()); | ||||
| 
 | ||||
|     std::vector<std::array<size_t, 3>> out(facenum, {UNASSIGNED_FACE, UNASSIGNED_FACE, UNASSIGNED_FACE}); | ||||
| 
 | ||||
|     auto add_neighbor = [](std::array<size_t, 3> &slot, size_t face_id) { | ||||
|         if (slot[0] == UNASSIGNED_FACE) { slot[0] = face_id; return; } | ||||
|         if (slot[1] == UNASSIGNED_FACE) { slot[1] = face_id; return; } | ||||
|         if (slot[2] == UNASSIGNED_FACE) { slot[2] = face_id; return; } | ||||
|     }; | ||||
| 
 | ||||
|     for (auto it = edge_map.begin(); it != edge_map.end();) { | ||||
|         size_t face_id = it->face_id; | ||||
|         uint64_t edge_id = it->id; | ||||
| 
 | ||||
|         while (++it != edge_map.end() &&  (it->id == edge_id)) { | ||||
|             size_t other_face_id = it->face_id; | ||||
|             add_neighbor(out[other_face_id], face_id); | ||||
|             add_neighbor(out[face_id], other_face_id); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| std::vector<std::array<size_t, 3>> its_create_neighbors_index_7(const indexed_triangle_set &its) | ||||
| { | ||||
|     constexpr auto UNASSIGNED_EDGE = std::numeric_limits<uint64_t>::max(); | ||||
|     constexpr auto UNASSIGNED_FACE = std::numeric_limits<size_t>::max(); | ||||
|     struct Edge | ||||
|     { | ||||
|         uint64_t id      = UNASSIGNED_EDGE; | ||||
|         size_t   face_id = UNASSIGNED_FACE; | ||||
|         bool operator < (const Edge &e) const { return id < e.id; } | ||||
|     }; | ||||
| 
 | ||||
|     const size_t facenum = its.indices.size(); | ||||
| 
 | ||||
|     // All vertex IDs will fit into this number of bits. (Used for hashing)
 | ||||
|     const int max_vertex_id_bits = std::ceil(std::log2(its.vertices.size())); | ||||
|     assert(max_vertex_id_bits <= 32); | ||||
| 
 | ||||
|     // Edge id is constructed by concatenating two vertex ids, starting with
 | ||||
|     // the lowest in MSB
 | ||||
|     auto hash = [max_vertex_id_bits] (uint64_t a, uint64_t b) { | ||||
|         if (a > b) std::swap(a, b); | ||||
|         return (a << max_vertex_id_bits) + b; | ||||
|     }; | ||||
| 
 | ||||
|     std::vector<Edge> edge_map(3 * facenum); | ||||
| 
 | ||||
|     // Go through all edges of all facets and mark the facets touching each edge
 | ||||
|     for (size_t face_id = 0; face_id < facenum; ++face_id) { | ||||
|         const Vec3i &face = its.indices[face_id]; | ||||
| 
 | ||||
|         edge_map[face_id * 3] = {hash(face(0), face(1)), face_id}; | ||||
|         edge_map[face_id * 3 + 1] = {hash(face(1), face(2)), face_id}; | ||||
|         edge_map[face_id * 3 + 2] = {hash(face(2), face(0)), face_id}; | ||||
|     } | ||||
| 
 | ||||
|     tbb::parallel_sort(edge_map.begin(), edge_map.end()); | ||||
| 
 | ||||
|     std::vector<std::array<size_t, 3>> out(facenum, {UNASSIGNED_FACE, UNASSIGNED_FACE, UNASSIGNED_FACE}); | ||||
| 
 | ||||
|     auto add_neighbor = [](std::array<size_t, 3> &slot, size_t face_id) { | ||||
|         if (slot[0] == UNASSIGNED_FACE) { slot[0] = face_id; return; } | ||||
|         if (slot[1] == UNASSIGNED_FACE) { slot[1] = face_id; return; } | ||||
|         if (slot[2] == UNASSIGNED_FACE) { slot[2] = face_id; return; } | ||||
|     }; | ||||
| 
 | ||||
|     for (auto it = edge_map.begin(); it != edge_map.end();) { | ||||
|         size_t face_id = it->face_id; | ||||
|         uint64_t edge_id = it->id; | ||||
| 
 | ||||
|         while (++it != edge_map.end() &&  (it->id == edge_id)) { | ||||
|             size_t other_face_id = it->face_id; | ||||
|             add_neighbor(out[other_face_id], face_id); | ||||
|             add_neighbor(out[face_id], other_face_id); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| FaceNeighborIndex its_create_neighbors_index_8(const indexed_triangle_set &its) | ||||
| { | ||||
|     // Just to be clear what type of object are we referencing
 | ||||
|     using FaceID = size_t; | ||||
|     using VertexID = uint64_t; | ||||
|     using EdgeID = uint64_t; | ||||
| 
 | ||||
|     constexpr auto UNASSIGNED = std::numeric_limits<FaceID>::max(); | ||||
| 
 | ||||
|     struct Edge // Will contain IDs of the two facets touching this edge
 | ||||
|     { | ||||
|         FaceID first, second; | ||||
|         Edge() : first{UNASSIGNED}, second{UNASSIGNED} {} | ||||
|         void   assign(FaceID fid) | ||||
|         { | ||||
|             first == UNASSIGNED ? first = fid : second = fid; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     // All vertex IDs will fit into this number of bits. (Used for hashing)
 | ||||
|     const int max_vertex_id_bits = std::ceil(std::log2(its.vertices.size())); | ||||
|     assert(max_vertex_id_bits <= 32); | ||||
| 
 | ||||
|     std::map< EdgeID, Edge > edge_index; | ||||
| 
 | ||||
|     // Edge id is constructed by concatenating two vertex ids, starting with
 | ||||
|     // the lowest in MSB
 | ||||
|     auto hash = [max_vertex_id_bits] (VertexID a, VertexID b) { | ||||
|         if (a > b) std::swap(a, b); | ||||
|         return (a << max_vertex_id_bits) + b; | ||||
|     }; | ||||
| 
 | ||||
|     // Go through all edges of all facets and mark the facets touching each edge
 | ||||
|     for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) { | ||||
|         const Vec3i &face = its.indices[face_id]; | ||||
| 
 | ||||
|         EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)), | ||||
|                e3 = hash(face(2), face(0)); | ||||
| 
 | ||||
|         edge_index[e1].assign(face_id); | ||||
|         edge_index[e2].assign(face_id); | ||||
|         edge_index[e3].assign(face_id); | ||||
|     } | ||||
| 
 | ||||
|     FaceNeighborIndex index(its.indices.size()); | ||||
| 
 | ||||
|     // Now collect the neighbors for each facet into the final index
 | ||||
|     for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) { | ||||
|         const Vec3i &face = its.indices[face_id]; | ||||
| 
 | ||||
|         EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)), | ||||
|                e3 = hash(face(2), face(0)); | ||||
| 
 | ||||
|         const Edge &neighs1 = edge_index[e1]; | ||||
|         const Edge &neighs2 = edge_index[e2]; | ||||
|         const Edge &neighs3 = edge_index[e3]; | ||||
| 
 | ||||
|         std::array<size_t, 3> &neighs = index[face_id]; | ||||
|         neighs[0] = neighs1.first == face_id ? neighs1.second : neighs1.first; | ||||
|         neighs[1] = neighs2.first == face_id ? neighs2.second : neighs2.first; | ||||
|         neighs[2] = neighs3.first == face_id ? neighs3.second : neighs3.first; | ||||
|     } | ||||
| 
 | ||||
|     return index; | ||||
| } | ||||
| 
 | ||||
| std::vector<Vec3crd> its_create_neighbors_index_9(const indexed_triangle_set &its) | ||||
| { | ||||
|     return create_neighbors_index(ex_seq, its); | ||||
| } | ||||
| 
 | ||||
| std::vector<Vec3i> its_create_neighbors_index_10(const indexed_triangle_set &its) | ||||
| { | ||||
|     return create_neighbors_index(ex_tbb, its); | ||||
| } | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
							
								
								
									
										19
									
								
								sandboxes/its_neighbor_index/ItsNeighborIndex.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								sandboxes/its_neighbor_index/ItsNeighborIndex.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| #include <libslic3r/TriangleMesh.hpp> | ||||
| #include "libslic3r/MeshSplitImpl.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| using FaceNeighborIndex = std::vector<std::array<size_t, 3>>; | ||||
| FaceNeighborIndex its_create_neighbors_index_1(const indexed_triangle_set &its); | ||||
| std::vector<Vec3i> its_create_neighbors_index_2(const indexed_triangle_set &its); | ||||
| std::vector<Vec3i> its_create_neighbors_index_3(const indexed_triangle_set &its); | ||||
| FaceNeighborIndex its_create_neighbors_index_4(const indexed_triangle_set &its); | ||||
| //FaceNeighborIndex its_create_neighbors_index_4(const indexed_triangle_set &its);
 | ||||
| std::vector<Vec3crd> its_create_neighbors_index_5(const indexed_triangle_set &its); | ||||
| std::vector<std::array<size_t, 3>> its_create_neighbors_index_6(const indexed_triangle_set &its); | ||||
| std::vector<std::array<size_t, 3>> its_create_neighbors_index_7(const indexed_triangle_set &its); | ||||
| FaceNeighborIndex its_create_neighbors_index_8(const indexed_triangle_set &its); | ||||
| std::vector<Vec3crd> its_create_neighbors_index_9(const indexed_triangle_set &its); | ||||
| std::vector<Vec3i> its_create_neighbors_index_10(const indexed_triangle_set &its); | ||||
| 
 | ||||
| std::vector<std::vector<size_t>> create_vertex_faces_index(const indexed_triangle_set &its); | ||||
| } | ||||
							
								
								
									
										273
									
								
								sandboxes/its_neighbor_index/main.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								sandboxes/its_neighbor_index/main.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,273 @@ | |||
| #include <iostream> | ||||
| #include <fstream> | ||||
| #include <vector> | ||||
| #include <tuple> | ||||
| #include <random> | ||||
| 
 | ||||
| #include "ItsNeighborIndex.hpp" | ||||
| 
 | ||||
| #include "libnest2d/tools/benchmark.h" | ||||
| #include "libnest2d/utils/metaloop.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| enum { IndexCreation, Split }; | ||||
| struct MeasureResult | ||||
| { | ||||
|     static constexpr const char * Names[] = { | ||||
|         "Index creation [s]", | ||||
|         "Split [s]" | ||||
|     }; | ||||
| 
 | ||||
|     double measurements[std::size(Names)] = {0.}; | ||||
| }; | ||||
| 
 | ||||
| template<class IndexCreatorFn> | ||||
| static MeasureResult measure_index(const indexed_triangle_set &its, IndexCreatorFn fn) | ||||
| { | ||||
|     Benchmark b; | ||||
| 
 | ||||
|     MeasureResult r; | ||||
|     for (int i = 0; i < 10; ++i) { | ||||
|         b.start(); | ||||
|         ItsNeighborsWrapper itsn{its, fn(its)}; | ||||
|         b.stop(); | ||||
| 
 | ||||
|         r.measurements[IndexCreation] += b.getElapsedSec(); | ||||
| 
 | ||||
|         b.start(); | ||||
|         auto res = its_split(itsn); | ||||
|         b.stop(); | ||||
| 
 | ||||
| //        if (res.size() != 2 || res[0].indices.size() != res[1].indices.size() )
 | ||||
| //            std::cerr << "Something is wrong, split result invalid" << std::endl;
 | ||||
| 
 | ||||
|         r.measurements[Split] += b.getElapsedSec(); | ||||
|     } | ||||
| 
 | ||||
|     r.measurements[IndexCreation] /= 10; | ||||
|     r.measurements[Split] /= 10; | ||||
| 
 | ||||
|     return r; | ||||
| } | ||||
| 
 | ||||
| const auto Seed = 0;// std::random_device{}();
 | ||||
| 
 | ||||
| static indexed_triangle_set make_sphere_rnd(double radius, double detail) | ||||
| { | ||||
|     using namespace Slic3r; | ||||
| 
 | ||||
|     auto sphere = its_make_sphere(radius, detail); | ||||
| 
 | ||||
|     auto vfidx = create_vertex_faces_index(sphere); | ||||
| 
 | ||||
|     const size_t vertexnum = sphere.vertices.size(); | ||||
|     const size_t facenum   = sphere.indices.size(); | ||||
| 
 | ||||
|     std::mt19937 rng{Seed}; | ||||
|     std::uniform_int_distribution<size_t> distv(sphere.vertices.size() / 2, sphere.vertices.size() - 1); | ||||
|     std::uniform_int_distribution<size_t> distf(sphere.indices.size() / 2, sphere.indices.size() - 1) ; | ||||
| 
 | ||||
|     std::vector<bool> was(vertexnum / 2, false); | ||||
| 
 | ||||
|     for (size_t i = 0; i < vertexnum / 2; ++i) { | ||||
|         size_t image = distv(rng); | ||||
|         if (was[image - vertexnum / 2]) continue; | ||||
|         was[image - vertexnum / 2] = true; | ||||
| 
 | ||||
|         std::swap(sphere.vertices[i], sphere.vertices[image]); | ||||
|         for (size_t face_id : vfidx[i]) { | ||||
|             for (int &vi : sphere.indices[face_id]) | ||||
|                 if (vi == int(i)) vi = image; | ||||
|         } | ||||
| 
 | ||||
|         for (size_t face_id : vfidx[image]) { | ||||
|             for (int &vi : sphere.indices[face_id]) | ||||
|                 if (vi == int(image)) vi = i; | ||||
|         } | ||||
| 
 | ||||
|         std::swap(vfidx[i], vfidx[image]); | ||||
|     } | ||||
| 
 | ||||
|     for (size_t i = 0; i < facenum / 2; ++i) { | ||||
|         size_t image = distf(rng); | ||||
|         std::swap(sphere.indices[i], sphere.indices[image]); | ||||
|     } | ||||
| 
 | ||||
|     return sphere; | ||||
| } | ||||
| 
 | ||||
| static indexed_triangle_set two_spheres(double detail) | ||||
| { | ||||
|     auto sphere1 = make_sphere_rnd(10., 2 * PI / detail), sphere2 = sphere1; | ||||
| 
 | ||||
|     its_transform(sphere1, identity3f().translate(Vec3f{-5.f, 0.f, 0.f})); | ||||
|     its_transform(sphere2, identity3f().translate(Vec3f{5.f, 0.f, 0.f})); | ||||
| 
 | ||||
|     its_merge(sphere1, sphere2); | ||||
| 
 | ||||
|     return sphere1; | ||||
| } | ||||
| 
 | ||||
| static indexed_triangle_set make_spheres(unsigned N, double detail) | ||||
| { | ||||
|     indexed_triangle_set ret, sphere = make_sphere_rnd(10., 2. * PI / detail); | ||||
| 
 | ||||
|     for (unsigned i = 0u ; i < N; ++i) | ||||
|         its_merge(ret, sphere); | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| constexpr double sq2 = std::sqrt(2.); | ||||
| 
 | ||||
| static const std::pair<const std::string, indexed_triangle_set> ToMeasure[] = { | ||||
| //    {"two_spheres_1x", two_spheres(60.)},
 | ||||
| //    {"two_spheres_2x", two_spheres(120.)},
 | ||||
| //    {"two_spheres_4x", two_spheres(240.)},
 | ||||
| //    {"two_spheres_8x", two_spheres(480.)},
 | ||||
| //    {"two_spheres_16x", two_spheres(2 * 480.)},
 | ||||
| //    {"two_spheres_32x", two_spheres(2 * 2 * 480.)},
 | ||||
| 
 | ||||
|     {"two_spheres_1x", two_spheres(60.)}, | ||||
|     {"two_spheres_2x", two_spheres(sq2 * 60.)}, | ||||
|     {"two_spheres_4x", two_spheres(2 * 60.)}, | ||||
|     {"two_spheres_8x", two_spheres(sq2 * 2. * 60.)}, | ||||
|     {"two_spheres_16x", two_spheres(4. * 60.)}, | ||||
|     {"two_spheres_32x", two_spheres(sq2 * 4. * 60.)}, | ||||
|     {"two_spheres_64x", two_spheres(8. * 60.)}, | ||||
|     {"two_spheres_128x", two_spheres(sq2 * 8. * 60.)}, | ||||
|     {"two_spheres_256x", two_spheres(16. * 60.)}, | ||||
|     {"two_spheres_512x", two_spheres(sq2 * 16. * 60.)}, | ||||
| 
 | ||||
|     {"2_spheres", make_spheres(2, 60.)}, | ||||
|     {"4_spheres", make_spheres(4, 60.)}, | ||||
|     {"8_spheres", make_spheres(8, 60.)}, | ||||
|     {"16_spheres", make_spheres(16,  60.)}, | ||||
|     {"32_spheres", make_spheres(32, 60.)}, | ||||
|     {"64_spheres", make_spheres(64, 60.)}, | ||||
|     {"128_spheres", make_spheres(128, 60.)}, | ||||
|     {"256_spheres", make_spheres(256, 60.)}, | ||||
|     {"512_spheres", make_spheres(512, 60.)}, | ||||
|     {"1024_spheres", make_spheres(1024, 60.)} | ||||
| }; | ||||
| 
 | ||||
| static const auto IndexFunctions = std::make_tuple( | ||||
|     std::make_pair("tamas's unordered_map based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_1); }), | ||||
|     std::make_pair("vojta std::sort based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_2); }), | ||||
|     std::make_pair("vojta tbb::parallel_sort based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_3); }), | ||||
|     std::make_pair("filip's vertex->face based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_5); }), | ||||
|     std::make_pair("vojta's vertex->face", [](const auto &its) { return measure_index(its, its_create_neighbors_index_9); }), | ||||
|     std::make_pair("vojta's vertex->face parallel", [](const auto &its) { return measure_index(its, its_create_neighbors_index_10); }), | ||||
|     std::make_pair("tamas's std::sort based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_6); }), | ||||
|     std::make_pair("tamas's tbb::parallel_sort based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_7); }), | ||||
|     std::make_pair("tamas's map based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_8); }), | ||||
|     std::make_pair("TriangleMesh split", [](const auto &its) { | ||||
| 
 | ||||
|         MeasureResult r; | ||||
|         for (int i = 0; i < 10; ++i) { | ||||
|             TriangleMesh m{its}; | ||||
|             Benchmark b; | ||||
| 
 | ||||
|             b.start(); | ||||
|             m.repair(); // FIXME: this does more than just create neighborhood map
 | ||||
|             b.stop(); | ||||
|             r.measurements[IndexCreation] += b.getElapsedSec(); | ||||
| 
 | ||||
|             b.start(); | ||||
|             auto res = m.split(); | ||||
|             b.stop(); | ||||
|             r.measurements[Split] += b.getElapsedSec(); | ||||
| 
 | ||||
| //            if (res.size() != 2 || res[0]->size() != res[1]->size())
 | ||||
| //                std::cerr << "Something is wrong, split result invalid" << std::endl;
 | ||||
|         } | ||||
|         r.measurements[IndexCreation] /= 10; | ||||
|         r.measurements[Split] /= 10; | ||||
| 
 | ||||
|         return r; | ||||
|     }) | ||||
| 
 | ||||
| //    std::make_pair("Vojta's vertex->face index", [](const auto &its){
 | ||||
| //        Benchmark b;
 | ||||
| //        b.start();
 | ||||
| //        auto index = create_vertex_faces_index(its);
 | ||||
| //        b.stop();
 | ||||
| 
 | ||||
| //        if (index.size() != its.vertices.size())
 | ||||
| //            std::cerr << "Something went wrong!";
 | ||||
| 
 | ||||
| //        return  MeasureResult{b.getElapsedSec(), 0., 0.};
 | ||||
| //    }),
 | ||||
| //    std::make_pair("Tamas's vertex->face index", [](const auto &its){
 | ||||
| //        Benchmark b;
 | ||||
| //        b.start();
 | ||||
| //        VertexFaceIndex index{its};
 | ||||
| //        b.stop();
 | ||||
| 
 | ||||
| //        if (index.size() < its.vertices.size())
 | ||||
| //            std::cerr << "Something went wrong!";
 | ||||
| 
 | ||||
| //        return  MeasureResult{b.getElapsedSec(), 0., 0.};
 | ||||
| //    })
 | ||||
| ); | ||||
| 
 | ||||
| static constexpr size_t IndexFuncNum = std::tuple_size_v<decltype (IndexFunctions)>; | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| int main(const int argc, const char * argv[]) | ||||
| { | ||||
|     using namespace Slic3r; | ||||
| 
 | ||||
|     std::array<MeasureResult, IndexFuncNum> results[std::size(ToMeasure)]; | ||||
|     std::array<std::string, IndexFuncNum> funcnames; | ||||
| 
 | ||||
|     for (size_t i = 0; i < std::size(ToMeasure); ++i) { | ||||
|         auto &m = ToMeasure[i]; | ||||
|         auto &name = m.first; | ||||
|         auto &mesh = m.second; | ||||
| 
 | ||||
| //        its_write_obj(mesh, (std::string(name) + ".obj").c_str());
 | ||||
| 
 | ||||
|         std::cout << "Mesh " << name << " has " << mesh.indices.size() << " faces and " << mesh.vertices.size() << " vertices." << std::endl; | ||||
|         libnest2d::opt::metaloop::apply([&mesh, i, &results, &funcnames](int N, auto &e) { | ||||
|             MeasureResult r = e.second(mesh); | ||||
|             funcnames[N] = e.first; | ||||
|             results[i][N] = r; | ||||
|         }, IndexFunctions); | ||||
|     } | ||||
| 
 | ||||
|     std::string outfilename = "out.csv"; | ||||
|     std::fstream outfile; | ||||
|     if (argc > 1) { | ||||
|         outfilename = argv[1]; | ||||
|         outfile.open(outfilename, std::fstream::out); | ||||
|         std::cout << outfilename << " will be used" << std::endl; | ||||
|     } | ||||
| 
 | ||||
|     std::ostream &out = outfile.is_open() ? outfile : std::cout; | ||||
| 
 | ||||
|     for (size_t m = 0; m < std::size(MeasureResult::Names); ++m) { | ||||
|         out << MeasureResult::Names[m] << "\n"; | ||||
|         out << std::endl; | ||||
|         out << "model;" ; | ||||
|         for (const std::string &funcname : funcnames) { | ||||
|             out << funcname << ";"; | ||||
|         } | ||||
| 
 | ||||
|         out << std::endl; | ||||
| 
 | ||||
|         for (size_t i = 0; i < std::size(ToMeasure); ++i) { | ||||
|             const auto &result_row = results[i]; | ||||
|             const std::string &name = ToMeasure[i].first; | ||||
|             out << name << ";"; | ||||
|             for (auto &r : result_row) | ||||
|                 out << r.measurements[m] << ";"; | ||||
| 
 | ||||
|             out << std::endl; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
|  | @ -149,9 +149,11 @@ struct indexed_triangle_set | |||
| 	} | ||||
| 
 | ||||
| 	std::vector<stl_triangle_vertex_indices> 	indices; | ||||
| 	std::vector<stl_vertex>       				    vertices; | ||||
|     std::vector<stl_vertex>       				    vertices; | ||||
| 	//FIXME add normals once we get rid of the stl_file from TriangleMesh completely.
 | ||||
| 	//std::vector<stl_normal> 					      normals
 | ||||
| 
 | ||||
|     bool empty() const { return indices.empty() || vertices.empty(); } | ||||
| }; | ||||
| 
 | ||||
| extern bool stl_open(stl_file *stl, const char *file); | ||||
|  |  | |||
|  | @ -205,6 +205,7 @@ add_library(libslic3r STATIC | |||
|     TriangleMesh.hpp | ||||
|     TriangleMeshSlicer.cpp | ||||
|     TriangleMeshSlicer.hpp | ||||
|     MeshSplitImpl.hpp | ||||
|     TriangulateWall.hpp | ||||
|     TriangulateWall.cpp | ||||
|     utils.cpp | ||||
|  | @ -266,8 +267,6 @@ add_library(libslic3r STATIC | |||
|     SLA/SupportPoint.hpp | ||||
|     SLA/SupportPointGenerator.hpp | ||||
|     SLA/SupportPointGenerator.cpp | ||||
|     SLA/Contour3D.hpp | ||||
|     SLA/Contour3D.cpp | ||||
|     SLA/IndexedMesh.hpp | ||||
|     SLA/IndexedMesh.cpp | ||||
|     SLA/Clustering.hpp | ||||
|  |  | |||
|  | @ -296,7 +296,7 @@ void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out) | |||
| void import_sla_archive( | ||||
|     const std::string &      zipfname, | ||||
|     Vec2i                    windowsize, | ||||
|     TriangleMesh &           out, | ||||
|     indexed_triangle_set &           out, | ||||
|     DynamicPrintConfig &     profile, | ||||
|     std::function<bool(int)> progr) | ||||
| { | ||||
|  | @ -316,7 +316,7 @@ void import_sla_archive( | |||
|         extract_slices_from_sla_archive(arch, rstp, progr); | ||||
| 
 | ||||
|     if (!slices.empty()) | ||||
|         out = slices_to_triangle_mesh(slices, 0, slicp.layerh, slicp.initial_layerh); | ||||
|         out = slices_to_mesh(slices, 0, slicp.layerh, slicp.initial_layerh); | ||||
| } | ||||
| 
 | ||||
| using ConfMap = std::map<std::string, std::string>; | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ class SL1Archive: public SLAPrinter { | |||
| protected: | ||||
|     uqptr<sla::RasterBase> create_raster() const override; | ||||
|     sla::RasterEncoder get_encoder() const override; | ||||
|      | ||||
| 
 | ||||
| public: | ||||
|      | ||||
|     SL1Archive() = default; | ||||
|  | @ -43,14 +43,14 @@ void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out); | |||
| void import_sla_archive( | ||||
|     const std::string &      zipfname, | ||||
|     Vec2i                    windowsize, | ||||
|     TriangleMesh &           out, | ||||
|     indexed_triangle_set &   out, | ||||
|     DynamicPrintConfig &     profile, | ||||
|     std::function<bool(int)> progr = [](int) { return true; }); | ||||
| 
 | ||||
| inline void import_sla_archive( | ||||
|     const std::string &      zipfname, | ||||
|     Vec2i                    windowsize, | ||||
|     TriangleMesh &           out, | ||||
|     indexed_triangle_set &   out, | ||||
|     std::function<bool(int)> progr = [](int) { return true; }) | ||||
| { | ||||
|     DynamicPrintConfig profile; | ||||
|  |  | |||
|  | @ -74,29 +74,6 @@ public: | |||
|     } | ||||
| }; | ||||
| 
 | ||||
| /// A very simple range concept implementation with iterator-like objects.
 | ||||
| template<class It> class Range | ||||
| { | ||||
|     It from, to; | ||||
| 
 | ||||
| public: | ||||
|     // The class is ready for range based for loops.
 | ||||
|     It begin() const { return from; } | ||||
|     It end() const { return to; } | ||||
| 
 | ||||
|     // The iterator type can be obtained this way.
 | ||||
|     using Type = It; | ||||
| 
 | ||||
|     Range() = default; | ||||
|     Range(It &&b, It &&e) | ||||
|         : from(std::forward<It>(b)), to(std::forward<It>(e)) | ||||
|     {} | ||||
| 
 | ||||
|     // Some useful container-like methods...
 | ||||
|     inline size_t size() const { return end() - begin(); } | ||||
|     inline bool   empty() const { return size() == 0; } | ||||
| }; | ||||
| 
 | ||||
| template<class C> bool all_of(const C &container) | ||||
| { | ||||
|     return std::all_of(container.begin(), | ||||
|  |  | |||
							
								
								
									
										226
									
								
								src/libslic3r/MeshSplitImpl.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										226
									
								
								src/libslic3r/MeshSplitImpl.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,226 @@ | |||
| #ifndef MESHSPLITIMPL_HPP | ||||
| #define MESHSPLITIMPL_HPP | ||||
| 
 | ||||
| #include "TriangleMesh.hpp" | ||||
| #include "libnest2d/tools/benchmark.h" | ||||
| #include "Execution/ExecutionTBB.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| template<class ExPolicy> | ||||
| std::vector<Vec3i> create_neighbors_index(ExPolicy &&ex, const indexed_triangle_set &its); | ||||
| 
 | ||||
| namespace meshsplit_detail { | ||||
| 
 | ||||
| template<class Its, class Enable = void> struct ItsWithNeighborsIndex_ { | ||||
|     using Index = typename Its::Index; | ||||
|     static const indexed_triangle_set &get_its(const Its &m) { return m.get_its();} | ||||
|     static const Index &get_index(const Its &m) { return m.get_index(); } | ||||
| }; | ||||
| 
 | ||||
| // Define a default neighbors index for indexed_triangle_set
 | ||||
| template<> struct ItsWithNeighborsIndex_<indexed_triangle_set> { | ||||
|     using Index = std::vector<Vec3i>; | ||||
|     static const indexed_triangle_set &get_its(const indexed_triangle_set &its) noexcept { return its; } | ||||
|     static Index get_index(const indexed_triangle_set &its) noexcept | ||||
|     { | ||||
|         return create_neighbors_index(ex_tbb, its); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| // Visit all unvisited neighboring facets that are reachable from the first unvisited facet,
 | ||||
| // and return them.
 | ||||
| template<class NeighborIndex> | ||||
| std::vector<size_t> its_find_unvisited_neighbors( | ||||
|     const indexed_triangle_set &its, | ||||
|     const NeighborIndex &       neighbor_index, | ||||
|     std::vector<char> &         visited) | ||||
| { | ||||
|     using stack_el = size_t; | ||||
| 
 | ||||
|     auto facestack = reserve_vector<stack_el>(its.indices.size()); | ||||
|     auto push = [&facestack] (const stack_el &s) { facestack.emplace_back(s); }; | ||||
|     auto pop  = [&facestack] () -> stack_el { | ||||
|         stack_el ret = facestack.back(); | ||||
|         facestack.pop_back(); | ||||
|         return ret; | ||||
|     }; | ||||
| 
 | ||||
|     // find the next unvisited facet and push the index
 | ||||
|     auto facet = std::find(visited.begin(), visited.end(), false); | ||||
|     std::vector<size_t> ret; | ||||
| 
 | ||||
|     if (facet != visited.end()) { | ||||
|         ret.reserve(its.indices.size()); | ||||
|         auto idx = size_t(facet - visited.begin()); | ||||
|         push(idx); | ||||
|         ret.emplace_back(idx); | ||||
|         visited[idx] = true; | ||||
|     } | ||||
| 
 | ||||
|     while (!facestack.empty()) { | ||||
|         size_t facet_idx = pop(); | ||||
|         const auto &neighbors = neighbor_index[facet_idx]; | ||||
|         for (auto neighbor_idx : neighbors) { | ||||
|             if (size_t(neighbor_idx) < visited.size() && !visited[size_t(neighbor_idx)]) { | ||||
|                 visited[size_t(neighbor_idx)] = true; | ||||
|                 push(stack_el(neighbor_idx)); | ||||
|                 ret.emplace_back(size_t(neighbor_idx)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| } // namespace meshsplit_detail
 | ||||
| 
 | ||||
| template<class IndexT> struct ItsNeighborsWrapper | ||||
| { | ||||
|     using Index = IndexT; | ||||
|     const indexed_triangle_set *its; | ||||
|     IndexT index; | ||||
| 
 | ||||
|     ItsNeighborsWrapper(const indexed_triangle_set &m, IndexT &&idx) | ||||
|         : its{&m}, index{std::move(idx)} | ||||
|     {} | ||||
| 
 | ||||
|     const auto& get_its() const noexcept { return *its; } | ||||
|     const auto& get_index() const noexcept { return index; } | ||||
| }; | ||||
| 
 | ||||
| // Splits a mesh into multiple meshes when possible.
 | ||||
| template<class Its, class OutputIt> | ||||
| void its_split(const Its &m, OutputIt out_it) | ||||
| { | ||||
|     using namespace meshsplit_detail; | ||||
| 
 | ||||
|     const indexed_triangle_set &its = ItsWithNeighborsIndex_<Its>::get_its(m); | ||||
| 
 | ||||
|     std::vector<char> visited(its.indices.size(), false); | ||||
| 
 | ||||
|     struct VertexConv { | ||||
|         size_t part_id      = std::numeric_limits<size_t>::max(); | ||||
|         size_t vertex_image; | ||||
|     }; | ||||
|     std::vector<VertexConv> vidx_conv(its.vertices.size()); | ||||
| 
 | ||||
|     const auto& neighbor_index = ItsWithNeighborsIndex_<Its>::get_index(m); | ||||
| 
 | ||||
|     for (size_t part_id = 0;; ++part_id) { | ||||
|         std::vector<size_t> facets = | ||||
|             its_find_unvisited_neighbors(its, neighbor_index, visited); | ||||
| 
 | ||||
|         if (facets.empty()) | ||||
|             break; | ||||
| 
 | ||||
|         // Create a new mesh for the part that was just split off.
 | ||||
|         indexed_triangle_set mesh; | ||||
|         mesh.indices.reserve(facets.size()); | ||||
|         mesh.vertices.reserve(std::min(facets.size() * 3, its.vertices.size())); | ||||
| 
 | ||||
|         // Assign the facets to the new mesh.
 | ||||
|         for (size_t face_id : facets) { | ||||
|             const auto &face = its.indices[face_id]; | ||||
|             Vec3i       new_face; | ||||
|             for (size_t v = 0; v < 3; ++v) { | ||||
|                 auto vi = face(v); | ||||
| 
 | ||||
|                 if (vidx_conv[vi].part_id != part_id) { | ||||
|                     vidx_conv[vi] = {part_id, mesh.vertices.size()}; | ||||
|                     mesh.vertices.emplace_back(its.vertices[size_t(vi)]); | ||||
|                 } | ||||
| 
 | ||||
|                 new_face(v) = vidx_conv[vi].vertex_image; | ||||
|             } | ||||
| 
 | ||||
|             mesh.indices.emplace_back(new_face); | ||||
|         } | ||||
| 
 | ||||
|         out_it = std::move(mesh); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template<class Its> | ||||
| std::vector<indexed_triangle_set> its_split(const Its &its) | ||||
| { | ||||
|     auto ret = reserve_vector<indexed_triangle_set>(3); | ||||
|     its_split(its, std::back_inserter(ret)); | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| template<class Its> bool its_is_splittable(const Its &m) | ||||
| { | ||||
|     using namespace meshsplit_detail; | ||||
|     const indexed_triangle_set &its = ItsWithNeighborsIndex_<Its>::get_its(m); | ||||
|     const auto& neighbor_index = ItsWithNeighborsIndex_<Its>::get_index(m); | ||||
| 
 | ||||
|     std::vector<char> visited(its.indices.size(), false); | ||||
|     auto faces = its_find_unvisited_neighbors(its, neighbor_index, visited); | ||||
| 
 | ||||
|     return !faces.empty(); | ||||
| } | ||||
| 
 | ||||
| inline int get_vertex_index(size_t vertex_index, const stl_triangle_vertex_indices &triangle_indices) { | ||||
|     if (int(vertex_index) == triangle_indices[0]) return 0; | ||||
|     if (int(vertex_index) == triangle_indices[1]) return 1; | ||||
|     if (int(vertex_index) == triangle_indices[2]) return 2; | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| inline Vec2crd get_edge_indices(int edge_index, const stl_triangle_vertex_indices &triangle_indices) | ||||
| { | ||||
|     int next_edge_index = (edge_index == 2) ? 0 : edge_index + 1; | ||||
|     int vi0             = triangle_indices[edge_index]; | ||||
|     int vi1             = triangle_indices[next_edge_index]; | ||||
|     return Vec2crd(vi0, vi1); | ||||
| } | ||||
| 
 | ||||
| template<class ExPolicy> | ||||
| std::vector<Vec3i> create_neighbors_index(ExPolicy &&ex, const indexed_triangle_set &its) | ||||
| { | ||||
|     const std::vector<stl_triangle_vertex_indices> &indices = its.indices; | ||||
|     size_t vertices_size = its.vertices.size(); | ||||
| 
 | ||||
|     if (indices.empty() || vertices_size == 0) return {}; | ||||
| 
 | ||||
|     auto               vertex_triangles = VertexFaceIndex{its}; | ||||
|     static constexpr int no_value         = -1; | ||||
|     std::vector<Vec3i> neighbors(indices.size(), | ||||
|                                  Vec3i(no_value, no_value, no_value)); | ||||
| 
 | ||||
|     //for (const stl_triangle_vertex_indices& triangle_indices : indices) {
 | ||||
|     execution::for_each(ex, size_t(0), indices.size(), | ||||
|         [&neighbors, &indices, &vertex_triangles] (size_t index) | ||||
|         { | ||||
|             Vec3i& neighbor = neighbors[index]; | ||||
|             const stl_triangle_vertex_indices & triangle_indices = indices[index]; | ||||
|             for (int edge_index = 0; edge_index < 3; ++edge_index) { | ||||
|                 // check if done
 | ||||
|                 int& neighbor_edge = neighbor[edge_index]; | ||||
|                 if (neighbor_edge != no_value) continue; | ||||
|                 Vec2crd edge_indices = get_edge_indices(edge_index, triangle_indices); | ||||
|                 // IMPROVE: use same vector for 2 sides of triangle
 | ||||
|                 const auto &faces_range = vertex_triangles[edge_indices[0]]; | ||||
|                 for (const size_t &face : faces_range) { | ||||
|                     if (face <= index) continue; | ||||
|                     const stl_triangle_vertex_indices &face_indices = indices[face]; | ||||
|                     int vertex_index = get_vertex_index(edge_indices[1], face_indices); | ||||
|                     // NOT Contain second vertex?
 | ||||
|                     if (vertex_index < 0) continue; | ||||
|                     // Has NOT oposit direction?
 | ||||
|                     if (edge_indices[0] != face_indices[(vertex_index + 1) % 3]) continue; | ||||
|                     neighbor_edge = face; | ||||
|                     neighbors[face][vertex_index] = index; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         }, execution::max_concurrency(ex)); | ||||
| 
 | ||||
|     return neighbors; | ||||
| } | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| #endif // MESHSPLITIMPL_HPP
 | ||||
|  | @ -21,11 +21,11 @@ namespace Slic3r { | |||
| 
 | ||||
| class TriangleMeshDataAdapter { | ||||
| public: | ||||
|     const TriangleMesh &mesh; | ||||
|     const indexed_triangle_set &its; | ||||
|     float voxel_scale; | ||||
| 
 | ||||
|     size_t polygonCount() const { return mesh.its.indices.size(); } | ||||
|     size_t pointCount() const   { return mesh.its.vertices.size(); } | ||||
|     size_t polygonCount() const { return its.indices.size(); } | ||||
|     size_t pointCount() const   { return its.vertices.size(); } | ||||
|     size_t vertexCount(size_t) const { return 3; } | ||||
| 
 | ||||
|     // Return position pos in local grid index space for polygon n and vertex v
 | ||||
|  | @ -33,19 +33,19 @@ public: | |||
|     // And the voxel count per unit volume can be affected this way.
 | ||||
|     void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const | ||||
|     { | ||||
|         auto vidx = size_t(mesh.its.indices[n](Eigen::Index(v))); | ||||
|         Slic3r::Vec3d p = mesh.its.vertices[vidx].cast<double>() * voxel_scale; | ||||
|         auto vidx = size_t(its.indices[n](Eigen::Index(v))); | ||||
|         Slic3r::Vec3d p = its.vertices[vidx].cast<double>() * voxel_scale; | ||||
|         pos = {p.x(), p.y(), p.z()}; | ||||
|     } | ||||
| 
 | ||||
|     TriangleMeshDataAdapter(const TriangleMesh &m, float voxel_sc = 1.f) | ||||
|         : mesh{m}, voxel_scale{voxel_sc} {}; | ||||
|     TriangleMeshDataAdapter(const indexed_triangle_set &m, float voxel_sc = 1.f) | ||||
|         : its{m}, voxel_scale{voxel_sc} {}; | ||||
| }; | ||||
| 
 | ||||
| // TODO: Do I need to call initialize? Seems to work without it as well but the
 | ||||
| // docs say it should be called ones. It does a mutex lock-unlock sequence all
 | ||||
| // even if was called previously.
 | ||||
| openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh &            mesh, | ||||
| openvdb::FloatGrid::Ptr mesh_to_grid(const indexed_triangle_set &    mesh, | ||||
|                                      const openvdb::math::Transform &tr, | ||||
|                                      float voxel_scale, | ||||
|                                      float exteriorBandWidth, | ||||
|  | @ -54,22 +54,17 @@ openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh &            mesh, | |||
| { | ||||
|     openvdb::initialize(); | ||||
| 
 | ||||
|     TriangleMeshPtrs meshparts_raw = mesh.split(); | ||||
|     auto meshparts = reserve_vector<std::unique_ptr<TriangleMesh>>(meshparts_raw.size()); | ||||
|     for (auto *p : meshparts_raw) | ||||
|         meshparts.emplace_back(p); | ||||
|     std::vector<indexed_triangle_set> meshparts = its_split(mesh); | ||||
| 
 | ||||
|     auto it = std::remove_if(meshparts.begin(), meshparts.end(), [](auto &m) { | ||||
|          m->require_shared_vertices(); | ||||
|          return m->volume() < EPSILON; | ||||
|      }); | ||||
|     auto it = std::remove_if(meshparts.begin(), meshparts.end(), | ||||
|                              [](auto &m) { return its_volume(m) < EPSILON; }); | ||||
| 
 | ||||
|     meshparts.erase(it, meshparts.end()); | ||||
| 
 | ||||
|     openvdb::FloatGrid::Ptr grid; | ||||
|     for (auto &m : meshparts) { | ||||
|         auto subgrid = openvdb::tools::meshToVolume<openvdb::FloatGrid>( | ||||
|             TriangleMeshDataAdapter{*m, voxel_scale}, tr, exteriorBandWidth, | ||||
|             TriangleMeshDataAdapter{m, voxel_scale}, tr, exteriorBandWidth, | ||||
|             interiorBandWidth, flags); | ||||
| 
 | ||||
|         if (grid && subgrid) openvdb::tools::csgUnion(*grid, *subgrid); | ||||
|  | @ -91,11 +86,10 @@ openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh &            mesh, | |||
|     return grid; | ||||
| } | ||||
| 
 | ||||
| template<class Grid> | ||||
| sla::Contour3D _volumeToMesh(const Grid &grid, | ||||
|                              double      isovalue, | ||||
|                              double      adaptivity, | ||||
|                              bool        relaxDisorientedTriangles) | ||||
| indexed_triangle_set grid_to_mesh(const openvdb::FloatGrid &grid, | ||||
|                           double                    isovalue, | ||||
|                           double                    adaptivity, | ||||
|                           bool                      relaxDisorientedTriangles) | ||||
| { | ||||
|     openvdb::initialize(); | ||||
| 
 | ||||
|  | @ -111,36 +105,20 @@ sla::Contour3D _volumeToMesh(const Grid &grid, | |||
|         scale = grid.template metaValue<float>("voxel_scale"); | ||||
|     }  catch (...) { } | ||||
| 
 | ||||
|     sla::Contour3D ret; | ||||
|     ret.points.reserve(points.size()); | ||||
|     ret.faces3.reserve(triangles.size()); | ||||
|     ret.faces4.reserve(quads.size()); | ||||
|     indexed_triangle_set ret; | ||||
|     ret.vertices.reserve(points.size()); | ||||
|     ret.indices.reserve(triangles.size() + quads.size() * 2); | ||||
| 
 | ||||
|     for (auto &v : points) ret.points.emplace_back(to_vec3d(v) / scale); | ||||
|     for (auto &v : triangles) ret.faces3.emplace_back(to_vec3i(v)); | ||||
|     for (auto &v : quads) ret.faces4.emplace_back(to_vec4i(v)); | ||||
|     for (auto &v : points) ret.vertices.emplace_back(to_vec3f(v) / scale); | ||||
|     for (auto &v : triangles) ret.indices.emplace_back(to_vec3i(v)); | ||||
|     for (auto &quad : quads) { | ||||
|         ret.indices.emplace_back(quad(0), quad(1), quad(2)); | ||||
|         ret.indices.emplace_back(quad(2), quad(3), quad(0)); | ||||
|     } | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| TriangleMesh grid_to_mesh(const openvdb::FloatGrid &grid, | ||||
|                           double                    isovalue, | ||||
|                           double                    adaptivity, | ||||
|                           bool                      relaxDisorientedTriangles) | ||||
| { | ||||
|     return to_triangle_mesh( | ||||
|         _volumeToMesh(grid, isovalue, adaptivity, relaxDisorientedTriangles)); | ||||
| } | ||||
| 
 | ||||
| sla::Contour3D grid_to_contour3d(const openvdb::FloatGrid &grid, | ||||
|                                  double                    isovalue, | ||||
|                                  double                    adaptivity, | ||||
|                                  bool relaxDisorientedTriangles) | ||||
| { | ||||
|     return _volumeToMesh(grid, isovalue, adaptivity, | ||||
|                          relaxDisorientedTriangles); | ||||
| } | ||||
| 
 | ||||
| openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid, | ||||
|                                         double                    iso, | ||||
|                                         double                    er, | ||||
|  |  | |||
|  | @ -2,7 +2,6 @@ | |||
| #define OPENVDBUTILS_HPP | ||||
| 
 | ||||
| #include <libslic3r/TriangleMesh.hpp> | ||||
| #include <libslic3r/SLA/Contour3D.hpp> | ||||
| 
 | ||||
| #ifdef _MSC_VER | ||||
| // Suppress warning C4146 in include/gmp.h(2177,31): unary minus operator applied to unsigned type, result still unsigned 
 | ||||
|  | @ -19,7 +18,6 @@ namespace Slic3r { | |||
| inline Vec3f to_vec3f(const openvdb::Vec3s &v) { return Vec3f{v.x(), v.y(), v.z()}; } | ||||
| inline Vec3d to_vec3d(const openvdb::Vec3s &v) { return to_vec3f(v).cast<double>(); } | ||||
| inline Vec3i to_vec3i(const openvdb::Vec3I &v) { return Vec3i{int(v[0]), int(v[1]), int(v[2])}; } | ||||
| inline Vec4i to_vec4i(const openvdb::Vec4I &v) { return Vec4i{int(v[0]), int(v[1]), int(v[2]), int(v[3])}; } | ||||
| 
 | ||||
| // Here voxel_scale defines the scaling of voxels which affects the voxel count.
 | ||||
| // 1.0 value means a voxel for every unit cube. 2 means the model is scaled to
 | ||||
|  | @ -28,22 +26,17 @@ inline Vec4i to_vec4i(const openvdb::Vec4I &v) { return Vec4i{int(v[0]), int(v[1 | |||
| // achievable through the Transform parameter. (TODO: or is it?)
 | ||||
| // The resulting grid will contain the voxel_scale in its metadata under the
 | ||||
| // "voxel_scale" key to be used in grid_to_mesh function.
 | ||||
| openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh &            mesh, | ||||
| openvdb::FloatGrid::Ptr mesh_to_grid(const indexed_triangle_set &    mesh, | ||||
|                                      const openvdb::math::Transform &tr = {}, | ||||
|                                      float voxel_scale = 1.f, | ||||
|                                      float voxel_scale                  = 1.f, | ||||
|                                      float exteriorBandWidth = 3.0f, | ||||
|                                      float interiorBandWidth = 3.0f, | ||||
|                                      int   flags             = 0); | ||||
| 
 | ||||
| sla::Contour3D grid_to_contour3d(const openvdb::FloatGrid &grid, | ||||
|                                  double                    isovalue, | ||||
|                                  double                    adaptivity, | ||||
|                                  bool relaxDisorientedTriangles = true); | ||||
| 
 | ||||
| TriangleMesh grid_to_mesh(const openvdb::FloatGrid &grid, | ||||
|                           double                    isovalue   = 0.0, | ||||
|                           double                    adaptivity = 0.0, | ||||
|                           bool relaxDisorientedTriangles       = true); | ||||
| indexed_triangle_set grid_to_mesh(const openvdb::FloatGrid &grid, | ||||
|                                   double                    isovalue   = 0.0, | ||||
|                                   double                    adaptivity = 0.0, | ||||
|                                   bool relaxDisorientedTriangles = true); | ||||
| 
 | ||||
| openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid, | ||||
|                                         double                    iso, | ||||
|  |  | |||
|  | @ -21,6 +21,12 @@ class MultiPoint; | |||
| class Point; | ||||
| using Vector = Point; | ||||
| 
 | ||||
| // Base template for eigen derived vectors
 | ||||
| template<int N, int M, class T> | ||||
| using Mat = Eigen::Matrix<T, N, M, Eigen::DontAlign, N, M>; | ||||
| 
 | ||||
| template<int N, class T> using Vec = Mat<N, 1, T>; | ||||
| 
 | ||||
| // Eigen types, to replace the Slic3r's own types in the future.
 | ||||
| // Vector types with a fixed point coordinate base type.
 | ||||
| using Vec2crd = Eigen::Matrix<coord_t,  2, 1, Eigen::DontAlign>; | ||||
|  | @ -54,11 +60,19 @@ using Matrix3d       = Eigen::Matrix<double, 3, 3, Eigen::DontAlign>; | |||
| using Matrix4f       = Eigen::Matrix<float,  4, 4, Eigen::DontAlign>; | ||||
| using Matrix4d       = Eigen::Matrix<double, 4, 4, Eigen::DontAlign>; | ||||
| 
 | ||||
| template<int N, class T> | ||||
| using Transform = Eigen::Transform<float, N, Eigen::Affine, Eigen::DontAlign>; | ||||
| 
 | ||||
| using Transform2f    = Eigen::Transform<float,  2, Eigen::Affine, Eigen::DontAlign>; | ||||
| using Transform2d    = Eigen::Transform<double, 2, Eigen::Affine, Eigen::DontAlign>; | ||||
| using Transform3f    = Eigen::Transform<float,  3, Eigen::Affine, Eigen::DontAlign>; | ||||
| using Transform3d    = Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign>; | ||||
| 
 | ||||
| // I don't know why Eigen::Transform::Identity() return a const object...
 | ||||
| template<int N, class T> Transform<N, T> identity() { return Transform<N, T>::Identity(); } | ||||
| inline const auto &identity3f = identity<3, float>; | ||||
| inline const auto &identity3d = identity<3, double>; | ||||
| 
 | ||||
| inline bool operator<(const Vec2d &lhs, const Vec2d &rhs) { return lhs.x() < rhs.x() || (lhs.x() == rhs.x() && lhs.y() < rhs.y()); } | ||||
| 
 | ||||
| template<int Options> | ||||
|  | @ -488,4 +502,19 @@ namespace cereal { | |||
| 	template<class Archive> void save(Archive& archive, Slic3r::Matrix2f &m) { archive.saveBinary((char*)m.data(), sizeof(float) * 4); } | ||||
| } | ||||
| 
 | ||||
| // To be able to use Vec<> and Mat<> in range based for loops:
 | ||||
| namespace Eigen { | ||||
| template<class T, int N, int M> | ||||
| T* begin(Slic3r::Mat<N, M, T> &mat) { return mat.data(); } | ||||
| 
 | ||||
| template<class T, int N, int M> | ||||
| T* end(Slic3r::Mat<N, M, T> &mat) { return mat.data() + N * M; } | ||||
| 
 | ||||
| template<class T, int N, int M> | ||||
| const T* begin(const Slic3r::Mat<N, M, T> &mat) { return mat.data(); } | ||||
| 
 | ||||
| template<class T, int N, int M> | ||||
| const T* end(const Slic3r::Mat<N, M, T> &mat) { return mat.data() + N * M; } | ||||
| } // namespace Eigen
 | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -1,149 +0,0 @@ | |||
| #include <libslic3r/SLA/Contour3D.hpp> | ||||
| #include <libslic3r/SLA/IndexedMesh.hpp> | ||||
| 
 | ||||
| #include <libslic3r/Format/objparser.hpp> | ||||
| 
 | ||||
| namespace Slic3r { namespace sla { | ||||
| 
 | ||||
| Contour3D::Contour3D(const TriangleMesh &trmesh) | ||||
| { | ||||
|     points.reserve(trmesh.its.vertices.size()); | ||||
|     faces3.reserve(trmesh.its.indices.size()); | ||||
|      | ||||
|     for (auto &v : trmesh.its.vertices) | ||||
|         points.emplace_back(v.cast<double>()); | ||||
|      | ||||
|     std::copy(trmesh.its.indices.begin(), trmesh.its.indices.end(), | ||||
|               std::back_inserter(faces3)); | ||||
| } | ||||
| 
 | ||||
| Contour3D::Contour3D(TriangleMesh &&trmesh) | ||||
| { | ||||
|     points.reserve(trmesh.its.vertices.size()); | ||||
|      | ||||
|     for (auto &v : trmesh.its.vertices) | ||||
|         points.emplace_back(v.cast<double>()); | ||||
|      | ||||
|     faces3.swap(trmesh.its.indices); | ||||
| } | ||||
| 
 | ||||
| Contour3D::Contour3D(const IndexedMesh &emesh) { | ||||
|     points.reserve(emesh.vertices().size()); | ||||
|     faces3.reserve(emesh.indices().size()); | ||||
|      | ||||
|     for (const Vec3f& vert : emesh.vertices()) | ||||
|         points.emplace_back(vert.cast<double>()); | ||||
|      | ||||
|     for (const auto& ind : emesh.indices()) | ||||
|         faces3.emplace_back(ind); | ||||
| } | ||||
| 
 | ||||
| Contour3D &Contour3D::merge(const Contour3D &ctr) | ||||
| { | ||||
|     auto N = coord_t(points.size()); | ||||
|     auto N_f3 = faces3.size(); | ||||
|     auto N_f4 = faces4.size(); | ||||
|      | ||||
|     points.insert(points.end(), ctr.points.begin(), ctr.points.end()); | ||||
|     faces3.insert(faces3.end(), ctr.faces3.begin(), ctr.faces3.end()); | ||||
|     faces4.insert(faces4.end(), ctr.faces4.begin(), ctr.faces4.end()); | ||||
|      | ||||
|     for(size_t n = N_f3; n < faces3.size(); n++) { | ||||
|         auto& idx = faces3[n]; idx.x() += N; idx.y() += N; idx.z() += N; | ||||
|     } | ||||
|      | ||||
|     for(size_t n = N_f4; n < faces4.size(); n++) { | ||||
|         auto& idx = faces4[n]; for (int k = 0; k < 4; k++) idx(k) += N; | ||||
|     }         | ||||
|      | ||||
|     return *this; | ||||
| } | ||||
| 
 | ||||
| Contour3D &Contour3D::merge(const Pointf3s &triangles) | ||||
| { | ||||
|     const size_t offs = points.size(); | ||||
|     points.insert(points.end(), triangles.begin(), triangles.end()); | ||||
|     faces3.reserve(faces3.size() + points.size() / 3); | ||||
|      | ||||
|     for(int i = int(offs); i < int(points.size()); i += 3) | ||||
|         faces3.emplace_back(i, i + 1, i + 2); | ||||
|      | ||||
|     return *this; | ||||
| } | ||||
| 
 | ||||
| void Contour3D::to_obj(std::ostream &stream) | ||||
| { | ||||
|     for(auto& p : points) | ||||
|         stream << "v " << p.transpose() << "\n"; | ||||
|      | ||||
|     for(auto& f : faces3)  | ||||
|         stream << "f " << (f + Vec3i(1, 1, 1)).transpose() << "\n"; | ||||
|      | ||||
|     for(auto& f : faces4) | ||||
|         stream << "f " << (f + Vec4i(1, 1, 1, 1)).transpose() << "\n"; | ||||
| } | ||||
| 
 | ||||
| void Contour3D::from_obj(std::istream &stream) | ||||
| { | ||||
|     ObjParser::ObjData data; | ||||
|     ObjParser::objparse(stream, data); | ||||
|      | ||||
|     points.reserve(data.coordinates.size() / 4 + 1); | ||||
|     auto &coords = data.coordinates; | ||||
|     for (size_t i = 0; i < coords.size(); i += 4) | ||||
|         points.emplace_back(coords[i], coords[i + 1], coords[i + 2]); | ||||
|      | ||||
|     Vec3i triangle; | ||||
|     Vec4i quad; | ||||
|     size_t v = 0; | ||||
|     while(v < data.vertices.size()) { | ||||
|         size_t N = 0; | ||||
|         size_t i = v; | ||||
|         while (data.vertices[v++].coordIdx != -1) ++N; | ||||
|          | ||||
|         std::function<void(int, int)> setfn; | ||||
|         if (N < 3 || N > 4) continue; | ||||
|         else if (N == 3) setfn = [&triangle](int k, int f) { triangle(k) = f; }; | ||||
|         else setfn = [&quad](int k, int f) { quad(k) = f; }; | ||||
|          | ||||
|         for (size_t j = 0; j < N; ++j) | ||||
|             setfn(int(j), data.vertices[i + j].coordIdx); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TriangleMesh to_triangle_mesh(const Contour3D &ctour) { | ||||
|     if (ctour.faces4.empty()) return {ctour.points, ctour.faces3}; | ||||
|      | ||||
|     std::vector<Vec3i> triangles; | ||||
|      | ||||
|     triangles.reserve(ctour.faces3.size() + 2 * ctour.faces4.size()); | ||||
|     std::copy(ctour.faces3.begin(), ctour.faces3.end(), | ||||
|               std::back_inserter(triangles)); | ||||
|      | ||||
|     for (auto &quad : ctour.faces4) { | ||||
|         triangles.emplace_back(quad(0), quad(1), quad(2)); | ||||
|         triangles.emplace_back(quad(2), quad(3), quad(0)); | ||||
|     } | ||||
|      | ||||
|     return {ctour.points, std::move(triangles)}; | ||||
| } | ||||
| 
 | ||||
| TriangleMesh to_triangle_mesh(Contour3D &&ctour) { | ||||
|     if (ctour.faces4.empty()) | ||||
|         return {std::move(ctour.points), std::move(ctour.faces3)}; | ||||
|      | ||||
|     std::vector<Vec3i> triangles; | ||||
|      | ||||
|     triangles.reserve(ctour.faces3.size() + 2 * ctour.faces4.size()); | ||||
|     std::copy(ctour.faces3.begin(), ctour.faces3.end(), | ||||
|               std::back_inserter(triangles)); | ||||
|      | ||||
|     for (auto &quad : ctour.faces4) { | ||||
|         triangles.emplace_back(quad(0), quad(1), quad(2)); | ||||
|         triangles.emplace_back(quad(2), quad(3), quad(0)); | ||||
|     } | ||||
|      | ||||
|     return {std::move(ctour.points), std::move(triangles)}; | ||||
| } | ||||
| 
 | ||||
| }} // namespace Slic3r::sla
 | ||||
|  | @ -1,48 +0,0 @@ | |||
| #ifndef SLA_CONTOUR3D_HPP | ||||
| #define SLA_CONTOUR3D_HPP | ||||
| 
 | ||||
| #include <libslic3r/TriangleMesh.hpp> | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| // Used for quads (TODO: remove this, and convert quads to triangles in OpenVDBUtils)
 | ||||
| using Vec4i = Eigen::Matrix<int, 4, 1, Eigen::DontAlign>; | ||||
| 
 | ||||
| namespace sla { | ||||
| 
 | ||||
| class IndexedMesh; | ||||
| 
 | ||||
| /// Dumb vertex mesh consisting of triangles (or) quads. Capable of merging with
 | ||||
| /// other meshes of this type and converting to and from other mesh formats.
 | ||||
| struct Contour3D { | ||||
|     std::vector<Vec3d> points; | ||||
|     std::vector<Vec3i> faces3; | ||||
|     std::vector<Vec4i> faces4; | ||||
|      | ||||
|     Contour3D() = default; | ||||
|     Contour3D(const TriangleMesh &trmesh); | ||||
|     Contour3D(TriangleMesh &&trmesh); | ||||
|     Contour3D(const IndexedMesh  &emesh); | ||||
|      | ||||
|     Contour3D& merge(const Contour3D& ctr); | ||||
|     Contour3D& merge(const Pointf3s& triangles); | ||||
|      | ||||
|     // Write the index triangle structure to OBJ file for debugging purposes.
 | ||||
|     void to_obj(std::ostream& stream); | ||||
|     void from_obj(std::istream &stream); | ||||
| 
 | ||||
|     inline bool empty() const | ||||
|     { | ||||
|         return points.empty() || (faces4.empty() && faces3.empty()); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /// Mesh from an existing contour.
 | ||||
| TriangleMesh to_triangle_mesh(const Contour3D& ctour); | ||||
| 
 | ||||
| /// Mesh from an evaporating 3D contour
 | ||||
| TriangleMesh to_triangle_mesh(Contour3D&& ctour); | ||||
| 
 | ||||
| }} // namespace Slic3r::sla
 | ||||
| 
 | ||||
| #endif // CONTOUR3D_HPP
 | ||||
|  | @ -22,14 +22,8 @@ | |||
| namespace Slic3r { | ||||
| namespace sla { | ||||
| 
 | ||||
| template<class S, class = FloatingOnly<S>> | ||||
| inline void _scale(S s, TriangleMesh &m) { m.scale(float(s)); } | ||||
| 
 | ||||
| template<class S, class = FloatingOnly<S>> | ||||
| inline void _scale(S s, Contour3D &m) { for (auto &p : m.points) p *= s; } | ||||
| 
 | ||||
| struct Interior { | ||||
|     TriangleMesh mesh; | ||||
|     indexed_triangle_set mesh; | ||||
|     openvdb::FloatGrid::Ptr gridptr; | ||||
|     mutable std::optional<openvdb::FloatGrid::ConstAccessor> accessor; | ||||
| 
 | ||||
|  | @ -53,12 +47,12 @@ void InteriorDeleter::operator()(Interior *p) | |||
|     delete p; | ||||
| } | ||||
| 
 | ||||
| TriangleMesh &get_mesh(Interior &interior) | ||||
| indexed_triangle_set &get_mesh(Interior &interior) | ||||
| { | ||||
|     return interior.mesh; | ||||
| } | ||||
| 
 | ||||
| const TriangleMesh &get_mesh(const Interior &interior) | ||||
| const indexed_triangle_set &get_mesh(const Interior &interior) | ||||
| { | ||||
|     return interior.mesh; | ||||
| } | ||||
|  | @ -77,7 +71,7 @@ static InteriorPtr generate_interior_verbose(const TriangleMesh & mesh, | |||
|     if (ctl.stopcondition()) return {}; | ||||
|     else ctl.statuscb(0, L("Hollowing")); | ||||
| 
 | ||||
|     auto gridptr = mesh_to_grid(mesh, {}, voxel_scale, out_range, in_range); | ||||
|     auto gridptr = mesh_to_grid(mesh.its, {}, voxel_scale, out_range, in_range); | ||||
| 
 | ||||
|     assert(gridptr); | ||||
| 
 | ||||
|  | @ -136,32 +130,28 @@ InteriorPtr generate_interior(const TriangleMesh &   mesh, | |||
| 
 | ||||
|     if (interior && !interior->mesh.empty()) { | ||||
| 
 | ||||
|         // This flips the normals to be outward facing...
 | ||||
|         interior->mesh.require_shared_vertices(); | ||||
|         indexed_triangle_set its = std::move(interior->mesh.its); | ||||
|         // flip normals back...
 | ||||
|         swap_normals(interior->mesh); | ||||
|         Slic3r::simplify_mesh(interior->mesh); | ||||
| 
 | ||||
|         Slic3r::simplify_mesh(its); | ||||
|         its_compactify_vertices(interior->mesh); | ||||
|         its_merge_vertices(interior->mesh); | ||||
| 
 | ||||
|         // flip normals back...
 | ||||
|         for (stl_triangle_vertex_indices &ind : its.indices) | ||||
|             std::swap(ind(0), ind(2)); | ||||
| 
 | ||||
|         interior->mesh = Slic3r::TriangleMesh{its}; | ||||
|         interior->mesh.repaired = true; | ||||
|         interior->mesh.require_shared_vertices(); | ||||
|         swap_normals(interior->mesh); | ||||
|     } | ||||
| 
 | ||||
|     return interior; | ||||
| } | ||||
| 
 | ||||
| Contour3D DrainHole::to_mesh() const | ||||
| indexed_triangle_set DrainHole::to_mesh() const | ||||
| { | ||||
|     auto r = double(radius); | ||||
|     auto h = double(height); | ||||
|     sla::Contour3D hole = sla::cylinder(r, h, steps); | ||||
|     Eigen::Quaterniond q; | ||||
|     q.setFromTwoVectors(Vec3d{0., 0., 1.}, normal.cast<double>()); | ||||
|     for(auto& p : hole.points) p = q * p + pos.cast<double>(); | ||||
|     indexed_triangle_set hole = sla::cylinder(r, h, steps); | ||||
|     Eigen::Quaternionf q; | ||||
|     q.setFromTwoVectors(Vec3f{0.f, 0.f, 1.f}, normal); | ||||
|     for(auto& p : hole.vertices) p = q * p + pos; | ||||
|      | ||||
|     return hole; | ||||
| } | ||||
|  | @ -292,7 +282,7 @@ void cut_drainholes(std::vector<ExPolygons> & obj_slices, | |||
| { | ||||
|     TriangleMesh mesh; | ||||
|     for (const sla::DrainHole &holept : holes) | ||||
|         mesh.merge(sla::to_triangle_mesh(holept.to_mesh())); | ||||
|         mesh.merge(TriangleMesh{holept.to_mesh()}); | ||||
|      | ||||
|     if (mesh.empty()) return; | ||||
|      | ||||
|  | @ -325,7 +315,7 @@ void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags) | |||
|     if (flags & hfRemoveInsideTriangles && interior.gridptr) | ||||
|         remove_inside_triangles(mesh, interior); | ||||
| 
 | ||||
|     mesh.merge(interior.mesh); | ||||
|     mesh.merge(TriangleMesh{interior.mesh}); | ||||
|     mesh.require_shared_vertices(); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,13 +2,11 @@ | |||
| #define SLA_HOLLOWING_HPP | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <libslic3r/SLA/Contour3D.hpp> | ||||
| #include <libslic3r/TriangleMesh.hpp> | ||||
| #include <libslic3r/SLA/JobController.hpp> | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| class TriangleMesh; | ||||
| 
 | ||||
| namespace sla { | ||||
| 
 | ||||
| struct HollowingConfig | ||||
|  | @ -27,8 +25,8 @@ struct Interior; | |||
| struct InteriorDeleter { void operator()(Interior *p); }; | ||||
| using  InteriorPtr = std::unique_ptr<Interior, InteriorDeleter>; | ||||
| 
 | ||||
| TriangleMesh &      get_mesh(Interior &interior); | ||||
| const TriangleMesh &get_mesh(const Interior &interior); | ||||
| indexed_triangle_set &      get_mesh(Interior &interior); | ||||
| const indexed_triangle_set &get_mesh(const Interior &interior); | ||||
| 
 | ||||
| struct DrainHole | ||||
| { | ||||
|  | @ -58,7 +56,7 @@ struct DrainHole | |||
|     bool get_intersections(const Vec3f& s, const Vec3f& dir, | ||||
|                            std::array<std::pair<float, Vec3d>, 2>& out) const; | ||||
|      | ||||
|     Contour3D to_mesh() const; | ||||
|     indexed_triangle_set to_mesh() const; | ||||
|      | ||||
|     template<class Archive> inline void serialize(Archive &ar) | ||||
|     { | ||||
|  | @ -99,7 +97,13 @@ void cut_drainholes(std::vector<ExPolygons> & obj_slices, | |||
|                     const sla::DrainHoles &   holes, | ||||
|                     std::function<void(void)> thr); | ||||
| 
 | ||||
| } | ||||
| inline void swap_normals(indexed_triangle_set &its) | ||||
| { | ||||
|     for (auto &face : its.indices) | ||||
|         std::swap(face(0), face(2)); | ||||
| } | ||||
| 
 | ||||
| } // namespace sla
 | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| #endif // HOLLOWINGFILTER_H
 | ||||
|  |  | |||
|  | @ -10,59 +10,75 @@ | |||
| #include <libslic3r/SLA/Hollowing.hpp> | ||||
| #endif | ||||
| 
 | ||||
| namespace Slic3r { namespace sla { | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| namespace sla { | ||||
| 
 | ||||
| class IndexedMesh::AABBImpl { | ||||
| private: | ||||
|     AABBTreeIndirect::Tree3f m_tree; | ||||
| 
 | ||||
| public: | ||||
|     void init(const TriangleMesh& tm) | ||||
|     void init(const indexed_triangle_set &its) | ||||
|     { | ||||
|         m_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set( | ||||
|             tm.its.vertices, tm.its.indices); | ||||
|             its.vertices, its.indices); | ||||
|     } | ||||
| 
 | ||||
|     void intersect_ray(const TriangleMesh& tm, | ||||
|                        const Vec3d& s, const Vec3d& dir, igl::Hit& hit) | ||||
|     void intersect_ray(const indexed_triangle_set &its, | ||||
|                        const Vec3d &               s, | ||||
|                        const Vec3d &               dir, | ||||
|                        igl::Hit &                  hit) | ||||
|     { | ||||
|         AABBTreeIndirect::intersect_ray_first_hit(tm.its.vertices, | ||||
|                                                   tm.its.indices, | ||||
|                                                   m_tree, | ||||
|                                                   s, dir, hit); | ||||
|         AABBTreeIndirect::intersect_ray_first_hit(its.vertices, its.indices, | ||||
|                                                   m_tree, s, dir, hit); | ||||
|     } | ||||
| 
 | ||||
|     void intersect_ray(const TriangleMesh& tm, | ||||
|                        const Vec3d& s, const Vec3d& dir, std::vector<igl::Hit>& hits) | ||||
|     void intersect_ray(const indexed_triangle_set &its, | ||||
|                        const Vec3d &               s, | ||||
|                        const Vec3d &               dir, | ||||
|                        std::vector<igl::Hit> &     hits) | ||||
|     { | ||||
|         AABBTreeIndirect::intersect_ray_all_hits(tm.its.vertices, | ||||
|                                                  tm.its.indices, | ||||
|                                                  m_tree, | ||||
|                                                  s, dir, hits); | ||||
|         AABBTreeIndirect::intersect_ray_all_hits(its.vertices, its.indices, | ||||
|                                                  m_tree, s, dir, hits); | ||||
|     } | ||||
| 
 | ||||
|     double squared_distance(const TriangleMesh& tm, | ||||
|                             const Vec3d& point, int& i, Eigen::Matrix<double, 1, 3>& closest) { | ||||
|     double squared_distance(const indexed_triangle_set & its, | ||||
|                             const Vec3d &                point, | ||||
|                             int &                        i, | ||||
|                             Eigen::Matrix<double, 1, 3> &closest) | ||||
|     { | ||||
|         size_t idx_unsigned = 0; | ||||
|         Vec3d closest_vec3d(closest); | ||||
|         double dist = AABBTreeIndirect::squared_distance_to_indexed_triangle_set( | ||||
|             tm.its.vertices, | ||||
|             tm.its.indices, | ||||
|             m_tree, point, idx_unsigned, closest_vec3d); | ||||
|         i = int(idx_unsigned); | ||||
|         Vec3d  closest_vec3d(closest); | ||||
|         double dist = | ||||
|             AABBTreeIndirect::squared_distance_to_indexed_triangle_set( | ||||
|                 its.vertices, its.indices, m_tree, point, idx_unsigned, | ||||
|                 closest_vec3d); | ||||
|         i       = int(idx_unsigned); | ||||
|         closest = closest_vec3d; | ||||
|         return dist; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| IndexedMesh::IndexedMesh(const TriangleMesh& tmesh) | ||||
|     : m_aabb(new AABBImpl()), m_tm(&tmesh) | ||||
| template<class M> void IndexedMesh::init(const M &mesh) | ||||
| { | ||||
|     auto&& bb = tmesh.bounding_box(); | ||||
|     BoundingBoxf3 bb = bounding_box(mesh); | ||||
|     m_ground_level += bb.min(Z); | ||||
| 
 | ||||
|     // Build the AABB accelaration tree
 | ||||
|     m_aabb->init(tmesh); | ||||
|     m_aabb->init(*m_tm); | ||||
| } | ||||
| 
 | ||||
| IndexedMesh::IndexedMesh(const indexed_triangle_set& tmesh) | ||||
|     : m_aabb(new AABBImpl()), m_tm(&tmesh) | ||||
| { | ||||
|     init(tmesh); | ||||
| } | ||||
| 
 | ||||
| IndexedMesh::IndexedMesh(const TriangleMesh &mesh) | ||||
|     : m_aabb(new AABBImpl()), m_tm(&mesh.its) | ||||
| { | ||||
|     init(mesh); | ||||
| } | ||||
| 
 | ||||
| IndexedMesh::~IndexedMesh() {} | ||||
|  | @ -87,34 +103,34 @@ IndexedMesh::IndexedMesh(IndexedMesh &&other) = default; | |||
| 
 | ||||
| const std::vector<Vec3f>& IndexedMesh::vertices() const | ||||
| { | ||||
|     return m_tm->its.vertices; | ||||
|     return m_tm->vertices; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| const std::vector<Vec3i>& IndexedMesh::indices()  const | ||||
| { | ||||
|     return m_tm->its.indices; | ||||
|     return m_tm->indices; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| const Vec3f& IndexedMesh::vertices(size_t idx) const | ||||
| { | ||||
|     return m_tm->its.vertices[idx]; | ||||
|     return m_tm->vertices[idx]; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| const Vec3i& IndexedMesh::indices(size_t idx) const | ||||
| { | ||||
|     return m_tm->its.indices[idx]; | ||||
|     return m_tm->indices[idx]; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| Vec3d IndexedMesh::normal_by_face_id(int face_id) const { | ||||
|     return m_tm->stl.facet_start[face_id].normal.cast<double>(); | ||||
| 
 | ||||
|     return its_unnormalized_normal(*m_tm, face_id).cast<double>().normalized(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,6 +15,8 @@ | |||
|   #include "libslic3r/SLA/Hollowing.hpp" | ||||
| #endif | ||||
| 
 | ||||
| struct indexed_triangle_set; | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| class TriangleMesh; | ||||
|  | @ -29,7 +31,7 @@ using PointSet = Eigen::MatrixXd; | |||
| class IndexedMesh { | ||||
|     class AABBImpl; | ||||
|      | ||||
|     const TriangleMesh* m_tm; | ||||
|     const indexed_triangle_set* m_tm; | ||||
|     double m_ground_level = 0, m_gnd_offset = 0; | ||||
|      | ||||
|     std::unique_ptr<AABBImpl> m_aabb; | ||||
|  | @ -40,9 +42,12 @@ class IndexedMesh { | |||
|     std::vector<DrainHole> m_holes; | ||||
| #endif | ||||
| 
 | ||||
|     template<class M> void init(const M &mesh); | ||||
| 
 | ||||
| public: | ||||
|      | ||||
|     explicit IndexedMesh(const TriangleMesh&); | ||||
|     explicit IndexedMesh(const indexed_triangle_set&); | ||||
|     explicit IndexedMesh(const TriangleMesh &mesh); | ||||
|      | ||||
|     IndexedMesh(const IndexedMesh& other); | ||||
|     IndexedMesh& operator=(const IndexedMesh&); | ||||
|  | @ -130,7 +135,7 @@ public: | |||
| 
 | ||||
|     Vec3d normal_by_face_id(int face_id) const; | ||||
| 
 | ||||
|     const TriangleMesh * get_triangle_mesh() const { return m_tm; } | ||||
|     const indexed_triangle_set * get_triangle_mesh() const { return m_tm; } | ||||
| }; | ||||
| 
 | ||||
| // Calculate the normals for the selected points (from 'points' set) on the
 | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| #include <libslic3r/SLA/Pad.hpp> | ||||
| #include <libslic3r/SLA/SpatIndex.hpp> | ||||
| #include <libslic3r/SLA/BoostAdapter.hpp> | ||||
| #include <libslic3r/SLA/Contour3D.hpp> | ||||
| //#include <libslic3r/SLA/Contour3D.hpp>
 | ||||
| #include <libslic3r/TriangleMeshSlicer.hpp> | ||||
| 
 | ||||
| #include "ConcaveHull.hpp" | ||||
|  | @ -29,25 +29,23 @@ namespace Slic3r { namespace sla { | |||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| Contour3D walls( | ||||
| indexed_triangle_set walls( | ||||
|     const Polygon &lower, | ||||
|     const Polygon &upper, | ||||
|     double         lower_z_mm, | ||||
|     double         upper_z_mm) | ||||
| { | ||||
|     Wall w = triangulate_wall(lower, upper, lower_z_mm, upper_z_mm); | ||||
| 
 | ||||
|     Contour3D ret; | ||||
|     ret.points = std::move(w.first); | ||||
|     ret.faces3 = std::move(w.second); | ||||
|     indexed_triangle_set w; | ||||
|     triangulate_wall(w.vertices, w.indices, lower, upper, lower_z_mm, | ||||
|                      upper_z_mm); | ||||
|      | ||||
|     return ret; | ||||
|     return w; | ||||
| } | ||||
| 
 | ||||
| // Same as walls() but with identical higher and lower polygons.
 | ||||
| Contour3D inline straight_walls(const Polygon &plate, | ||||
|                                 double         lo_z, | ||||
|                                 double         hi_z) | ||||
| inline indexed_triangle_set straight_walls(const Polygon &plate, | ||||
|                                            double         lo_z, | ||||
|                                            double         hi_z) | ||||
| { | ||||
|     return walls(plate, plate, lo_z, hi_z); | ||||
| } | ||||
|  | @ -357,8 +355,10 @@ ExPolygon offset_contour_only(const ExPolygon &poly, coord_t delta, Args...args) | |||
|     return std::move(tmp2.front()); | ||||
| } | ||||
| 
 | ||||
| bool add_cavity(Contour3D &pad, ExPolygon &top_poly, const PadConfig3D &cfg, | ||||
|                 ThrowOnCancel thr) | ||||
| bool add_cavity(indexed_triangle_set &pad, | ||||
|                 ExPolygon &           top_poly, | ||||
|                 const PadConfig3D &   cfg, | ||||
|                 ThrowOnCancel         thr) | ||||
| { | ||||
|     auto logerr = []{BOOST_LOG_TRIVIAL(error)<<"Could not create pad cavity";}; | ||||
| 
 | ||||
|  | @ -377,18 +377,18 @@ bool add_cavity(Contour3D &pad, ExPolygon &top_poly, const PadConfig3D &cfg, | |||
|     top_poly = pdiff.front(); | ||||
| 
 | ||||
|     double z_min = -cfg.wing_height, z_max = 0; | ||||
|     pad.merge(walls(inner_base.contour, middle_base.contour, z_min, z_max)); | ||||
|     its_merge(pad, walls(inner_base.contour, middle_base.contour, z_min, z_max)); | ||||
|     thr(); | ||||
|     pad.merge(triangulate_expolygon_3d(inner_base, z_min, NORMALS_UP)); | ||||
|     its_merge(pad, triangulate_expolygon_3d(inner_base, z_min, NORMALS_UP)); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| Contour3D create_outer_pad_geometry(const ExPolygons & skeleton, | ||||
|                                     const PadConfig3D &cfg, | ||||
|                                     ThrowOnCancel      thr) | ||||
| indexed_triangle_set create_outer_pad_geometry(const ExPolygons & skeleton, | ||||
|                                                const PadConfig3D &cfg, | ||||
|                                                ThrowOnCancel      thr) | ||||
| { | ||||
|     Contour3D ret; | ||||
|     indexed_triangle_set ret; | ||||
| 
 | ||||
|     for (const ExPolygon &pad_part : skeleton) { | ||||
|         ExPolygon top_poly{pad_part}; | ||||
|  | @ -399,45 +399,45 @@ Contour3D create_outer_pad_geometry(const ExPolygons & skeleton, | |||
|         thr(); | ||||
|          | ||||
|         double z_min = -cfg.height, z_max = 0; | ||||
|         ret.merge(walls(top_poly.contour, bottom_poly.contour, z_max, z_min)); | ||||
|         its_merge(ret, walls(top_poly.contour, bottom_poly.contour, z_max, z_min)); | ||||
| 
 | ||||
|         if (cfg.wing_height > 0. && add_cavity(ret, top_poly, cfg, thr)) | ||||
|             z_max = -cfg.wing_height; | ||||
| 
 | ||||
|         for (auto &h : bottom_poly.holes) | ||||
|             ret.merge(straight_walls(h, z_max, z_min)); | ||||
|             its_merge(ret, straight_walls(h, z_max, z_min)); | ||||
|          | ||||
|         ret.merge(triangulate_expolygon_3d(bottom_poly, z_min, NORMALS_DOWN)); | ||||
|         ret.merge(triangulate_expolygon_3d(top_poly, NORMALS_UP)); | ||||
|         its_merge(ret, triangulate_expolygon_3d(bottom_poly, z_min, NORMALS_DOWN)); | ||||
|         its_merge(ret, triangulate_expolygon_3d(top_poly, NORMALS_UP)); | ||||
|     } | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| Contour3D create_inner_pad_geometry(const ExPolygons & skeleton, | ||||
|                                     const PadConfig3D &cfg, | ||||
|                                     ThrowOnCancel      thr) | ||||
| indexed_triangle_set create_inner_pad_geometry(const ExPolygons & skeleton, | ||||
|                                                const PadConfig3D &cfg, | ||||
|                                                ThrowOnCancel      thr) | ||||
| { | ||||
|     Contour3D ret; | ||||
|     indexed_triangle_set ret; | ||||
| 
 | ||||
|     double z_max = 0., z_min = -cfg.height; | ||||
|     for (const ExPolygon &pad_part : skeleton) { | ||||
|         thr(); | ||||
|         ret.merge(straight_walls(pad_part.contour, z_max, z_min)); | ||||
|         its_merge(ret, straight_walls(pad_part.contour, z_max, z_min)); | ||||
| 
 | ||||
|         for (auto &h : pad_part.holes) | ||||
|             ret.merge(straight_walls(h, z_max, z_min)); | ||||
|             its_merge(ret, straight_walls(h, z_max, z_min)); | ||||
|      | ||||
|         ret.merge(triangulate_expolygon_3d(pad_part, z_min, NORMALS_DOWN)); | ||||
|         ret.merge(triangulate_expolygon_3d(pad_part, z_max, NORMALS_UP)); | ||||
|         its_merge(ret, triangulate_expolygon_3d(pad_part, z_min, NORMALS_DOWN)); | ||||
|         its_merge(ret, triangulate_expolygon_3d(pad_part, z_max, NORMALS_UP)); | ||||
|     } | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| Contour3D create_pad_geometry(const PadSkeleton &skelet, | ||||
|                               const PadConfig &  cfg, | ||||
|                               ThrowOnCancel      thr) | ||||
| indexed_triangle_set create_pad_geometry(const PadSkeleton &skelet, | ||||
|                                          const PadConfig &  cfg, | ||||
|                                          ThrowOnCancel      thr) | ||||
| { | ||||
| #ifndef NDEBUG | ||||
|     SVG svg("pad_skeleton.svg"); | ||||
|  | @ -447,14 +447,16 @@ Contour3D create_pad_geometry(const PadSkeleton &skelet, | |||
| #endif | ||||
| 
 | ||||
|     PadConfig3D cfg3d(cfg); | ||||
|     return create_outer_pad_geometry(skelet.outer, cfg3d, thr) | ||||
|         .merge(create_inner_pad_geometry(skelet.inner, cfg3d, thr)); | ||||
|     auto pg = create_outer_pad_geometry(skelet.outer, cfg3d, thr); | ||||
|     its_merge(pg, create_inner_pad_geometry(skelet.inner, cfg3d, thr)); | ||||
| 
 | ||||
|     return pg; | ||||
| } | ||||
| 
 | ||||
| Contour3D create_pad_geometry(const ExPolygons &supp_bp, | ||||
|                               const ExPolygons &model_bp, | ||||
|                               const PadConfig & cfg, | ||||
|                               ThrowOnCancel thr) | ||||
| indexed_triangle_set create_pad_geometry(const ExPolygons &supp_bp, | ||||
|                                          const ExPolygons &model_bp, | ||||
|                                          const PadConfig & cfg, | ||||
|                                          ThrowOnCancel     thr) | ||||
| { | ||||
|     PadSkeleton skelet; | ||||
| 
 | ||||
|  | @ -471,15 +473,14 @@ Contour3D create_pad_geometry(const ExPolygons &supp_bp, | |||
| 
 | ||||
| } // namespace
 | ||||
| 
 | ||||
| void pad_blueprint(const TriangleMesh &      mesh, | ||||
|                    ExPolygons &              output, | ||||
|                    const std::vector<float> &heights, | ||||
|                    ThrowOnCancel             thrfn) | ||||
| void pad_blueprint(const indexed_triangle_set &mesh, | ||||
|                    ExPolygons &                output, | ||||
|                    const std::vector<float> &  heights, | ||||
|                    ThrowOnCancel               thrfn) | ||||
| { | ||||
|     if (mesh.empty()) return; | ||||
| 
 | ||||
|     assert(mesh.has_shared_vertices()); | ||||
|     std::vector<ExPolygons> out = slice_mesh_ex(mesh.its, heights, thrfn); | ||||
|     std::vector<ExPolygons> out = slice_mesh_ex(mesh, heights, thrfn); | ||||
| 
 | ||||
|     size_t count = 0; | ||||
|     for(auto& o : out) count += o.size(); | ||||
|  | @ -500,26 +501,26 @@ void pad_blueprint(const TriangleMesh &      mesh, | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void pad_blueprint(const TriangleMesh &mesh, | ||||
|                    ExPolygons &        output, | ||||
|                    float               h, | ||||
|                    float               layerh, | ||||
|                    ThrowOnCancel       thrfn) | ||||
| void pad_blueprint(const indexed_triangle_set &mesh, | ||||
|                    ExPolygons &                output, | ||||
|                    float                       h, | ||||
|                    float                       layerh, | ||||
|                    ThrowOnCancel               thrfn) | ||||
| { | ||||
|     float gnd = float(mesh.bounding_box().min(Z)); | ||||
|     float gnd = float(bounding_box(mesh).min(Z)); | ||||
| 
 | ||||
|     std::vector<float> slicegrid = grid(gnd, gnd + h, layerh); | ||||
|     pad_blueprint(mesh, output, slicegrid, thrfn); | ||||
| } | ||||
| 
 | ||||
| void create_pad(const ExPolygons &sup_blueprint, | ||||
|                 const ExPolygons &model_blueprint, | ||||
|                 TriangleMesh &    out, | ||||
|                 const PadConfig & cfg, | ||||
|                 ThrowOnCancel thr) | ||||
| void create_pad(const ExPolygons &    sup_blueprint, | ||||
|                 const ExPolygons &    model_blueprint, | ||||
|                 indexed_triangle_set &out, | ||||
|                 const PadConfig &     cfg, | ||||
|                 ThrowOnCancel         thr) | ||||
| { | ||||
|     Contour3D t = create_pad_geometry(sup_blueprint, model_blueprint, cfg, thr); | ||||
|     out.merge(to_triangle_mesh(std::move(t))); | ||||
|     auto t = create_pad_geometry(sup_blueprint, model_blueprint, cfg, thr); | ||||
|     its_merge(out, t); | ||||
| } | ||||
| 
 | ||||
| std::string PadConfig::validate() const | ||||
|  |  | |||
|  | @ -6,6 +6,8 @@ | |||
| #include <cmath> | ||||
| #include <string> | ||||
| 
 | ||||
| struct indexed_triangle_set; | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| class ExPolygon; | ||||
|  | @ -13,25 +15,23 @@ class Polygon; | |||
| using ExPolygons = std::vector<ExPolygon>; | ||||
| using Polygons = std::vector<Polygon>; | ||||
| 
 | ||||
| class TriangleMesh; | ||||
| 
 | ||||
| namespace sla { | ||||
| 
 | ||||
| using ThrowOnCancel = std::function<void(void)>; | ||||
| 
 | ||||
| /// Calculate the polygon representing the silhouette.
 | ||||
| void pad_blueprint( | ||||
|     const TriangleMesh &mesh,       // input mesh
 | ||||
|     const indexed_triangle_set &mesh,       // input mesh
 | ||||
|     ExPolygons &        output,     // Output will be merged with
 | ||||
|     const std::vector<float> &,     // Exact Z levels to sample
 | ||||
|     ThrowOnCancel thrfn = [] {}); // Function that throws if cancel was requested
 | ||||
| 
 | ||||
| void pad_blueprint( | ||||
|     const TriangleMesh &mesh, | ||||
|     ExPolygons &        output, | ||||
|     float               samplingheight = 0.1f,  // The height range to sample
 | ||||
|     float               layerheight    = 0.05f, // The sampling height
 | ||||
|     ThrowOnCancel       thrfn = [] {}); | ||||
|     const indexed_triangle_set &mesh, | ||||
|     ExPolygons &                output, | ||||
|     float         samplingheight = 0.1f,  // The height range to sample
 | ||||
|     float         layerheight    = 0.05f, // The sampling height
 | ||||
|     ThrowOnCancel thrfn          = [] {}); | ||||
| 
 | ||||
| struct PadConfig { | ||||
|     double wall_thickness_mm = 1.; | ||||
|  | @ -82,11 +82,12 @@ struct PadConfig { | |||
|     std::string validate() const; | ||||
| }; | ||||
| 
 | ||||
| void create_pad(const ExPolygons &support_contours, | ||||
|                 const ExPolygons &model_contours, | ||||
|                 TriangleMesh &    output_mesh, | ||||
|                 const PadConfig & = PadConfig(), | ||||
|                 ThrowOnCancel throw_on_cancel = []{}); | ||||
| void create_pad( | ||||
|     const ExPolygons &    support_contours, | ||||
|     const ExPolygons &    model_contours, | ||||
|     indexed_triangle_set &output_mesh, | ||||
|     const PadConfig &             = PadConfig(), | ||||
|     ThrowOnCancel throw_on_cancel = [] {}); | ||||
| 
 | ||||
| } // namespace sla
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -29,38 +29,36 @@ | |||
| namespace Slic3r { | ||||
| namespace sla { | ||||
| 
 | ||||
| void SupportTree::retrieve_full_mesh(TriangleMesh &outmesh) const { | ||||
|     outmesh.merge(retrieve_mesh(MeshType::Support)); | ||||
|     outmesh.merge(retrieve_mesh(MeshType::Pad)); | ||||
| void SupportTree::retrieve_full_mesh(indexed_triangle_set &outmesh) const { | ||||
|     its_merge(outmesh, retrieve_mesh(MeshType::Support)); | ||||
|     its_merge(outmesh, retrieve_mesh(MeshType::Pad)); | ||||
| } | ||||
| 
 | ||||
| std::vector<ExPolygons> SupportTree::slice( | ||||
|     const std::vector<float> &grid, float cr) const | ||||
| std::vector<ExPolygons> SupportTree::slice(const std::vector<float> &grid, | ||||
|                                            float                     cr) const | ||||
| { | ||||
|     const TriangleMesh &sup_mesh = retrieve_mesh(MeshType::Support); | ||||
|     const TriangleMesh &pad_mesh = retrieve_mesh(MeshType::Pad); | ||||
|     const indexed_triangle_set &sup_mesh = retrieve_mesh(MeshType::Support); | ||||
|     const indexed_triangle_set &pad_mesh = retrieve_mesh(MeshType::Pad); | ||||
| 
 | ||||
|     using Slices = std::vector<ExPolygons>; | ||||
|     auto slices = reserve_vector<Slices>(2); | ||||
| 
 | ||||
|     if (!sup_mesh.empty()) { | ||||
|         slices.emplace_back(); | ||||
|         assert(sup_mesh.has_shared_vertices()); | ||||
|         slices.back() = slice_mesh_ex(sup_mesh.its, grid, cr, ctl().cancelfn); | ||||
|         slices.back() = slice_mesh_ex(sup_mesh, grid, cr, ctl().cancelfn); | ||||
|     } | ||||
| 
 | ||||
|     if (!pad_mesh.empty()) { | ||||
|         slices.emplace_back(); | ||||
| 
 | ||||
|         auto bb = pad_mesh.bounding_box(); | ||||
|         auto bb = bounding_box(pad_mesh); | ||||
|         auto maxzit = std::upper_bound(grid.begin(), grid.end(), bb.max.z()); | ||||
|          | ||||
|         auto cap = grid.end() - maxzit; | ||||
|         auto padgrid = reserve_vector<float>(size_t(cap > 0 ? cap : 0)); | ||||
|         std::copy(grid.begin(), maxzit, std::back_inserter(padgrid)); | ||||
| 
 | ||||
|         assert(pad_mesh.has_shared_vertices()); | ||||
|         slices.back() = slice_mesh_ex(pad_mesh.its, padgrid, cr, ctl().cancelfn); | ||||
|         slices.back() = slice_mesh_ex(pad_mesh, padgrid, cr, ctl().cancelfn); | ||||
|     } | ||||
| 
 | ||||
|     size_t len = grid.size(); | ||||
|  |  | |||
|  | @ -122,9 +122,9 @@ struct SupportableMesh | |||
|     IndexedMesh  emesh; | ||||
|     SupportPoints pts; | ||||
|     SupportTreeConfig cfg; | ||||
|     PadConfig     pad_cfg; | ||||
| //    PadConfig     pad_cfg;
 | ||||
| 
 | ||||
|     explicit SupportableMesh(const TriangleMesh & trmsh, | ||||
|     explicit SupportableMesh(const indexed_triangle_set & trmsh, | ||||
|                              const SupportPoints &sp, | ||||
|                              const SupportTreeConfig &c) | ||||
|         : emesh{trmsh}, pts{sp}, cfg{c} | ||||
|  | @ -149,22 +149,22 @@ public: | |||
| 
 | ||||
|     virtual ~SupportTree() = default; | ||||
| 
 | ||||
|     virtual const TriangleMesh &retrieve_mesh(MeshType meshtype) const = 0; | ||||
|     virtual const indexed_triangle_set &retrieve_mesh(MeshType meshtype) const = 0; | ||||
| 
 | ||||
|     /// Adding the "pad" under the supports.
 | ||||
|     /// modelbase will be used according to the embed_object flag in PoolConfig.
 | ||||
|     /// If set, the plate will be interpreted as the model's intrinsic pad. 
 | ||||
|     /// Otherwise, the modelbase will be unified with the base plate calculated
 | ||||
|     /// from the supports.
 | ||||
|     virtual const TriangleMesh &add_pad(const ExPolygons &modelbase, | ||||
|                                         const PadConfig & pcfg) = 0; | ||||
|      | ||||
|     virtual const indexed_triangle_set &add_pad(const ExPolygons &modelbase, | ||||
|                                                 const PadConfig & pcfg) = 0; | ||||
| 
 | ||||
|     virtual void remove_pad() = 0; | ||||
|      | ||||
|     std::vector<ExPolygons> slice(const std::vector<float> &, | ||||
|                                   float closing_radius) const; | ||||
|      | ||||
|     void retrieve_full_mesh(TriangleMesh &outmesh) const; | ||||
|     void retrieve_full_mesh(indexed_triangle_set &outmesh) const; | ||||
|      | ||||
|     const JobController &ctl() const { return m_ctl; } | ||||
| }; | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| #include <libslic3r/SLA/SupportTreeBuilder.hpp> | ||||
| #include <libslic3r/SLA/SupportTreeBuildsteps.hpp> | ||||
| #include <libslic3r/SLA/SupportTreeMesher.hpp> | ||||
| #include <libslic3r/SLA/Contour3D.hpp> | ||||
| //#include <libslic3r/SLA/Contour3D.hpp>
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| namespace sla { | ||||
|  | @ -23,11 +23,11 @@ Head::Head(double       r_big_mm, | |||
| { | ||||
| } | ||||
| 
 | ||||
| Pad::Pad(const TriangleMesh &support_mesh, | ||||
|          const ExPolygons &  model_contours, | ||||
|          double              ground_level, | ||||
|          const PadConfig &   pcfg, | ||||
|          ThrowOnCancel       thr) | ||||
| Pad::Pad(const indexed_triangle_set &support_mesh, | ||||
|          const ExPolygons &          model_contours, | ||||
|          double                      ground_level, | ||||
|          const PadConfig &           pcfg, | ||||
|          ThrowOnCancel               thr) | ||||
|     : cfg(pcfg) | ||||
|     , zlevel(ground_level + pcfg.full_height() - pcfg.required_elevation()) | ||||
| { | ||||
|  | @ -41,12 +41,14 @@ Pad::Pad(const TriangleMesh &support_mesh, | |||
|     pad_blueprint(support_mesh, sup_contours, grid(zstart, zend, 0.1f), thr); | ||||
|     create_pad(sup_contours, model_contours, tmesh, pcfg); | ||||
|      | ||||
|     tmesh.translate(0, 0, float(zlevel)); | ||||
|     if (!tmesh.empty()) tmesh.require_shared_vertices(); | ||||
|     Vec3f offs{.0f, .0f, float(zlevel)}; | ||||
|     for (auto &p : tmesh.vertices) p += offs; | ||||
| 
 | ||||
|     its_merge_vertices(tmesh); | ||||
| } | ||||
| 
 | ||||
| const TriangleMesh &SupportTreeBuilder::add_pad(const ExPolygons &modelbase, | ||||
|                                                 const PadConfig & cfg) | ||||
| const indexed_triangle_set &SupportTreeBuilder::add_pad( | ||||
|     const ExPolygons &modelbase, const PadConfig &cfg) | ||||
| { | ||||
|     m_pad = Pad{merged_mesh(), modelbase, ground_level, cfg, ctl().cancelfn}; | ||||
|     return m_pad.tmesh; | ||||
|  | @ -120,74 +122,74 @@ void SupportTreeBuilder::add_pillar_base(long pid, double baseheight, double rad | |||
|     m_meshcache_valid = false; | ||||
| } | ||||
| 
 | ||||
| const TriangleMesh &SupportTreeBuilder::merged_mesh(size_t steps) const | ||||
| const indexed_triangle_set &SupportTreeBuilder::merged_mesh(size_t steps) const | ||||
| { | ||||
|     if (m_meshcache_valid) return m_meshcache; | ||||
|      | ||||
|     Contour3D merged; | ||||
|     indexed_triangle_set merged; | ||||
|      | ||||
|     for (auto &head : m_heads) { | ||||
|         if (ctl().stopcondition()) break; | ||||
|         if (head.is_valid()) merged.merge(get_mesh(head, steps)); | ||||
|         if (head.is_valid()) its_merge(merged, get_mesh(head, steps)); | ||||
|     } | ||||
|      | ||||
|     for (auto &pill : m_pillars) { | ||||
|         if (ctl().stopcondition()) break; | ||||
|         merged.merge(get_mesh(pill, steps)); | ||||
|         its_merge(merged, get_mesh(pill, steps)); | ||||
|     } | ||||
| 
 | ||||
|     for (auto &pedest : m_pedestals) { | ||||
|         if (ctl().stopcondition()) break; | ||||
|         merged.merge(get_mesh(pedest, steps)); | ||||
|         its_merge(merged, get_mesh(pedest, steps)); | ||||
|     } | ||||
|      | ||||
|     for (auto &j : m_junctions) { | ||||
|         if (ctl().stopcondition()) break; | ||||
|         merged.merge(get_mesh(j, steps)); | ||||
|         its_merge(merged, get_mesh(j, steps)); | ||||
|     } | ||||
| 
 | ||||
|     for (auto &bs : m_bridges) { | ||||
|         if (ctl().stopcondition()) break; | ||||
|         merged.merge(get_mesh(bs, steps)); | ||||
|         its_merge(merged, get_mesh(bs, steps)); | ||||
|     } | ||||
|      | ||||
|     for (auto &bs : m_crossbridges) { | ||||
|         if (ctl().stopcondition()) break; | ||||
|         merged.merge(get_mesh(bs, steps)); | ||||
|         its_merge(merged, get_mesh(bs, steps)); | ||||
|     } | ||||
| 
 | ||||
|     for (auto &bs : m_diffbridges) { | ||||
|         if (ctl().stopcondition()) break; | ||||
|         merged.merge(get_mesh(bs, steps)); | ||||
|         its_merge(merged, get_mesh(bs, steps)); | ||||
|     } | ||||
| 
 | ||||
|     for (auto &anch : m_anchors) { | ||||
|         if (ctl().stopcondition()) break; | ||||
|         merged.merge(get_mesh(anch, steps)); | ||||
|         its_merge(merged, get_mesh(anch, steps)); | ||||
|     } | ||||
| 
 | ||||
|     if (ctl().stopcondition()) { | ||||
|         // In case of failure we have to return an empty mesh
 | ||||
|         m_meshcache = TriangleMesh(); | ||||
|         m_meshcache = {}; | ||||
|         return m_meshcache; | ||||
|     } | ||||
|      | ||||
|     m_meshcache = to_triangle_mesh(merged); | ||||
|     m_meshcache = merged; | ||||
|      | ||||
|     // The mesh will be passed by const-pointer to TriangleMeshSlicer,
 | ||||
|     // which will need this.
 | ||||
|     if (!m_meshcache.empty()) m_meshcache.require_shared_vertices(); | ||||
|      | ||||
|     BoundingBoxf3 &&bb = m_meshcache.bounding_box(); | ||||
|     m_model_height       = bb.max(Z) - bb.min(Z); | ||||
|     its_merge_vertices(m_meshcache); | ||||
|      | ||||
|     BoundingBoxf3 bb = bounding_box(m_meshcache); | ||||
|     m_model_height   = bb.max(Z) - bb.min(Z); | ||||
| 
 | ||||
|     m_meshcache_valid = true; | ||||
|     return m_meshcache; | ||||
| } | ||||
| 
 | ||||
| double SupportTreeBuilder::full_height() const | ||||
| { | ||||
|     if (merged_mesh().empty() && !pad().empty()) | ||||
|     if (merged_mesh().indices.empty() && !pad().empty()) | ||||
|         return pad().cfg.full_height(); | ||||
|      | ||||
|     double h = mesh_height(); | ||||
|  | @ -195,7 +197,7 @@ double SupportTreeBuilder::full_height() const | |||
|     return h; | ||||
| } | ||||
| 
 | ||||
| const TriangleMesh &SupportTreeBuilder::merge_and_cleanup() | ||||
| const indexed_triangle_set &SupportTreeBuilder::merge_and_cleanup() | ||||
| { | ||||
|     // in case the mesh is not generated, it should be...
 | ||||
|     auto &ret = merged_mesh();  | ||||
|  | @ -210,7 +212,7 @@ const TriangleMesh &SupportTreeBuilder::merge_and_cleanup() | |||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| const TriangleMesh &SupportTreeBuilder::retrieve_mesh(MeshType meshtype) const | ||||
| const indexed_triangle_set &SupportTreeBuilder::retrieve_mesh(MeshType meshtype) const | ||||
| { | ||||
|     switch(meshtype) { | ||||
|     case MeshType::Support: return merged_mesh(); | ||||
|  |  | |||
|  | @ -3,7 +3,8 @@ | |||
| 
 | ||||
| #include <libslic3r/SLA/Concurrency.hpp> | ||||
| #include <libslic3r/SLA/SupportTree.hpp> | ||||
| #include <libslic3r/SLA/Contour3D.hpp> | ||||
| //#include <libslic3r/SLA/Contour3D.hpp>
 | ||||
| #include <libslic3r/TriangleMesh.hpp> | ||||
| #include <libslic3r/SLA/Pad.hpp> | ||||
| #include <libslic3r/MTUtils.hpp> | ||||
| 
 | ||||
|  | @ -187,19 +188,19 @@ struct DiffBridge: public Bridge { | |||
| 
 | ||||
| // A wrapper struct around the pad
 | ||||
| struct Pad { | ||||
|     TriangleMesh tmesh; | ||||
|     indexed_triangle_set tmesh; | ||||
|     PadConfig cfg; | ||||
|     double zlevel = 0; | ||||
|      | ||||
|     Pad() = default; | ||||
|      | ||||
|     Pad(const TriangleMesh &support_mesh, | ||||
|         const ExPolygons &  model_contours, | ||||
|         double              ground_level, | ||||
|         const PadConfig &   pcfg, | ||||
|         ThrowOnCancel       thr); | ||||
|      | ||||
|     bool empty() const { return tmesh.facets_count() == 0; } | ||||
| 
 | ||||
|     Pad(const indexed_triangle_set &support_mesh, | ||||
|         const ExPolygons &          model_contours, | ||||
|         double                      ground_level, | ||||
|         const PadConfig &           pcfg, | ||||
|         ThrowOnCancel               thr); | ||||
| 
 | ||||
|     bool empty() const { return tmesh.indices.size() == 0; } | ||||
| }; | ||||
| 
 | ||||
| // This class will hold the support tree meshes with some additional
 | ||||
|  | @ -232,7 +233,7 @@ class SupportTreeBuilder: public SupportTree { | |||
|      | ||||
|     using Mutex = ccr::SpinningMutex; | ||||
|      | ||||
|     mutable TriangleMesh m_meshcache; | ||||
|     mutable indexed_triangle_set m_meshcache; | ||||
|     mutable Mutex m_mutex; | ||||
|     mutable bool m_meshcache_valid = false; | ||||
|     mutable double m_model_height = 0; // the full height of the model
 | ||||
|  | @ -418,7 +419,7 @@ public: | |||
|     const Pad& pad() const { return m_pad; } | ||||
|      | ||||
|     // WITHOUT THE PAD!!!
 | ||||
|     const TriangleMesh &merged_mesh(size_t steps = 45) const; | ||||
|     const indexed_triangle_set &merged_mesh(size_t steps = 45) const; | ||||
|      | ||||
|     // WITH THE PAD
 | ||||
|     double full_height() const; | ||||
|  | @ -431,16 +432,16 @@ public: | |||
|     } | ||||
|      | ||||
|     // Intended to be called after the generation is fully complete
 | ||||
|     const TriangleMesh & merge_and_cleanup(); | ||||
|     const indexed_triangle_set & merge_and_cleanup(); | ||||
|      | ||||
|     // Implement SupportTree interface:
 | ||||
| 
 | ||||
|     const TriangleMesh &add_pad(const ExPolygons &modelbase, | ||||
|                                 const PadConfig & pcfg) override; | ||||
|      | ||||
|     const indexed_triangle_set &add_pad(const ExPolygons &modelbase, | ||||
|                                         const PadConfig & pcfg) override; | ||||
| 
 | ||||
|     void remove_pad() override { m_pad = Pad(); } | ||||
|      | ||||
|     virtual const TriangleMesh &retrieve_mesh( | ||||
| 
 | ||||
|     virtual const indexed_triangle_set &retrieve_mesh( | ||||
|         MeshType meshtype = MeshType::Support) const override; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,22 +2,22 @@ | |||
| 
 | ||||
| namespace Slic3r { namespace sla { | ||||
| 
 | ||||
| Contour3D sphere(double rho, Portion portion, double fa) { | ||||
| indexed_triangle_set sphere(double rho, Portion portion, double fa) { | ||||
| 
 | ||||
|     Contour3D ret; | ||||
|     indexed_triangle_set ret; | ||||
| 
 | ||||
|     // prohibit close to zero radius
 | ||||
|     if(rho <= 1e-6 && rho >= -1e-6) return ret; | ||||
| 
 | ||||
|     auto& vertices = ret.points; | ||||
|     auto& facets = ret.faces3; | ||||
|     auto& vertices = ret.vertices; | ||||
|     auto& facets = ret.indices; | ||||
| 
 | ||||
|     // Algorithm:
 | ||||
|     // Add points one-by-one to the sphere grid and form facets using relative
 | ||||
|     // coordinates. Sphere is composed effectively of a mesh of stacked circles.
 | ||||
| 
 | ||||
|     // adjust via rounding to get an even multiple for any provided angle.
 | ||||
|     double angle = (2*PI / floor(2*PI / fa)); | ||||
|     double angle = (2 * PI / floor(2*PI / fa) ); | ||||
| 
 | ||||
|     // Ring to be scaled to generate the steps of the sphere
 | ||||
|     std::vector<double> ring; | ||||
|  | @ -32,8 +32,9 @@ Contour3D sphere(double rho, Portion portion, double fa) { | |||
| 
 | ||||
|     // special case: first ring connects to 0,0,0
 | ||||
|     // insert and form facets.
 | ||||
|     if(sbegin == 0) | ||||
|         vertices.emplace_back(Vec3d(0.0, 0.0, -rho + increment*sbegin*2.0*rho)); | ||||
|     if (sbegin == 0) | ||||
|         vertices.emplace_back( | ||||
|             Vec3f(0.f, 0.f, float(-rho + increment * sbegin * 2. * rho))); | ||||
| 
 | ||||
|     auto id = coord_t(vertices.size()); | ||||
|     for (size_t i = 0; i < ring.size(); i++) { | ||||
|  | @ -42,7 +43,7 @@ Contour3D sphere(double rho, Portion portion, double fa) { | |||
|         // radius of the circle for this step.
 | ||||
|         const double r = std::sqrt(std::abs(rho*rho - z*z)); | ||||
|         Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r); | ||||
|         vertices.emplace_back(Vec3d(b(0), b(1), z)); | ||||
|         vertices.emplace_back(Vec3d(b(0), b(1), z).cast<float>()); | ||||
| 
 | ||||
|         if (sbegin == 0) | ||||
|             (i == 0) ? facets.emplace_back(coord_t(ring.size()), 0, 1) : | ||||
|  | @ -53,12 +54,12 @@ Contour3D sphere(double rho, Portion portion, double fa) { | |||
|     // General case: insert and form facets for each step,
 | ||||
|     // joining it to the ring below it.
 | ||||
|     for (size_t s = sbegin + 2; s < send - 1; s++) { | ||||
|         const double z = -rho + increment*double(s*2.0*rho); | ||||
|         const double z = -rho + increment * double(s * 2. * rho); | ||||
|         const double r = std::sqrt(std::abs(rho*rho - z*z)); | ||||
| 
 | ||||
|         for (size_t i = 0; i < ring.size(); i++) { | ||||
|             Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r); | ||||
|             vertices.emplace_back(Vec3d(b(0), b(1), z)); | ||||
|             vertices.emplace_back(Vec3d(b(0), b(1), z).cast<float>()); | ||||
|             auto id_ringsize = coord_t(id - int(ring.size())); | ||||
|             if (i == 0) { | ||||
|                 // wrap around
 | ||||
|  | @ -75,7 +76,7 @@ Contour3D sphere(double rho, Portion portion, double fa) { | |||
|     // special case: last ring connects to 0,0,rho*2.0
 | ||||
|     // only form facets.
 | ||||
|     if(send >= size_t(2*PI / angle)) { | ||||
|         vertices.emplace_back(Vec3d(0.0, 0.0, -rho + increment*send*2.0*rho)); | ||||
|         vertices.emplace_back(0.f, 0.f, float(-rho + increment*send*2.0*rho)); | ||||
|         for (size_t i = 0; i < ring.size(); i++) { | ||||
|             auto id_ringsize = coord_t(id - int(ring.size())); | ||||
|             if (i == 0) { | ||||
|  | @ -92,15 +93,15 @@ Contour3D sphere(double rho, Portion portion, double fa) { | |||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp) | ||||
| indexed_triangle_set cylinder(double r, double h, size_t ssteps, const Vec3d &sp) | ||||
| { | ||||
|     assert(ssteps > 0); | ||||
| 
 | ||||
|     Contour3D ret; | ||||
|     indexed_triangle_set ret; | ||||
| 
 | ||||
|     auto steps = int(ssteps); | ||||
|     auto& points = ret.points; | ||||
|     auto& indices = ret.faces3; | ||||
|     auto& points = ret.vertices; | ||||
|     auto& indices = ret.indices; | ||||
|     points.reserve(2*ssteps); | ||||
|     double a = 2*PI/steps; | ||||
| 
 | ||||
|  | @ -110,17 +111,17 @@ Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp) | |||
|     // Upper circle points
 | ||||
|     for(int i = 0; i < steps; ++i) { | ||||
|         double phi = i*a; | ||||
|         double ex = endp(X) + r*std::cos(phi); | ||||
|         double ey = endp(Y) + r*std::sin(phi); | ||||
|         points.emplace_back(ex, ey, endp(Z)); | ||||
|         auto ex = float(endp(X) + r*std::cos(phi)); | ||||
|         auto ey = float(endp(Y) + r*std::sin(phi)); | ||||
|         points.emplace_back(ex, ey, float(endp(Z))); | ||||
|     } | ||||
| 
 | ||||
|     // Lower circle points
 | ||||
|     for(int i = 0; i < steps; ++i) { | ||||
|         double phi = i*a; | ||||
|         double x = jp(X) + r*std::cos(phi); | ||||
|         double y = jp(Y) + r*std::sin(phi); | ||||
|         points.emplace_back(x, y, jp(Z)); | ||||
|         auto x = float(jp(X) + r*std::cos(phi)); | ||||
|         auto y = float(jp(Y) + r*std::sin(phi)); | ||||
|         points.emplace_back(x, y, float(jp(Z))); | ||||
|     } | ||||
| 
 | ||||
|     // Now create long triangles connecting upper and lower circles
 | ||||
|  | @ -139,13 +140,13 @@ Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp) | |||
|     // According to the slicing algorithms, we need to aid them with generating
 | ||||
|     // a watertight body. So we create a triangle fan for the upper and lower
 | ||||
|     // ending of the cylinder to close the geometry.
 | ||||
|     points.emplace_back(jp); int ci = int(points.size() - 1); | ||||
|     points.emplace_back(jp.cast<float>()); int ci = int(points.size() - 1); | ||||
|     for(int i = 0; i < steps - 1; ++i) | ||||
|         indices.emplace_back(i + offs + 1, i + offs, ci); | ||||
| 
 | ||||
|     indices.emplace_back(offs, steps + offs - 1, ci); | ||||
| 
 | ||||
|     points.emplace_back(endp); ci = int(points.size() - 1); | ||||
|     points.emplace_back(endp.cast<float>()); ci = int(points.size() - 1); | ||||
|     for(int i = 0; i < steps - 1; ++i) | ||||
|         indices.emplace_back(ci, i, i + 1); | ||||
| 
 | ||||
|  | @ -154,14 +155,17 @@ Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp) | |||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| Contour3D pinhead(double r_pin, double r_back, double length, size_t steps) | ||||
| indexed_triangle_set pinhead(double r_pin, | ||||
|                              double r_back, | ||||
|                              double length, | ||||
|                              size_t steps) | ||||
| { | ||||
|     assert(steps > 0); | ||||
|     assert(length >= 0.); | ||||
|     assert(r_back > 0.); | ||||
|     assert(r_pin > 0.); | ||||
| 
 | ||||
|     Contour3D mesh; | ||||
|     indexed_triangle_set mesh; | ||||
| 
 | ||||
|     // We create two spheres which will be connected with a robe that fits
 | ||||
|     // both circles perfectly.
 | ||||
|  | @ -187,66 +191,66 @@ Contour3D pinhead(double r_pin, double r_back, double length, size_t steps) | |||
|     auto &&s1 = sphere(r_back, make_portion(0, PI / 2 + phi), detail); | ||||
|     auto &&s2 = sphere(r_pin, make_portion(PI / 2 + phi, PI), detail); | ||||
| 
 | ||||
|     for (auto &p : s2.points) p.z() += h; | ||||
|     for (auto &p : s2.vertices) p.z() += h; | ||||
| 
 | ||||
|     mesh.merge(s1); | ||||
|     mesh.merge(s2); | ||||
|     its_merge(mesh, s1); | ||||
|     its_merge(mesh, s2); | ||||
| 
 | ||||
|     for (size_t idx1 = s1.points.size() - steps, idx2 = s1.points.size(); | ||||
|          idx1 < s1.points.size() - 1; idx1++, idx2++) { | ||||
|     for (size_t idx1 = s1.vertices.size() - steps, idx2 = s1.vertices.size(); | ||||
|          idx1 < s1.vertices.size() - 1; idx1++, idx2++) { | ||||
|         coord_t i1s1 = coord_t(idx1), i1s2 = coord_t(idx2); | ||||
|         coord_t i2s1 = i1s1 + 1, i2s2 = i1s2 + 1; | ||||
| 
 | ||||
|         mesh.faces3.emplace_back(i1s1, i2s1, i2s2); | ||||
|         mesh.faces3.emplace_back(i1s1, i2s2, i1s2); | ||||
|         mesh.indices.emplace_back(i1s1, i2s1, i2s2); | ||||
|         mesh.indices.emplace_back(i1s1, i2s2, i1s2); | ||||
|     } | ||||
| 
 | ||||
|     auto i1s1 = coord_t(s1.points.size()) - coord_t(steps); | ||||
|     auto i2s1 = coord_t(s1.points.size()) - 1; | ||||
|     auto i1s2 = coord_t(s1.points.size()); | ||||
|     auto i2s2 = coord_t(s1.points.size()) + coord_t(steps) - 1; | ||||
|     auto i1s1 = coord_t(s1.vertices.size()) - coord_t(steps); | ||||
|     auto i2s1 = coord_t(s1.vertices.size()) - 1; | ||||
|     auto i1s2 = coord_t(s1.vertices.size()); | ||||
|     auto i2s2 = coord_t(s1.vertices.size()) + coord_t(steps) - 1; | ||||
| 
 | ||||
|     mesh.faces3.emplace_back(i2s2, i2s1, i1s1); | ||||
|     mesh.faces3.emplace_back(i1s2, i2s2, i1s1); | ||||
|     mesh.indices.emplace_back(i2s2, i2s1, i1s1); | ||||
|     mesh.indices.emplace_back(i1s2, i2s2, i1s1); | ||||
| 
 | ||||
|     return mesh; | ||||
| } | ||||
| 
 | ||||
| Contour3D halfcone(double       baseheight, | ||||
|                    double       r_bottom, | ||||
|                    double       r_top, | ||||
|                    const Vec3d &pos, | ||||
|                    size_t       steps) | ||||
| indexed_triangle_set halfcone(double       baseheight, | ||||
|                               double       r_bottom, | ||||
|                               double       r_top, | ||||
|                               const Vec3d &pos, | ||||
|                               size_t       steps) | ||||
| { | ||||
|     assert(steps > 0); | ||||
| 
 | ||||
|     if (baseheight <= 0 || steps <= 0) return {}; | ||||
| 
 | ||||
|     Contour3D base; | ||||
|     indexed_triangle_set base; | ||||
| 
 | ||||
|     double a    = 2 * PI / steps; | ||||
|     auto   last = int(steps - 1); | ||||
|     Vec3d  ep{pos.x(), pos.y(), pos.z() + baseheight}; | ||||
|     for (size_t i = 0; i < steps; ++i) { | ||||
|         double phi = i * a; | ||||
|         double x   = pos.x() + r_top * std::cos(phi); | ||||
|         double y   = pos.y() + r_top * std::sin(phi); | ||||
|         base.points.emplace_back(x, y, ep.z()); | ||||
|         auto x   = float(pos.x() + r_top * std::cos(phi)); | ||||
|         auto y   = float(pos.y() + r_top * std::sin(phi)); | ||||
|         base.vertices.emplace_back(x, y, float(ep.z())); | ||||
|     } | ||||
| 
 | ||||
|     for (size_t i = 0; i < steps; ++i) { | ||||
|         double phi = i * a; | ||||
|         double x   = pos.x() + r_bottom * std::cos(phi); | ||||
|         double y   = pos.y() + r_bottom * std::sin(phi); | ||||
|         base.points.emplace_back(x, y, pos.z()); | ||||
|         auto x   = float(pos.x() + r_bottom * std::cos(phi)); | ||||
|         auto y   = float(pos.y() + r_bottom * std::sin(phi)); | ||||
|         base.vertices.emplace_back(x, y, float(pos.z())); | ||||
|     } | ||||
| 
 | ||||
|     base.points.emplace_back(pos); | ||||
|     base.points.emplace_back(ep); | ||||
|     base.vertices.emplace_back(pos.cast<float>()); | ||||
|     base.vertices.emplace_back(ep.cast<float>()); | ||||
| 
 | ||||
|     auto &indices = base.faces3; | ||||
|     auto  hcenter = int(base.points.size() - 1); | ||||
|     auto  lcenter = int(base.points.size() - 2); | ||||
|     auto &indices = base.indices; | ||||
|     auto  hcenter = int(base.vertices.size() - 1); | ||||
|     auto  lcenter = int(base.vertices.size() - 2); | ||||
|     auto  offs    = int(steps); | ||||
|     for (int i = 0; i < last; ++i) { | ||||
|         indices.emplace_back(i, i + offs, offs + i + 1); | ||||
|  |  | |||
|  | @ -4,7 +4,8 @@ | |||
| #include "libslic3r/Point.hpp" | ||||
| 
 | ||||
| #include "libslic3r/SLA/SupportTreeBuilder.hpp" | ||||
| #include "libslic3r/SLA/Contour3D.hpp" | ||||
| #include "libslic3r/TriangleMesh.hpp" | ||||
| //#include "libslic3r/SLA/Contour3D.hpp"
 | ||||
| 
 | ||||
| namespace Slic3r { namespace sla { | ||||
| 
 | ||||
|  | @ -15,48 +16,53 @@ inline Portion make_portion(double a, double b) | |||
|     return std::make_tuple(a, b); | ||||
| } | ||||
| 
 | ||||
| Contour3D sphere(double  rho, | ||||
|                  Portion portion = make_portion(0., 2. * PI), | ||||
|                  double  fa      = (2. * PI / 360.)); | ||||
| indexed_triangle_set sphere(double  rho, | ||||
|                             Portion portion = make_portion(0., 2. * PI), | ||||
|                             double  fa      = (2. * PI / 360.)); | ||||
| 
 | ||||
| // Down facing cylinder in Z direction with arguments:
 | ||||
| // r: radius
 | ||||
| // h: Height
 | ||||
| // ssteps: how many edges will create the base circle
 | ||||
| // sp: starting point
 | ||||
| Contour3D cylinder(double       r, | ||||
|                    double       h, | ||||
|                    size_t       steps = 45, | ||||
|                    const Vec3d &sp    = Vec3d::Zero()); | ||||
| indexed_triangle_set cylinder(double       r, | ||||
|                               double       h, | ||||
|                               size_t       steps = 45, | ||||
|                               const Vec3d &sp    = Vec3d::Zero()); | ||||
| 
 | ||||
| Contour3D pinhead(double r_pin, double r_back, double length, size_t steps = 45); | ||||
| indexed_triangle_set pinhead(double r_pin, | ||||
|                              double r_back, | ||||
|                              double length, | ||||
|                              size_t steps = 45); | ||||
| 
 | ||||
| Contour3D halfcone(double       baseheight, | ||||
|                    double       r_bottom, | ||||
|                    double       r_top, | ||||
|                    const Vec3d &pt    = Vec3d::Zero(), | ||||
|                    size_t       steps = 45); | ||||
| indexed_triangle_set halfcone(double       baseheight, | ||||
|                               double       r_bottom, | ||||
|                               double       r_top, | ||||
|                               const Vec3d &pt    = Vec3d::Zero(), | ||||
|                               size_t       steps = 45); | ||||
| 
 | ||||
| inline Contour3D get_mesh(const Head &h, size_t steps) | ||||
| inline indexed_triangle_set get_mesh(const Head &h, size_t steps) | ||||
| { | ||||
|     Contour3D mesh = pinhead(h.r_pin_mm, h.r_back_mm, h.width_mm, steps); | ||||
|     indexed_triangle_set mesh = pinhead(h.r_pin_mm, h.r_back_mm, h.width_mm, steps); | ||||
| 
 | ||||
|     for(auto& p : mesh.points) p.z() -= (h.fullwidth() - h.r_back_mm); | ||||
|     for (auto& p : mesh.vertices) p.z() -= (h.fullwidth() - h.r_back_mm); | ||||
| 
 | ||||
|     using Quaternion = Eigen::Quaternion<double>; | ||||
|     using Quaternion = Eigen::Quaternion<float>; | ||||
| 
 | ||||
|     // We rotate the head to the specified direction. The head's pointing
 | ||||
|     // side is facing upwards so this means that it would hold a support
 | ||||
|     // point with a normal pointing straight down. This is the reason of
 | ||||
|     // the -1 z coordinate
 | ||||
|     auto quatern = Quaternion::FromTwoVectors(Vec3d{0, 0, -1}, h.dir); | ||||
|     auto quatern = Quaternion::FromTwoVectors(Vec3f{0.f, 0.f, -1.f}, | ||||
|                                               h.dir.cast<float>()); | ||||
| 
 | ||||
|     for(auto& p : mesh.points) p = quatern * p + h.pos; | ||||
|     Vec3f pos = h.pos.cast<float>(); | ||||
|     for (auto& p : mesh.vertices) p = quatern * p + pos; | ||||
| 
 | ||||
|     return mesh; | ||||
| } | ||||
| 
 | ||||
| inline Contour3D get_mesh(const Pillar &p, size_t steps) | ||||
| inline indexed_triangle_set get_mesh(const Pillar &p, size_t steps) | ||||
| { | ||||
|     if(p.height > EPSILON) { // Endpoint is below the starting point
 | ||||
|         // We just create a bridge geometry with the pillar parameters and
 | ||||
|  | @ -67,47 +73,53 @@ inline Contour3D get_mesh(const Pillar &p, size_t steps) | |||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| inline Contour3D get_mesh(const Pedestal &p, size_t steps) | ||||
| inline indexed_triangle_set get_mesh(const Pedestal &p, size_t steps) | ||||
| { | ||||
|     return halfcone(p.height, p.r_bottom, p.r_top, p.pos, steps); | ||||
| } | ||||
| 
 | ||||
| inline Contour3D get_mesh(const Junction &j, size_t steps) | ||||
| inline indexed_triangle_set get_mesh(const Junction &j, size_t steps) | ||||
| { | ||||
|     Contour3D mesh = sphere(j.r, make_portion(0, PI), 2 *PI / steps); | ||||
|     for(auto& p : mesh.points) p += j.pos; | ||||
|     indexed_triangle_set mesh = sphere(j.r, make_portion(0, PI), 2 *PI / steps); | ||||
|     Vec3f pos = j.pos.cast<float>(); | ||||
|     for(auto& p : mesh.vertices) p += pos; | ||||
|     return mesh; | ||||
| } | ||||
| 
 | ||||
| inline Contour3D get_mesh(const Bridge &br, size_t steps) | ||||
| inline indexed_triangle_set get_mesh(const Bridge &br, size_t steps) | ||||
| { | ||||
|     using Quaternion = Eigen::Quaternion<double>; | ||||
|     using Quaternion = Eigen::Quaternion<float>; | ||||
|     Vec3d v = (br.endp - br.startp); | ||||
|     Vec3d dir = v.normalized(); | ||||
|     double d = v.norm(); | ||||
| 
 | ||||
|     Contour3D mesh = cylinder(br.r, d, steps); | ||||
|     indexed_triangle_set mesh = cylinder(br.r, d, steps); | ||||
| 
 | ||||
|     auto quater = Quaternion::FromTwoVectors(Vec3d{0,0,1}, dir); | ||||
|     for(auto& p : mesh.points) p = quater * p + br.startp; | ||||
|     auto quater = Quaternion::FromTwoVectors(Vec3f{0.f, 0.f, 1.f}, | ||||
|                                              dir.cast<float>()); | ||||
| 
 | ||||
|     Vec3f startp = br.startp.cast<float>(); | ||||
|     for(auto& p : mesh.vertices) p = quater * p + startp; | ||||
| 
 | ||||
|     return mesh; | ||||
| } | ||||
| 
 | ||||
| inline Contour3D get_mesh(const DiffBridge &br, size_t steps) | ||||
| inline indexed_triangle_set get_mesh(const DiffBridge &br, size_t steps) | ||||
| { | ||||
|     double h = br.get_length(); | ||||
|     Contour3D mesh = halfcone(h, br.r, br.end_r, Vec3d::Zero(), steps); | ||||
|     indexed_triangle_set mesh = halfcone(h, br.r, br.end_r, Vec3d::Zero(), steps); | ||||
| 
 | ||||
|     using Quaternion = Eigen::Quaternion<double>; | ||||
|     using Quaternion = Eigen::Quaternion<float>; | ||||
| 
 | ||||
|     // We rotate the head to the specified direction. The head's pointing
 | ||||
|     // side is facing upwards so this means that it would hold a support
 | ||||
|     // point with a normal pointing straight down. This is the reason of
 | ||||
|     // the -1 z coordinate
 | ||||
|     auto quatern = Quaternion::FromTwoVectors(Vec3d{0, 0, 1}, br.get_dir()); | ||||
|     auto quatern = Quaternion::FromTwoVectors(Vec3f{0.f, 0.f, 1.f}, | ||||
|                                               br.get_dir().cast<float>()); | ||||
| 
 | ||||
|     for(auto& p : mesh.points) p = quatern * p + br.startp; | ||||
|     Vec3f startp = br.startp.cast<float>(); | ||||
|     for(auto& p : mesh.vertices) p = quatern * p + startp; | ||||
| 
 | ||||
|     return mesh; | ||||
| } | ||||
|  |  | |||
|  | @ -109,7 +109,7 @@ sla::PadConfig make_pad_cfg(const SLAPrintObjectConfig& c) | |||
|     return pcfg; | ||||
| } | ||||
| 
 | ||||
| bool validate_pad(const TriangleMesh &pad, const sla::PadConfig &pcfg)  | ||||
| bool validate_pad(const indexed_triangle_set &pad, const sla::PadConfig &pcfg) | ||||
| { | ||||
|     // An empty pad can only be created if embed_object mode is enabled
 | ||||
|     // and the pad is not forced everywhere
 | ||||
|  | @ -1067,6 +1067,7 @@ Vec3d SLAPrint::relative_correction() const | |||
| namespace { // dummy empty static containers for return values in some methods
 | ||||
| const std::vector<ExPolygons> EMPTY_SLICES; | ||||
| const TriangleMesh EMPTY_MESH; | ||||
| const indexed_triangle_set EMPTY_TRIANGLE_SET; | ||||
| const ExPolygons EMPTY_SLICE; | ||||
| const std::vector<sla::SupportPoint> EMPTY_SUPPORT_POINTS; | ||||
| } | ||||
|  | @ -1129,31 +1130,27 @@ TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const | |||
| 
 | ||||
| const TriangleMesh& SLAPrintObject::support_mesh() const | ||||
| { | ||||
|     sla::SupportTree::UPtr &stree = m_supportdata->support_tree_ptr; | ||||
|      | ||||
|     if(m_config.supports_enable.getBool() && m_supportdata && stree) | ||||
|         return stree->retrieve_mesh(sla::MeshType::Support); | ||||
|     if(m_config.supports_enable.getBool() && m_supportdata) | ||||
|         return m_supportdata->tree_mesh; | ||||
|      | ||||
|     return EMPTY_MESH; | ||||
| } | ||||
| 
 | ||||
| const TriangleMesh& SLAPrintObject::pad_mesh() const | ||||
| { | ||||
|     sla::SupportTree::UPtr &stree = m_supportdata->support_tree_ptr; | ||||
|      | ||||
|     if(m_config.pad_enable.getBool() && m_supportdata && stree) | ||||
|         return stree->retrieve_mesh(sla::MeshType::Pad); | ||||
|     if(m_config.pad_enable.getBool() && m_supportdata) | ||||
|         return m_supportdata->pad_mesh; | ||||
| 
 | ||||
|     return EMPTY_MESH; | ||||
| } | ||||
| 
 | ||||
| const TriangleMesh &SLAPrintObject::hollowed_interior_mesh() const | ||||
| const indexed_triangle_set &SLAPrintObject::hollowed_interior_mesh() const | ||||
| { | ||||
|     if (m_hollowing_data && m_hollowing_data->interior && | ||||
|         m_config.hollowing_enable.getBool()) | ||||
|         return sla::get_mesh(*m_hollowing_data->interior); | ||||
|      | ||||
|     return EMPTY_MESH; | ||||
|     return EMPTY_TRIANGLE_SET; | ||||
| } | ||||
| 
 | ||||
| const TriangleMesh &SLAPrintObject::transformed_mesh() const { | ||||
|  |  | |||
|  | @ -79,8 +79,8 @@ public: | |||
|     const TriangleMesh&     pad_mesh() const; | ||||
|      | ||||
|     // Ready after this->is_step_done(slaposDrillHoles) is true
 | ||||
|     const TriangleMesh&     hollowed_interior_mesh() const; | ||||
|      | ||||
|     const indexed_triangle_set &hollowed_interior_mesh() const; | ||||
| 
 | ||||
|     // Get the mesh that is going to be printed with all the modifications
 | ||||
|     // like hollowing and drilled holes.
 | ||||
|     const TriangleMesh & get_mesh_to_print() const { | ||||
|  | @ -313,16 +313,27 @@ private: | |||
|     public: | ||||
|         sla::SupportTree::UPtr  support_tree_ptr; // the supports
 | ||||
|         std::vector<ExPolygons> support_slices;   // sliced supports
 | ||||
|         TriangleMesh tree_mesh, pad_mesh, full_mesh; | ||||
|          | ||||
|         inline SupportData(const TriangleMesh &t) | ||||
|             : sla::SupportableMesh{t, {}, {}} | ||||
|             : sla::SupportableMesh{t.its, {}, {}} | ||||
|         {} | ||||
|          | ||||
|         sla::SupportTree::UPtr &create_support_tree(const sla::JobController &ctl) | ||||
|         { | ||||
|             support_tree_ptr = sla::SupportTree::create(*this, ctl); | ||||
|             tree_mesh = TriangleMesh{support_tree_ptr->retrieve_mesh(sla::MeshType::Support)}; | ||||
|             return support_tree_ptr; | ||||
|         } | ||||
| 
 | ||||
|         void create_pad(const ExPolygons &blueprint, const sla::PadConfig &pcfg) | ||||
|         { | ||||
|             if (!support_tree_ptr) | ||||
|                 return; | ||||
| 
 | ||||
|             support_tree_ptr->add_pad(blueprint, pcfg); | ||||
|             pad_mesh = TriangleMesh{support_tree_ptr->retrieve_mesh(sla::MeshType::Pad)}; | ||||
|         } | ||||
|     }; | ||||
|      | ||||
|     std::unique_ptr<SupportData> m_supportdata; | ||||
|  | @ -569,7 +580,7 @@ sla::PadConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c); | |||
| 
 | ||||
| sla::PadConfig make_pad_cfg(const SLAPrintObjectConfig& c); | ||||
| 
 | ||||
| bool validate_pad(const TriangleMesh &pad, const sla::PadConfig &pcfg); | ||||
| bool validate_pad(const indexed_triangle_set &pad, const sla::PadConfig &pcfg); | ||||
| 
 | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -194,18 +194,18 @@ static std::vector<bool> create_exclude_mask( | |||
|         const sla::Interior &interior, | ||||
|         const std::vector<sla::DrainHole> &holes) | ||||
| { | ||||
|     FaceHash interior_hash{sla::get_mesh(interior).its}; | ||||
|     FaceHash interior_hash{sla::get_mesh(interior)}; | ||||
| 
 | ||||
|     std::vector<bool> exclude_mask(its.indices.size(), false); | ||||
| 
 | ||||
|     std::vector< std::vector<size_t> > neighbor_index = | ||||
|             create_vertex_faces_index(its); | ||||
|     VertexFaceIndex neighbor_index{its}; | ||||
| 
 | ||||
|     auto exclude_neighbors = [&neighbor_index, &exclude_mask](const Vec3i &face) | ||||
|     { | ||||
|         for (int i = 0; i < 3; ++i) { | ||||
|             const std::vector<size_t> &neighbors = neighbor_index[face(i)]; | ||||
|             for (size_t fi_n : neighbors) exclude_mask[fi_n] = true; | ||||
|             const auto &neighbors_range = neighbor_index[face(i)]; | ||||
|             for (size_t fi_n : neighbors_range) | ||||
|                 exclude_mask[fi_n] = true; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|  | @ -360,7 +360,7 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) | |||
|         holept.normal += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)}; | ||||
|         holept.normal.normalize(); | ||||
|         holept.pos += Vec3f{dist(m_rng), dist(m_rng), dist(m_rng)}; | ||||
|         TriangleMesh m = sla::to_triangle_mesh(holept.to_mesh()); | ||||
|         TriangleMesh m{holept.to_mesh()}; | ||||
|         m.require_shared_vertices(); | ||||
| 
 | ||||
|         part_to_drill.indices.clear(); | ||||
|  | @ -489,11 +489,11 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) | |||
|                                   nullptr; | ||||
| 
 | ||||
|     if (interior && ! sla::get_mesh(*interior).empty()) { | ||||
|         TriangleMesh interiormesh = sla::get_mesh(*interior); | ||||
|         interiormesh.repaired = false; | ||||
|         interiormesh.repair(true); | ||||
|         indexed_triangle_set interiormesh = sla::get_mesh(*interior); | ||||
|         sla::swap_normals(interiormesh); | ||||
|         params.mode = MeshSlicingParams::SlicingMode::Regular; | ||||
|         std::vector<ExPolygons> interior_slices = slice_mesh_ex(interiormesh.its, slice_grid, params, thr); | ||||
| 
 | ||||
|         std::vector<ExPolygons> interior_slices = slice_mesh_ex(interiormesh, slice_grid, params, thr); | ||||
| 
 | ||||
|         sla::ccr::for_each(size_t(0), interior_slices.size(), | ||||
|                            [&po, &interior_slices] (size_t i) { | ||||
|  | @ -667,15 +667,14 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { | |||
|             // we sometimes call it "builtin pad" is enabled so we will
 | ||||
|             // get a sample from the bottom of the mesh and use it for pad
 | ||||
|             // creation.
 | ||||
|             sla::pad_blueprint(trmesh, bp, float(pad_h), | ||||
|             sla::pad_blueprint(trmesh.its, bp, float(pad_h), | ||||
|                                float(po.m_config.layer_height.getFloat()), | ||||
|                                [this](){ throw_if_canceled(); }); | ||||
|         } | ||||
| 
 | ||||
|         po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg); | ||||
|         auto &pad_mesh = po.m_supportdata->support_tree_ptr->retrieve_mesh(sla::MeshType::Pad); | ||||
|         po.m_supportdata->create_pad(bp, pcfg); | ||||
| 
 | ||||
|         if (!validate_pad(pad_mesh, pcfg)) | ||||
|         if (!validate_pad(po.m_supportdata->support_tree_ptr->retrieve_mesh(sla::MeshType::Pad), pcfg)) | ||||
|             throw Slic3r::SlicingError( | ||||
|                     L("No pad can be generated for this model with the " | ||||
|                       "current configuration")); | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| 
 | ||||
| #include "SlicesToTriangleMesh.hpp" | ||||
| 
 | ||||
| #include "libslic3r/MTUtils.hpp" | ||||
| #include "libslic3r/SLA/Contour3D.hpp" | ||||
| //#include "libslic3r/MTUtils.hpp"
 | ||||
| #include "libslic3r/Execution/ExecutionTBB.hpp" | ||||
| #include "libslic3r/ClipperUtils.hpp" | ||||
| #include "libslic3r/Tesselate.hpp" | ||||
| 
 | ||||
|  | @ -11,71 +11,73 @@ | |||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| inline sla::Contour3D wall_strip(const Polygon &poly, | ||||
|                                  double         lower_z_mm, | ||||
|                                  double         upper_z_mm) | ||||
| inline indexed_triangle_set wall_strip(const Polygon &poly, | ||||
|                                        double         lower_z_mm, | ||||
|                                        double         upper_z_mm) | ||||
| { | ||||
|     sla::Contour3D ret; | ||||
|     indexed_triangle_set ret; | ||||
|      | ||||
|     size_t startidx = ret.points.size(); | ||||
|     size_t startidx = ret.vertices.size(); | ||||
|     size_t offs     = poly.points.size(); | ||||
|      | ||||
|     ret.points.reserve(ret.points.size() + 2 *offs); | ||||
|     ret.vertices.reserve(ret.vertices.size() + 2 *offs); | ||||
|      | ||||
|     for (const Point &p : poly.points) | ||||
|         ret.points.emplace_back(to_3d(unscaled(p), lower_z_mm)); | ||||
|         ret.vertices.emplace_back(to_3d(unscaled<float>(p), float(lower_z_mm))); | ||||
|      | ||||
|     for (const Point &p : poly.points) | ||||
|         ret.points.emplace_back(to_3d(unscaled(p), upper_z_mm)); | ||||
|         ret.vertices.emplace_back(to_3d(unscaled<float>(p), float(upper_z_mm))); | ||||
|      | ||||
|     for (size_t i = startidx + 1; i < startidx + offs; ++i) { | ||||
|         ret.faces3.emplace_back(i - 1, i, i + offs - 1); | ||||
|         ret.faces3.emplace_back(i, i + offs, i + offs - 1); | ||||
|         ret.indices.emplace_back(i - 1, i, i + offs - 1); | ||||
|         ret.indices.emplace_back(i, i + offs, i + offs - 1); | ||||
|     } | ||||
|      | ||||
|     ret.faces3.emplace_back(startidx + offs - 1, startidx, startidx + 2 * offs - 1); | ||||
|     ret.faces3.emplace_back(startidx, startidx + offs, startidx + 2 * offs - 1); | ||||
|     ret.indices.emplace_back(startidx + offs - 1, startidx, startidx + 2 * offs - 1); | ||||
|     ret.indices.emplace_back(startidx, startidx + offs, startidx + 2 * offs - 1); | ||||
|      | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| // Same as walls() but with identical higher and lower polygons.
 | ||||
| sla::Contour3D inline straight_walls(const Polygon &plate, | ||||
| indexed_triangle_set inline straight_walls(const Polygon &plate, | ||||
|                                      double         lo_z, | ||||
|                                      double         hi_z) | ||||
| { | ||||
|     return wall_strip(plate, lo_z, hi_z); | ||||
| } | ||||
| 
 | ||||
| sla::Contour3D inline straight_walls(const ExPolygon &plate, | ||||
| indexed_triangle_set inline straight_walls(const ExPolygon &plate, | ||||
|                                      double           lo_z, | ||||
|                                      double           hi_z) | ||||
| { | ||||
|     sla::Contour3D ret; | ||||
|     ret.merge(straight_walls(plate.contour, lo_z, hi_z)); | ||||
|     for (auto &h : plate.holes) ret.merge(straight_walls(h, lo_z, hi_z)); | ||||
|     indexed_triangle_set ret = straight_walls(plate.contour, lo_z, hi_z); | ||||
|     for (auto &h : plate.holes) | ||||
|         its_merge(ret, straight_walls(h, lo_z, hi_z)); | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| sla::Contour3D inline straight_walls(const ExPolygons &slice, | ||||
| indexed_triangle_set inline straight_walls(const ExPolygons &slice, | ||||
|                                      double            lo_z, | ||||
|                                      double            hi_z) | ||||
| {    | ||||
|     sla::Contour3D ret; | ||||
| { | ||||
|     indexed_triangle_set ret; | ||||
|     for (const ExPolygon &poly : slice) | ||||
|         ret.merge(straight_walls(poly, lo_z, hi_z)); | ||||
|      | ||||
|         its_merge(ret, straight_walls(poly, lo_z, hi_z)); | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| sla::Contour3D slices_to_triangle_mesh(const std::vector<ExPolygons> &slices, | ||||
|                                        double zmin, | ||||
|                                        const std::vector<float> &    grid) | ||||
| indexed_triangle_set slices_to_mesh( | ||||
|     const std::vector<ExPolygons> &slices, | ||||
|     double                         zmin, | ||||
|     const std::vector<float> &     grid) | ||||
| { | ||||
|     assert(slices.size() == grid.size()); | ||||
| 
 | ||||
|     using Layers = std::vector<sla::Contour3D>; | ||||
|     std::vector<sla::Contour3D> layers(slices.size()); | ||||
|     using Layers = std::vector<indexed_triangle_set>; | ||||
|     Layers layers(slices.size()); | ||||
|     size_t len = slices.size() - 1; | ||||
| 
 | ||||
|     tbb::parallel_for(size_t(0), len, [&slices, &layers, &grid](size_t i) { | ||||
|  | @ -84,45 +86,52 @@ sla::Contour3D slices_to_triangle_mesh(const std::vector<ExPolygons> &slices, | |||
| 
 | ||||
|         ExPolygons dff1 = diff_ex(lower, upper); | ||||
|         ExPolygons dff2 = diff_ex(upper, lower); | ||||
|         layers[i].merge(triangulate_expolygons_3d(dff1, grid[i], NORMALS_UP)); | ||||
|         layers[i].merge(triangulate_expolygons_3d(dff2, grid[i], NORMALS_DOWN)); | ||||
|         layers[i].merge(straight_walls(upper, grid[i], grid[i + 1])); | ||||
|         its_merge(layers[i], triangulate_expolygons_3d(dff1, grid[i], NORMALS_UP)); | ||||
|         its_merge(layers[i], triangulate_expolygons_3d(dff2, grid[i], NORMALS_DOWN)); | ||||
|         its_merge(layers[i], straight_walls(upper, grid[i], grid[i + 1])); | ||||
|          | ||||
|     }); | ||||
| 
 | ||||
|     auto merge_fn = []( const indexed_triangle_set &a, const indexed_triangle_set &b ) { | ||||
|         indexed_triangle_set res{a}; its_merge(res, b); return res; | ||||
|     }; | ||||
| 
 | ||||
|     auto ret = execution::reduce(ex_tbb, layers.begin(), layers.end(), | ||||
|                                  indexed_triangle_set{}, merge_fn); | ||||
| 
 | ||||
|     //    sla::Contour3D ret = tbb::parallel_reduce(
 | ||||
|     //        tbb::blocked_range(layers.begin(), layers.end()),
 | ||||
|     //        sla::Contour3D{},
 | ||||
|     //        [](const tbb::blocked_range<Layers::iterator>& r, sla::Contour3D
 | ||||
|     //        init) {
 | ||||
|     //            for(auto it = r.begin(); it != r.end(); ++it )
 | ||||
|     //            init.merge(*it); return init;
 | ||||
|     //        },
 | ||||
|     //        []( const sla::Contour3D &a, const sla::Contour3D &b ) {
 | ||||
|     //            sla::Contour3D res{a}; res.merge(b); return res;
 | ||||
|     //        });
 | ||||
|      | ||||
|     sla::Contour3D ret = tbb::parallel_reduce( | ||||
|         tbb::blocked_range(layers.begin(), layers.end()), | ||||
|         sla::Contour3D{}, | ||||
|         [](const tbb::blocked_range<Layers::iterator>& r, sla::Contour3D init) { | ||||
|             for(auto it = r.begin(); it != r.end(); ++it ) init.merge(*it); | ||||
|             return init; | ||||
|         }, | ||||
|         []( const sla::Contour3D &a, const sla::Contour3D &b ) { | ||||
|             sla::Contour3D res{a}; res.merge(b); return res; | ||||
|         }); | ||||
|      | ||||
|     ret.merge(triangulate_expolygons_3d(slices.front(), zmin, NORMALS_DOWN)); | ||||
|     ret.merge(straight_walls(slices.front(), zmin, grid.front())); | ||||
|     ret.merge(triangulate_expolygons_3d(slices.back(), grid.back(), NORMALS_UP)); | ||||
|     its_merge(ret, triangulate_expolygons_3d(slices.front(), zmin, NORMALS_DOWN)); | ||||
|     its_merge(ret, straight_walls(slices.front(), zmin, grid.front())); | ||||
|     its_merge(ret, triangulate_expolygons_3d(slices.back(), grid.back(), NORMALS_UP)); | ||||
|          | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| void slices_to_triangle_mesh(TriangleMesh &                 mesh, | ||||
| void slices_to_mesh(indexed_triangle_set &         mesh, | ||||
|                              const std::vector<ExPolygons> &slices, | ||||
|                              double                         zmin, | ||||
|                              double                         lh, | ||||
|                              double                         ilh) | ||||
| { | ||||
|     std::vector<sla::Contour3D> wall_meshes(slices.size()); | ||||
|     std::vector<indexed_triangle_set> wall_meshes(slices.size()); | ||||
|     std::vector<float> grid(slices.size(), zmin + ilh); | ||||
|      | ||||
|     for (size_t i = 1; i < grid.size(); ++i) grid[i] = grid[i - 1] + lh; | ||||
|     for (size_t i = 1; i < grid.size(); ++i) | ||||
|         grid[i] = grid[i - 1] + lh; | ||||
|      | ||||
|     sla::Contour3D cntr = slices_to_triangle_mesh(slices, zmin, grid); | ||||
|     mesh.merge(sla::to_triangle_mesh(cntr)); | ||||
|     mesh.repaired = true; | ||||
|     mesh.require_shared_vertices(); | ||||
|     indexed_triangle_set cntr = slices_to_mesh(slices, zmin, grid); | ||||
|     its_merge(mesh, cntr); | ||||
| } | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -6,16 +6,18 @@ | |||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| void slices_to_triangle_mesh(TriangleMesh &                 mesh, | ||||
| void slices_to_mesh(indexed_triangle_set &         mesh, | ||||
|                              const std::vector<ExPolygons> &slices, | ||||
|                              double                         zmin, | ||||
|                              double                         lh, | ||||
|                              double                         ilh); | ||||
| 
 | ||||
| inline TriangleMesh slices_to_triangle_mesh( | ||||
| inline indexed_triangle_set slices_to_mesh( | ||||
|     const std::vector<ExPolygons> &slices, double zmin, double lh, double ilh) | ||||
| { | ||||
|     TriangleMesh out; slices_to_triangle_mesh(out, slices, zmin, lh, ilh); | ||||
|     indexed_triangle_set out; | ||||
|     slices_to_mesh(out, slices, zmin, lh, ilh); | ||||
| 
 | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,8 +1,12 @@ | |||
| #include "Exception.hpp" | ||||
| #include "TriangleMesh.hpp" | ||||
| #include "TriangleMeshSlicer.hpp" | ||||
| #include "MeshSplitImpl.hpp" | ||||
| #include "ClipperUtils.hpp" | ||||
| #include "Geometry.hpp" | ||||
| #include "Point.hpp" | ||||
| #include "Execution/ExecutionTBB.hpp" | ||||
| #include "Execution/ExecutionSeq.hpp" | ||||
| 
 | ||||
| #include <libqhullcpp/Qhull.h> | ||||
| #include <libqhullcpp/QhullFacetList.h> | ||||
|  | @ -447,27 +451,40 @@ std::deque<uint32_t> TriangleMesh::find_unvisited_neighbors(std::vector<unsigned | |||
|  */ | ||||
| TriangleMeshPtrs TriangleMesh::split() const | ||||
| { | ||||
|     // Loop while we have remaining facets.
 | ||||
|     std::vector<unsigned char> facet_visited; | ||||
|     struct MeshAdder { | ||||
|         TriangleMeshPtrs &meshes; | ||||
|         MeshAdder(TriangleMeshPtrs &ptrs): meshes{ptrs} {} | ||||
|         void operator=(const indexed_triangle_set &its) | ||||
|         { | ||||
|             meshes.emplace_back(new TriangleMesh(its)); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     TriangleMeshPtrs meshes; | ||||
|     for (;;) { | ||||
|         std::deque<uint32_t> facets = find_unvisited_neighbors(facet_visited); | ||||
|         if (facets.empty()) | ||||
|             break; | ||||
|     if (has_shared_vertices()) { | ||||
|         its_split(its, MeshAdder{meshes}); | ||||
|     } else { | ||||
|         // Loop while we have remaining facets.
 | ||||
|         std::vector<unsigned char> facet_visited; | ||||
|         for (;;) { | ||||
|             std::deque<uint32_t> facets = find_unvisited_neighbors(facet_visited); | ||||
|             if (facets.empty()) | ||||
|                 break; | ||||
| 
 | ||||
|         // Create a new mesh for the part that was just split off.
 | ||||
|         TriangleMesh* mesh = new TriangleMesh; | ||||
|         meshes.emplace_back(mesh); | ||||
|         mesh->stl.stats.type = inmemory; | ||||
|         mesh->stl.stats.number_of_facets = (uint32_t)facets.size(); | ||||
|         mesh->stl.stats.original_num_facets = mesh->stl.stats.number_of_facets; | ||||
|         stl_allocate(&mesh->stl); | ||||
|             // Create a new mesh for the part that was just split off.
 | ||||
|             TriangleMesh* mesh = new TriangleMesh; | ||||
|             meshes.emplace_back(mesh); | ||||
|             mesh->stl.stats.type = inmemory; | ||||
|             mesh->stl.stats.number_of_facets = (uint32_t)facets.size(); | ||||
|             mesh->stl.stats.original_num_facets = mesh->stl.stats.number_of_facets; | ||||
|             stl_allocate(&mesh->stl); | ||||
| 
 | ||||
|         // Assign the facets to the new mesh.
 | ||||
|         bool first = true; | ||||
|         for (auto facet = facets.begin(); facet != facets.end(); ++ facet) { | ||||
|             mesh->stl.facet_start[facet - facets.begin()] = this->stl.facet_start[*facet]; | ||||
|             stl_facet_stats(&mesh->stl, this->stl.facet_start[*facet], first); | ||||
|             // Assign the facets to the new mesh.
 | ||||
|             bool first = true; | ||||
|             for (auto facet = facets.begin(); facet != facets.end(); ++ facet) { | ||||
|                 mesh->stl.facet_start[facet - facets.begin()] = this->stl.facet_start[*facet]; | ||||
|                 stl_facet_stats(&mesh->stl, this->stl.facet_start[*facet], first); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -667,69 +684,24 @@ void TriangleMesh::restore_optional() | |||
|     } | ||||
| } | ||||
| 
 | ||||
| std::vector<std::vector<size_t>> create_vertex_faces_index(const indexed_triangle_set &its) | ||||
| { | ||||
|     std::vector<std::vector<size_t>> index; | ||||
| // Create a mapping from triangle edge into face.
 | ||||
| struct EdgeToFace { | ||||
|     // Index of the 1st vertex of the triangle edge. vertex_low <= vertex_high.
 | ||||
|     int  vertex_low; | ||||
|     // Index of the 2nd vertex of the triangle edge.
 | ||||
|     int  vertex_high; | ||||
|     // Index of a triangular face.
 | ||||
|     int  face; | ||||
|     // Index of edge in the face, starting with 1. Negative indices if the edge was stored reverse in (vertex_low, vertex_high).
 | ||||
|     int  face_edge; | ||||
|     bool operator==(const EdgeToFace &other) const { return vertex_low == other.vertex_low && vertex_high == other.vertex_high; } | ||||
|     bool operator<(const EdgeToFace &other) const { return vertex_low < other.vertex_low || (vertex_low == other.vertex_low && vertex_high < other.vertex_high); } | ||||
| }; | ||||
| 
 | ||||
|     if (! its.vertices.empty()) { | ||||
|         size_t res = its.indices.size() / its.vertices.size(); | ||||
|         index.assign(its.vertices.size(), reserve_vector<size_t>(res)); | ||||
|         for (size_t fi = 0; fi < its.indices.size(); ++fi) { | ||||
|             auto &face = its.indices[fi]; | ||||
|             index[face(0)].emplace_back(fi); | ||||
|             index[face(1)].emplace_back(fi); | ||||
|             index[face(2)].emplace_back(fi); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return index; | ||||
| } | ||||
| 
 | ||||
| void VertexFaceIndex::create(const indexed_triangle_set &its) | ||||
| { | ||||
|     m_vertex_to_face_start.assign(its.vertices.size() + 1, 0); | ||||
|     // 1) Calculate vertex incidence by scatter.
 | ||||
|     for (auto &face : its.indices) { | ||||
|         ++ m_vertex_to_face_start[face(0) + 1]; | ||||
|         ++ m_vertex_to_face_start[face(1) + 1]; | ||||
|         ++ m_vertex_to_face_start[face(2) + 1]; | ||||
|     } | ||||
|     // 2) Prefix sum to calculate offsets to m_vertex_faces_all.
 | ||||
|     for (size_t i = 2; i < m_vertex_to_face_start.size(); ++ i) | ||||
|         m_vertex_to_face_start[i] += m_vertex_to_face_start[i - 1]; | ||||
|     // 3) Scatter indices of faces incident to a vertex into m_vertex_faces_all.
 | ||||
|     m_vertex_faces_all.assign(m_vertex_to_face_start.back(), 0); | ||||
|     for (size_t face_idx = 0; face_idx < its.indices.size(); ++ face_idx) { | ||||
|         auto &face = its.indices[face_idx]; | ||||
|         for (int i = 0; i < 3; ++ i) | ||||
|             m_vertex_faces_all[m_vertex_to_face_start[face(i)] ++] = face_idx; | ||||
|     } | ||||
|     // 4) The previous loop modified m_vertex_to_face_start. Revert the change.
 | ||||
|     for (auto i = int(m_vertex_to_face_start.size()) - 1; i > 0; -- i) | ||||
|         m_vertex_to_face_start[i] = m_vertex_to_face_start[i - 1]; | ||||
|     m_vertex_to_face_start.front() = 0; | ||||
| } | ||||
| 
 | ||||
| // Map from a face edge to a unique edge identifier or -1 if no neighbor exists.
 | ||||
| // Two neighbor faces share a unique edge identifier even if they are flipped.
 | ||||
| template<typename ThrowOnCancelCallback> | ||||
| static inline std::vector<Vec3i> create_face_neighbors_index_impl(const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel) | ||||
| static std::vector<EdgeToFace> create_edge_map( | ||||
|     const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel) | ||||
| { | ||||
|     std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1)); | ||||
| 
 | ||||
|     // Create a mapping from triangle edge into face.
 | ||||
|     struct EdgeToFace { | ||||
|         // Index of the 1st vertex of the triangle edge. vertex_low <= vertex_high.
 | ||||
|         int  vertex_low; | ||||
|         // Index of the 2nd vertex of the triangle edge.
 | ||||
|         int  vertex_high; | ||||
|         // Index of a triangular face.
 | ||||
|         int  face; | ||||
|         // Index of edge in the face, starting with 1. Negative indices if the edge was stored reverse in (vertex_low, vertex_high).
 | ||||
|         int  face_edge; | ||||
|         bool operator==(const EdgeToFace &other) const { return vertex_low == other.vertex_low && vertex_high == other.vertex_high; } | ||||
|         bool operator<(const EdgeToFace &other) const { return vertex_low < other.vertex_low || (vertex_low == other.vertex_low && vertex_high < other.vertex_high); } | ||||
|     }; | ||||
|     std::vector<EdgeToFace> edges_map; | ||||
|     edges_map.assign(its.indices.size() * 3, EdgeToFace()); | ||||
|     for (uint32_t facet_idx = 0; facet_idx < its.indices.size(); ++ facet_idx) | ||||
|  | @ -750,6 +722,18 @@ static inline std::vector<Vec3i> create_face_neighbors_index_impl(const indexed_ | |||
|     throw_on_cancel(); | ||||
|     std::sort(edges_map.begin(), edges_map.end()); | ||||
| 
 | ||||
|     return edges_map; | ||||
| } | ||||
| 
 | ||||
| // Map from a face edge to a unique edge identifier or -1 if no neighbor exists.
 | ||||
| // Two neighbor faces share a unique edge identifier even if they are flipped.
 | ||||
| template<typename ThrowOnCancelCallback> | ||||
| static inline std::vector<Vec3i> create_face_neighbors_index_impl(const indexed_triangle_set &its, ThrowOnCancelCallback throw_on_cancel) | ||||
| { | ||||
|     std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1)); | ||||
| 
 | ||||
|     std::vector<EdgeToFace> edges_map = create_edge_map(its, throw_on_cancel); | ||||
| 
 | ||||
|     // Assign a unique common edge id to touching triangle edges.
 | ||||
|     int num_edges = 0; | ||||
|     for (size_t i = 0; i < edges_map.size(); ++ i) { | ||||
|  | @ -981,20 +965,22 @@ Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Transfor | |||
| } | ||||
| 
 | ||||
| // Generate the vertex list for a cube solid of arbitrary size in X/Y/Z.
 | ||||
| indexed_triangle_set its_make_cube(double xd, double yd, double zd) | ||||
| { | ||||
|     auto x = float(xd), y = float(yd), z = float(zd); | ||||
|     indexed_triangle_set mesh; | ||||
|     mesh.vertices = {{x, y, 0}, {x, 0, 0}, {0, 0, 0}, {0, y, 0}, | ||||
|                      {x, y, z}, {0, y, z}, {0, 0, z}, {x, 0, z}}; | ||||
|     mesh.indices  = {{0, 1, 2}, {0, 2, 3}, {4, 5, 6}, {4, 6, 7}, | ||||
|                     {0, 4, 7}, {0, 7, 1}, {1, 7, 6}, {1, 6, 2}, | ||||
|                     {2, 6, 5}, {2, 5, 3}, {4, 0, 3}, {4, 3, 5}}; | ||||
| 
 | ||||
|     return mesh; | ||||
| } | ||||
| 
 | ||||
| TriangleMesh make_cube(double x, double y, double z)  | ||||
| { | ||||
|     TriangleMesh mesh( | ||||
|         { | ||||
|             {x, y, 0}, {x, 0, 0}, {0, 0, 0}, | ||||
|             {0, y, 0}, {x, y, z}, {0, y, z}, | ||||
|             {0, 0, z}, {x, 0, z} | ||||
|         }, | ||||
|         { | ||||
|             {0, 1, 2}, {0, 2, 3}, {4, 5, 6}, | ||||
|             {4, 6, 7}, {0, 4, 7}, {0, 7, 1}, | ||||
|             {1, 7, 6}, {1, 6, 2}, {2, 6, 5}, | ||||
|             {2, 5, 3}, {4, 0, 3}, {4, 3, 5} | ||||
|         }); | ||||
|     TriangleMesh mesh(its_make_cube(x, y, z)); | ||||
|     mesh.repair(); | ||||
|     return mesh; | ||||
| } | ||||
|  | @ -1002,31 +988,32 @@ TriangleMesh make_cube(double x, double y, double z) | |||
| // Generate the mesh for a cylinder and return it, using 
 | ||||
| // the generated angle to calculate the top mesh triangles.
 | ||||
| // Default is 360 sides, angle fa is in radians.
 | ||||
| TriangleMesh make_cylinder(double r, double h, double fa) | ||||
| indexed_triangle_set its_make_cylinder(double r, double h, double fa) | ||||
| { | ||||
|     indexed_triangle_set mesh; | ||||
|     size_t n_steps    = (size_t)ceil(2. * PI / fa); | ||||
|     double angle_step = 2. * PI / n_steps; | ||||
| 
 | ||||
|     Pointf3s            vertices; | ||||
|     std::vector<Vec3i>  facets; | ||||
|     auto &vertices = mesh.vertices; | ||||
|     auto &facets   = mesh.indices; | ||||
|     vertices.reserve(2 * n_steps + 2); | ||||
|     facets.reserve(4 * n_steps); | ||||
| 
 | ||||
|     // 2 special vertices, top and bottom center, rest are relative to this
 | ||||
|     vertices.emplace_back(Vec3d(0.0, 0.0, 0.0)); | ||||
|     vertices.emplace_back(Vec3d(0.0, 0.0, h)); | ||||
|     vertices.emplace_back(Vec3f(0.f, 0.f, 0.f)); | ||||
|     vertices.emplace_back(Vec3f(0.f, 0.f, float(h))); | ||||
| 
 | ||||
|     // for each line along the polygon approximating the top/bottom of the
 | ||||
|     // circle, generate four points and four facets (2 for the wall, 2 for the
 | ||||
|     // top and bottom.
 | ||||
|     // Special case: Last line shares 2 vertices with the first line.
 | ||||
|     Vec2d p = Eigen::Rotation2Dd(0.) * Eigen::Vector2d(0, r); | ||||
|     vertices.emplace_back(Vec3d(p(0), p(1), 0.)); | ||||
|     vertices.emplace_back(Vec3d(p(0), p(1), h)); | ||||
|     Vec2f p = Eigen::Rotation2Df(0.f) * Eigen::Vector2f(0, r); | ||||
|     vertices.emplace_back(Vec3f(p(0), p(1), 0.f)); | ||||
|     vertices.emplace_back(Vec3f(p(0), p(1), float(h))); | ||||
|     for (size_t i = 1; i < n_steps; ++i) { | ||||
|         p = Eigen::Rotation2Dd(angle_step * i) * Eigen::Vector2d(0, r); | ||||
|         vertices.emplace_back(Vec3d(p(0), p(1), 0.)); | ||||
|         vertices.emplace_back(Vec3d(p(0), p(1), h)); | ||||
|         p = Eigen::Rotation2Df(angle_step * i) * Eigen::Vector2f(0, float(r)); | ||||
|         vertices.emplace_back(Vec3f(p(0), p(1), 0.f)); | ||||
|         vertices.emplace_back(Vec3f(p(0), p(1), float(h))); | ||||
|         int id = (int)vertices.size() - 1; | ||||
|         facets.emplace_back( 0, id - 1, id - 3); // top
 | ||||
|         facets.emplace_back(id,      1, id - 2); // bottom
 | ||||
|  | @ -1039,9 +1026,15 @@ TriangleMesh make_cylinder(double r, double h, double fa) | |||
|     facets.emplace_back( 3, 1,     id); | ||||
|     facets.emplace_back(id, 2,      3); | ||||
|     facets.emplace_back(id, id - 1, 2); | ||||
|      | ||||
|     TriangleMesh mesh(std::move(vertices), std::move(facets)); | ||||
| 
 | ||||
|     return mesh; | ||||
| } | ||||
| 
 | ||||
| TriangleMesh make_cylinder(double r, double h, double fa) | ||||
| { | ||||
|     TriangleMesh mesh{its_make_cylinder(r, h, fa)}; | ||||
|     mesh.repair(); | ||||
| 
 | ||||
|     return mesh; | ||||
| } | ||||
| 
 | ||||
|  | @ -1076,14 +1069,15 @@ TriangleMesh make_cone(double r, double h, double fa) | |||
| // to determine the granularity. 
 | ||||
| // Default angle is 1 degree.
 | ||||
| //FIXME better to discretize an Icosahedron recursively http://www.songho.ca/opengl/gl_sphere.html
 | ||||
| TriangleMesh make_sphere(double radius, double fa) | ||||
| indexed_triangle_set its_make_sphere(double radius, double fa) | ||||
| { | ||||
|     int   sectorCount = int(ceil(2. * M_PI / fa)); | ||||
|     int   stackCount  = int(ceil(M_PI / fa)); | ||||
|     float sectorStep  = float(2. * M_PI / sectorCount); | ||||
|     float stackStep   = float(M_PI / stackCount); | ||||
| 
 | ||||
|     Pointf3s vertices; | ||||
|     indexed_triangle_set mesh; | ||||
|     auto& vertices = mesh.vertices; | ||||
|     vertices.reserve((stackCount - 1) * sectorCount + 2); | ||||
|     for (int i = 0; i <= stackCount; ++ i) { | ||||
|         // from pi/2 to -pi/2
 | ||||
|  | @ -1091,16 +1085,16 @@ TriangleMesh make_sphere(double radius, double fa) | |||
|         double xy = radius * cos(stackAngle); | ||||
|         double z  = radius * sin(stackAngle); | ||||
|         if (i == 0 || i == stackCount) | ||||
|             vertices.emplace_back(Vec3d(xy, 0., z)); | ||||
|             vertices.emplace_back(Vec3f(float(xy), 0.f, float(z))); | ||||
|         else | ||||
|             for (int j = 0; j < sectorCount; ++ j) { | ||||
|                 // from 0 to 2pi
 | ||||
|                 double sectorAngle = sectorStep * j; | ||||
|                 vertices.emplace_back(Vec3d(xy * cos(sectorAngle), xy * sin(sectorAngle), z)); | ||||
|                 vertices.emplace_back(Vec3d(xy * std::cos(sectorAngle), xy * std::sin(sectorAngle), z).cast<float>()); | ||||
|             } | ||||
|     } | ||||
| 
 | ||||
|     std::vector<Vec3i> facets; | ||||
|     auto& facets = mesh.indices; | ||||
|     facets.reserve(2 * (stackCount - 1) * sectorCount); | ||||
|     for (int i = 0; i < stackCount; ++ i) { | ||||
|         // Beginning of current stack.
 | ||||
|  | @ -1125,9 +1119,114 @@ TriangleMesh make_sphere(double radius, double fa) | |||
|             k2 = k2_next; | ||||
|         } | ||||
|     } | ||||
|     TriangleMesh mesh(std::move(vertices), std::move(facets)); | ||||
|     mesh.repair(); | ||||
| 
 | ||||
|     return mesh; | ||||
| } | ||||
| 
 | ||||
| TriangleMesh make_sphere(double radius, double fa) | ||||
| { | ||||
|     TriangleMesh mesh(its_make_sphere(radius, fa)); | ||||
|     mesh.repair(); | ||||
| 
 | ||||
|     return mesh; | ||||
| } | ||||
| 
 | ||||
| void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B) | ||||
| { | ||||
|     auto N   = int(A.vertices.size()); | ||||
|     auto N_f = A.indices.size(); | ||||
| 
 | ||||
|     A.vertices.insert(A.vertices.end(), B.vertices.begin(), B.vertices.end()); | ||||
|     A.indices.insert(A.indices.end(), B.indices.begin(), B.indices.end()); | ||||
| 
 | ||||
|     for(size_t n = N_f; n < A.indices.size(); n++) | ||||
|         A.indices[n] += Vec3i{N, N, N}; | ||||
| } | ||||
| 
 | ||||
| void its_merge(indexed_triangle_set &A, const std::vector<Vec3f> &triangles) | ||||
| { | ||||
|     const size_t offs = A.vertices.size(); | ||||
|     A.vertices.insert(A.vertices.end(), triangles.begin(), triangles.end()); | ||||
|     A.indices.reserve(A.indices.size() + A.vertices.size() / 3); | ||||
| 
 | ||||
|     for(int i = int(offs); i < int(A.vertices.size()); i += 3) | ||||
|         A.indices.emplace_back(i, i + 1, i + 2); | ||||
| } | ||||
| 
 | ||||
| void its_merge(indexed_triangle_set &A, const Pointf3s &triangles) | ||||
| { | ||||
|     auto trianglesf = reserve_vector<Vec3f> (triangles.size()); | ||||
|     for (auto &t : triangles) | ||||
|         trianglesf.emplace_back(t.cast<float>()); | ||||
| 
 | ||||
|     its_merge(A, trianglesf); | ||||
| } | ||||
| 
 | ||||
| float its_volume(const indexed_triangle_set &its) | ||||
| { | ||||
|     if (its.empty()) return 0.; | ||||
| 
 | ||||
|     // Choose a point, any point as the reference.
 | ||||
|     auto p0 = its.vertices.front(); | ||||
|     float volume = 0.f; | ||||
|     for (size_t i = 0; i < its.indices.size(); ++ i) { | ||||
|         // Do dot product to get distance from point to plane.
 | ||||
|         its_triangle triangle = its_triangle_vertices(its, i); | ||||
|         Vec3f U = triangle[1] - triangle[0]; | ||||
|         Vec3f V = triangle[2] - triangle[0]; | ||||
|         Vec3f C = U.cross(V); | ||||
|         Vec3f normal = C.normalized(); | ||||
|         float area = 0.5 * C.norm(); | ||||
|         float height = normal.dot(triangle[0] - p0); | ||||
|         volume += (area * height) / 3.0f; | ||||
|     } | ||||
| 
 | ||||
|     return volume; | ||||
| } | ||||
| 
 | ||||
| std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its) | ||||
| { | ||||
|     return its_split<>(its); | ||||
| } | ||||
| 
 | ||||
| bool its_is_splittable(const indexed_triangle_set &its) | ||||
| { | ||||
|     return its_is_splittable<>(its); | ||||
| } | ||||
| 
 | ||||
| void VertexFaceIndex::create(const indexed_triangle_set &its) | ||||
| { | ||||
|     m_vertex_to_face_start.assign(its.vertices.size() + 1, 0); | ||||
|     // 1) Calculate vertex incidence by scatter.
 | ||||
|     for (auto &face : its.indices) { | ||||
|         ++ m_vertex_to_face_start[face(0) + 1]; | ||||
|         ++ m_vertex_to_face_start[face(1) + 1]; | ||||
|         ++ m_vertex_to_face_start[face(2) + 1]; | ||||
|     } | ||||
|     // 2) Prefix sum to calculate offsets to m_vertex_faces_all.
 | ||||
|     for (size_t i = 2; i < m_vertex_to_face_start.size(); ++ i) | ||||
|         m_vertex_to_face_start[i] += m_vertex_to_face_start[i - 1]; | ||||
|     // 3) Scatter indices of faces incident to a vertex into m_vertex_faces_all.
 | ||||
|     m_vertex_faces_all.assign(m_vertex_to_face_start.back(), 0); | ||||
|     for (size_t face_idx = 0; face_idx < its.indices.size(); ++ face_idx) { | ||||
|         auto &face = its.indices[face_idx]; | ||||
|         for (int i = 0; i < 3; ++ i) | ||||
|             m_vertex_faces_all[m_vertex_to_face_start[face(i)] ++] = face_idx; | ||||
|     } | ||||
|     // 4) The previous loop modified m_vertex_to_face_start. Revert the change.
 | ||||
|     for (auto i = int(m_vertex_to_face_start.size()) - 1; i > 0; -- i) | ||||
|         m_vertex_to_face_start[i] = m_vertex_to_face_start[i - 1]; | ||||
|     m_vertex_to_face_start.front() = 0; | ||||
| } | ||||
| 
 | ||||
| std::vector<Vec3i> its_create_neighbors_index(const indexed_triangle_set &its) | ||||
| { | ||||
|     return create_neighbors_index(ex_seq, its); | ||||
| } | ||||
| 
 | ||||
| std::vector<Vec3i> its_create_neighbors_index_par(const indexed_triangle_set &its) | ||||
| { | ||||
|     return create_neighbors_index(ex_tbb, its); | ||||
| } | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -88,11 +88,6 @@ private: | |||
|     std::deque<uint32_t> find_unvisited_neighbors(std::vector<unsigned char> &facet_visited) const; | ||||
| }; | ||||
| 
 | ||||
| // Create an index of faces belonging to each vertex. The returned vector can
 | ||||
| // be indexed with vertex indices and contains a list of face indices for each
 | ||||
| // vertex.
 | ||||
| std::vector<std::vector<size_t>> create_vertex_faces_index(const indexed_triangle_set &its); | ||||
| 
 | ||||
| // Index of face indices incident with a vertex index.
 | ||||
| struct VertexFaceIndex | ||||
| { | ||||
|  | @ -111,6 +106,8 @@ public: | |||
|     // Vertex incidence.
 | ||||
|     size_t   count(size_t vertex_id) const throw() { return m_vertex_to_face_start[vertex_id + 1] - m_vertex_to_face_start[vertex_id]; } | ||||
| 
 | ||||
|     const Range<iterator> operator[](size_t vertex_id) const { return {begin(vertex_id), end(vertex_id)}; } | ||||
| 
 | ||||
| private: | ||||
|     std::vector<size_t>     m_vertex_to_face_start; | ||||
|     std::vector<size_t>     m_vertex_faces_all; | ||||
|  | @ -122,6 +119,11 @@ private: | |||
| std::vector<Vec3i> create_face_neighbors_index(const indexed_triangle_set &its); | ||||
| std::vector<Vec3i> create_face_neighbors_index(const indexed_triangle_set &its, std::function<void()> throw_on_cancel_callback); | ||||
| 
 | ||||
| // Create index that gives neighbor faces for each face. Ignores face orientations.
 | ||||
| // TODO: naming...
 | ||||
| std::vector<Vec3i> its_create_neighbors_index(const indexed_triangle_set &its); | ||||
| std::vector<Vec3i> its_create_neighbors_index_par(const indexed_triangle_set &its); | ||||
| 
 | ||||
| // After applying a transformation with negative determinant, flip the faces to keep the transformed mesh volume positive.
 | ||||
| void its_flip_triangles(indexed_triangle_set &its); | ||||
| 
 | ||||
|  | @ -136,6 +138,10 @@ int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit = | |||
| // Remove vertices, which none of the faces references. Return number of freed vertices.
 | ||||
| int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit = true); | ||||
| 
 | ||||
| std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its); | ||||
| 
 | ||||
| bool its_is_splittable(const indexed_triangle_set &its); | ||||
| 
 | ||||
| // Shrink the vectors of its.vertices and its.faces to a minimum size by reallocating the two vectors.
 | ||||
| void its_shrink_to_fit(indexed_triangle_set &its); | ||||
| 
 | ||||
|  | @ -147,11 +153,56 @@ void its_collect_mesh_projection_points_above(const indexed_triangle_set &its, c | |||
| Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Matrix3f &m, const float z); | ||||
| Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Transform3f &t, const float z); | ||||
| 
 | ||||
| using its_triangle = std::array<stl_vertex, 3>; | ||||
| 
 | ||||
| inline its_triangle its_triangle_vertices(const indexed_triangle_set &its, | ||||
|                                           size_t                      face_id) | ||||
| { | ||||
|     return {its.vertices[its.indices[face_id](0)], | ||||
|             its.vertices[its.indices[face_id](1)], | ||||
|             its.vertices[its.indices[face_id](2)]}; | ||||
| } | ||||
| 
 | ||||
| inline stl_normal its_unnormalized_normal(const indexed_triangle_set &its, | ||||
|                                           size_t                      face_id) | ||||
| { | ||||
|     its_triangle tri = its_triangle_vertices(its, face_id); | ||||
|     return (tri[1] - tri[0]).cross(tri[2] - tri[0]); | ||||
| } | ||||
| 
 | ||||
| float its_volume(const indexed_triangle_set &its); | ||||
| 
 | ||||
| void its_merge(indexed_triangle_set &A, const indexed_triangle_set &B); | ||||
| void its_merge(indexed_triangle_set &A, const std::vector<Vec3f> &triangles); | ||||
| void its_merge(indexed_triangle_set &A, const Pointf3s &triangles); | ||||
| 
 | ||||
| indexed_triangle_set its_make_cube(double x, double y, double z); | ||||
| TriangleMesh make_cube(double x, double y, double z); | ||||
| 
 | ||||
| // Generate a TriangleMesh of a cylinder
 | ||||
| indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360)); | ||||
| TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360)); | ||||
| 
 | ||||
| indexed_triangle_set its_make_sphere(double rho, double fa=(2*PI/360)); | ||||
| TriangleMesh make_cone(double r, double h, double fa=(2*PI/360)); | ||||
| TriangleMesh make_sphere(double rho, double fa=(2*PI/360)); | ||||
| 
 | ||||
| inline BoundingBoxf3 bounding_box(const TriangleMesh &m) { return m.bounding_box(); } | ||||
| inline BoundingBoxf3 bounding_box(const indexed_triangle_set& its) | ||||
| { | ||||
|     if (its.vertices.empty()) | ||||
|         return {}; | ||||
| 
 | ||||
|     Vec3f bmin = its.vertices.front(), bmax = its.vertices.front(); | ||||
| 
 | ||||
|     for (const Vec3f &p : its.vertices) { | ||||
|         bmin = p.cwiseMin(bmin); | ||||
|         bmax = p.cwiseMax(bmax); | ||||
|     } | ||||
| 
 | ||||
|     return {bmin.cast<double>(), bmax.cast<double>()}; | ||||
| } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| // Serialization through the Cereal library
 | ||||
|  |  | |||
|  | @ -3,131 +3,157 @@ | |||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| class Ring { | ||||
|     size_t idx = 0, nextidx = 1, startidx = 0, begin = 0, end = 0; | ||||
| //class Ring {
 | ||||
| //    size_t idx = 0, nextidx = 1, startidx = 0, begin = 0, end = 0;
 | ||||
|      | ||||
| public: | ||||
|     explicit Ring(size_t from, size_t to) : begin(from), end(to) { init(begin); } | ||||
| //public:
 | ||||
| //    explicit Ring(size_t from, size_t to) : begin(from), end(to) { init(begin); }
 | ||||
| 
 | ||||
|     size_t size() const { return end - begin; } | ||||
|     std::pair<size_t, size_t> pos() const { return {idx, nextidx}; } | ||||
|     bool is_lower() const { return idx < size(); } | ||||
| //    size_t size() const { return end - begin; }
 | ||||
| //    std::pair<size_t, size_t> pos() const { return {idx, nextidx}; }
 | ||||
| //    bool is_lower() const { return idx < size(); }
 | ||||
|      | ||||
|     void inc() | ||||
|     { | ||||
|         if (nextidx != startidx) nextidx++; | ||||
|         if (nextidx == end) nextidx = begin; | ||||
|         idx ++; | ||||
|         if (idx == end) idx = begin; | ||||
|     } | ||||
| //    void inc()
 | ||||
| //    {
 | ||||
| //        if (nextidx != startidx) nextidx++;
 | ||||
| //        if (nextidx == end) nextidx = begin;
 | ||||
| //        idx ++;
 | ||||
| //        if (idx == end) idx = begin;
 | ||||
| //    }
 | ||||
|      | ||||
|     void init(size_t pos) | ||||
|     { | ||||
|         startidx = begin + (pos - begin) % size(); | ||||
|         idx = startidx; | ||||
|         nextidx = begin + (idx + 1 - begin) % size(); | ||||
|     } | ||||
| //    void init(size_t pos)
 | ||||
| //    {
 | ||||
| //        startidx = begin + (pos - begin) % size();
 | ||||
| //        idx = startidx;
 | ||||
| //        nextidx = begin + (idx + 1 - begin) % size();
 | ||||
| //    }
 | ||||
|      | ||||
|     bool is_finished() const { return nextidx == idx; } | ||||
| }; | ||||
| //    bool is_finished() const { return nextidx == idx; }
 | ||||
| //};
 | ||||
| 
 | ||||
| static double sq_dst(const Vec3d &v1, const Vec3d& v2) | ||||
| { | ||||
|     Vec3d v = v1 - v2; | ||||
|     return v.x() * v.x() + v.y() * v.y() /*+ v.z() * v.z()*/; | ||||
| } | ||||
| //template<class Sc>
 | ||||
| //static Sc sq_dst(const Vec<3, Sc> &v1, const Vec<3, Sc>& v2)
 | ||||
| //{
 | ||||
| //    Vec<3, Sc> v = v1 - v2;
 | ||||
| //    return v.x() * v.x() + v.y() * v.y() /*+ v.z() * v.z()*/;
 | ||||
| //}
 | ||||
| 
 | ||||
| static double score(const Ring& onring, const Ring &offring, | ||||
|                     const std::vector<Vec3d> &pts) | ||||
| { | ||||
|     double a = sq_dst(pts[onring.pos().first], pts[offring.pos().first]); | ||||
|     double b = sq_dst(pts[onring.pos().second], pts[offring.pos().first]); | ||||
|     return (std::abs(a) + std::abs(b)) / 2.; | ||||
| } | ||||
| //template<class Sc>
 | ||||
| //static Sc trscore(const Ring &                   onring,
 | ||||
| //                  const Ring &                   offring,
 | ||||
| //                  const std::vector<Vec<3, Sc>> &pts)
 | ||||
| //{
 | ||||
| //    Sc a = sq_dst(pts[onring.pos().first], pts[offring.pos().first]);
 | ||||
| //    Sc b = sq_dst(pts[onring.pos().second], pts[offring.pos().first]);
 | ||||
| //    return (std::abs(a) + std::abs(b)) / 2.;
 | ||||
| //}
 | ||||
| 
 | ||||
| class Triangulator { | ||||
|     const std::vector<Vec3d> *pts; | ||||
|     Ring *onring, *offring; | ||||
| //template<class Sc>
 | ||||
| //class Triangulator {
 | ||||
| //    const std::vector<Vec<3, Sc>> *pts;
 | ||||
| //    Ring *onring, *offring;
 | ||||
|      | ||||
|     double calc_score() const | ||||
|     { | ||||
|         return Slic3r::score(*onring, *offring, *pts); | ||||
|     } | ||||
| //    double calc_score() const
 | ||||
| //    {
 | ||||
| //        return trscore(*onring, *offring, *pts);
 | ||||
| //    }
 | ||||
|      | ||||
|     void synchronize_rings() | ||||
|     { | ||||
|         Ring lring = *offring; | ||||
|         auto minsc = Slic3r::score(*onring, lring, *pts); | ||||
|         size_t imin = lring.pos().first; | ||||
| //    void synchronize_rings()
 | ||||
| //    {
 | ||||
| //        Ring lring = *offring;
 | ||||
| //        auto minsc = trscore(*onring, lring, *pts);
 | ||||
| //        size_t imin = lring.pos().first;
 | ||||
|          | ||||
|         lring.inc(); | ||||
| //        lring.inc();
 | ||||
|          | ||||
|         while(!lring.is_finished()) { | ||||
|             double score = Slic3r::score(*onring, lring, *pts); | ||||
|             if (score < minsc) { minsc = score; imin = lring.pos().first; } | ||||
|             lring.inc(); | ||||
|         } | ||||
| //        while(!lring.is_finished()) {
 | ||||
| //            double score = trscore(*onring, lring, *pts);
 | ||||
| //            if (score < minsc) { minsc = score; imin = lring.pos().first; }
 | ||||
| //            lring.inc();
 | ||||
| //        }
 | ||||
|          | ||||
|         offring->init(imin); | ||||
|     } | ||||
| //        offring->init(imin);
 | ||||
| //    }
 | ||||
|      | ||||
|     void emplace_indices(std::vector<Vec3i> &indices) | ||||
|     { | ||||
|         Vec3i tr{int(onring->pos().first), int(onring->pos().second), | ||||
|                  int(offring->pos().first)}; | ||||
|         if (onring->is_lower()) std::swap(tr(0), tr(1)); | ||||
|         indices.emplace_back(tr); | ||||
|     } | ||||
| //    void emplace_indices(std::vector<Vec3i> &indices)
 | ||||
| //    {
 | ||||
| //        Vec3i tr{int(onring->pos().first), int(onring->pos().second),
 | ||||
| //                 int(offring->pos().first)};
 | ||||
| //        if (onring->is_lower()) std::swap(tr(0), tr(1));
 | ||||
| //        indices.emplace_back(tr);
 | ||||
| //    }
 | ||||
|      | ||||
| public: | ||||
|     void run(std::vector<Vec3i> &indices) | ||||
|     {    | ||||
|         synchronize_rings(); | ||||
| //public:
 | ||||
| //    void run(std::vector<Vec3i> &indices)
 | ||||
| //    {
 | ||||
| //        synchronize_rings();
 | ||||
|          | ||||
|         double score = 0, prev_score = 0;         | ||||
|         while (!onring->is_finished() || !offring->is_finished()) { | ||||
|             prev_score = score; | ||||
|             if (onring->is_finished() || (score = calc_score()) > prev_score) { | ||||
|                 std::swap(onring, offring); | ||||
|             } else { | ||||
|                 emplace_indices(indices); | ||||
|                 onring->inc(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| //        double score = 0, prev_score = 0;
 | ||||
| //        while (!onring->is_finished() || !offring->is_finished()) {
 | ||||
| //            prev_score = score;
 | ||||
| //            if (onring->is_finished() || (score = calc_score()) > prev_score) {
 | ||||
| //                std::swap(onring, offring);
 | ||||
| //            } else {
 | ||||
| //                emplace_indices(indices);
 | ||||
| //                onring->inc();
 | ||||
| //            }
 | ||||
| //        }
 | ||||
| //    }
 | ||||
| 
 | ||||
|     explicit Triangulator(const std::vector<Vec3d> *points, | ||||
|                           Ring &                    lower, | ||||
|                           Ring &                    upper) | ||||
|         : pts{points}, onring{&upper}, offring{&lower} | ||||
|     {} | ||||
| }; | ||||
| //    explicit Triangulator(const std::vector<Vec<3, Sc>> *points,
 | ||||
| //                          Ring &                    lower,
 | ||||
| //                          Ring &                    upper)
 | ||||
| //        : pts{points}, onring{&upper}, offring{&lower}
 | ||||
| //    {}
 | ||||
| //};
 | ||||
| 
 | ||||
| Wall triangulate_wall( | ||||
|     const Polygon &          lower, | ||||
|     const Polygon &          upper, | ||||
|     double                   lower_z_mm, | ||||
|     double                   upper_z_mm) | ||||
| { | ||||
|     if (upper.points.size() < 3 || lower.points.size() < 3) return {}; | ||||
|      | ||||
|     Wall wall; | ||||
|     auto &pts = wall.first; | ||||
|     auto &ind = wall.second; | ||||
| //template<class Sc, class I>
 | ||||
| //void triangulate_wall(std::vector<Vec<3, Sc>> &pts,
 | ||||
| //                      std::vector<Vec<3, I>> & ind,
 | ||||
| //                      const Polygon &          lower,
 | ||||
| //                      const Polygon &          upper,
 | ||||
| //                      double                   lower_z_mm,
 | ||||
| //                      double                   upper_z_mm)
 | ||||
| //{
 | ||||
| //    if (upper.points.size() < 3 || lower.points.size() < 3) return;
 | ||||
| 
 | ||||
|     pts.reserve(lower.points.size() + upper.points.size()); | ||||
|     for (auto &p : lower.points) | ||||
|         wall.first.emplace_back(unscaled(p.x()), unscaled(p.y()), lower_z_mm); | ||||
|     for (auto &p : upper.points) | ||||
|         wall.first.emplace_back(unscaled(p.x()), unscaled(p.y()), upper_z_mm); | ||||
| //    pts.reserve(lower.points.size() + upper.points.size());
 | ||||
| //    for (auto &p : lower.points)
 | ||||
| //        pts.emplace_back(unscaled(p.x()), unscaled(p.y()), lower_z_mm);
 | ||||
| //    for (auto &p : upper.points)
 | ||||
| //        pts.emplace_back(unscaled(p.x()), unscaled(p.y()), upper_z_mm);
 | ||||
| 
 | ||||
| //    ind.reserve(2 * (lower.size() + upper.size()));
 | ||||
| 
 | ||||
| //    Ring lring{0, lower.points.size()}, uring{lower.points.size(), pts.size()};
 | ||||
| //    Triangulator t{&pts, lring, uring};
 | ||||
| //    t.run(ind);
 | ||||
| //}
 | ||||
| 
 | ||||
| //Wall triangulate_wall(const Polygon &lower,
 | ||||
| //                      const Polygon &upper,
 | ||||
| //                      double         lower_z_mm,
 | ||||
| //                      double         upper_z_mm)
 | ||||
| //{
 | ||||
| //    if (upper.points.size() < 3 || lower.points.size() < 3) return {};
 | ||||
|      | ||||
|     ind.reserve(2 * (lower.size() + upper.size())); | ||||
| //    Wall wall;
 | ||||
| //    auto &pts = wall.first;
 | ||||
| //    auto &ind = wall.second;
 | ||||
| 
 | ||||
| //    pts.reserve(lower.points.size() + upper.points.size());
 | ||||
| //    for (auto &p : lower.points)
 | ||||
| //        wall.first.emplace_back(unscaled(p.x()), unscaled(p.y()), lower_z_mm);
 | ||||
| //    for (auto &p : upper.points)
 | ||||
| //        wall.first.emplace_back(unscaled(p.x()), unscaled(p.y()), upper_z_mm);
 | ||||
|      | ||||
|     Ring lring{0, lower.points.size()}, uring{lower.points.size(), pts.size()}; | ||||
|     Triangulator t{&pts, lring, uring}; | ||||
|     t.run(ind); | ||||
| //    ind.reserve(2 * (lower.size() + upper.size()));
 | ||||
|      | ||||
|     return wall; | ||||
| } | ||||
| //    Ring lring{0, lower.points.size()}, uring{lower.points.size(), pts.size()};
 | ||||
| //    Triangulator t{&pts, lring, uring};
 | ||||
| //    t.run(ind);
 | ||||
|      | ||||
| //    return wall;
 | ||||
| //}
 | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -5,13 +5,148 @@ | |||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| using Wall = std::pair<std::vector<Vec3d>, std::vector<Vec3i>>; | ||||
| namespace trianglulate_wall_detail { | ||||
| 
 | ||||
| Wall triangulate_wall( | ||||
|     const Polygon &       lower, | ||||
|     const Polygon &       upper, | ||||
|     double                lower_z_mm, | ||||
|     double                upper_z_mm); | ||||
| class Ring { | ||||
|     size_t idx = 0, nextidx = 1, startidx = 0, begin = 0, end = 0; | ||||
| 
 | ||||
| public: | ||||
|     explicit Ring(size_t from, size_t to) : begin(from), end(to) { init(begin); } | ||||
| 
 | ||||
|     size_t size() const { return end - begin; } | ||||
|     std::pair<size_t, size_t> pos() const { return {idx, nextidx}; } | ||||
|     bool is_lower() const { return idx < size(); } | ||||
| 
 | ||||
|     void inc() | ||||
|     { | ||||
|         if (nextidx != startidx) nextidx++; | ||||
|         if (nextidx == end) nextidx = begin; | ||||
|         idx ++; | ||||
|         if (idx == end) idx = begin; | ||||
|     } | ||||
| 
 | ||||
|     void init(size_t pos) | ||||
|     { | ||||
|         startidx = begin + (pos - begin) % size(); | ||||
|         idx = startidx; | ||||
|         nextidx = begin + (idx + 1 - begin) % size(); | ||||
|     } | ||||
| 
 | ||||
|     bool is_finished() const { return nextidx == idx; } | ||||
| }; | ||||
| 
 | ||||
| template<class Sc> | ||||
| static Sc sq_dst(const Vec<3, Sc> &v1, const Vec<3, Sc>& v2) | ||||
| { | ||||
|     Vec<3, Sc> v = v1 - v2; | ||||
|     return v.x() * v.x() + v.y() * v.y() /*+ v.z() * v.z()*/; | ||||
| } | ||||
| 
 | ||||
| template<class Sc> | ||||
| static Sc trscore(const Ring &                   onring, | ||||
|                   const Ring &                   offring, | ||||
|                   const std::vector<Vec<3, Sc>> &pts) | ||||
| { | ||||
|     Sc a = sq_dst(pts[onring.pos().first], pts[offring.pos().first]); | ||||
|     Sc b = sq_dst(pts[onring.pos().second], pts[offring.pos().first]); | ||||
|     return (std::abs(a) + std::abs(b)) / 2.; | ||||
| } | ||||
| 
 | ||||
| template<class Sc> | ||||
| class Triangulator { | ||||
|     const std::vector<Vec<3, Sc>> *pts; | ||||
|     Ring *onring, *offring; | ||||
| 
 | ||||
|     double calc_score() const | ||||
|     { | ||||
|         return trscore(*onring, *offring, *pts); | ||||
|     } | ||||
| 
 | ||||
|     void synchronize_rings() | ||||
|     { | ||||
|         Ring lring = *offring; | ||||
|         auto minsc = trscore(*onring, lring, *pts); | ||||
|         size_t imin = lring.pos().first; | ||||
| 
 | ||||
|         lring.inc(); | ||||
| 
 | ||||
|         while(!lring.is_finished()) { | ||||
|             double score = trscore(*onring, lring, *pts); | ||||
|             if (score < minsc) { minsc = score; imin = lring.pos().first; } | ||||
|             lring.inc(); | ||||
|         } | ||||
| 
 | ||||
|         offring->init(imin); | ||||
|     } | ||||
| 
 | ||||
|     void emplace_indices(std::vector<Vec3i> &indices) | ||||
|     { | ||||
|         Vec3i tr{int(onring->pos().first), int(onring->pos().second), | ||||
|                  int(offring->pos().first)}; | ||||
|         if (onring->is_lower()) std::swap(tr(0), tr(1)); | ||||
|         indices.emplace_back(tr); | ||||
|     } | ||||
| 
 | ||||
| public: | ||||
|     void run(std::vector<Vec3i> &indices) | ||||
|     { | ||||
|         synchronize_rings(); | ||||
| 
 | ||||
|         double score = 0, prev_score = 0; | ||||
|         while (!onring->is_finished() || !offring->is_finished()) { | ||||
|             prev_score = score; | ||||
|             if (onring->is_finished() || (score = calc_score()) > prev_score) { | ||||
|                 std::swap(onring, offring); | ||||
|             } else { | ||||
|                 emplace_indices(indices); | ||||
|                 onring->inc(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     explicit Triangulator(const std::vector<Vec<3, Sc>> *points, | ||||
|                           Ring &                    lower, | ||||
|                           Ring &                    upper) | ||||
|         : pts{points}, onring{&upper}, offring{&lower} | ||||
|     {} | ||||
| }; | ||||
| 
 | ||||
| } // namespace trianglulate_wall_detail
 | ||||
| 
 | ||||
| template<class Sc, class I> | ||||
| void triangulate_wall(std::vector<Vec<3, Sc>> &pts, | ||||
|                       std::vector<Vec<3, I>> & ind, | ||||
|                       const Polygon &          lower, | ||||
|                       const Polygon &          upper, | ||||
|                       double                   lower_z_mm, | ||||
|                       double                   upper_z_mm) | ||||
| { | ||||
|     using namespace trianglulate_wall_detail; | ||||
| 
 | ||||
|     if (upper.points.size() < 3 || lower.points.size() < 3) return; | ||||
| 
 | ||||
|     pts.reserve(lower.points.size() + upper.points.size()); | ||||
|     for (auto &p : lower.points) | ||||
|         pts.emplace_back(unscaled(p.x()), unscaled(p.y()), lower_z_mm); | ||||
|     for (auto &p : upper.points) | ||||
|         pts.emplace_back(unscaled(p.x()), unscaled(p.y()), upper_z_mm); | ||||
| 
 | ||||
|     ind.reserve(2 * (lower.size() + upper.size())); | ||||
| 
 | ||||
|     Ring lring{0, lower.points.size()}, uring{lower.points.size(), pts.size()}; | ||||
|     Triangulator t{&pts, lring, uring}; | ||||
|     t.run(ind); | ||||
| } | ||||
| 
 | ||||
| //using Wall = std::pair<std::vector<Vec3d>, std::vector<Vec3i>>;
 | ||||
| 
 | ||||
| //Wall triangulate_wall(
 | ||||
| //    const Polygon &       lower,
 | ||||
| //    const Polygon &       upper,
 | ||||
| //    double                lower_z_mm,
 | ||||
| //    double                upper_z_mm);
 | ||||
| //}
 | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| #endif // TRIANGULATEWALL_HPP
 | ||||
|  |  | |||
|  | @ -306,6 +306,29 @@ IntegerOnly<I, std::vector<T, Args...>> reserve_vector(I capacity) | |||
| template<class T> | ||||
| using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>; | ||||
| 
 | ||||
| // A very simple range concept implementation with iterator-like objects.
 | ||||
| // This should be replaced by std::ranges::subrange (C++20)
 | ||||
| template<class It> class Range | ||||
| { | ||||
|     It from, to; | ||||
| public: | ||||
| 
 | ||||
|     // The class is ready for range based for loops.
 | ||||
|     It begin() const { return from; } | ||||
|     It end() const { return to; } | ||||
| 
 | ||||
|     // The iterator type can be obtained this way.
 | ||||
|     using iterator = It; | ||||
|     using value_type = typename std::iterator_traits<It>::value_type; | ||||
| 
 | ||||
|     Range() = default; | ||||
|     Range(It b, It e) : from(std::move(b)), to(std::move(e)) {} | ||||
| 
 | ||||
|     // Some useful container-like methods...
 | ||||
|     inline size_t size() const { return end() - begin(); } | ||||
|     inline bool   empty() const { return size() == 0; } | ||||
| }; | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -208,7 +208,7 @@ void HollowedMesh::on_update() | |||
|                     m_drainholes = print_object->model_object()->sla_drain_holes; | ||||
|                     m_old_hollowing_timestamp = timestamp; | ||||
| 
 | ||||
|                     const TriangleMesh &interior = print_object->hollowed_interior_mesh(); | ||||
|                     const indexed_triangle_set &interior = print_object->hollowed_interior_mesh(); | ||||
|                     if (!interior.empty()) { | ||||
|                         m_hollowed_interior_transformed = std::make_unique<TriangleMesh>(interior); | ||||
|                         m_hollowed_interior_transformed->repaired = false; | ||||
|  |  | |||
|  | @ -113,14 +113,14 @@ public: | |||
|     Plater *plater; | ||||
|      | ||||
|     Sel sel = Sel::modelAndProfile; | ||||
|      | ||||
|     TriangleMesh       mesh; | ||||
|     DynamicPrintConfig profile; | ||||
|     wxString           path; | ||||
|     Vec2i              win = {2, 2}; | ||||
|     std::string        err; | ||||
|      | ||||
|     priv(Plater *plt): plater{plt} {} | ||||
| 
 | ||||
|     indexed_triangle_set mesh; | ||||
|     DynamicPrintConfig   profile; | ||||
|     wxString             path; | ||||
|     Vec2i                win = {2, 2}; | ||||
|     std::string          err; | ||||
| 
 | ||||
|     priv(Plater *plt) : plater{plt} {} | ||||
| }; | ||||
| 
 | ||||
| SLAImportJob::SLAImportJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater) | ||||
|  | @ -222,7 +222,8 @@ void SLAImportJob::finalize() | |||
|      | ||||
|     if (!p->mesh.empty()) { | ||||
|         bool is_centered = false; | ||||
|         p->plater->sidebar().obj_list()->load_mesh_object(p->mesh, name, is_centered); | ||||
|         p->plater->sidebar().obj_list()->load_mesh_object(TriangleMesh{p->mesh}, | ||||
|                                                           name, is_centered); | ||||
|     } | ||||
|      | ||||
|     reset(); | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ add_executable(${_TEST_NAME}_tests | |||
|     test_optimizers.cpp | ||||
|     test_png_io.cpp | ||||
|     test_timeutils.cpp | ||||
|     test_indexed_triangle_set.cpp | ||||
| 	) | ||||
| 
 | ||||
| if (TARGET OpenVDB::openvdb) | ||||
|  |  | |||
							
								
								
									
										102
									
								
								tests/libslic3r/test_indexed_triangle_set.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								tests/libslic3r/test_indexed_triangle_set.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,102 @@ | |||
| #include <iostream> | ||||
| #include <fstream> | ||||
| #include <catch2/catch.hpp> | ||||
| 
 | ||||
| #include "libslic3r/TriangleMesh.hpp" | ||||
| 
 | ||||
| TEST_CASE("Split empty mesh", "[its_split][its]") { | ||||
|     using namespace Slic3r; | ||||
| 
 | ||||
|     indexed_triangle_set its; | ||||
| 
 | ||||
|     std::vector<indexed_triangle_set> res = its_split(its); | ||||
| 
 | ||||
|     REQUIRE(res.empty()); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("Split simple mesh consisting of one part", "[its_split][its]") { | ||||
|     using namespace Slic3r; | ||||
| 
 | ||||
|     auto cube = its_make_cube(10., 10., 10.); | ||||
| 
 | ||||
|     std::vector<indexed_triangle_set> res = its_split(cube); | ||||
| 
 | ||||
|     REQUIRE(res.size() == 1); | ||||
|     REQUIRE(res.front().indices.size() == cube.indices.size()); | ||||
|     REQUIRE(res.front().vertices.size() == cube.vertices.size()); | ||||
| } | ||||
| 
 | ||||
| void debug_write_obj(const std::vector<indexed_triangle_set> &res, const std::string &name) | ||||
| { | ||||
| #ifndef NDEBUG | ||||
|     size_t part_idx = 0; | ||||
|     for (auto &part_its : res) { | ||||
|         its_write_obj(part_its, (name + std::to_string(part_idx++) + ".obj").c_str()); | ||||
|     } | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("Split two non-watertight mesh", "[its_split][its]") { | ||||
|     using namespace Slic3r; | ||||
| 
 | ||||
|     auto cube1 = its_make_cube(10., 10., 10.); | ||||
|     cube1.indices.pop_back(); | ||||
|     auto cube2 = cube1; | ||||
| 
 | ||||
|     its_transform(cube1, identity3f().translate(Vec3f{-5.f, 0.f, 0.f})); | ||||
|     its_transform(cube2, identity3f().translate(Vec3f{5.f, 0.f, 0.f})); | ||||
| 
 | ||||
|     its_merge(cube1, cube2); | ||||
| 
 | ||||
|     std::vector<indexed_triangle_set> res = its_split(cube1); | ||||
| 
 | ||||
|     REQUIRE(res.size() == 2); | ||||
|     REQUIRE(res[0].indices.size() == res[1].indices.size()); | ||||
|     REQUIRE(res[0].indices.size() == cube2.indices.size()); | ||||
|     REQUIRE(res[0].vertices.size() == res[1].vertices.size()); | ||||
|     REQUIRE(res[0].vertices.size() == cube2.vertices.size()); | ||||
| 
 | ||||
|     debug_write_obj(res, "parts_non_watertight"); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("Split non-manifold mesh", "[its_split][its]") { | ||||
|     using namespace Slic3r; | ||||
| 
 | ||||
|     auto cube = its_make_cube(10., 10., 10.), cube_low = cube; | ||||
| 
 | ||||
|     its_transform(cube_low, identity3f().translate(Vec3f{10.f, 10.f, 10.f})); | ||||
|     its_merge(cube, cube_low); | ||||
|     its_merge_vertices(cube); | ||||
| 
 | ||||
|     std::vector<indexed_triangle_set> res = its_split(cube); | ||||
| 
 | ||||
|     REQUIRE(res.size() == 2); | ||||
|     REQUIRE(res[0].indices.size() == res[1].indices.size()); | ||||
|     REQUIRE(res[0].indices.size() == cube_low.indices.size()); | ||||
|     REQUIRE(res[0].vertices.size() == res[1].vertices.size()); | ||||
|     REQUIRE(res[0].vertices.size() == cube_low.vertices.size()); | ||||
| 
 | ||||
|     debug_write_obj(res, "cubes_non_manifold"); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("Split two watertight meshes", "[its_split][its]") { | ||||
|     using namespace Slic3r; | ||||
| 
 | ||||
|     auto sphere1 = its_make_sphere(10., 2 * PI / 200.), sphere2 = sphere1; | ||||
| 
 | ||||
|     its_transform(sphere1, identity3f().translate(Vec3f{-5.f, 0.f, 0.f})); | ||||
|     its_transform(sphere2, identity3f().translate(Vec3f{5.f, 0.f, 0.f})); | ||||
| 
 | ||||
|     its_merge(sphere1, sphere2); | ||||
| 
 | ||||
|     std::vector<indexed_triangle_set> res = its_split(sphere1); | ||||
| 
 | ||||
|     REQUIRE(res.size() == 2); | ||||
|     REQUIRE(res[0].indices.size() == res[1].indices.size()); | ||||
|     REQUIRE(res[0].indices.size() == sphere2.indices.size()); | ||||
|     REQUIRE(res[0].vertices.size() == res[1].vertices.size()); | ||||
|     REQUIRE(res[0].vertices.size() == sphere2.vertices.size()); | ||||
| 
 | ||||
|     debug_write_obj(res, "parts_watertight"); | ||||
| } | ||||
| 
 | ||||
|  | @ -17,7 +17,6 @@ | |||
| #include <libslic3r/TriangulateWall.hpp> | ||||
| #include <libslic3r/Tesselate.hpp> | ||||
| #include <libslic3r/SlicesToTriangleMesh.hpp> | ||||
| #include <libslic3r/SLA/Contour3D.hpp> | ||||
| 
 | ||||
| using namespace Slic3r; | ||||
| 
 | ||||
|  | @ -368,10 +367,9 @@ static void recreate_object_from_rasters(const std::string &objname, float lh) { | |||
|         layer = std::move(layer_); | ||||
|     } | ||||
|      | ||||
|     TriangleMesh out = slices_to_triangle_mesh(layers, bb.min.z(), double(lh), double(lh)); | ||||
|      | ||||
|     out.require_shared_vertices(); | ||||
|     out.WriteOBJFile("out_from_rasters.obj"); | ||||
|     indexed_triangle_set out = slices_to_mesh(layers, bb.min.z(), double(lh), double(lh)); | ||||
| 
 | ||||
|     its_write_obj(out, "out_from_rasters.obj"); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("Recreate object from rasters", "[SL1Import]") { | ||||
|  |  | |||
|  | @ -223,23 +223,14 @@ TEST_CASE("RasterizedPolygonAreaShouldMatch", "[SLARasterOutput]") { | |||
|     REQUIRE(raster_pxsum(raster0) == 0); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("Triangle mesh conversions should be correct", "[SLAConversions]") | ||||
| { | ||||
|     sla::Contour3D cntr; | ||||
|      | ||||
|     { | ||||
|         std::fstream infile{"extruder_idler_quads.obj", std::ios::in}; | ||||
|         cntr.from_obj(infile); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("halfcone test", "[halfcone]") { | ||||
|     sla::DiffBridge br{Vec3d{1., 1., 1.}, Vec3d{10., 10., 10.}, 0.25, 0.5}; | ||||
| 
 | ||||
|     TriangleMesh m = sla::to_triangle_mesh(sla::get_mesh(br, 45)); | ||||
|     indexed_triangle_set m = sla::get_mesh(br, 45); | ||||
| 
 | ||||
|     m.require_shared_vertices(); | ||||
|     m.WriteOBJFile("Halfcone.obj"); | ||||
|     its_merge_vertices(m); | ||||
|     its_write_obj(m, "Halfcone.obj"); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("Test concurrency") | ||||
|  |  | |||
|  | @ -69,9 +69,10 @@ void export_failed_case(const std::vector<ExPolygons> &support_slices, const Sup | |||
|             svg.Close(); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     TriangleMesh m; | ||||
|     byproducts.supporttree.retrieve_full_mesh(m); | ||||
| 
 | ||||
|     indexed_triangle_set its; | ||||
|     byproducts.supporttree.retrieve_full_mesh(its); | ||||
|     TriangleMesh m{its}; | ||||
|     m.merge(byproducts.input_mesh); | ||||
|     m.repair(); | ||||
|     m.require_shared_vertices(); | ||||
|  | @ -93,7 +94,7 @@ void test_supports(const std::string          &obj_filename, | |||
|     if (hollowingcfg.enabled) { | ||||
|         sla::InteriorPtr interior = sla::generate_interior(mesh, hollowingcfg); | ||||
|         REQUIRE(interior); | ||||
|         mesh.merge(sla::get_mesh(*interior)); | ||||
|         mesh.merge(TriangleMesh{sla::get_mesh(*interior)}); | ||||
|         mesh.require_shared_vertices(); | ||||
|     } | ||||
|      | ||||
|  | @ -151,7 +152,7 @@ void test_supports(const std::string          &obj_filename, | |||
|      | ||||
|     check_support_tree_integrity(treebuilder, supportcfg); | ||||
|      | ||||
|     const TriangleMesh &output_mesh = treebuilder.retrieve_mesh(); | ||||
|     TriangleMesh output_mesh{treebuilder.retrieve_mesh(sla::MeshType::Support)}; | ||||
|      | ||||
|     check_validity(output_mesh, validityflags); | ||||
|      | ||||
|  | @ -228,14 +229,16 @@ void test_pad(const std::string &obj_filename, const sla::PadConfig &padcfg, Pad | |||
|     REQUIRE_FALSE(mesh.empty()); | ||||
|      | ||||
|     // Create pad skeleton only from the model
 | ||||
|     Slic3r::sla::pad_blueprint(mesh, out.model_contours); | ||||
|     Slic3r::sla::pad_blueprint(mesh.its, out.model_contours); | ||||
|      | ||||
|     test_concave_hull(out.model_contours); | ||||
|      | ||||
|     REQUIRE_FALSE(out.model_contours.empty()); | ||||
|      | ||||
|     // Create the pad geometry for the model contours only
 | ||||
|     Slic3r::sla::create_pad({}, out.model_contours, out.mesh, padcfg); | ||||
|     indexed_triangle_set out_its; | ||||
|     Slic3r::sla::create_pad({}, out.model_contours, out_its, padcfg); | ||||
|     out.mesh = TriangleMesh{out_its}; | ||||
|      | ||||
|     check_validity(out.mesh); | ||||
|      | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 tamasmeszaros
						tamasmeszaros