Merge branch 'master' into fs_emboss
# Conflicts: # src/libslic3r/Format/3mf.cpp # src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp
This commit is contained in:
commit
3bd9fc07d2
213 changed files with 11755 additions and 7841 deletions
File diff suppressed because one or more lines are too long
|
@ -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,19 +262,13 @@ 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.stl.stats.number_of_facets == 2 * cube2.stl.stats.number_of_facets);
|
||||
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);
|
||||
|
|
|
@ -23,6 +23,7 @@ add_executable(${_TEST_NAME}_tests
|
|||
test_png_io.cpp
|
||||
test_timeutils.cpp
|
||||
test_indexed_triangle_set.cpp
|
||||
../libnest2d/printer_parts.cpp
|
||||
)
|
||||
|
||||
if (TARGET OpenVDB::openvdb)
|
||||
|
|
|
@ -65,10 +65,7 @@ SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") {
|
|||
|
||||
// compare meshes
|
||||
TriangleMesh src_mesh = src_model.mesh();
|
||||
src_mesh.repair();
|
||||
|
||||
TriangleMesh dst_mesh = dst_model.mesh();
|
||||
dst_mesh.repair();
|
||||
|
||||
bool res = src_mesh.its.vertices.size() == dst_mesh.its.vertices.size();
|
||||
if (res) {
|
||||
|
|
|
@ -9,7 +9,6 @@ using namespace Slic3r;
|
|||
TEST_CASE("Building a tree over a box, ray caster and closest query", "[AABBIndirect]")
|
||||
{
|
||||
TriangleMesh tmesh = make_cube(1., 1., 1.);
|
||||
tmesh.repair();
|
||||
|
||||
auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(tmesh.its.vertices, tmesh.its.indices);
|
||||
REQUIRE(! tree.empty());
|
||||
|
|
|
@ -99,7 +99,7 @@ SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
|
|||
{ 74730000, 74730000 }, { 55270000, 74730000 }, { 55270000, 68063296 }, { 44730000, 68063296 }, { 44730000, 74730000 }, { 25270000, 74730000 }, { 25270000, 55270000 }, { 31936670, 55270000 },
|
||||
{ 31936670, 44730000 }, { 25270000, 44730000 }, { 25270000, 25270000 }, { 44730000, 25270000 }, { 44730000, 31936670 } };
|
||||
Slic3r::Polygon clip { {75200000, 45200000}, {54800000, 45200000}, {54800000, 24800000}, {75200000, 24800000} };
|
||||
Slic3r::Polylines result = Slic3r::intersection_pl({ subject }, { clip });
|
||||
Slic3r::Polylines result = Slic3r::intersection_pl(subject, { clip });
|
||||
THEN("intersection_pl - result is not empty") {
|
||||
REQUIRE(result.size() == 1);
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ SCENARIO("Various Clipper operations - xs/t/11_clipper.t", "[ClipperUtils]") {
|
|||
GIVEN("Clipper bug #126") {
|
||||
Slic3r::Polyline subject { { 200000, 19799999 }, { 200000, 200000 }, { 24304692, 200000 }, { 15102879, 17506106 }, { 13883200, 19799999 }, { 200000, 19799999 } };
|
||||
Slic3r::Polygon clip { { 15257205, 18493894 }, { 14350057, 20200000 }, { -200000, 20200000 }, { -200000, -200000 }, { 25196917, -200000 } };
|
||||
Slic3r::Polylines result = Slic3r::intersection_pl({ subject }, { clip });
|
||||
Slic3r::Polylines result = Slic3r::intersection_pl(subject, { clip });
|
||||
THEN("intersection_pl - result is not empty") {
|
||||
REQUIRE(result.size() == 1);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,11 @@
|
|||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
|
||||
#include <cereal/types/polymorphic.hpp>
|
||||
#include <cereal/types/string.hpp>
|
||||
#include <cereal/types/vector.hpp>
|
||||
#include <cereal/archives/binary.hpp>
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
SCENARIO("Generic config validation performs as expected.", "[Config]") {
|
||||
|
@ -202,3 +207,33 @@ SCENARIO("Config ini load/save interface", "[Config]") {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("DynamicPrintConfig serialization", "[Config]") {
|
||||
WHEN("DynamicPrintConfig is serialized and deserialized") {
|
||||
FullPrintConfig full_print_config;
|
||||
DynamicPrintConfig cfg;
|
||||
cfg.apply(full_print_config, false);
|
||||
|
||||
std::string serialized;
|
||||
try {
|
||||
std::ostringstream ss;
|
||||
cereal::BinaryOutputArchive oarchive(ss);
|
||||
oarchive(cfg);
|
||||
serialized = ss.str();
|
||||
} catch (std::runtime_error e) {
|
||||
e.what();
|
||||
}
|
||||
|
||||
THEN("Config object contains ini file options.") {
|
||||
DynamicPrintConfig cfg2;
|
||||
try {
|
||||
std::stringstream ss(serialized);
|
||||
cereal::BinaryInputArchive iarchive(ss);
|
||||
iarchive(cfg2);
|
||||
} catch (std::runtime_error e) {
|
||||
e.what();
|
||||
}
|
||||
REQUIRE(cfg == cfg2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,14 @@
|
|||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/ShortestPath.hpp"
|
||||
|
||||
//#include <random>
|
||||
//#include "libnest2d/tools/benchmark.h"
|
||||
#include "libslic3r/SVG.hpp"
|
||||
|
||||
#include "../libnest2d/printer_parts.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
TEST_CASE("Polygon::contains works properly", "[Geometry]"){
|
||||
|
@ -452,3 +460,225 @@ SCENARIO("Ported from xs/t/14_geometry.t", "[Geometry]"){
|
|||
REQUIRE(! Slic3r::Geometry::directions_parallel(M_PI /2, PI, M_PI /180));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Convex polygon intersection on two disjoint squares", "[Geometry][Rotcalip]") {
|
||||
Polygon A{{0, 0}, {10, 0}, {10, 10}, {0, 10}};
|
||||
A.scale(1. / SCALING_FACTOR);
|
||||
|
||||
Polygon B = A;
|
||||
B.translate(20 / SCALING_FACTOR, 0);
|
||||
|
||||
bool is_inters = Geometry::intersects(A, B);
|
||||
|
||||
REQUIRE(is_inters == false);
|
||||
}
|
||||
|
||||
TEST_CASE("Convex polygon intersection on two intersecting squares", "[Geometry][Rotcalip]") {
|
||||
Polygon A{{0, 0}, {10, 0}, {10, 10}, {0, 10}};
|
||||
A.scale(1. / SCALING_FACTOR);
|
||||
|
||||
Polygon B = A;
|
||||
B.translate(5 / SCALING_FACTOR, 5 / SCALING_FACTOR);
|
||||
|
||||
bool is_inters = Geometry::intersects(A, B);
|
||||
|
||||
REQUIRE(is_inters == true);
|
||||
}
|
||||
|
||||
TEST_CASE("Convex polygon intersection on two squares touching one edge", "[Geometry][Rotcalip]") {
|
||||
Polygon A{{0, 0}, {10, 0}, {10, 10}, {0, 10}};
|
||||
A.scale(1. / SCALING_FACTOR);
|
||||
|
||||
Polygon B = A;
|
||||
B.translate(10 / SCALING_FACTOR, 0);
|
||||
|
||||
bool is_inters = Geometry::intersects(A, B);
|
||||
|
||||
REQUIRE(is_inters == false);
|
||||
}
|
||||
|
||||
TEST_CASE("Convex polygon intersection on two squares touching one vertex", "[Geometry][Rotcalip]") {
|
||||
Polygon A{{0, 0}, {10, 0}, {10, 10}, {0, 10}};
|
||||
A.scale(1. / SCALING_FACTOR);
|
||||
|
||||
Polygon B = A;
|
||||
B.translate(10 / SCALING_FACTOR, 10 / SCALING_FACTOR);
|
||||
|
||||
SVG svg{std::string("one_vertex_touch") + ".svg"};
|
||||
svg.draw(A, "blue");
|
||||
svg.draw(B, "green");
|
||||
svg.Close();
|
||||
|
||||
bool is_inters = Geometry::intersects(A, B);
|
||||
|
||||
REQUIRE(is_inters == false);
|
||||
}
|
||||
|
||||
TEST_CASE("Convex polygon intersection on two overlapping squares", "[Geometry][Rotcalip]") {
|
||||
Polygon A{{0, 0}, {10, 0}, {10, 10}, {0, 10}};
|
||||
A.scale(1. / SCALING_FACTOR);
|
||||
|
||||
Polygon B = A;
|
||||
|
||||
bool is_inters = Geometry::intersects(A, B);
|
||||
|
||||
REQUIRE(is_inters == true);
|
||||
}
|
||||
|
||||
//// Only for benchmarking
|
||||
//static Polygon gen_convex_poly(std::mt19937_64 &rg, size_t point_cnt)
|
||||
//{
|
||||
// std::uniform_int_distribution<coord_t> dist(0, 100);
|
||||
|
||||
// Polygon out;
|
||||
// out.points.reserve(point_cnt);
|
||||
|
||||
// coord_t tr = dist(rg) * 2 / SCALING_FACTOR;
|
||||
|
||||
// for (size_t i = 0; i < point_cnt; ++i)
|
||||
// out.points.emplace_back(tr + dist(rg) / SCALING_FACTOR,
|
||||
// tr + dist(rg) / SCALING_FACTOR);
|
||||
|
||||
// return Geometry::convex_hull(out.points);
|
||||
//}
|
||||
//TEST_CASE("Convex polygon intersection test on random polygons", "[Geometry]") {
|
||||
// constexpr size_t TEST_CNT = 1000;
|
||||
// constexpr size_t POINT_CNT = 1000;
|
||||
|
||||
// auto seed = std::random_device{}();
|
||||
//// unsigned long seed = 2525634386;
|
||||
// std::mt19937_64 rg{seed};
|
||||
// Benchmark bench;
|
||||
|
||||
// auto tests = reserve_vector<std::pair<Polygon, Polygon>>(TEST_CNT);
|
||||
// auto results = reserve_vector<bool>(TEST_CNT);
|
||||
// auto expects = reserve_vector<bool>(TEST_CNT);
|
||||
|
||||
// for (size_t i = 0; i < TEST_CNT; ++i) {
|
||||
// tests.emplace_back(gen_convex_poly(rg, POINT_CNT), gen_convex_poly(rg, POINT_CNT));
|
||||
// }
|
||||
|
||||
// bench.start();
|
||||
// for (const auto &test : tests)
|
||||
// results.emplace_back(Geometry::intersects(test.first, test.second));
|
||||
// bench.stop();
|
||||
|
||||
// std::cout << "Test time: " << bench.getElapsedSec() << std::endl;
|
||||
|
||||
// bench.start();
|
||||
// for (const auto &test : tests)
|
||||
// expects.emplace_back(!intersection(test.first, test.second).empty());
|
||||
// bench.stop();
|
||||
|
||||
// std::cout << "Clipper time: " << bench.getElapsedSec() << std::endl;
|
||||
|
||||
// REQUIRE(results.size() == expects.size());
|
||||
|
||||
// auto seedstr = std::to_string(seed);
|
||||
// for (size_t i = 0; i < results.size(); ++i) {
|
||||
// // std::cout << expects[i] << " ";
|
||||
|
||||
// if (results[i] != expects[i]) {
|
||||
// SVG svg{std::string("fail_seed") + seedstr + "_" + std::to_string(i) + ".svg"};
|
||||
// svg.draw(tests[i].first, "blue");
|
||||
// svg.draw(tests[i].second, "green");
|
||||
// svg.Close();
|
||||
|
||||
// // std::cout << std::endl;
|
||||
// }
|
||||
// REQUIRE(results[i] == expects[i]);
|
||||
// }
|
||||
// std::cout << std::endl;
|
||||
|
||||
//}
|
||||
|
||||
struct Pair
|
||||
{
|
||||
size_t first, second;
|
||||
bool operator==(const Pair &b) const { return first == b.first && second == b.second; }
|
||||
};
|
||||
|
||||
template<> struct std::hash<Pair> {
|
||||
size_t operator()(const Pair &c) const
|
||||
{
|
||||
return c.first * PRINTER_PART_POLYGONS.size() + c.second;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("Convex polygon intersection test prusa polygons", "[Geometry][Rotcalip]") {
|
||||
|
||||
// Overlap of the same polygon should always be an intersection
|
||||
for (size_t i = 0; i < PRINTER_PART_POLYGONS.size(); ++i) {
|
||||
Polygon P = PRINTER_PART_POLYGONS[i];
|
||||
P = Geometry::convex_hull(P.points);
|
||||
bool res = Geometry::intersects(P, P);
|
||||
if (!res) {
|
||||
SVG svg{std::string("fail_self") + std::to_string(i) + ".svg"};
|
||||
svg.draw(P, "green");
|
||||
svg.Close();
|
||||
}
|
||||
REQUIRE(res == true);
|
||||
}
|
||||
|
||||
std::unordered_set<Pair> combos;
|
||||
for (size_t i = 0; i < PRINTER_PART_POLYGONS.size(); ++i) {
|
||||
for (size_t j = 0; j < PRINTER_PART_POLYGONS.size(); ++j) {
|
||||
if (i != j) {
|
||||
size_t a = std::min(i, j), b = std::max(i, j);
|
||||
combos.insert(Pair{a, b});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All disjoint
|
||||
for (const auto &combo : combos) {
|
||||
Polygon A = PRINTER_PART_POLYGONS[combo.first], B = PRINTER_PART_POLYGONS[combo.second];
|
||||
A = Geometry::convex_hull(A.points);
|
||||
B = Geometry::convex_hull(B.points);
|
||||
|
||||
auto bba = A.bounding_box();
|
||||
auto bbb = B.bounding_box();
|
||||
|
||||
A.translate(-bba.center());
|
||||
B.translate(-bbb.center());
|
||||
|
||||
B.translate(bba.size() + bbb.size());
|
||||
|
||||
bool res = Geometry::intersects(A, B);
|
||||
bool ref = !intersection(A, B).empty();
|
||||
|
||||
if (res != ref) {
|
||||
SVG svg{std::string("fail") + std::to_string(combo.first) + "_" + std::to_string(combo.second) + ".svg"};
|
||||
svg.draw(A, "blue");
|
||||
svg.draw(B, "green");
|
||||
svg.Close();
|
||||
}
|
||||
|
||||
REQUIRE(res == ref);
|
||||
}
|
||||
|
||||
// All intersecting
|
||||
for (const auto &combo : combos) {
|
||||
Polygon A = PRINTER_PART_POLYGONS[combo.first], B = PRINTER_PART_POLYGONS[combo.second];
|
||||
A = Geometry::convex_hull(A.points);
|
||||
B = Geometry::convex_hull(B.points);
|
||||
|
||||
auto bba = A.bounding_box();
|
||||
auto bbb = B.bounding_box();
|
||||
|
||||
A.translate(-bba.center());
|
||||
B.translate(-bbb.center());
|
||||
|
||||
bool res = Geometry::intersects(A, B);
|
||||
bool ref = !intersection(A, B).empty();
|
||||
|
||||
if (res != ref) {
|
||||
SVG svg{std::string("fail") + std::to_string(combo.first) + "_" + std::to_string(combo.second) + ".svg"};
|
||||
svg.draw(A, "blue");
|
||||
svg.draw(B, "green");
|
||||
svg.Close();
|
||||
}
|
||||
|
||||
REQUIRE(res == ref);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ TEST_CASE("Hollow two overlapping spheres") {
|
|||
sphere2.translate( 5.f, 0.f, 0.f);
|
||||
|
||||
sphere1.merge(sphere2);
|
||||
sphere1.require_shared_vertices();
|
||||
|
||||
sla::hollow_mesh(sphere1, sla::HollowingConfig{}, sla::HollowingFlags::hfRemoveInsideTriangles);
|
||||
|
||||
|
|
|
@ -319,7 +319,6 @@ static void recreate_object_from_rasters(const std::string &objname, float lh) {
|
|||
mesh.translate(tr.x(), tr.y(), tr.z());
|
||||
bb = mesh.bounding_box();
|
||||
|
||||
assert(mesh.has_shared_vertices());
|
||||
std::vector<ExPolygons> layers = slice_mesh_ex(mesh.its, grid(float(bb.min.z()) + lh, float(bb.max.z()), lh));
|
||||
|
||||
sla::RasterBase::Resolution res{2560, 1440};
|
||||
|
|
|
@ -57,7 +57,6 @@ TEST_CASE("Support point generator should be deterministic if seeded",
|
|||
auto layer_h = 0.05f;
|
||||
|
||||
auto slicegrid = grid(float(gnd), float(zmax), layer_h);
|
||||
assert(mesh.has_shared_vertices());
|
||||
std::vector<ExPolygons> slices = slice_mesh_ex(mesh.its, slicegrid, CLOSING_RADIUS);
|
||||
|
||||
point_gen.seed(0);
|
||||
|
|
|
@ -63,7 +63,6 @@ TEST_CASE("Raycaster with loaded drillholes", "[sla_raycast]")
|
|||
sla::DrainHoles holes = { sla::DrainHole{p, normal, radius, hole_length} };
|
||||
|
||||
cube.merge(*cube_inside);
|
||||
cube.require_shared_vertices();
|
||||
|
||||
sla::IndexedMesh emesh{cube};
|
||||
emesh.load_holes(holes);
|
||||
|
|
|
@ -13,7 +13,6 @@ TEST_CASE("Overhanging point should be supported", "[SupGen]") {
|
|||
// Pyramid with 45 deg slope
|
||||
TriangleMesh mesh = make_pyramid(10.f, 10.f);
|
||||
mesh.rotate_y(float(PI));
|
||||
mesh.require_shared_vertices();
|
||||
mesh.WriteOBJFile("Pyramid.obj");
|
||||
|
||||
sla::SupportPoints pts = calc_support_pts(mesh);
|
||||
|
@ -56,7 +55,6 @@ TEST_CASE("Overhanging horizontal surface should be supported", "[SupGen]") {
|
|||
|
||||
TriangleMesh mesh = make_cube(width, depth, height);
|
||||
mesh.translate(0., 0., 5.); // lift up
|
||||
mesh.require_shared_vertices();
|
||||
mesh.WriteOBJFile("Cuboid.obj");
|
||||
|
||||
sla::SupportPointGenerator::Config cfg;
|
||||
|
@ -83,7 +81,6 @@ TEST_CASE("Overhanging edge should be supported", "[SupGen]") {
|
|||
TriangleMesh mesh = make_prism(width, depth, height);
|
||||
mesh.rotate_y(float(PI)); // rotate on its back
|
||||
mesh.translate(0., 0., height);
|
||||
mesh.require_shared_vertices();
|
||||
mesh.WriteOBJFile("Prism.obj");
|
||||
|
||||
sla::SupportPointGenerator::Config cfg;
|
||||
|
@ -115,7 +112,6 @@ TEST_CASE("Hollowed cube should be supported from the inside", "[SupGen][Hollowe
|
|||
auto h = float(bb.max.z() - bb.min.z());
|
||||
Vec3f mv = bb.center().cast<float>() - Vec3f{0.f, 0.f, 0.5f * h};
|
||||
mesh.translate(-mv);
|
||||
mesh.require_shared_vertices();
|
||||
|
||||
sla::SupportPointGenerator::Config cfg;
|
||||
sla::SupportPoints pts = calc_support_pts(mesh, cfg);
|
||||
|
@ -132,7 +128,6 @@ TEST_CASE("Two parallel plates should be supported", "[SupGen][Hollowed]")
|
|||
TriangleMesh mesh_high = center_around_bb(make_cube(width, depth, height));
|
||||
mesh_high.translate(0., 0., 10.); // lift up
|
||||
mesh.merge(mesh_high);
|
||||
mesh.require_shared_vertices();
|
||||
|
||||
mesh.WriteOBJFile("parallel_plates.obj");
|
||||
|
||||
|
|
|
@ -74,8 +74,6 @@ void export_failed_case(const std::vector<ExPolygons> &support_slices, const Sup
|
|||
byproducts.supporttree.retrieve_full_mesh(its);
|
||||
TriangleMesh m{its};
|
||||
m.merge(byproducts.input_mesh);
|
||||
m.repair();
|
||||
m.require_shared_vertices();
|
||||
m.WriteOBJFile((Catch::getResultCapture().getCurrentTestName() + "_" +
|
||||
byproducts.obj_fname).c_str());
|
||||
}
|
||||
|
@ -95,7 +93,6 @@ void test_supports(const std::string &obj_filename,
|
|||
sla::InteriorPtr interior = sla::generate_interior(mesh, hollowingcfg);
|
||||
REQUIRE(interior);
|
||||
mesh.merge(TriangleMesh{sla::get_mesh(*interior)});
|
||||
mesh.require_shared_vertices();
|
||||
}
|
||||
|
||||
auto bb = mesh.bounding_box();
|
||||
|
@ -105,7 +102,6 @@ void test_supports(const std::string &obj_filename,
|
|||
auto layer_h = 0.05f;
|
||||
|
||||
out.slicegrid = grid(float(gnd), float(zmax), layer_h);
|
||||
assert(mesh.has_shared_vertices());
|
||||
out.model_slices = slice_mesh_ex(mesh.its, out.slicegrid, CLOSING_RADIUS);
|
||||
sla::cut_drainholes(out.model_slices, out.slicegrid, CLOSING_RADIUS, drainholes, []{});
|
||||
|
||||
|
@ -283,8 +279,10 @@ void test_concave_hull(const ExPolygons &polys) {
|
|||
_test_concave_hull(waffl, polys);
|
||||
}
|
||||
|
||||
//FIXME this functionality is gone after TriangleMesh refactoring to get rid of admesh.
|
||||
void check_validity(const TriangleMesh &input_mesh, int flags)
|
||||
{
|
||||
/*
|
||||
TriangleMesh mesh{input_mesh};
|
||||
|
||||
if (flags & ASSUME_NO_EMPTY) {
|
||||
|
@ -292,20 +290,18 @@ void check_validity(const TriangleMesh &input_mesh, int flags)
|
|||
} else if (mesh.empty())
|
||||
return; // If it can be empty and it is, there is nothing left to do.
|
||||
|
||||
REQUIRE(stl_validate(&mesh.stl));
|
||||
|
||||
bool do_update_shared_vertices = false;
|
||||
mesh.repair(do_update_shared_vertices);
|
||||
|
||||
if (flags & ASSUME_NO_REPAIR) {
|
||||
REQUIRE_FALSE(mesh.needed_repair());
|
||||
REQUIRE_FALSE(mesh.repaired());
|
||||
}
|
||||
|
||||
if (flags & ASSUME_MANIFOLD) {
|
||||
mesh.require_shared_vertices();
|
||||
if (!mesh.is_manifold()) mesh.WriteOBJFile("non_manifold.obj");
|
||||
REQUIRE(mesh.is_manifold());
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void check_raster_transformations(sla::RasterBase::Orientation o, sla::RasterBase::TMirroring mirroring)
|
||||
|
@ -420,53 +416,6 @@ double predict_error(const ExPolygon &p, const sla::RasterBase::PixelDim &pd)
|
|||
return error;
|
||||
}
|
||||
|
||||
|
||||
// Make a 3D pyramid
|
||||
TriangleMesh make_pyramid(float base, float height)
|
||||
{
|
||||
float a = base / 2.f;
|
||||
|
||||
TriangleMesh mesh(
|
||||
{
|
||||
{-a, -a, 0}, {a, -a, 0}, {a, a, 0},
|
||||
{-a, a, 0}, {0.f, 0.f, height}
|
||||
},
|
||||
{
|
||||
{0, 1, 2},
|
||||
{0, 2, 3},
|
||||
{0, 1, 4},
|
||||
{1, 2, 4},
|
||||
{2, 3, 4},
|
||||
{3, 0, 4}
|
||||
});
|
||||
|
||||
mesh.repair();
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
TriangleMesh make_prism(double width, double length, double height)
|
||||
{
|
||||
// We need two upward facing triangles
|
||||
|
||||
double x = width / 2., y = length / 2.;
|
||||
|
||||
TriangleMesh mesh(
|
||||
{
|
||||
{-x, -y, 0.}, {x, -y, 0.}, {0., -y, height},
|
||||
{-x, y, 0.}, {x, y, 0.}, {0., y, height},
|
||||
},
|
||||
{
|
||||
{0, 1, 2}, // side 1
|
||||
{4, 3, 5}, // side 2
|
||||
{1, 4, 2}, {2, 4, 5}, // roof 1
|
||||
{0, 2, 5}, {0, 5, 3}, // roof 2
|
||||
{3, 4, 1}, {3, 1, 0} // bottom
|
||||
});
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
sla::SupportPoints calc_support_pts(
|
||||
const TriangleMesh & mesh,
|
||||
const sla::SupportPointGenerator::Config &cfg)
|
||||
|
@ -474,7 +423,6 @@ sla::SupportPoints calc_support_pts(
|
|||
// Prepare the slice grid and the slices
|
||||
auto bb = cast<float>(mesh.bounding_box());
|
||||
std::vector<float> heights = grid(bb.min.z(), bb.max.z(), 0.1f);
|
||||
assert(mesh.has_shared_vertices());
|
||||
std::vector<ExPolygons> slices = slice_mesh_ex(mesh.its, heights, CLOSING_RADIUS);
|
||||
|
||||
// Prepare the support point calculator
|
||||
|
|
|
@ -185,11 +185,6 @@ long raster_pxsum(const sla::RasterGrayscaleAA &raster);
|
|||
|
||||
double predict_error(const ExPolygon &p, const sla::RasterBase::PixelDim &pd);
|
||||
|
||||
// Make a 3D pyramid
|
||||
TriangleMesh make_pyramid(float base, float height);
|
||||
|
||||
TriangleMesh make_prism(double width, double length, double height);
|
||||
|
||||
sla::SupportPoints calc_support_pts(
|
||||
const TriangleMesh & mesh,
|
||||
const sla::SupportPointGenerator::Config &cfg = {});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue