927b81ea97
Fixed fatal bug with anchors for mini supports Make the optimization cleaner in support generatior Much better widening behaviour Add an optimizer interface and the NLopt implementation into libslic3r New optimizer based only on nlopt C interfase Fix build and tests
242 lines
7.4 KiB
C++
242 lines
7.4 KiB
C++
#include <unordered_set>
|
|
#include <unordered_map>
|
|
#include <random>
|
|
|
|
#include "sla_test_utils.hpp"
|
|
|
|
#include <libslic3r/SLA/SupportTreeMesher.hpp>
|
|
|
|
namespace {
|
|
|
|
const char *const BELOW_PAD_TEST_OBJECTS[] = {
|
|
"20mm_cube.obj",
|
|
"V.obj",
|
|
};
|
|
|
|
const char *const AROUND_PAD_TEST_OBJECTS[] = {
|
|
"20mm_cube.obj",
|
|
"V.obj",
|
|
"frog_legs.obj",
|
|
"cube_with_concave_hole_enlarged.obj",
|
|
};
|
|
|
|
const char *const SUPPORT_TEST_MODELS[] = {
|
|
"cube_with_concave_hole_enlarged_standing.obj",
|
|
"A_upsidedown.obj",
|
|
"extruder_idler.obj"
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TEST_CASE("Pillar pairhash should be unique", "[SLASupportGeneration]") {
|
|
test_pairhash<int, int>();
|
|
test_pairhash<int, long>();
|
|
test_pairhash<unsigned, unsigned>();
|
|
test_pairhash<unsigned, unsigned long>();
|
|
}
|
|
|
|
TEST_CASE("Support point generator should be deterministic if seeded",
|
|
"[SLASupportGeneration], [SLAPointGen]") {
|
|
TriangleMesh mesh = load_model("A_upsidedown.obj");
|
|
|
|
sla::IndexedMesh emesh{mesh};
|
|
|
|
sla::SupportTreeConfig supportcfg;
|
|
sla::SupportPointGenerator::Config autogencfg;
|
|
autogencfg.head_diameter = float(2 * supportcfg.head_front_radius_mm);
|
|
sla::SupportPointGenerator point_gen{emesh, autogencfg, [] {}, [](int) {}};
|
|
|
|
TriangleMeshSlicer slicer{&mesh};
|
|
|
|
auto bb = mesh.bounding_box();
|
|
double zmin = bb.min.z();
|
|
double zmax = bb.max.z();
|
|
double gnd = zmin - supportcfg.object_elevation_mm;
|
|
auto layer_h = 0.05f;
|
|
|
|
auto slicegrid = grid(float(gnd), float(zmax), layer_h);
|
|
std::vector<ExPolygons> slices;
|
|
slicer.slice(slicegrid, SlicingMode::Regular, CLOSING_RADIUS, &slices, []{});
|
|
|
|
point_gen.seed(0);
|
|
point_gen.execute(slices, slicegrid);
|
|
|
|
auto get_chksum = [](const std::vector<sla::SupportPoint> &pts){
|
|
long long chksum = 0;
|
|
for (auto &pt : pts) {
|
|
auto p = scaled(pt.pos);
|
|
chksum += p.x() + p.y() + p.z();
|
|
}
|
|
|
|
return chksum;
|
|
};
|
|
|
|
long long checksum = get_chksum(point_gen.output());
|
|
size_t ptnum = point_gen.output().size();
|
|
REQUIRE(point_gen.output().size() > 0);
|
|
|
|
for (int i = 0; i < 20; ++i) {
|
|
point_gen.output().clear();
|
|
point_gen.seed(0);
|
|
point_gen.execute(slices, slicegrid);
|
|
REQUIRE(point_gen.output().size() == ptnum);
|
|
REQUIRE(checksum == get_chksum(point_gen.output()));
|
|
}
|
|
}
|
|
|
|
TEST_CASE("Flat pad geometry is valid", "[SLASupportGeneration]") {
|
|
sla::PadConfig padcfg;
|
|
|
|
// Disable wings
|
|
padcfg.wall_height_mm = .0;
|
|
|
|
for (auto &fname : BELOW_PAD_TEST_OBJECTS) test_pad(fname, padcfg);
|
|
}
|
|
|
|
TEST_CASE("WingedPadGeometryIsValid", "[SLASupportGeneration]") {
|
|
sla::PadConfig padcfg;
|
|
|
|
// Add some wings to the pad to test the cavity
|
|
padcfg.wall_height_mm = 1.;
|
|
|
|
for (auto &fname : BELOW_PAD_TEST_OBJECTS) test_pad(fname, padcfg);
|
|
}
|
|
|
|
TEST_CASE("FlatPadAroundObjectIsValid", "[SLASupportGeneration]") {
|
|
sla::PadConfig padcfg;
|
|
|
|
// Add some wings to the pad to test the cavity
|
|
padcfg.wall_height_mm = 0.;
|
|
// padcfg.embed_object.stick_stride_mm = 0.;
|
|
padcfg.embed_object.enabled = true;
|
|
padcfg.embed_object.everywhere = true;
|
|
|
|
for (auto &fname : AROUND_PAD_TEST_OBJECTS) test_pad(fname, padcfg);
|
|
}
|
|
|
|
TEST_CASE("WingedPadAroundObjectIsValid", "[SLASupportGeneration]") {
|
|
sla::PadConfig padcfg;
|
|
|
|
// Add some wings to the pad to test the cavity
|
|
padcfg.wall_height_mm = 1.;
|
|
padcfg.embed_object.enabled = true;
|
|
padcfg.embed_object.everywhere = true;
|
|
|
|
for (auto &fname : AROUND_PAD_TEST_OBJECTS) test_pad(fname, padcfg);
|
|
}
|
|
|
|
TEST_CASE("ElevatedSupportGeometryIsValid", "[SLASupportGeneration]") {
|
|
sla::SupportTreeConfig supportcfg;
|
|
supportcfg.object_elevation_mm = 5.;
|
|
|
|
for (auto fname : SUPPORT_TEST_MODELS) test_supports(fname);
|
|
}
|
|
|
|
TEST_CASE("FloorSupportGeometryIsValid", "[SLASupportGeneration]") {
|
|
sla::SupportTreeConfig supportcfg;
|
|
supportcfg.object_elevation_mm = 0;
|
|
|
|
for (auto &fname: SUPPORT_TEST_MODELS) test_supports(fname, supportcfg);
|
|
}
|
|
|
|
TEST_CASE("ElevatedSupportsDoNotPierceModel", "[SLASupportGeneration]") {
|
|
|
|
sla::SupportTreeConfig supportcfg;
|
|
|
|
for (auto fname : SUPPORT_TEST_MODELS)
|
|
test_support_model_collision(fname, supportcfg);
|
|
}
|
|
|
|
TEST_CASE("FloorSupportsDoNotPierceModel", "[SLASupportGeneration]") {
|
|
|
|
sla::SupportTreeConfig supportcfg;
|
|
supportcfg.object_elevation_mm = 0;
|
|
|
|
for (auto fname : SUPPORT_TEST_MODELS)
|
|
test_support_model_collision(fname, supportcfg);
|
|
}
|
|
|
|
TEST_CASE("InitializedRasterShouldBeNONEmpty", "[SLARasterOutput]") {
|
|
// Default Prusa SL1 display parameters
|
|
sla::RasterBase::Resolution res{2560, 1440};
|
|
sla::RasterBase::PixelDim pixdim{120. / res.width_px, 68. / res.height_px};
|
|
|
|
sla::RasterGrayscaleAAGammaPower raster(res, pixdim, {}, 1.);
|
|
REQUIRE(raster.resolution().width_px == res.width_px);
|
|
REQUIRE(raster.resolution().height_px == res.height_px);
|
|
REQUIRE(raster.pixel_dimensions().w_mm == Approx(pixdim.w_mm));
|
|
REQUIRE(raster.pixel_dimensions().h_mm == Approx(pixdim.h_mm));
|
|
}
|
|
|
|
TEST_CASE("MirroringShouldBeCorrect", "[SLARasterOutput]") {
|
|
sla::RasterBase::TMirroring mirrorings[] = {sla::RasterBase::NoMirror,
|
|
sla::RasterBase::MirrorX,
|
|
sla::RasterBase::MirrorY,
|
|
sla::RasterBase::MirrorXY};
|
|
|
|
sla::RasterBase::Orientation orientations[] =
|
|
{sla::RasterBase::roLandscape, sla::RasterBase::roPortrait};
|
|
|
|
for (auto orientation : orientations)
|
|
for (auto &mirror : mirrorings)
|
|
check_raster_transformations(orientation, mirror);
|
|
}
|
|
|
|
|
|
TEST_CASE("RasterizedPolygonAreaShouldMatch", "[SLARasterOutput]") {
|
|
double disp_w = 120., disp_h = 68.;
|
|
sla::RasterBase::Resolution res{2560, 1440};
|
|
sla::RasterBase::PixelDim pixdim{disp_w / res.width_px, disp_h / res.height_px};
|
|
|
|
double gamma = 1.;
|
|
sla::RasterGrayscaleAAGammaPower raster(res, pixdim, {}, gamma);
|
|
auto bb = BoundingBox({0, 0}, {scaled(disp_w), scaled(disp_h)});
|
|
|
|
ExPolygon poly = square_with_hole(10.);
|
|
poly.translate(bb.center().x(), bb.center().y());
|
|
raster.draw(poly);
|
|
|
|
double a = poly.area() / (scaled<double>(1.) * scaled(1.));
|
|
double ra = raster_white_area(raster);
|
|
double diff = std::abs(a - ra);
|
|
|
|
REQUIRE(diff <= predict_error(poly, pixdim));
|
|
|
|
raster.clear();
|
|
poly = square_with_hole(60.);
|
|
poly.translate(bb.center().x(), bb.center().y());
|
|
raster.draw(poly);
|
|
|
|
a = poly.area() / (scaled<double>(1.) * scaled(1.));
|
|
ra = raster_white_area(raster);
|
|
diff = std::abs(a - ra);
|
|
|
|
REQUIRE(diff <= predict_error(poly, pixdim));
|
|
|
|
sla::RasterGrayscaleAA raster0(res, pixdim, {}, [](double) { return 0.; });
|
|
REQUIRE(raster_pxsum(raster0) == 0);
|
|
|
|
raster0.draw(poly);
|
|
ra = raster_white_area(raster);
|
|
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));
|
|
|
|
m.require_shared_vertices();
|
|
m.WriteOBJFile("Halfcone.obj");
|
|
}
|