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:
parent
be7428d66e
commit
8ca7e56d0f
@ -271,8 +271,6 @@ ConfigOptionDef* ConfigDef::add_nullable(const t_config_option_key &opt_key, Con
|
||||
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
|
||||
{
|
||||
// prepare a function for wrapping text
|
||||
|
@ -1444,7 +1444,7 @@ public:
|
||||
std::vector<std::string> cli_args(const std::string &key) const;
|
||||
|
||||
// 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.
|
||||
|
@ -310,20 +310,13 @@ void breakstick_holes(Points& pts,
|
||||
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,
|
||||
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);
|
||||
|
||||
ExPolygons ret = input;
|
||||
for (ExPolygon &p : ret) {
|
||||
breakstick_holes(p.contour.points, cfg);
|
||||
for (auto &h : p.holes) breakstick_holes(h.points, cfg);
|
||||
breakstick_holes(p.contour.points, args...);
|
||||
for (auto &h : p.holes) breakstick_holes(h.points, args...);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -428,7 +421,7 @@ class ConcaveHull {
|
||||
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()));
|
||||
offset(r, scaled(1.));
|
||||
offset(r, scaled<float>(1.));
|
||||
|
||||
m_polys.emplace_back(r);
|
||||
}
|
||||
@ -473,7 +466,7 @@ public:
|
||||
|
||||
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)
|
||||
@ -593,11 +586,19 @@ public:
|
||||
|
||||
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 =
|
||||
wafflized_concave_hull(support_blueprint, model_blueprint, cfg, thr);
|
||||
wafflized_concave_hull(support_blueprint, model_bp_offs, cfg, thr);
|
||||
|
||||
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);
|
||||
|
||||
@ -673,7 +674,7 @@ public:
|
||||
template<class...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 {};
|
||||
|
||||
@ -856,8 +857,12 @@ void create_pad(const ExPolygons &sup_blueprint,
|
||||
|
||||
std::string PadConfig::validate() const
|
||||
{
|
||||
if (bottom_offset() > brim_size_mm + wing_distance())
|
||||
return L("Pad brim size is too low for the current slope.");
|
||||
static const double constexpr MIN_BRIM_SIZE_MM = .1;
|
||||
|
||||
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 "";
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -15,8 +15,6 @@ namespace ClipperLib { struct Polygon; }
|
||||
namespace Slic3r {
|
||||
namespace sla {
|
||||
|
||||
class Raster;
|
||||
|
||||
/**
|
||||
* @brief Raster captures an anti-aliased monochrome canvas where vectorial
|
||||
* polygons can be rasterized. Fill color is always white and the background is
|
||||
|
@ -70,7 +70,7 @@ std::vector<ExPolygons> SupportTree::slice(
|
||||
auto bb = pad_mesh.bounding_box();
|
||||
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));
|
||||
std::copy(grid.begin(), maxzit, std::back_inserter(padgrid));
|
||||
|
||||
|
@ -96,6 +96,8 @@ struct Head {
|
||||
// If there is a pillar connecting to this head, then the id will be set.
|
||||
long pillar_id = ID_UNSET;
|
||||
|
||||
long bridge_id = ID_UNSET;
|
||||
|
||||
inline void invalidate() { id = ID_UNSET; }
|
||||
inline bool is_valid() const { return id >= 0; }
|
||||
|
||||
@ -335,6 +337,12 @@ public:
|
||||
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)
|
||||
{
|
||||
std::lock_guard<Mutex> lk(m_mutex);
|
||||
|
@ -444,7 +444,8 @@ bool SupportTreeBuildsteps::connect_to_nearpillar(const Head &head,
|
||||
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 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)
|
||||
if(t < distance(bridgestart, bridgeend)) return false;
|
||||
|
||||
std::lock_guard<ccr::BlockingMutex> lk(m_bridge_mutex);
|
||||
|
||||
if (m_builder.bridgecount(nearpillar()) < m_cfg.max_bridges_on_pillar) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
m_builder.add_bridge(bridgestart, bridgeend, 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;
|
||||
}
|
||||
@ -646,9 +654,7 @@ void SupportTreeBuildsteps::create_ground_pillar(const Vec3d &jp,
|
||||
|
||||
if (normal_mode) {
|
||||
Pillar &plr = head_id >= 0
|
||||
? m_builder.add_pillar(unsigned(head_id),
|
||||
endp,
|
||||
radius)
|
||||
? m_builder.add_pillar(unsigned(head_id), endp, radius)
|
||||
: m_builder.add_pillar(jp, endp, radius);
|
||||
|
||||
if (can_add_base)
|
||||
@ -963,7 +969,7 @@ void SupportTreeBuildsteps::routing_to_model()
|
||||
m_builder.add_bridge(hjp, 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;
|
||||
@ -1240,12 +1246,15 @@ void SupportTreeBuildsteps::interconnect_pillars()
|
||||
needpillars = 3;
|
||||
else if (pillar().links < 2 && pillar().height > H2) {
|
||||
// Not enough neighbors to support this pillar
|
||||
needpillars = 2 - pillar().links;
|
||||
needpillars = 2;
|
||||
} else if (pillar().links < 1 && pillar().height > H1) {
|
||||
// No neighbors could be found and the pillar is too long.
|
||||
needpillars = 1;
|
||||
}
|
||||
|
||||
needpillars = std::max(pillar().links, needpillars) - pillar().links;
|
||||
if (needpillars == 0) continue;
|
||||
|
||||
// Search for new pillar locations:
|
||||
|
||||
bool found = false;
|
||||
@ -1318,6 +1327,7 @@ void SupportTreeBuildsteps::interconnect_pillars()
|
||||
|
||||
newpills.emplace_back(pp.id);
|
||||
m_builder.increment_links(pillar());
|
||||
m_builder.increment_links(pp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,8 +136,8 @@ IntegerOnly<DoubleI> pairhash(I a, I b)
|
||||
I g = min(a, b), l = max(a, b);
|
||||
|
||||
// Assume the hash will fit into the output variable
|
||||
assert((g ? (ceil(log2(g))) : 0) < shift);
|
||||
assert((l ? (ceil(log2(l))) : 0) < shift);
|
||||
assert((g ? (ceil(log2(g))) : 0) <= shift);
|
||||
assert((l ? (ceil(log2(l))) : 0) <= shift);
|
||||
|
||||
return (DoubleI(g) << shift) + l;
|
||||
}
|
||||
@ -176,6 +176,9 @@ class SupportTreeBuildsteps {
|
||||
// A spatial index to easily find strong pillars to connect to.
|
||||
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,
|
||||
const Vec3d& dir)
|
||||
{
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <system_error>
|
||||
|
||||
#include "libslic3r.h"
|
||||
|
||||
|
@ -7,7 +7,10 @@ set(TEST_DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data)
|
||||
file(TO_NATIVE_PATH "${TEST_DATA_DIR}" TEST_DATA_DIR)
|
||||
|
||||
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)
|
||||
if (APPLE)
|
||||
target_link_libraries(test_common INTERFACE "-liconv -framework IOKit" "-framework CoreFoundation" -lc++)
|
||||
endif()
|
||||
|
||||
add_subdirectory(sla_print)
|
||||
|
@ -1,4 +1,6 @@
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <random>
|
||||
|
||||
// Debug
|
||||
#include <fstream>
|
||||
@ -19,9 +21,9 @@
|
||||
#include "libslic3r/SVG.hpp"
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
#define PATH_SEPARATOR "\\"
|
||||
#define PATH_SEPARATOR R"(\)"
|
||||
#else
|
||||
#define PATH_SEPARATOR "/"
|
||||
#define PATH_SEPARATOR R"(/)"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
@ -30,7 +32,7 @@ using namespace Slic3r;
|
||||
TriangleMesh load_model(const std::string &obj_filename)
|
||||
{
|
||||
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);
|
||||
return mesh;
|
||||
}
|
||||
@ -123,6 +125,12 @@ void check_support_tree_integrity(const sla::SupportTreeBuilder &stree,
|
||||
double H1 = cfg.max_solo_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()) {
|
||||
if (std::abs(pillar.endpoint().z() - gnd) < EPSILON) {
|
||||
double h = pillar.height;
|
||||
@ -144,7 +152,7 @@ void check_support_tree_integrity(const sla::SupportTreeBuilder &stree,
|
||||
double z = n.z();
|
||||
double polar = std::acos(z / d);
|
||||
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);
|
||||
@ -283,24 +291,51 @@ const char *const SUPPORT_TEST_MODELS[] = {
|
||||
|
||||
} // namespace
|
||||
|
||||
// Test pair hash for 'nums' random number pairs.
|
||||
template <class I, class II> void test_pairhash()
|
||||
{
|
||||
std::map<II, std::pair<I, I> > ints;
|
||||
for (I i = 0; i < 1000; ++i)
|
||||
for (I j = 0; j < 1000; ++j) {
|
||||
if (j != i) {
|
||||
II hash_ij = sla::pairhash<I, II>(i, j);
|
||||
II hash_ji = sla::pairhash<I, II>(j, i);
|
||||
ASSERT_EQ(hash_ij, hash_ji);
|
||||
const constexpr size_t nums = 1000;
|
||||
I A[nums] = {0}, B[nums] = {0};
|
||||
std::unordered_set<I> CH;
|
||||
std::unordered_map<II, std::pair<I, I>> ints;
|
||||
|
||||
auto it = ints.find(hash_ij);
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
|
||||
const I Ibits = int(sizeof(I) * CHAR_BIT);
|
||||
const II IIbits = int(sizeof(II) * CHAR_BIT);
|
||||
const int bits = IIbits / 2 < Ibits ? Ibits / 2 : Ibits;
|
||||
|
||||
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 == i && it->second.second == j) ||
|
||||
(it->second.first == j && it->second.second == i));
|
||||
} else ints[hash_ij] = std::make_pair(i, j);
|
||||
}
|
||||
(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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user