mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-21 13:47:59 -06:00
Eradicated admesh from TriangleMesh:
TriangleMesh newly only holds indexed_triangle_set and TriangleMeshStats. TriangleMeshStats contains an excerpt of stl_stats. TriangleMeshStats are updated when initializing with indexed_triangle_set. Admesh triangle mesh fixing is newly only used when loading an STL. AMF / 3MF / OBJ file formats are already indexed triangle sets, thus they are no more converted to admesh stl_file format, nor fixed through admesh repair machinery. When importing AMF / 3MF / OBJ files, volume is calculated and if negative, all faces are flipped. Also a bounding box and number of open edges is calculated. Implemented its_number_of_patches(), its_num_open_edges() Optimized its_split(), its_is_splittable() using a visitor pattern. Reworked QHull integration into TriangleMesh: 1) Face normals were not right. 2) Indexed triangle set is newly emitted instead of duplicating vertices for each face. Fixed cut_mesh(): Orient the triangulated faces correctly.
This commit is contained in:
parent
f484953a5a
commit
8a2a9dba2f
59 changed files with 1056 additions and 1758 deletions
|
@ -17,12 +17,13 @@
|
|||
using namespace Slic3r;
|
||||
using namespace std;
|
||||
|
||||
static inline TriangleMesh make_cube() { return make_cube(20., 20, 20); }
|
||||
|
||||
SCENARIO( "TriangleMesh: Basic mesh statistics") {
|
||||
GIVEN( "A 20mm cube, built from constexpr std::array" ) {
|
||||
std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
|
||||
std::vector<Vec3f> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
|
||||
std::vector<Vec3i> facets { {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 cube(vertices, facets);
|
||||
cube.repair();
|
||||
TriangleMesh cube(vertices, facets);
|
||||
|
||||
THEN( "Volume is appropriate for 20mm square cube.") {
|
||||
REQUIRE(abs(cube.volume() - 20.0*20.0*20.0) < 1e-2);
|
||||
|
@ -68,64 +69,11 @@ SCENARIO( "TriangleMesh: Basic mesh statistics") {
|
|||
}
|
||||
|
||||
}
|
||||
GIVEN( "A 20mm cube with one corner on the origin") {
|
||||
const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
|
||||
const std::vector<Vec3i> facets { {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 cube(vertices, facets);
|
||||
cube.repair();
|
||||
|
||||
THEN( "Volume is appropriate for 20mm square cube.") {
|
||||
REQUIRE(abs(cube.volume() - 20.0*20.0*20.0) < 1e-2);
|
||||
}
|
||||
|
||||
THEN( "Vertices array matches input.") {
|
||||
for (size_t i = 0U; i < cube.its.vertices.size(); i++) {
|
||||
REQUIRE(cube.its.vertices.at(i) == vertices.at(i).cast<float>());
|
||||
}
|
||||
for (size_t i = 0U; i < vertices.size(); i++) {
|
||||
REQUIRE(vertices.at(i).cast<float>() == cube.its.vertices.at(i));
|
||||
}
|
||||
}
|
||||
THEN( "Vertex count matches vertex array size.") {
|
||||
REQUIRE(cube.facets_count() == facets.size());
|
||||
}
|
||||
|
||||
THEN( "Facet array matches input.") {
|
||||
for (size_t i = 0U; i < cube.its.indices.size(); i++) {
|
||||
REQUIRE(cube.its.indices.at(i) == facets.at(i));
|
||||
}
|
||||
|
||||
for (size_t i = 0U; i < facets.size(); i++) {
|
||||
REQUIRE(facets.at(i) == cube.its.indices.at(i));
|
||||
}
|
||||
}
|
||||
THEN( "Facet count matches facet array size.") {
|
||||
REQUIRE(cube.facets_count() == facets.size());
|
||||
}
|
||||
|
||||
#if 0
|
||||
THEN( "Number of normals is equal to the number of facets.") {
|
||||
REQUIRE(cube.normals().size() == facets.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
THEN( "center() returns the center of the object.") {
|
||||
REQUIRE(cube.center() == Vec3d(10.0,10.0,10.0));
|
||||
}
|
||||
|
||||
THEN( "Size of cube is (20,20,20)") {
|
||||
REQUIRE(cube.size() == Vec3d(20,20,20));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") {
|
||||
GIVEN( "A 20mm cube with one corner on the origin") {
|
||||
const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
|
||||
const std::vector<Vec3i> facets { {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 cube(vertices, facets);
|
||||
cube.repair();
|
||||
auto cube = make_cube();
|
||||
|
||||
WHEN( "The cube is scaled 200% uniformly") {
|
||||
cube.scale(2.0);
|
||||
|
@ -134,7 +82,7 @@ SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") {
|
|||
}
|
||||
}
|
||||
WHEN( "The resulting cube is scaled 200% in the X direction") {
|
||||
cube.scale(Vec3d(2.0, 1, 1));
|
||||
cube.scale(Vec3f(2.0, 1, 1));
|
||||
THEN( "The volume is doubled.") {
|
||||
REQUIRE(abs(cube.volume() - 2*20.0*20.0*20.0) < 1e-2);
|
||||
}
|
||||
|
@ -144,7 +92,7 @@ SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") {
|
|||
}
|
||||
|
||||
WHEN( "The cube is scaled 25% in the X direction") {
|
||||
cube.scale(Vec3d(0.25, 1, 1));
|
||||
cube.scale(Vec3f(0.25, 1, 1));
|
||||
THEN( "The volume is 25% of the previous volume.") {
|
||||
REQUIRE(abs(cube.volume() - 0.25*20.0*20.0*20.0) < 1e-2);
|
||||
}
|
||||
|
@ -177,7 +125,10 @@ SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") {
|
|||
cube.translate(5.0, 10.0, 0.0);
|
||||
cube.align_to_origin();
|
||||
THEN( "The third vertex is located at 0,0,0") {
|
||||
REQUIRE(cube.its.vertices.at(2) == Vec3f(0.0, 0.0, 0.0));
|
||||
REQUIRE(cube.its.vertices.at(2) == Vec3f::Zero());
|
||||
}
|
||||
THEN( "Size is OK") {
|
||||
REQUIRE(cube.stats().size == Vec3f(20.f, 20.f, 20.f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -185,11 +136,8 @@ SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") {
|
|||
|
||||
SCENARIO( "TriangleMesh: slice behavior.") {
|
||||
GIVEN( "A 20mm cube with one corner on the origin") {
|
||||
const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
|
||||
const std::vector<Vec3i> facets { {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 cube(vertices, facets);
|
||||
cube.repair();
|
||||
|
||||
auto cube = make_cube();
|
||||
|
||||
WHEN("Cube is sliced with z = [0+EPSILON,2,4,8,6,8,10,12,14,16,18,20]") {
|
||||
std::vector<double> z { 0+EPSILON,2,4,8,6,8,10,12,14,16,18,20 };
|
||||
std::vector<ExPolygons> result = cube.slice(z);
|
||||
|
@ -206,12 +154,12 @@ SCENARIO( "TriangleMesh: slice behavior.") {
|
|||
}
|
||||
}
|
||||
GIVEN( "A STL with an irregular shape.") {
|
||||
const std::vector<Vec3d> vertices {{0,0,0},{0,0,20},{0,5,0},{0,5,20},{50,0,0},{50,0,20},{15,5,0},{35,5,0},{15,20,0},{50,5,0},{35,20,0},{15,5,10},{50,5,20},{35,5,10},{35,20,10},{15,20,10}};
|
||||
const std::vector<Vec3f> vertices {{0,0,0},{0,0,20},{0,5,0},{0,5,20},{50,0,0},{50,0,20},{15,5,0},{35,5,0},{15,20,0},{50,5,0},{35,20,0},{15,5,10},{50,5,20},{35,5,10},{35,20,10},{15,20,10}};
|
||||
const std::vector<Vec3i> facets {{0,1,2},{2,1,3},{1,0,4},{5,1,4},{0,2,4},{4,2,6},{7,6,8},{4,6,7},{9,4,7},{7,8,10},{2,3,6},{11,3,12},{7,12,9},{13,12,7},{6,3,11},{11,12,13},{3,1,5},{12,3,5},{5,4,9},{12,5,9},{13,7,10},{14,13,10},{8,15,10},{10,15,14},{6,11,8},{8,11,15},{15,11,13},{14,15,13}};
|
||||
|
||||
TriangleMesh cube(vertices, facets);
|
||||
cube.repair();
|
||||
auto cube = make_cube();
|
||||
WHEN(" a top tangent plane is sliced") {
|
||||
// At Z = 10 we have a top horizontal surface.
|
||||
std::vector<ExPolygons> slices = cube.slice({5.0, 10.0});
|
||||
THEN( "its area is included") {
|
||||
REQUIRE(slices.at(0).at(0).area() > 0);
|
||||
|
@ -240,9 +188,6 @@ SCENARIO( "make_xxx functions produce meshes.") {
|
|||
THEN("The mesh volume is 20*20*20") {
|
||||
REQUIRE(abs(cube.volume() - 20.0*20.0*20.0) < 1e-2);
|
||||
}
|
||||
THEN("The resulting mesh is in the repaired state.") {
|
||||
REQUIRE(cube.repaired == true);
|
||||
}
|
||||
THEN("There are 12 facets.") {
|
||||
REQUIRE(cube.its.indices.size() == 12);
|
||||
}
|
||||
|
@ -266,9 +211,6 @@ SCENARIO( "make_xxx functions produce meshes.") {
|
|||
THEN("Resulting mesh has 2*PI/angle * 4 facets") {
|
||||
REQUIRE(cyl.its.indices.size() == (2*PI/angle)*4);
|
||||
}
|
||||
THEN("The resulting mesh is in the repaired state.") {
|
||||
REQUIRE(cyl.repaired == true);
|
||||
}
|
||||
THEN( "The mesh volume is approximately 10pi * 10^2") {
|
||||
REQUIRE(abs(cyl.volume() - (10.0 * M_PI * std::pow(10,2))) < 1);
|
||||
}
|
||||
|
@ -283,9 +225,6 @@ SCENARIO( "make_xxx functions produce meshes.") {
|
|||
REQUIRE(std::count_if(verts.begin(), verts.end(), [](const Vec3f& t) { return is_approx(t, Vec3f(0.f, 0.f, 10.f)); } ) == 1);
|
||||
REQUIRE(std::count_if(verts.begin(), verts.end(), [](const Vec3f& t) { return is_approx(t, Vec3f(0.f, 0.f, -10.f)); } ) == 1);
|
||||
}
|
||||
THEN("The resulting mesh is in the repaired state.") {
|
||||
REQUIRE(sph.repaired == true);
|
||||
}
|
||||
THEN( "The mesh volume is approximately 4/3 * pi * 10^3") {
|
||||
REQUIRE(abs(sph.volume() - (4.0/3.0 * M_PI * std::pow(10,3))) < 1); // 1% tolerance?
|
||||
}
|
||||
|
@ -295,32 +234,25 @@ SCENARIO( "make_xxx functions produce meshes.") {
|
|||
|
||||
SCENARIO( "TriangleMesh: split functionality.") {
|
||||
GIVEN( "A 20mm cube with one corner on the origin") {
|
||||
const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
|
||||
const std::vector<Vec3i> facets { {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 cube(vertices, facets);
|
||||
cube.repair();
|
||||
auto cube = make_cube();
|
||||
WHEN( "The mesh is split into its component parts.") {
|
||||
std::vector<TriangleMesh*> meshes = cube.split();
|
||||
std::vector<TriangleMesh> meshes = cube.split();
|
||||
THEN(" The bounding box statistics are propagated to the split copies") {
|
||||
REQUIRE(meshes.size() == 1);
|
||||
REQUIRE((meshes.at(0)->bounding_box() == cube.bounding_box()));
|
||||
REQUIRE((meshes.front().bounding_box() == cube.bounding_box()));
|
||||
}
|
||||
}
|
||||
}
|
||||
GIVEN( "Two 20mm cubes, each with one corner on the origin, merged into a single TriangleMesh") {
|
||||
const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
|
||||
const std::vector<Vec3i> facets { {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 cube(vertices, facets);
|
||||
cube.repair();
|
||||
TriangleMesh cube2(vertices, facets);
|
||||
cube2.repair();
|
||||
auto cube = make_cube();
|
||||
TriangleMesh cube2(cube);
|
||||
|
||||
cube.merge(cube2);
|
||||
cube.repair();
|
||||
WHEN( "The combined mesh is split") {
|
||||
std::vector<TriangleMesh*> meshes = cube.split();
|
||||
THEN( "Number of faces is 2x the source.") {
|
||||
REQUIRE(cube.facets_count() == 2 * cube2.facets_count());
|
||||
}
|
||||
std::vector<TriangleMesh> meshes = cube.split();
|
||||
THEN( "Two meshes are in the output vector.") {
|
||||
REQUIRE(meshes.size() == 2);
|
||||
}
|
||||
|
@ -330,17 +262,11 @@ SCENARIO( "TriangleMesh: split functionality.") {
|
|||
|
||||
SCENARIO( "TriangleMesh: Mesh merge functions") {
|
||||
GIVEN( "Two 20mm cubes, each with one corner on the origin") {
|
||||
const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
|
||||
const std::vector<Vec3i> facets { {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 cube(vertices, facets);
|
||||
cube.repair();
|
||||
TriangleMesh cube2(vertices, facets);
|
||||
cube2.repair();
|
||||
auto cube = make_cube();
|
||||
TriangleMesh cube2(cube);
|
||||
|
||||
WHEN( "The two meshes are merged") {
|
||||
cube.merge(cube2);
|
||||
cube.repair();
|
||||
THEN( "There are twice as many facets in the merged mesh as the original.") {
|
||||
REQUIRE(cube.facets_count() == 2 * cube2.facets_count());
|
||||
}
|
||||
|
@ -350,11 +276,7 @@ SCENARIO( "TriangleMesh: Mesh merge functions") {
|
|||
|
||||
SCENARIO( "TriangleMeshSlicer: Cut behavior.") {
|
||||
GIVEN( "A 20mm cube with one corner on the origin") {
|
||||
const std::vector<Vec3d> vertices { {20,20,0}, {20,0,0}, {0,0,0}, {0,20,0}, {20,20,20}, {0,20,20}, {0,0,20}, {20,0,20} };
|
||||
const std::vector<Vec3i> facets { {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 cube(vertices, facets);
|
||||
cube.repair();
|
||||
auto cube = make_cube();
|
||||
WHEN( "Object is cut at the bottom") {
|
||||
indexed_triangle_set upper {};
|
||||
indexed_triangle_set lower {};
|
||||
|
@ -384,7 +306,6 @@ TEST_CASE("Regression test for issue #4486 - files take forever to slice") {
|
|||
TriangleMesh mesh;
|
||||
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||
mesh.ReadSTLFile(std::string(testfile_dir) + "test_trianglemesh/4486/100_000.stl");
|
||||
mesh.repair();
|
||||
|
||||
config.set("layer_height", 500);
|
||||
config.set("first_layer_height", 250);
|
||||
|
@ -412,7 +333,6 @@ TEST_CASE("Profile test for issue #4486 - files take forever to slice") {
|
|||
TriangleMesh mesh;
|
||||
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||
mesh.ReadSTLFile(std::string(testfile_dir) + "test_trianglemesh/4486/10_000.stl");
|
||||
mesh.repair();
|
||||
|
||||
config.set("layer_height", 500);
|
||||
config.set("first_layer_height", 250);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue