mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-15 15:35:52 +08:00
Pushed ::slice and ::stats() and ::bb3() into TriangleMesh.
Added more tests to TriangleMesh.
This commit is contained in:
parent
0facb7dcb2
commit
0fc288b6d7
@ -1,8 +1,11 @@
|
||||
#include <catch.hpp>
|
||||
|
||||
#include "TriangleMesh.hpp"
|
||||
#include "libslic3r.h"
|
||||
#include "Point.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
SCENARIO( "TriangleMesh: Basic mesh statistics") {
|
||||
@ -119,20 +122,164 @@ SCENARIO( "TriangleMesh: Transformation functions affect mesh as expected.") {
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO( "TriangleMesh: split functionality.", "[!mayfail]") {
|
||||
REQUIRE(false); // TODO
|
||||
SCENARIO( "TriangleMesh: slice behavior.") {
|
||||
GIVEN( "A 20mm cube with one corner on the origin") {
|
||||
const Pointf3s vertices { Pointf3(20,20,0), Pointf3(20,0,0), Pointf3(0,0,0), Pointf3(0,20,0), Pointf3(20,20,20), Pointf3(0,20,20), Pointf3(0,0,20), Pointf3(20,0,20) };
|
||||
const std::vector<Point3> facets { Point3(0,1,2), Point3(0,2,3), Point3(4,5,6), Point3(4,6,7), Point3(0,4,7), Point3(0,7,1), Point3(1,7,6), Point3(1,6,2), Point3(2,6,5), Point3(2,5,3), Point3(4,0,3), Point3(4,3,5) };
|
||||
auto cube {TriangleMesh(vertices, facets)};
|
||||
cube.repair();
|
||||
|
||||
WHEN("Cube is sliced with z = [0,2,4,8,6,8,10,12,14,16,18,20]") {
|
||||
std::vector<double> z { 0,2,4,8,6,8,10,12,14,16,18,20 };
|
||||
auto result {cube.slice(z)};
|
||||
THEN( "The correct number of polygons are returned per layer.") {
|
||||
for (auto i = 0U; i < z.size(); i++) {
|
||||
REQUIRE(result.at(i).size() == 1);
|
||||
}
|
||||
}
|
||||
THEN( "The area of the returned polygons is correct.") {
|
||||
for (auto i = 0U; i < z.size(); i++) {
|
||||
REQUIRE(result.at(i).at(0).area() == 20.0*20/(std::pow(SCALING_FACTOR,2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GIVEN( "A STL with an irregular shape.") {
|
||||
const Pointf3s vertices {Pointf3(0,0,0),Pointf3(0,0,20),Pointf3(0,5,0),Pointf3(0,5,20),Pointf3(50,0,0),Pointf3(50,0,20),Pointf3(15,5,0),Pointf3(35,5,0),Pointf3(15,20,0),Pointf3(50,5,0),Pointf3(35,20,0),Pointf3(15,5,10),Pointf3(50,5,20),Pointf3(35,5,10),Pointf3(35,20,10),Pointf3(15,20,10)};
|
||||
const Point3s facets {Point3(0,1,2),Point3(2,1,3),Point3(1,0,4),Point3(5,1,4),Point3(0,2,4),Point3(4,2,6),Point3(7,6,8),Point3(4,6,7),Point3(9,4,7),Point3(7,8,10),Point3(2,3,6),Point3(11,3,12),Point3(7,12,9),Point3(13,12,7),Point3(6,3,11),Point3(11,12,13),Point3(3,1,5),Point3(12,3,5),Point3(5,4,9),Point3(12,5,9),Point3(13,7,10),Point3(14,13,10),Point3(8,15,10),Point3(10,15,14),Point3(6,11,8),Point3(8,11,15),Point3(15,11,13),Point3(14,15,13)};
|
||||
|
||||
auto cube {TriangleMesh(vertices, facets)};
|
||||
cube.repair();
|
||||
WHEN(" a top tangent plane is sliced") {
|
||||
auto slices {cube.slice({5.0, 10.0})};
|
||||
THEN( "its area is included") {
|
||||
REQUIRE(slices.at(0).at(0).area() > 0);
|
||||
REQUIRE(slices.at(1).at(0).area() > 0);
|
||||
}
|
||||
}
|
||||
WHEN(" a model that has been transformed is sliced") {
|
||||
cube.mirror_z();
|
||||
auto slices {cube.slice({-5.0, -10.0})};
|
||||
THEN( "it is sliced properly (mirrored bottom plane area is included)") {
|
||||
REQUIRE(slices.at(0).at(0).area() > 0);
|
||||
REQUIRE(slices.at(1).at(0).area() > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO( "TriangleMesh: slice behavior.", "[!mayfail]") {
|
||||
REQUIRE(false); // TODO
|
||||
SCENARIO( "make_xxx functions produce meshes.") {
|
||||
GIVEN("make_cube() function") {
|
||||
WHEN("make_cube() is called with arguments 20,20,20") {
|
||||
auto cube {TriangleMesh::make_cube(20,20,20)};
|
||||
THEN("The resulting mesh has one and only one vertex at 0,0,0") {
|
||||
auto verts {cube.vertices()};
|
||||
REQUIRE(std::count_if(verts.begin(), verts.end(), [](Pointf3& t) { return t.x == 0 && t.y == 0 && t.z == 0; } ) == 1);
|
||||
}
|
||||
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.facets().size() == 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
GIVEN("make_cylinder() function") {
|
||||
WHEN("make_cylinder() is called with arguments 10,10, PI / 3") {
|
||||
auto cyl {TriangleMesh::make_cylinder(10, 10, PI / 3.0)};
|
||||
double angle = (2*PI / floor(2*PI / (PI / 3.0)));
|
||||
THEN("The resulting mesh has one and only one vertex at 0,0,0") {
|
||||
auto verts {cyl.vertices()};
|
||||
REQUIRE(std::count_if(verts.begin(), verts.end(), [](Pointf3& t) { return t.x == 0 && t.y == 0 && t.z == 0; } ) == 1);
|
||||
}
|
||||
THEN("The resulting mesh has one and only one vertex at 0,0,10") {
|
||||
auto verts {cyl.vertices()};
|
||||
REQUIRE(std::count_if(verts.begin(), verts.end(), [](Pointf3& t) { return t.x == 0 && t.y == 0 && t.z == 10; } ) == 1);
|
||||
}
|
||||
THEN("Resulting mesh has 2 + (2*PI/angle * 2) vertices.") {
|
||||
REQUIRE(cyl.vertices().size() == (2 + ((2*PI/angle)*2)));
|
||||
}
|
||||
THEN("Resulting mesh has 2*PI/angle * 4 facets") {
|
||||
REQUIRE(cyl.facets().size() == (2*PI/angle)*4);
|
||||
}
|
||||
THEN("The resulting mesh is in the repaired state.") {
|
||||
REQUIRE(cyl.repaired == true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("make_sphere() function") {
|
||||
WHEN("make_sphere() is called with arguments 10, PI / 3") {
|
||||
auto sph {TriangleMesh::make_sphere(10, PI / 3.0)};
|
||||
double angle = (2.0*PI / floor(2.0*PI / (PI / 3.0)));
|
||||
THEN("Resulting mesh has one point at 0,0,-10 and one at 0,0,10") {
|
||||
auto verts {sph.vertices()};
|
||||
REQUIRE(std::count_if(verts.begin(), verts.end(), [](Pointf3& t) { return t.x == 0 && t.y == 0 && t.z == 10; } ) == 1);
|
||||
REQUIRE(std::count_if(verts.begin(), verts.end(), [](Pointf3& t) { return t.x == 0 && t.y == 0 && t.z == -10; } ) == 1);
|
||||
}
|
||||
THEN("The resulting mesh is in the repaired state.") {
|
||||
REQUIRE(sph.repaired == true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO( "make_xxx functions produce meshes.", "[!mayfail]") {
|
||||
REQUIRE(false); // TODO
|
||||
SCENARIO( "TriangleMesh: split functionality.") {
|
||||
GIVEN( "A 20mm cube with one corner on the origin") {
|
||||
const Pointf3s vertices { Pointf3(20,20,0), Pointf3(20,0,0), Pointf3(0,0,0), Pointf3(0,20,0), Pointf3(20,20,20), Pointf3(0,20,20), Pointf3(0,0,20), Pointf3(20,0,20) };
|
||||
const Point3s facets { Point3(0,1,2), Point3(0,2,3), Point3(4,5,6), Point3(4,6,7), Point3(0,4,7), Point3(0,7,1), Point3(1,7,6), Point3(1,6,2), Point3(2,6,5), Point3(2,5,3), Point3(4,0,3), Point3(4,3,5) };
|
||||
|
||||
auto cube {TriangleMesh(vertices, facets)};
|
||||
cube.repair();
|
||||
WHEN( "The mesh is split into its component parts.") {
|
||||
auto meshes {cube.split()};
|
||||
THEN(" The bounding box statistics are propagated to the split copies") {
|
||||
REQUIRE(meshes.size() == 1);
|
||||
REQUIRE(meshes.at(0)->bb3() == cube.bb3());
|
||||
}
|
||||
}
|
||||
}
|
||||
GIVEN( "Two 20mm cubes, each with one corner on the origin, merged into a single TriangleMesh") {
|
||||
const Pointf3s vertices { Pointf3(20,20,0), Pointf3(20,0,0), Pointf3(0,0,0), Pointf3(0,20,0), Pointf3(20,20,20), Pointf3(0,20,20), Pointf3(0,0,20), Pointf3(20,0,20) };
|
||||
const Point3s facets { Point3(0,1,2), Point3(0,2,3), Point3(4,5,6), Point3(4,6,7), Point3(0,4,7), Point3(0,7,1), Point3(1,7,6), Point3(1,6,2), Point3(2,6,5), Point3(2,5,3), Point3(4,0,3), Point3(4,3,5) };
|
||||
|
||||
auto cube {TriangleMesh(vertices, facets)};
|
||||
cube.repair();
|
||||
auto cube2 {TriangleMesh(vertices, facets)};
|
||||
cube2.repair();
|
||||
|
||||
cube.merge(cube2);
|
||||
cube.repair();
|
||||
WHEN( "The combined mesh is split") {
|
||||
auto meshes {cube.split()};
|
||||
THEN( "Two meshes are in the output vector.") {
|
||||
REQUIRE(meshes.size() == 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO( "TriangleMesh: Mesh merge functions", "[!mayfail]") {
|
||||
REQUIRE(false); // TODO
|
||||
SCENARIO( "TriangleMesh: Mesh merge functions") {
|
||||
GIVEN( "Two 20mm cubes, each with one corner on the origin") {
|
||||
const Pointf3s vertices { Pointf3(20,20,0), Pointf3(20,0,0), Pointf3(0,0,0), Pointf3(0,20,0), Pointf3(20,20,20), Pointf3(0,20,20), Pointf3(0,0,20), Pointf3(20,0,20) };
|
||||
const Point3s facets { Point3(0,1,2), Point3(0,2,3), Point3(4,5,6), Point3(4,6,7), Point3(0,4,7), Point3(0,7,1), Point3(1,7,6), Point3(1,6,2), Point3(2,6,5), Point3(2,5,3), Point3(4,0,3), Point3(4,3,5) };
|
||||
|
||||
auto cube {TriangleMesh(vertices, facets)};
|
||||
cube.repair();
|
||||
auto cube2 {TriangleMesh(vertices, facets)};
|
||||
cube2.repair();
|
||||
|
||||
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.stats().number_of_facets == 2 * cube2.stats().number_of_facets);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO( "TriangleMeshSlicer: Cut behavior.") {
|
||||
@ -165,5 +312,4 @@ SCENARIO( "TriangleMeshSlicer: Cut behavior.") {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -432,6 +432,41 @@ TriangleMesh::center() const {
|
||||
return this->bounding_box().center();
|
||||
}
|
||||
|
||||
std::vector<ExPolygons>
|
||||
TriangleMesh::slice(const std::vector<double>& z)
|
||||
{
|
||||
// convert doubles to floats
|
||||
std::vector<float> z_f(z.begin(), z.end());
|
||||
TriangleMeshSlicer<Z> mslicer(this);
|
||||
std::vector<ExPolygons> layers;
|
||||
|
||||
mslicer.slice(z_f, &layers);
|
||||
|
||||
return std::move(layers);
|
||||
}
|
||||
|
||||
mesh_stats
|
||||
TriangleMesh::stats() const {
|
||||
mesh_stats tmp_stats;
|
||||
tmp_stats.number_of_facets = this->stl.stats.number_of_facets;
|
||||
tmp_stats.number_of_parts = this->stl.stats.number_of_parts;
|
||||
tmp_stats.volume = this->stl.stats.volume;
|
||||
tmp_stats.degenerate_facets = this->stl.stats.degenerate_facets;
|
||||
tmp_stats.edges_fixed = this->stl.stats.edges_fixed;
|
||||
tmp_stats.facets_removed = this->stl.stats.facets_removed;
|
||||
tmp_stats.facets_added = this->stl.stats.facets_added;
|
||||
tmp_stats.facets_reversed = this->stl.stats.facets_reversed;
|
||||
tmp_stats.backwards_edges = this->stl.stats.backwards_edges;
|
||||
tmp_stats.normals_fixed = this->stl.stats.normals_fixed;
|
||||
return std::move(tmp_stats);
|
||||
}
|
||||
|
||||
BoundingBoxf3 TriangleMesh::bb3() const {
|
||||
Pointf3 min(this->stl.stats.min.x, this->stl.stats.min.y, this->stl.stats.min.z);
|
||||
Pointf3 max(this->stl.stats.max.x, this->stl.stats.max.y, this->stl.stats.max.z);
|
||||
return std::move(BoundingBoxf3(min, max));
|
||||
}
|
||||
|
||||
TriangleMeshPtrs
|
||||
TriangleMesh::split() const
|
||||
{
|
||||
|
@ -17,6 +17,21 @@ class TriangleMesh;
|
||||
template <Axis A> class TriangleMeshSlicer;
|
||||
typedef std::vector<TriangleMesh*> TriangleMeshPtrs;
|
||||
|
||||
|
||||
/// Interface to available statistics from the underlying mesh.
|
||||
struct mesh_stats {
|
||||
size_t number_of_facets {0};
|
||||
size_t number_of_parts {0};
|
||||
double volume {0};
|
||||
size_t degenerate_facets {0};
|
||||
size_t edges_fixed {0};
|
||||
size_t facets_removed {0};
|
||||
size_t facets_added {0};
|
||||
size_t facets_reversed {0};
|
||||
size_t backwards_edges {0};
|
||||
size_t normals_fixed {0};
|
||||
};
|
||||
|
||||
class TriangleMesh
|
||||
{
|
||||
public:
|
||||
@ -87,6 +102,14 @@ class TriangleMesh
|
||||
/// Return the center of the related bounding box.
|
||||
Pointf3 center() const;
|
||||
|
||||
/// Slice this mesh at the provided Z levels and return the vector
|
||||
std::vector<ExPolygons> slice(const std::vector<double>& z);
|
||||
|
||||
/// Contains general statistics from underlying mesh structure.
|
||||
mesh_stats stats() const;
|
||||
|
||||
BoundingBoxf3 bb3() const;
|
||||
|
||||
/// Perform a cut of the mesh and put the output in upper and lower
|
||||
void cut(Axis axis, double z, TriangleMesh* upper, TriangleMesh* lower);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user