sla::Raster interface clarified and covered with tests.

Also renamed sla::SupportTreeAlgorithm to SupportTreeBuildsteps.
This commit is contained in:
tamasmeszaros 2019-10-01 13:27:58 +02:00
parent 705e82ec8e
commit be7428d66e
15 changed files with 484 additions and 361 deletions

View file

@ -1,4 +1,5 @@
add_executable(sla_print_tests sla_print_tests_main.cpp)
target_link_libraries(sla_print_tests test_common libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES})
gtest_discover_tests(sla_print_tests TEST_PREFIX sla_print.)
add_test(sla_print_tests sla_print_tests)
#gtest_discover_tests(sla_print_tests TEST_PREFIX sla_print.)

View file

@ -1,5 +1,8 @@
#include <map>
// Debug
#include <fstream>
#include <gtest/gtest.h>
#include "libslic3r/libslic3r.h"
@ -8,8 +11,9 @@
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/SLA/SLAPad.hpp"
#include "libslic3r/SLA/SLASupportTreeBuilder.hpp"
#include "libslic3r/SLA/SLASupportTreeAlgorithm.hpp"
#include "libslic3r/SLA/SLASupportTreeBuildsteps.hpp"
#include "libslic3r/SLA/SLAAutoSupports.hpp"
#include "libslic3r/SLA/SLARaster.hpp"
#include "libslic3r/MTUtils.hpp"
#include "libslic3r/SVG.hpp"
@ -86,7 +90,7 @@ void test_pad(const std::string & obj_filename,
ASSERT_FALSE(out.model_contours.empty());
// Create the pad geometry the model contours only
// Create the pad geometry for the model contours only
Slic3r::sla::create_pad({}, out.model_contours, out.mesh, padcfg);
check_validity(out.mesh);
@ -369,6 +373,180 @@ TEST(SLASupportGeneration, SupportsDoNotPierceModel) {
test_support_model_collision(fname, supportcfg);
}
TEST(SLARasterOutput, DefaultRasterShouldBeEmpty) {
sla::Raster raster;
ASSERT_TRUE(raster.empty());
}
TEST(SLARasterOutput, InitializedRasterShouldBeNONEmpty) {
// Default Prusa SL1 display parameters
sla::Raster::Resolution res{2560, 1440};
sla::Raster::PixelDim pixdim{120. / res.width_px, 68. / res.height_px};
sla::Raster raster;
raster.reset(res, pixdim);
ASSERT_FALSE(raster.empty());
ASSERT_EQ(raster.resolution().width_px, res.width_px);
ASSERT_EQ(raster.resolution().height_px, res.height_px);
ASSERT_DOUBLE_EQ(raster.pixel_dimensions().w_mm, pixdim.w_mm);
ASSERT_DOUBLE_EQ(raster.pixel_dimensions().h_mm, pixdim.h_mm);
}
using TPixel = uint8_t;
static constexpr const TPixel FullWhite = 255;
static constexpr const TPixel FullBlack = 0;
template <class A, int N> constexpr int arraysize(const A (&)[N]) { return N; }
static void check_raster_transformations(sla::Raster::Orientation o,
sla::Raster::TMirroring mirroring)
{
double disp_w = 120., disp_h = 68.;
sla::Raster::Resolution res{2560, 1440};
sla::Raster::PixelDim pixdim{disp_w / res.width_px, disp_h / res.height_px};
auto bb = BoundingBox({0, 0}, {scaled(disp_w), scaled(disp_h)});
sla::Raster::Trafo trafo{o, mirroring};
trafo.origin_x = bb.center().x();
trafo.origin_y = bb.center().y();
sla::Raster raster{res, pixdim, trafo};
// create box of size 32x32 pixels (not 1x1 to avoid antialiasing errors)
coord_t pw = 32 * coord_t(std::ceil(scaled<double>(pixdim.w_mm)));
coord_t ph = 32 * coord_t(std::ceil(scaled<double>(pixdim.h_mm)));
ExPolygon box;
box.contour.points = {{-pw, -ph}, {pw, -ph}, {pw, ph}, {-pw, ph}};
double tr_x = scaled<double>(20.), tr_y = tr_x;
box.translate(tr_x, tr_y);
ExPolygon expected_box = box;
// Now calculate the position of the translated box according to output
// trafo.
if (o == sla::Raster::Orientation::roPortrait) expected_box.rotate(PI / 2.);
if (mirroring[X])
for (auto &p : expected_box.contour.points) p.x() = -p.x();
if (mirroring[Y])
for (auto &p : expected_box.contour.points) p.y() = -p.y();
raster.draw(box);
Point expected_coords = expected_box.contour.bounding_box().center();
double rx = unscaled(expected_coords.x() + bb.center().x()) / pixdim.w_mm;
double ry = unscaled(expected_coords.y() + bb.center().y()) / pixdim.h_mm;
auto w = size_t(std::floor(rx));
auto h = res.height_px - size_t(std::floor(ry));
ASSERT_TRUE(w < res.width_px && h < res.height_px);
auto px = raster.read_pixel(w, h);
if (px != FullWhite) {
sla::PNGImage img;
std::fstream outf("out.png", std::ios::out);
outf << img.serialize(raster);
}
ASSERT_EQ(px, FullWhite);
}
TEST(SLARasterOutput, MirroringShouldBeCorrect) {
sla::Raster::TMirroring mirrorings[] = {sla::Raster::NoMirror,
sla::Raster::MirrorX,
sla::Raster::MirrorY,
sla::Raster::MirrorXY};
sla::Raster::Orientation orientations[] = {sla::Raster::roLandscape,
sla::Raster::roPortrait};
for (auto orientation : orientations)
for (auto &mirror : mirrorings)
check_raster_transformations(orientation, mirror);
}
static ExPolygon square_with_hole(double v)
{
ExPolygon poly;
coord_t V = scaled(v / 2.);
poly.contour.points = {{-V, -V}, {V, -V}, {V, V}, {-V, V}};
poly.holes.emplace_back();
V = V / 2;
poly.holes.front().points = {{-V, V}, {V, V}, {V, -V}, {-V, -V}};
return poly;
}
static double pixel_area(TPixel px, const sla::Raster::PixelDim &pxdim)
{
return (pxdim.h_mm * pxdim.w_mm) * px * 1. / (FullWhite - FullBlack);
}
static double raster_white_area(const sla::Raster &raster)
{
if (raster.empty()) return std::nan("");
auto res = raster.resolution();
double a = 0;
for (size_t x = 0; x < res.width_px; ++x)
for (size_t y = 0; y < res.height_px; ++y) {
auto px = raster.read_pixel(x, y);
a += pixel_area(px, raster.pixel_dimensions());
}
return a;
}
static double predict_error(const ExPolygon &p, const sla::Raster::PixelDim &pd)
{
auto lines = p.lines();
double pix_err = pixel_area(FullWhite, pd) / 2.;
// Worst case is when a line is parallel to the shorter axis of one pixel,
// when the line will be composed of the max number of pixels
double pix_l = std::min(pd.h_mm, pd.w_mm);
double error = 0.;
for (auto &l : lines)
error += (unscaled(l.length()) / pix_l) * pix_err;
return error;
}
TEST(SLARasterOutput, RasterizedPolygonAreaShouldMatch) {
double disp_w = 120., disp_h = 68.;
sla::Raster::Resolution res{2560, 1440};
sla::Raster::PixelDim pixdim{disp_w / res.width_px, disp_h / res.height_px};
sla::Raster raster{res, pixdim};
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);
ASSERT_LE(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);
ASSERT_LE(diff, predict_error(poly, pixdim));
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();