Fix tests on all platforms

Try to link tests on Mac.


Fix inaccurate pad brim size


fix build on mac (attempt 2)


Fixes for support tree faults and race conditions in release mode.


Fix crashing test executable on gcc 4.9


fix warning on msvc
This commit is contained in:
tamasmeszaros 2019-10-02 14:55:02 +02:00
parent be7428d66e
commit 8ca7e56d0f
12 changed files with 381 additions and 319 deletions

View File

@ -271,8 +271,6 @@ ConfigOptionDef* ConfigDef::add_nullable(const t_config_option_key &opt_key, Con
return def; return def;
} }
std::string ConfigOptionDef::nocli = "~~~noCLI";
std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, std::function<bool(const ConfigOptionDef &)> filter) const std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, std::function<bool(const ConfigOptionDef &)> filter) const
{ {
// prepare a function for wrapping text // prepare a function for wrapping text

View File

@ -1444,7 +1444,7 @@ public:
std::vector<std::string> cli_args(const std::string &key) const; std::vector<std::string> cli_args(const std::string &key) const;
// Assign this key to cli to disable CLI for this option. // Assign this key to cli to disable CLI for this option.
static std::string nocli; static const constexpr char *nocli = "~~~noCLI";
}; };
// Map from a config option name to its definition. // Map from a config option name to its definition.

View File

@ -310,20 +310,13 @@ void breakstick_holes(Points& pts,
pts.swap(out); pts.swap(out);
} }
void breakstick_holes(Points &pts, const PadConfig::EmbedObject &c) template<class...Args>
ExPolygons breakstick_holes(const ExPolygons &input, Args...args)
{ {
breakstick_holes(pts, c.object_gap_mm, c.stick_stride_mm, ExPolygons ret = input;
c.stick_width_mm, c.stick_penetration_mm);
}
ExPolygons breakstick_holes(const ExPolygons &input,
const PadConfig::EmbedObject &cfg)
{
ExPolygons ret = offset_ex(input, scaled(cfg.object_gap_mm), ClipperLib::jtMiter, 1);
for (ExPolygon &p : ret) { for (ExPolygon &p : ret) {
breakstick_holes(p.contour.points, cfg); breakstick_holes(p.contour.points, args...);
for (auto &h : p.holes) breakstick_holes(h.points, cfg); for (auto &h : p.holes) breakstick_holes(h.points, args...);
} }
return ret; return ret;
@ -428,7 +421,7 @@ class ConcaveHull {
Point d(scaled(nx), scaled(ny)); Point d(scaled(nx), scaled(ny));
r.points.emplace_back(c + Point(-d.y(), d.x())); r.points.emplace_back(c + Point(-d.y(), d.x()));
r.points.emplace_back(c + Point(d.y(), -d.x())); r.points.emplace_back(c + Point(d.y(), -d.x()));
offset(r, scaled(1.)); offset(r, scaled<float>(1.));
m_polys.emplace_back(r); m_polys.emplace_back(r);
} }
@ -473,7 +466,7 @@ public:
static inline coord_t get_waffle_offset(const PadConfig &c) static inline coord_t get_waffle_offset(const PadConfig &c)
{ {
return scaled(c.brim_size_mm + c.wing_distance() + c.wall_thickness_mm); return scaled(c.brim_size_mm + c.wing_distance());
} }
static inline double get_merge_distance(const PadConfig &c) static inline double get_merge_distance(const PadConfig &c)
@ -593,11 +586,19 @@ public:
add_supports_to_index(support_blueprint); add_supports_to_index(support_blueprint);
auto model_bp_offs =
offset_ex(model_blueprint,
scaled<float>(cfg.embed_object.object_gap_mm),
ClipperLib::jtMiter, 1);
ConcaveHull fullcvh = ConcaveHull fullcvh =
wafflized_concave_hull(support_blueprint, model_blueprint, cfg, thr); wafflized_concave_hull(support_blueprint, model_bp_offs, cfg, thr);
auto model_bp_sticks = auto model_bp_sticks =
breakstick_holes(model_blueprint, cfg.embed_object); breakstick_holes(model_bp_offs, cfg.embed_object.object_gap_mm,
cfg.embed_object.stick_stride_mm,
cfg.embed_object.stick_width_mm,
cfg.embed_object.stick_penetration_mm);
ExPolygons fullpad = diff_ex(fullcvh.to_expolygons(), model_bp_sticks); ExPolygons fullpad = diff_ex(fullcvh.to_expolygons(), model_bp_sticks);
@ -673,7 +674,7 @@ public:
template<class...Args> template<class...Args>
ExPolygon offset_contour_only(const ExPolygon &poly, coord_t delta, Args...args) ExPolygon offset_contour_only(const ExPolygon &poly, coord_t delta, Args...args)
{ {
ExPolygons tmp = offset_ex(poly.contour, delta, args...); ExPolygons tmp = offset_ex(poly.contour, float(delta), args...);
if (tmp.empty()) return {}; if (tmp.empty()) return {};
@ -856,8 +857,12 @@ void create_pad(const ExPolygons &sup_blueprint,
std::string PadConfig::validate() const std::string PadConfig::validate() const
{ {
if (bottom_offset() > brim_size_mm + wing_distance()) static const double constexpr MIN_BRIM_SIZE_MM = .1;
return L("Pad brim size is too low for the current slope.");
if (brim_size_mm < MIN_BRIM_SIZE_MM ||
bottom_offset() > brim_size_mm + wing_distance() ||
ConcaveHull::get_waffle_offset(*this) <= MIN_BRIM_SIZE_MM)
return L("Pad brim size is too small for the current configuration.");
return ""; return "";
} }

View File

@ -4,6 +4,7 @@
#include <vector> #include <vector>
#include <functional> #include <functional>
#include <cmath> #include <cmath>
#include <string>
namespace Slic3r { namespace Slic3r {

View File

@ -15,8 +15,6 @@ namespace ClipperLib { struct Polygon; }
namespace Slic3r { namespace Slic3r {
namespace sla { namespace sla {
class Raster;
/** /**
* @brief Raster captures an anti-aliased monochrome canvas where vectorial * @brief Raster captures an anti-aliased monochrome canvas where vectorial
* polygons can be rasterized. Fill color is always white and the background is * polygons can be rasterized. Fill color is always white and the background is

View File

@ -70,7 +70,7 @@ std::vector<ExPolygons> SupportTree::slice(
auto bb = pad_mesh.bounding_box(); auto bb = pad_mesh.bounding_box();
auto maxzit = std::upper_bound(grid.begin(), grid.end(), bb.max.z()); auto maxzit = std::upper_bound(grid.begin(), grid.end(), bb.max.z());
long cap = grid.end() - maxzit; auto cap = grid.end() - maxzit;
auto padgrid = reserve_vector<float>(size_t(cap > 0 ? cap : 0)); auto padgrid = reserve_vector<float>(size_t(cap > 0 ? cap : 0));
std::copy(grid.begin(), maxzit, std::back_inserter(padgrid)); std::copy(grid.begin(), maxzit, std::back_inserter(padgrid));

View File

@ -96,6 +96,8 @@ struct Head {
// If there is a pillar connecting to this head, then the id will be set. // If there is a pillar connecting to this head, then the id will be set.
long pillar_id = ID_UNSET; long pillar_id = ID_UNSET;
long bridge_id = ID_UNSET;
inline void invalidate() { id = ID_UNSET; } inline void invalidate() { id = ID_UNSET; }
inline bool is_valid() const { return id >= 0; } inline bool is_valid() const { return id >= 0; }
@ -335,6 +337,12 @@ public:
m_pillars[size_t(pillar.id)].links++; m_pillars[size_t(pillar.id)].links++;
} }
unsigned bridgecount(const Pillar &pillar) const {
std::lock_guard<Mutex> lk(m_mutex);
assert(pillar.id >= 0 && size_t(pillar.id) < m_pillars.size());
return pillar.bridges;
}
template<class...Args> Pillar& add_pillar(Args&&...args) template<class...Args> Pillar& add_pillar(Args&&...args)
{ {
std::lock_guard<Mutex> lk(m_mutex); std::lock_guard<Mutex> lk(m_mutex);

View File

@ -345,7 +345,7 @@ EigenMesh3D::hit_result SupportTreeBuildsteps::bridge_mesh_intersect(
} }
bool SupportTreeBuildsteps::interconnect(const Pillar &pillar, bool SupportTreeBuildsteps::interconnect(const Pillar &pillar,
const Pillar &nextpillar) const Pillar &nextpillar)
{ {
// We need to get the starting point of the zig-zag pattern. We have to // We need to get the starting point of the zig-zag pattern. We have to
// be aware that the two head junctions are at different heights. We // be aware that the two head junctions are at different heights. We
@ -438,13 +438,14 @@ bool SupportTreeBuildsteps::interconnect(const Pillar &pillar,
} }
bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head, bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head,
long nearpillar_id) long nearpillar_id)
{ {
auto nearpillar = [this, nearpillar_id]() { auto nearpillar = [this, nearpillar_id]() {
return m_builder.pillar(nearpillar_id); return m_builder.pillar(nearpillar_id);
}; };
if (nearpillar().bridges > m_cfg.max_bridges_on_pillar) return false; if (m_builder.bridgecount(nearpillar()) > m_cfg.max_bridges_on_pillar)
return false;
Vec3d headjp = head.junction_point(); Vec3d headjp = head.junction_point();
Vec3d nearjp_u = nearpillar().startpoint(); Vec3d nearjp_u = nearpillar().startpoint();
@ -502,14 +503,21 @@ bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head,
// Cannot insert the bridge. (further search might not worth the hassle) // Cannot insert the bridge. (further search might not worth the hassle)
if(t < distance(bridgestart, bridgeend)) return false; if(t < distance(bridgestart, bridgeend)) return false;
// A partial pillar is needed under the starting head. std::lock_guard<ccr::BlockingMutex> lk(m_bridge_mutex);
if(zdiff > 0) {
m_builder.add_pillar(unsigned(head.id), bridgestart, r);
m_builder.add_junction(bridgestart, r);
}
m_builder.add_bridge(bridgestart, bridgeend, r); if (m_builder.bridgecount(nearpillar()) < m_cfg.max_bridges_on_pillar) {
m_builder.increment_bridges(nearpillar()); // A partial pillar is needed under the starting head.
if(zdiff > 0) {
m_builder.add_pillar(unsigned(head.id), bridgestart, r);
m_builder.add_junction(bridgestart, r);
}
auto &br = m_builder.add_bridge(bridgestart, bridgeend, r);
m_builder.increment_bridges(nearpillar());
if (head.pillar_id == ID_UNSET)
m_builder.head(unsigned(head.id)).bridge_id = br.id;
} else return false;
return true; return true;
} }
@ -550,9 +558,9 @@ bool SupportTreeBuildsteps::search_pillar_and_connect(const Head &head)
} }
void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp, void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp,
const Vec3d &sourcedir, const Vec3d &sourcedir,
double radius, double radius,
long head_id) long head_id)
{ {
// People were killed for this number (seriously) // People were killed for this number (seriously)
static const double SQR2 = std::sqrt(2.0); static const double SQR2 = std::sqrt(2.0);
@ -646,10 +654,8 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp,
if (normal_mode) { if (normal_mode) {
Pillar &plr = head_id >= 0 Pillar &plr = head_id >= 0
? m_builder.add_pillar(unsigned(head_id), ? m_builder.add_pillar(unsigned(head_id), endp, radius)
endp, : m_builder.add_pillar(jp, endp, radius);
radius)
: m_builder.add_pillar(jp, endp, radius);
if (can_add_base) if (can_add_base)
plr.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm); plr.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm);
@ -963,7 +969,7 @@ void SupportTreeBuildsteps::routing_to_model()
m_builder.add_bridge(hjp, endp, head.r_back_mm); m_builder.add_bridge(hjp, endp, head.r_back_mm);
m_builder.add_junction(endp, head.r_back_mm); m_builder.add_junction(endp, head.r_back_mm);
this->create_ground_pillar(endp, dir, head.r_back_mm); this->create_ground_pillar(endp, dir, head.r_back_mm, head.id);
}; };
std::vector<unsigned> modelpillars; std::vector<unsigned> modelpillars;
@ -1240,12 +1246,15 @@ void SupportTreeBuildsteps::interconnect_pillars()
needpillars = 3; needpillars = 3;
else if (pillar().links < 2 && pillar().height > H2) { else if (pillar().links < 2 && pillar().height > H2) {
// Not enough neighbors to support this pillar // Not enough neighbors to support this pillar
needpillars = 2 - pillar().links; needpillars = 2;
} else if (pillar().links < 1 && pillar().height > H1) { } else if (pillar().links < 1 && pillar().height > H1) {
// No neighbors could be found and the pillar is too long. // No neighbors could be found and the pillar is too long.
needpillars = 1; needpillars = 1;
} }
needpillars = std::max(pillar().links, needpillars) - pillar().links;
if (needpillars == 0) continue;
// Search for new pillar locations: // Search for new pillar locations:
bool found = false; bool found = false;
@ -1318,6 +1327,7 @@ void SupportTreeBuildsteps::interconnect_pillars()
newpills.emplace_back(pp.id); newpills.emplace_back(pp.id);
m_builder.increment_links(pillar()); m_builder.increment_links(pillar());
m_builder.increment_links(pp);
} }
} }

View File

@ -136,8 +136,8 @@ IntegerOnly<DoubleI> pairhash(I a, I b)
I g = min(a, b), l = max(a, b); I g = min(a, b), l = max(a, b);
// Assume the hash will fit into the output variable // Assume the hash will fit into the output variable
assert((g ? (ceil(log2(g))) : 0) < shift); assert((g ? (ceil(log2(g))) : 0) <= shift);
assert((l ? (ceil(log2(l))) : 0) < shift); assert((l ? (ceil(log2(l))) : 0) <= shift);
return (DoubleI(g) << shift) + l; return (DoubleI(g) << shift) + l;
} }
@ -176,6 +176,9 @@ class SupportTreeBuildsteps {
// A spatial index to easily find strong pillars to connect to. // A spatial index to easily find strong pillars to connect to.
PillarIndex m_pillar_index; PillarIndex m_pillar_index;
// When bridging heads to pillars... TODO: find a cleaner solution
ccr::BlockingMutex m_bridge_mutex;
inline double ray_mesh_intersect(const Vec3d& s, inline double ray_mesh_intersect(const Vec3d& s,
const Vec3d& dir) const Vec3d& dir)
{ {

View File

@ -5,6 +5,7 @@
#include <utility> #include <utility>
#include <functional> #include <functional>
#include <type_traits> #include <type_traits>
#include <system_error>
#include "libslic3r.h" #include "libslic3r.h"

View File

@ -7,7 +7,10 @@ set(TEST_DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data)
file(TO_NATIVE_PATH "${TEST_DATA_DIR}" TEST_DATA_DIR) file(TO_NATIVE_PATH "${TEST_DATA_DIR}" TEST_DATA_DIR)
add_library(test_common INTERFACE) add_library(test_common INTERFACE)
target_compile_definitions(test_common INTERFACE TEST_DATA_DIR="${TEST_DATA_DIR}") target_compile_definitions(test_common INTERFACE TEST_DATA_DIR=R"\(${TEST_DATA_DIR}\)")
target_link_libraries(test_common INTERFACE GTest::GTest GTest::Main) target_link_libraries(test_common INTERFACE GTest::GTest GTest::Main)
if (APPLE)
target_link_libraries(test_common INTERFACE "-liconv -framework IOKit" "-framework CoreFoundation" -lc++)
endif()
add_subdirectory(sla_print) add_subdirectory(sla_print)

View File

@ -1,4 +1,6 @@
#include <map> #include <unordered_set>
#include <unordered_map>
#include <random>
// Debug // Debug
#include <fstream> #include <fstream>
@ -19,9 +21,9 @@
#include "libslic3r/SVG.hpp" #include "libslic3r/SVG.hpp"
#if defined(WIN32) || defined(_WIN32) #if defined(WIN32) || defined(_WIN32)
#define PATH_SEPARATOR "\\" #define PATH_SEPARATOR R"(\)"
#else #else
#define PATH_SEPARATOR "/" #define PATH_SEPARATOR R"(/)"
#endif #endif
namespace { namespace {
@ -30,7 +32,7 @@ using namespace Slic3r;
TriangleMesh load_model(const std::string &obj_filename) TriangleMesh load_model(const std::string &obj_filename)
{ {
TriangleMesh mesh; TriangleMesh mesh;
auto fpath = std::string(TEST_DATA_DIR PATH_SEPARATOR) + obj_filename; auto fpath = TEST_DATA_DIR PATH_SEPARATOR + obj_filename;
load_obj(fpath.c_str(), &mesh); load_obj(fpath.c_str(), &mesh);
return mesh; return mesh;
} }
@ -123,6 +125,12 @@ void check_support_tree_integrity(const sla::SupportTreeBuilder &stree,
double H1 = cfg.max_solo_pillar_height_mm; double H1 = cfg.max_solo_pillar_height_mm;
double H2 = cfg.max_dual_pillar_height_mm; double H2 = cfg.max_dual_pillar_height_mm;
for (const sla::Head &head : stree.heads()) {
ASSERT_TRUE(!head.is_valid() ||
head.pillar_id != sla::ID_UNSET ||
head.bridge_id != sla::ID_UNSET);
}
for (const sla::Pillar &pillar : stree.pillars()) { for (const sla::Pillar &pillar : stree.pillars()) {
if (std::abs(pillar.endpoint().z() - gnd) < EPSILON) { if (std::abs(pillar.endpoint().z() - gnd) < EPSILON) {
double h = pillar.height; double h = pillar.height;
@ -144,7 +152,7 @@ void check_support_tree_integrity(const sla::SupportTreeBuilder &stree,
double z = n.z(); double z = n.z();
double polar = std::acos(z / d); double polar = std::acos(z / d);
double slope = -polar + PI / 2.; double slope = -polar + PI / 2.;
ASSERT_TRUE(slope >= cfg.bridge_slope || slope <= -cfg.bridge_slope); ASSERT_GE(std::abs(slope), cfg.bridge_slope - EPSILON);
}; };
for (auto &bridge : stree.bridges()) chck_bridge(bridge, max_bridgelen); for (auto &bridge : stree.bridges()) chck_bridge(bridge, max_bridgelen);
@ -283,25 +291,52 @@ const char *const SUPPORT_TEST_MODELS[] = {
} // namespace } // namespace
// Test pair hash for 'nums' random number pairs.
template <class I, class II> void test_pairhash() template <class I, class II> void test_pairhash()
{ {
std::map<II, std::pair<I, I> > ints; const constexpr size_t nums = 1000;
for (I i = 0; i < 1000; ++i) I A[nums] = {0}, B[nums] = {0};
for (I j = 0; j < 1000; ++j) { std::unordered_set<I> CH;
if (j != i) { std::unordered_map<II, std::pair<I, I>> ints;
II hash_ij = sla::pairhash<I, II>(i, j);
II hash_ji = sla::pairhash<I, II>(j, i);
ASSERT_EQ(hash_ij, hash_ji);
auto it = ints.find(hash_ij); std::random_device rd;
std::mt19937 gen(rd());
if (it != ints.end()) { const I Ibits = int(sizeof(I) * CHAR_BIT);
ASSERT_TRUE( const II IIbits = int(sizeof(II) * CHAR_BIT);
(it->second.first == i && it->second.second == j) || const int bits = IIbits / 2 < Ibits ? Ibits / 2 : Ibits;
(it->second.first == j && it->second.second == i));
} else ints[hash_ij] = std::make_pair(i, j); const I Imax = I(std::pow(2., bits) - 1);
} std::uniform_int_distribution<I> dis(0, Imax);
}
for (size_t i = 0; i < nums;) {
I a = dis(gen);
if (CH.find(a) == CH.end()) { CH.insert(a); A[i] = a; ++i; }
}
for (size_t i = 0; i < nums;) {
I b = dis(gen);
if (CH.find(b) == CH.end()) { CH.insert(b); B[i] = b; ++i; }
}
for (size_t i = 0; i < nums; ++i) {
I a = A[i], b = B[i];
ASSERT_TRUE(a != b);
II hash_ab = sla::pairhash<I, II>(a, b);
II hash_ba = sla::pairhash<I, II>(b, a);
ASSERT_EQ(hash_ab, hash_ba);
auto it = ints.find(hash_ab);
if (it != ints.end()) {
ASSERT_TRUE(
(it->second.first == a && it->second.second == b) ||
(it->second.first == b && it->second.second == a));
} else
ints[hash_ab] = std::make_pair(a, b);
}
} }
TEST(SLASupportGeneration, PillarPairHashShouldBeUnique) { TEST(SLASupportGeneration, PillarPairHashShouldBeUnique) {