Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_3dconnexion
This commit is contained in:
commit
e773f667b1
37 changed files with 1721 additions and 715 deletions
|
@ -173,10 +173,11 @@ if (NOT MSVC AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMP
|
||||||
# On GCC and Clang, no return from a non-void function is a warning only. Here, we make it an error.
|
# On GCC and Clang, no return from a non-void function is a warning only. Here, we make it an error.
|
||||||
add_compile_options(-Werror=return-type)
|
add_compile_options(-Werror=return-type)
|
||||||
|
|
||||||
#removes LOTS of extraneous Eigen warnings (GCC only supports it since 6.1)
|
# removes LOTS of extraneous Eigen warnings (GCC only supports it since 6.1)
|
||||||
#if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 6.1)
|
# https://eigen.tuxfamily.org/bz/show_bug.cgi?id=1221
|
||||||
# add_compile_options(-Wno-ignored-attributes) # Tamas: Eigen include dirs are marked as SYSTEM
|
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6.0)
|
||||||
#endif()
|
add_compile_options(-Wno-ignored-attributes) # Tamas: Eigen include dirs are marked as SYSTEM
|
||||||
|
endif()
|
||||||
|
|
||||||
#GCC generates loads of -Wunknown-pragmas when compiling igl. The fix is not easy due to a bug in gcc, see
|
#GCC generates loads of -Wunknown-pragmas when compiling igl. The fix is not easy due to a bug in gcc, see
|
||||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66943 or
|
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66943 or
|
||||||
|
|
|
@ -260,6 +260,10 @@ if(NOT TBB_FOUND)
|
||||||
if(TBB_LIBRARIES_${TBB_BUILD_TYPE})
|
if(TBB_LIBRARIES_${TBB_BUILD_TYPE})
|
||||||
set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}")
|
set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(NOT MSVC AND NOT TBB_LIBRARIES)
|
||||||
|
set(TBB_LIBRARIES ${TBB_LIBRARIES_RELEASE})
|
||||||
|
endif()
|
||||||
|
|
||||||
if (MSVC AND TBB_STATIC)
|
if (MSVC AND TBB_STATIC)
|
||||||
set(TBB_DEFINITIONS __TBB_NO_IMPLICIT_LINKAGE)
|
set(TBB_DEFINITIONS __TBB_NO_IMPLICIT_LINKAGE)
|
||||||
|
|
|
@ -425,7 +425,30 @@ std::string ConfigBase::opt_serialize(const t_config_option_key &opt_key) const
|
||||||
return opt->serialize();
|
return opt->serialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConfigBase::set_deserialize(const t_config_option_key &opt_key_src, const std::string &value_src, bool append)
|
void ConfigBase::set(const std::string &opt_key, int value, bool create)
|
||||||
|
{
|
||||||
|
ConfigOption *opt = this->option_throw(opt_key, create);
|
||||||
|
switch (opt->type()) {
|
||||||
|
case coInt: static_cast<ConfigOptionInt*>(opt)->value = value; break;
|
||||||
|
case coFloat: static_cast<ConfigOptionFloat*>(opt)->value = value; break;
|
||||||
|
case coFloatOrPercent: static_cast<ConfigOptionFloatOrPercent*>(opt)->value = value; static_cast<ConfigOptionFloatOrPercent*>(opt)->percent = false; break;
|
||||||
|
case coString: static_cast<ConfigOptionString*>(opt)->value = std::to_string(value); break;
|
||||||
|
default: throw BadOptionTypeException("Configbase::set() - conversion from int not possible");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigBase::set(const std::string &opt_key, double value, bool create)
|
||||||
|
{
|
||||||
|
ConfigOption *opt = this->option_throw(opt_key, create);
|
||||||
|
switch (opt->type()) {
|
||||||
|
case coFloat: static_cast<ConfigOptionFloat*>(opt)->value = value; break;
|
||||||
|
case coFloatOrPercent: static_cast<ConfigOptionFloatOrPercent*>(opt)->value = value; static_cast<ConfigOptionFloatOrPercent*>(opt)->percent = false; break;
|
||||||
|
case coString: static_cast<ConfigOptionString*>(opt)->value = std::to_string(value); break;
|
||||||
|
default: throw BadOptionTypeException("Configbase::set() - conversion from float not possible");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigBase::set_deserialize_nothrow(const t_config_option_key &opt_key_src, const std::string &value_src, bool append)
|
||||||
{
|
{
|
||||||
t_config_option_key opt_key = opt_key_src;
|
t_config_option_key opt_key = opt_key_src;
|
||||||
std::string value = value_src;
|
std::string value = value_src;
|
||||||
|
@ -438,6 +461,18 @@ bool ConfigBase::set_deserialize(const t_config_option_key &opt_key_src, const s
|
||||||
return this->set_deserialize_raw(opt_key, value, append);
|
return this->set_deserialize_raw(opt_key, value, append);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigBase::set_deserialize(const t_config_option_key &opt_key_src, const std::string &value_src, bool append)
|
||||||
|
{
|
||||||
|
if (! this->set_deserialize_nothrow(opt_key_src, value_src, append))
|
||||||
|
throw BadOptionTypeException("ConfigBase::set_deserialize() failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigBase::set_deserialize(std::initializer_list<SetDeserializeItem> items)
|
||||||
|
{
|
||||||
|
for (const SetDeserializeItem &item : items)
|
||||||
|
this->set_deserialize(item.opt_key, item.opt_value, item.append);
|
||||||
|
}
|
||||||
|
|
||||||
bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, const std::string &value, bool append)
|
bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, const std::string &value, bool append)
|
||||||
{
|
{
|
||||||
t_config_option_key opt_key = opt_key_src;
|
t_config_option_key opt_key = opt_key_src;
|
||||||
|
@ -823,7 +858,7 @@ bool DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra,
|
||||||
static_cast<ConfigOptionString*>(opt_base)->value = value;
|
static_cast<ConfigOptionString*>(opt_base)->value = value;
|
||||||
} else {
|
} else {
|
||||||
// Any scalar value of a type different from Bool and String.
|
// Any scalar value of a type different from Bool and String.
|
||||||
if (! this->set_deserialize(opt_key, value, false)) {
|
if (! this->set_deserialize_nothrow(opt_key, value, false)) {
|
||||||
boost::nowide::cerr << "Invalid value supplied for --" << token.c_str() << std::endl;
|
boost::nowide::cerr << "Invalid value supplied for --" << token.c_str() << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,16 @@ public:
|
||||||
std::runtime_error(std::string("No definition exception: ") + opt_key) {}
|
std::runtime_error(std::string("No definition exception: ") + opt_key) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Indicate that an unsupported accessor was called on a config option.
|
||||||
|
class BadOptionTypeException : public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BadOptionTypeException() :
|
||||||
|
std::runtime_error("Bad option type exception") {}
|
||||||
|
BadOptionTypeException(const char* message) :
|
||||||
|
std::runtime_error(message) {}
|
||||||
|
};
|
||||||
|
|
||||||
// Type of a configuration value.
|
// Type of a configuration value.
|
||||||
enum ConfigOptionType {
|
enum ConfigOptionType {
|
||||||
coVectorType = 0x4000,
|
coVectorType = 0x4000,
|
||||||
|
@ -117,10 +127,10 @@ public:
|
||||||
virtual ConfigOption* clone() const = 0;
|
virtual ConfigOption* clone() const = 0;
|
||||||
// Set a value from a ConfigOption. The two options should be compatible.
|
// Set a value from a ConfigOption. The two options should be compatible.
|
||||||
virtual void set(const ConfigOption *option) = 0;
|
virtual void set(const ConfigOption *option) = 0;
|
||||||
virtual int getInt() const { throw std::runtime_error("Calling ConfigOption::getInt on a non-int ConfigOption"); }
|
virtual int getInt() const { throw BadOptionTypeException("Calling ConfigOption::getInt on a non-int ConfigOption"); }
|
||||||
virtual double getFloat() const { throw std::runtime_error("Calling ConfigOption::getFloat on a non-float ConfigOption"); }
|
virtual double getFloat() const { throw BadOptionTypeException("Calling ConfigOption::getFloat on a non-float ConfigOption"); }
|
||||||
virtual bool getBool() const { throw std::runtime_error("Calling ConfigOption::getBool on a non-boolean ConfigOption"); }
|
virtual bool getBool() const { throw BadOptionTypeException("Calling ConfigOption::getBool on a non-boolean ConfigOption"); }
|
||||||
virtual void setInt(int /* val */) { throw std::runtime_error("Calling ConfigOption::setInt on a non-int ConfigOption"); }
|
virtual void setInt(int /* val */) { throw BadOptionTypeException("Calling ConfigOption::setInt on a non-int ConfigOption"); }
|
||||||
virtual bool operator==(const ConfigOption &rhs) const = 0;
|
virtual bool operator==(const ConfigOption &rhs) const = 0;
|
||||||
bool operator!=(const ConfigOption &rhs) const { return ! (*this == rhs); }
|
bool operator!=(const ConfigOption &rhs) const { return ! (*this == rhs); }
|
||||||
bool is_scalar() const { return (int(this->type()) & int(coVectorType)) == 0; }
|
bool is_scalar() const { return (int(this->type()) & int(coVectorType)) == 0; }
|
||||||
|
@ -1513,32 +1523,48 @@ protected:
|
||||||
public:
|
public:
|
||||||
// Non-virtual methods:
|
// Non-virtual methods:
|
||||||
bool has(const t_config_option_key &opt_key) const { return this->option(opt_key) != nullptr; }
|
bool has(const t_config_option_key &opt_key) const { return this->option(opt_key) != nullptr; }
|
||||||
|
|
||||||
const ConfigOption* option(const t_config_option_key &opt_key) const
|
const ConfigOption* option(const t_config_option_key &opt_key) const
|
||||||
{ return const_cast<ConfigBase*>(this)->option(opt_key, false); }
|
{ return const_cast<ConfigBase*>(this)->option(opt_key, false); }
|
||||||
|
|
||||||
ConfigOption* option(const t_config_option_key &opt_key, bool create = false)
|
ConfigOption* option(const t_config_option_key &opt_key, bool create = false)
|
||||||
{ return this->optptr(opt_key, create); }
|
{ return this->optptr(opt_key, create); }
|
||||||
|
|
||||||
template<typename TYPE>
|
template<typename TYPE>
|
||||||
TYPE* option(const t_config_option_key &opt_key, bool create = false)
|
TYPE* option(const t_config_option_key &opt_key, bool create = false)
|
||||||
{
|
{
|
||||||
ConfigOption *opt = this->optptr(opt_key, create);
|
ConfigOption *opt = this->optptr(opt_key, create);
|
||||||
return (opt == nullptr || opt->type() != TYPE::static_type()) ? nullptr : static_cast<TYPE*>(opt);
|
return (opt == nullptr || opt->type() != TYPE::static_type()) ? nullptr : static_cast<TYPE*>(opt);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TYPE>
|
template<typename TYPE>
|
||||||
const TYPE* option(const t_config_option_key &opt_key) const
|
const TYPE* option(const t_config_option_key &opt_key) const
|
||||||
{ return const_cast<ConfigBase*>(this)->option<TYPE>(opt_key, false); }
|
{ return const_cast<ConfigBase*>(this)->option<TYPE>(opt_key, false); }
|
||||||
template<typename TYPE>
|
|
||||||
TYPE* option_throw(const t_config_option_key &opt_key, bool create = false)
|
ConfigOption* option_throw(const t_config_option_key &opt_key, bool create = false)
|
||||||
{
|
{
|
||||||
ConfigOption *opt = this->optptr(opt_key, create);
|
ConfigOption *opt = this->optptr(opt_key, create);
|
||||||
if (opt == nullptr)
|
if (opt == nullptr)
|
||||||
throw UnknownOptionException(opt_key);
|
throw UnknownOptionException(opt_key);
|
||||||
|
return opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConfigOption* option_throw(const t_config_option_key &opt_key) const
|
||||||
|
{ return const_cast<ConfigBase*>(this)->option_throw(opt_key, false); }
|
||||||
|
|
||||||
|
template<typename TYPE>
|
||||||
|
TYPE* option_throw(const t_config_option_key &opt_key, bool create = false)
|
||||||
|
{
|
||||||
|
ConfigOption *opt = this->option_throw(opt_key, create);
|
||||||
if (opt->type() != TYPE::static_type())
|
if (opt->type() != TYPE::static_type())
|
||||||
throw std::runtime_error("Conversion to a wrong type");
|
throw BadOptionTypeException("Conversion to a wrong type");
|
||||||
return static_cast<TYPE*>(opt);
|
return static_cast<TYPE*>(opt);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TYPE>
|
template<typename TYPE>
|
||||||
const TYPE* option_throw(const t_config_option_key &opt_key) const
|
const TYPE* option_throw(const t_config_option_key &opt_key) const
|
||||||
{ return const_cast<ConfigBase*>(this)->option_throw<TYPE>(opt_key, false); }
|
{ return const_cast<ConfigBase*>(this)->option_throw<TYPE>(opt_key, false); }
|
||||||
|
|
||||||
// Apply all keys of other ConfigBase defined by this->def() to this ConfigBase.
|
// Apply all keys of other ConfigBase defined by this->def() to this ConfigBase.
|
||||||
// An UnknownOptionException is thrown in case some option keys of other are not defined by this->def(),
|
// An UnknownOptionException is thrown in case some option keys of other are not defined by this->def(),
|
||||||
// or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set.
|
// or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set.
|
||||||
|
@ -1551,9 +1577,40 @@ public:
|
||||||
t_config_option_keys diff(const ConfigBase &other) const;
|
t_config_option_keys diff(const ConfigBase &other) const;
|
||||||
t_config_option_keys equal(const ConfigBase &other) const;
|
t_config_option_keys equal(const ConfigBase &other) const;
|
||||||
std::string opt_serialize(const t_config_option_key &opt_key) const;
|
std::string opt_serialize(const t_config_option_key &opt_key) const;
|
||||||
|
|
||||||
|
// Set a value. Convert numeric types using a C style implicit conversion / promotion model.
|
||||||
|
// Throw if option is not avaiable and create is not enabled,
|
||||||
|
// or if the conversion is not possible.
|
||||||
|
// Conversion to string is always possible.
|
||||||
|
void set(const std::string &opt_key, bool value, bool create = false)
|
||||||
|
{ this->option_throw<ConfigOptionBool>(opt_key, create)->value = value; }
|
||||||
|
void set(const std::string &opt_key, int value, bool create = false);
|
||||||
|
void set(const std::string &opt_key, double value, bool create = false);
|
||||||
|
void set(const std::string &opt_key, const char *value, bool create = false)
|
||||||
|
{ this->option_throw<ConfigOptionString>(opt_key, create)->value = value; }
|
||||||
|
void set(const std::string &opt_key, const std::string &value, bool create = false)
|
||||||
|
{ this->option_throw<ConfigOptionString>(opt_key, create)->value = value; }
|
||||||
|
|
||||||
// Set a configuration value from a string, it will call an overridable handle_legacy()
|
// Set a configuration value from a string, it will call an overridable handle_legacy()
|
||||||
// to resolve renamed and removed configuration keys.
|
// to resolve renamed and removed configuration keys.
|
||||||
bool set_deserialize(const t_config_option_key &opt_key, const std::string &str, bool append = false);
|
bool set_deserialize_nothrow(const t_config_option_key &opt_key_src, const std::string &value_src, bool append = false);
|
||||||
|
// May throw BadOptionTypeException() if the operation fails.
|
||||||
|
void set_deserialize(const t_config_option_key &opt_key, const std::string &str, bool append = false);
|
||||||
|
struct SetDeserializeItem {
|
||||||
|
SetDeserializeItem(const char *opt_key, const char *opt_value, bool append = false) : opt_key(opt_key), opt_value(opt_value), append(append) {}
|
||||||
|
SetDeserializeItem(const std::string &opt_key, const std::string &opt_value, bool append = false) : opt_key(opt_key), opt_value(opt_value), append(append) {}
|
||||||
|
SetDeserializeItem(const char *opt_key, const bool value, bool append = false) : opt_key(opt_key), opt_value(value ? "1" : "0"), append(append) {}
|
||||||
|
SetDeserializeItem(const std::string &opt_key, const bool value, bool append = false) : opt_key(opt_key), opt_value(value ? "1" : "0"), append(append) {}
|
||||||
|
SetDeserializeItem(const char *opt_key, const int value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
|
||||||
|
SetDeserializeItem(const std::string &opt_key, const int value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
|
||||||
|
SetDeserializeItem(const char *opt_key, const float value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
|
||||||
|
SetDeserializeItem(const std::string &opt_key, const float value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
|
||||||
|
SetDeserializeItem(const char *opt_key, const double value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
|
||||||
|
SetDeserializeItem(const std::string &opt_key, const double value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
|
||||||
|
std::string opt_key; std::string opt_value; bool append = false;
|
||||||
|
};
|
||||||
|
// May throw BadOptionTypeException() if the operation fails.
|
||||||
|
void set_deserialize(std::initializer_list<SetDeserializeItem> items);
|
||||||
|
|
||||||
double get_abs_value(const t_config_option_key &opt_key) const;
|
double get_abs_value(const t_config_option_key &opt_key) const;
|
||||||
double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const;
|
double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const;
|
||||||
|
|
|
@ -126,18 +126,26 @@ size_t ExtrusionEntityCollection::items_count() const
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a single vector of pointers to all non-collection items contained in this one.
|
// Returns a single vector of pointers to all non-collection items contained in this one.
|
||||||
ExtrusionEntityCollection ExtrusionEntityCollection::flatten() const
|
ExtrusionEntityCollection ExtrusionEntityCollection::flatten(bool preserve_ordering) const
|
||||||
{
|
{
|
||||||
struct Flatten {
|
struct Flatten {
|
||||||
|
Flatten(bool preserve_ordering) : preserve_ordering(preserve_ordering) {}
|
||||||
ExtrusionEntityCollection out;
|
ExtrusionEntityCollection out;
|
||||||
|
bool preserve_ordering;
|
||||||
void recursive_do(const ExtrusionEntityCollection &collection) {
|
void recursive_do(const ExtrusionEntityCollection &collection) {
|
||||||
for (const ExtrusionEntity* entity : collection.entities)
|
if (collection.no_sort && preserve_ordering) {
|
||||||
if (entity->is_collection())
|
// Don't flatten whatever happens below this level.
|
||||||
this->recursive_do(*static_cast<const ExtrusionEntityCollection*>(entity));
|
out.append(collection);
|
||||||
else
|
} else {
|
||||||
out.append(*entity);
|
for (const ExtrusionEntity *entity : collection.entities)
|
||||||
|
if (entity->is_collection())
|
||||||
|
this->recursive_do(*static_cast<const ExtrusionEntityCollection*>(entity));
|
||||||
|
else
|
||||||
|
out.append(*entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} flatten;
|
} flatten(preserve_ordering);
|
||||||
|
|
||||||
flatten.recursive_do(*this);
|
flatten.recursive_do(*this);
|
||||||
return flatten.out;
|
return flatten.out;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,17 +15,17 @@ public:
|
||||||
|
|
||||||
ExtrusionEntitiesPtr entities; // we own these entities
|
ExtrusionEntitiesPtr entities; // we own these entities
|
||||||
bool no_sort;
|
bool no_sort;
|
||||||
ExtrusionEntityCollection(): no_sort(false) {};
|
ExtrusionEntityCollection(): no_sort(false) {}
|
||||||
ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : no_sort(other.no_sort) { this->append(other.entities); }
|
ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : no_sort(other.no_sort) { this->append(other.entities); }
|
||||||
ExtrusionEntityCollection(ExtrusionEntityCollection &&other) : entities(std::move(other.entities)), no_sort(other.no_sort) {}
|
ExtrusionEntityCollection(ExtrusionEntityCollection &&other) : entities(std::move(other.entities)), no_sort(other.no_sort) {}
|
||||||
explicit ExtrusionEntityCollection(const ExtrusionPaths &paths);
|
explicit ExtrusionEntityCollection(const ExtrusionPaths &paths);
|
||||||
ExtrusionEntityCollection& operator=(const ExtrusionEntityCollection &other);
|
ExtrusionEntityCollection& operator=(const ExtrusionEntityCollection &other);
|
||||||
ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other)
|
ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other)
|
||||||
{ this->entities = std::move(other.entities); this->no_sort = other.no_sort; return *this; }
|
{ this->entities = std::move(other.entities); this->no_sort = other.no_sort; return *this; }
|
||||||
~ExtrusionEntityCollection() { clear(); }
|
~ExtrusionEntityCollection() { clear(); }
|
||||||
explicit operator ExtrusionPaths() const;
|
explicit operator ExtrusionPaths() const;
|
||||||
|
|
||||||
bool is_collection() const { return true; };
|
bool is_collection() const { return true; }
|
||||||
ExtrusionRole role() const override {
|
ExtrusionRole role() const override {
|
||||||
ExtrusionRole out = erNone;
|
ExtrusionRole out = erNone;
|
||||||
for (const ExtrusionEntity *ee : entities) {
|
for (const ExtrusionEntity *ee : entities) {
|
||||||
|
@ -34,8 +34,8 @@ public:
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
bool can_reverse() const { return !this->no_sort; };
|
bool can_reverse() const { return !this->no_sort; }
|
||||||
bool empty() const { return this->entities.empty(); };
|
bool empty() const { return this->entities.empty(); }
|
||||||
void clear();
|
void clear();
|
||||||
void swap (ExtrusionEntityCollection &c);
|
void swap (ExtrusionEntityCollection &c);
|
||||||
void append(const ExtrusionEntity &entity) { this->entities.emplace_back(entity.clone()); }
|
void append(const ExtrusionEntity &entity) { this->entities.emplace_back(entity.clone()); }
|
||||||
|
@ -81,7 +81,10 @@ public:
|
||||||
Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const
|
Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const
|
||||||
{ Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; }
|
{ Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; }
|
||||||
size_t items_count() const;
|
size_t items_count() const;
|
||||||
ExtrusionEntityCollection flatten() const;
|
/// Returns a flattened copy of this ExtrusionEntityCollection. That is, all of the items in its entities vector are not collections.
|
||||||
|
/// You should be iterating over flatten().entities if you are interested in the underlying ExtrusionEntities (and don't care about hierarchy).
|
||||||
|
/// \param preserve_ordering Flag to method that will flatten if and only if the underlying collection is sortable when True (default: False).
|
||||||
|
ExtrusionEntityCollection flatten(bool preserve_ordering = false) const;
|
||||||
double min_mm3_per_mm() const;
|
double min_mm3_per_mm() const;
|
||||||
double total_volume() const override { double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
|
double total_volume() const override { double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
|
||||||
|
|
||||||
|
|
|
@ -584,8 +584,16 @@ void AMFParserContext::endElement(const char * /* name */)
|
||||||
stl_get_size(&stl);
|
stl_get_size(&stl);
|
||||||
mesh.repair();
|
mesh.repair();
|
||||||
m_volume->set_mesh(std::move(mesh));
|
m_volume->set_mesh(std::move(mesh));
|
||||||
// pass false if the mesh offset has been already taken from the data
|
if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART))
|
||||||
m_volume->center_geometry_after_creation(m_volume->source.input_file.empty());
|
{
|
||||||
|
m_volume->source.object_idx = (int)m_model.objects.size() - 1;
|
||||||
|
m_volume->source.volume_idx = (int)m_model.objects.back()->volumes.size() - 1;
|
||||||
|
m_volume->center_geometry_after_creation();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// pass false if the mesh offset has been already taken from the data
|
||||||
|
m_volume->center_geometry_after_creation(m_volume->source.input_file.empty());
|
||||||
|
|
||||||
m_volume->calculate_convex_hull();
|
m_volume->calculate_convex_hull();
|
||||||
m_volume_facets.clear();
|
m_volume_facets.clear();
|
||||||
m_volume = nullptr;
|
m_volume = nullptr;
|
||||||
|
@ -799,6 +807,15 @@ bool load_amf_file(const char *path, DynamicPrintConfig *config, Model *model)
|
||||||
if (result)
|
if (result)
|
||||||
ctx.endDocument();
|
ctx.endDocument();
|
||||||
|
|
||||||
|
for (ModelObject* o : model->objects)
|
||||||
|
{
|
||||||
|
for (ModelVolume* v : o->volumes)
|
||||||
|
{
|
||||||
|
if (v->source.input_file.empty() && (v->type() == ModelVolumeType::MODEL_PART))
|
||||||
|
v->source.input_file = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,6 +141,7 @@ void GCodeAnalyzer::reset()
|
||||||
_set_start_extrusion(DEFAULT_START_EXTRUSION);
|
_set_start_extrusion(DEFAULT_START_EXTRUSION);
|
||||||
_set_fan_speed(DEFAULT_FAN_SPEED);
|
_set_fan_speed(DEFAULT_FAN_SPEED);
|
||||||
_reset_axes_position();
|
_reset_axes_position();
|
||||||
|
_reset_axes_origin();
|
||||||
_reset_cached_position();
|
_reset_cached_position();
|
||||||
|
|
||||||
m_moves_map.clear();
|
m_moves_map.clear();
|
||||||
|
@ -284,6 +285,11 @@ void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLi
|
||||||
_processM108orM135(line);
|
_processM108orM135(line);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 132: // Recall stored home offsets
|
||||||
|
{
|
||||||
|
_processM132(line);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 401: // Repetier: Store x, y and z position
|
case 401: // Repetier: Store x, y and z position
|
||||||
{
|
{
|
||||||
_processM401(line);
|
_processM401(line);
|
||||||
|
@ -310,31 +316,32 @@ void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLi
|
||||||
m_process_output += line.raw() + "\n";
|
m_process_output += line.raw() + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the new absolute position on the given axis in dependence of the given parameters
|
|
||||||
float axis_absolute_position_from_G1_line(GCodeAnalyzer::EAxis axis, const GCodeReader::GCodeLine& lineG1, GCodeAnalyzer::EUnits units, bool is_relative, float current_absolute_position)
|
|
||||||
{
|
|
||||||
float lengthsScaleFactor = (units == GCodeAnalyzer::Inches) ? INCHES_TO_MM : 1.0f;
|
|
||||||
if (lineG1.has(Slic3r::Axis(axis)))
|
|
||||||
{
|
|
||||||
float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor;
|
|
||||||
return is_relative ? current_absolute_position + ret : ret;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return current_absolute_position;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GCodeAnalyzer::_processG1(const GCodeReader::GCodeLine& line)
|
void GCodeAnalyzer::_processG1(const GCodeReader::GCodeLine& line)
|
||||||
{
|
{
|
||||||
|
auto axis_absolute_position = [this](GCodeAnalyzer::EAxis axis, const GCodeReader::GCodeLine& lineG1) -> float
|
||||||
|
{
|
||||||
|
float current_absolute_position = _get_axis_position(axis);
|
||||||
|
float current_origin = _get_axis_origin(axis);
|
||||||
|
float lengthsScaleFactor = (_get_units() == GCodeAnalyzer::Inches) ? INCHES_TO_MM : 1.0f;
|
||||||
|
|
||||||
|
bool is_relative = (_get_global_positioning_type() == Relative);
|
||||||
|
if (axis == E)
|
||||||
|
is_relative |= (_get_e_local_positioning_type() == Relative);
|
||||||
|
|
||||||
|
if (lineG1.has(Slic3r::Axis(axis)))
|
||||||
|
{
|
||||||
|
float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor;
|
||||||
|
return is_relative ? current_absolute_position + ret : ret + current_origin;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return current_absolute_position;
|
||||||
|
};
|
||||||
|
|
||||||
// updates axes positions from line
|
// updates axes positions from line
|
||||||
EUnits units = _get_units();
|
|
||||||
float new_pos[Num_Axis];
|
float new_pos[Num_Axis];
|
||||||
for (unsigned char a = X; a < Num_Axis; ++a)
|
for (unsigned char a = X; a < Num_Axis; ++a)
|
||||||
{
|
{
|
||||||
bool is_relative = (_get_global_positioning_type() == Relative);
|
new_pos[a] = axis_absolute_position((EAxis)a, line);
|
||||||
if (a == E)
|
|
||||||
is_relative |= (_get_e_local_positioning_type() == Relative);
|
|
||||||
|
|
||||||
new_pos[a] = axis_absolute_position_from_G1_line((EAxis)a, line, units, is_relative, _get_axis_position((EAxis)a));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// updates feedrate from line, if present
|
// updates feedrate from line, if present
|
||||||
|
@ -424,25 +431,25 @@ void GCodeAnalyzer::_processG92(const GCodeReader::GCodeLine& line)
|
||||||
|
|
||||||
if (line.has_x())
|
if (line.has_x())
|
||||||
{
|
{
|
||||||
_set_axis_position(X, line.x() * lengthsScaleFactor);
|
_set_axis_origin(X, _get_axis_position(X) - line.x() * lengthsScaleFactor);
|
||||||
anyFound = true;
|
anyFound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.has_y())
|
if (line.has_y())
|
||||||
{
|
{
|
||||||
_set_axis_position(Y, line.y() * lengthsScaleFactor);
|
_set_axis_origin(Y, _get_axis_position(Y) - line.y() * lengthsScaleFactor);
|
||||||
anyFound = true;
|
anyFound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.has_z())
|
if (line.has_z())
|
||||||
{
|
{
|
||||||
_set_axis_position(Z, line.z() * lengthsScaleFactor);
|
_set_axis_origin(Z, _get_axis_position(Z) - line.z() * lengthsScaleFactor);
|
||||||
anyFound = true;
|
anyFound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.has_e())
|
if (line.has_e())
|
||||||
{
|
{
|
||||||
_set_axis_position(E, line.e() * lengthsScaleFactor);
|
_set_axis_origin(E, _get_axis_position(E) - line.e() * lengthsScaleFactor);
|
||||||
anyFound = true;
|
anyFound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -450,7 +457,7 @@ void GCodeAnalyzer::_processG92(const GCodeReader::GCodeLine& line)
|
||||||
{
|
{
|
||||||
for (unsigned char a = X; a < Num_Axis; ++a)
|
for (unsigned char a = X; a < Num_Axis; ++a)
|
||||||
{
|
{
|
||||||
_set_axis_position((EAxis)a, 0.0f);
|
_set_axis_origin((EAxis)a, _get_axis_position((EAxis)a));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -502,6 +509,25 @@ void GCodeAnalyzer::_processM108orM135(const GCodeReader::GCodeLine& line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GCodeAnalyzer::_processM132(const GCodeReader::GCodeLine& line)
|
||||||
|
{
|
||||||
|
// This command is used by Makerbot to load the current home position from EEPROM
|
||||||
|
// see: https://github.com/makerbot/s3g/blob/master/doc/GCodeProtocol.md
|
||||||
|
// Using this command to reset the axis origin to zero helps in fixing: https://github.com/prusa3d/PrusaSlicer/issues/3082
|
||||||
|
|
||||||
|
if (line.has_x())
|
||||||
|
_set_axis_origin(X, 0.0f);
|
||||||
|
|
||||||
|
if (line.has_y())
|
||||||
|
_set_axis_origin(Y, 0.0f);
|
||||||
|
|
||||||
|
if (line.has_z())
|
||||||
|
_set_axis_origin(Z, 0.0f);
|
||||||
|
|
||||||
|
if (line.has_e())
|
||||||
|
_set_axis_origin(E, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
void GCodeAnalyzer::_processM401(const GCodeReader::GCodeLine& line)
|
void GCodeAnalyzer::_processM401(const GCodeReader::GCodeLine& line)
|
||||||
{
|
{
|
||||||
if (m_gcode_flavor != gcfRepetier)
|
if (m_gcode_flavor != gcfRepetier)
|
||||||
|
@ -781,11 +807,26 @@ float GCodeAnalyzer::_get_axis_position(EAxis axis) const
|
||||||
return m_state.position[axis];
|
return m_state.position[axis];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GCodeAnalyzer::_set_axis_origin(EAxis axis, float position)
|
||||||
|
{
|
||||||
|
m_state.origin[axis] = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GCodeAnalyzer::_get_axis_origin(EAxis axis) const
|
||||||
|
{
|
||||||
|
return m_state.origin[axis];
|
||||||
|
}
|
||||||
|
|
||||||
void GCodeAnalyzer::_reset_axes_position()
|
void GCodeAnalyzer::_reset_axes_position()
|
||||||
{
|
{
|
||||||
::memset((void*)m_state.position, 0, Num_Axis * sizeof(float));
|
::memset((void*)m_state.position, 0, Num_Axis * sizeof(float));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GCodeAnalyzer::_reset_axes_origin()
|
||||||
|
{
|
||||||
|
::memset((void*)m_state.origin, 0, Num_Axis * sizeof(float));
|
||||||
|
}
|
||||||
|
|
||||||
void GCodeAnalyzer::_set_start_position(const Vec3d& position)
|
void GCodeAnalyzer::_set_start_position(const Vec3d& position)
|
||||||
{
|
{
|
||||||
m_state.start_position = position;
|
m_state.start_position = position;
|
||||||
|
|
|
@ -101,6 +101,7 @@ private:
|
||||||
float cached_position[5];
|
float cached_position[5];
|
||||||
float start_extrusion;
|
float start_extrusion;
|
||||||
float position[Num_Axis];
|
float position[Num_Axis];
|
||||||
|
float origin[Num_Axis];
|
||||||
unsigned int cur_cp_color_id = 0;
|
unsigned int cur_cp_color_id = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -181,6 +182,9 @@ private:
|
||||||
// Set tool (MakerWare and Sailfish flavor)
|
// Set tool (MakerWare and Sailfish flavor)
|
||||||
void _processM108orM135(const GCodeReader::GCodeLine& line);
|
void _processM108orM135(const GCodeReader::GCodeLine& line);
|
||||||
|
|
||||||
|
// Recall stored home offsets
|
||||||
|
void _processM132(const GCodeReader::GCodeLine& line);
|
||||||
|
|
||||||
// Repetier: Store x, y and z position
|
// Repetier: Store x, y and z position
|
||||||
void _processM401(const GCodeReader::GCodeLine& line);
|
void _processM401(const GCodeReader::GCodeLine& line);
|
||||||
|
|
||||||
|
@ -246,8 +250,13 @@ private:
|
||||||
void _set_axis_position(EAxis axis, float position);
|
void _set_axis_position(EAxis axis, float position);
|
||||||
float _get_axis_position(EAxis axis) const;
|
float _get_axis_position(EAxis axis) const;
|
||||||
|
|
||||||
|
void _set_axis_origin(EAxis axis, float position);
|
||||||
|
float _get_axis_origin(EAxis axis) const;
|
||||||
|
|
||||||
// Sets axes position to zero
|
// Sets axes position to zero
|
||||||
void _reset_axes_position();
|
void _reset_axes_position();
|
||||||
|
// Sets origin position to zero
|
||||||
|
void _reset_axes_origin();
|
||||||
|
|
||||||
void _set_start_position(const Vec3d& position);
|
void _set_start_position(const Vec3d& position);
|
||||||
const Vec3d& _get_start_position() const;
|
const Vec3d& _get_start_position() const;
|
||||||
|
|
|
@ -318,12 +318,15 @@ namespace Slic3r {
|
||||||
|
|
||||||
assert((g1_line_id >= (int)data->g1_line_ids.size()) || (data->g1_line_ids[g1_line_id].first >= g1_lines_count));
|
assert((g1_line_id >= (int)data->g1_line_ids.size()) || (data->g1_line_ids[g1_line_id].first >= g1_lines_count));
|
||||||
const Block* block = nullptr;
|
const Block* block = nullptr;
|
||||||
const G1LineIdToBlockId& map_item = data->g1_line_ids[g1_line_id];
|
if (g1_line_id < (int)data->g1_line_ids.size())
|
||||||
if ((g1_line_id < (int)data->g1_line_ids.size()) && (map_item.first == g1_lines_count))
|
|
||||||
{
|
{
|
||||||
if (line.has_e() && (map_item.second < (unsigned int)data->blocks.size()))
|
const G1LineIdToBlockId& map_item = data->g1_line_ids[g1_line_id];
|
||||||
block = &data->blocks[map_item.second];
|
if (map_item.first == g1_lines_count)
|
||||||
++g1_line_id;
|
{
|
||||||
|
if (line.has_e() && (map_item.second < (unsigned int)data->blocks.size()))
|
||||||
|
block = &data->blocks[map_item.second];
|
||||||
|
++g1_line_id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((block != nullptr) && (block->elapsed_time != -1.0f))
|
if ((block != nullptr) && (block->elapsed_time != -1.0f))
|
||||||
|
@ -412,6 +415,11 @@ namespace Slic3r {
|
||||||
m_state.axis[axis].position = position;
|
m_state.axis[axis].position = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GCodeTimeEstimator::set_axis_origin(EAxis axis, float position)
|
||||||
|
{
|
||||||
|
m_state.axis[axis].origin = position;
|
||||||
|
}
|
||||||
|
|
||||||
void GCodeTimeEstimator::set_axis_max_feedrate(EAxis axis, float feedrate_mm_sec)
|
void GCodeTimeEstimator::set_axis_max_feedrate(EAxis axis, float feedrate_mm_sec)
|
||||||
{
|
{
|
||||||
m_state.axis[axis].max_feedrate = feedrate_mm_sec;
|
m_state.axis[axis].max_feedrate = feedrate_mm_sec;
|
||||||
|
@ -432,6 +440,11 @@ namespace Slic3r {
|
||||||
return m_state.axis[axis].position;
|
return m_state.axis[axis].position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float GCodeTimeEstimator::get_axis_origin(EAxis axis) const
|
||||||
|
{
|
||||||
|
return m_state.axis[axis].origin;
|
||||||
|
}
|
||||||
|
|
||||||
float GCodeTimeEstimator::get_axis_max_feedrate(EAxis axis) const
|
float GCodeTimeEstimator::get_axis_max_feedrate(EAxis axis) const
|
||||||
{
|
{
|
||||||
return m_state.axis[axis].max_feedrate;
|
return m_state.axis[axis].max_feedrate;
|
||||||
|
@ -758,6 +771,10 @@ namespace Slic3r {
|
||||||
set_axis_position(X, 0.0f);
|
set_axis_position(X, 0.0f);
|
||||||
set_axis_position(Y, 0.0f);
|
set_axis_position(Y, 0.0f);
|
||||||
set_axis_position(Z, 0.0f);
|
set_axis_position(Z, 0.0f);
|
||||||
|
set_axis_origin(X, 0.0f);
|
||||||
|
set_axis_origin(Y, 0.0f);
|
||||||
|
set_axis_origin(Z, 0.0f);
|
||||||
|
|
||||||
if (get_e_local_positioning_type() == Absolute)
|
if (get_e_local_positioning_type() == Absolute)
|
||||||
set_axis_position(E, 0.0f);
|
set_axis_position(E, 0.0f);
|
||||||
|
|
||||||
|
@ -954,34 +971,35 @@ namespace Slic3r {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the new absolute position on the given axis in dependence of the given parameters
|
|
||||||
float axis_absolute_position_from_G1_line(GCodeTimeEstimator::EAxis axis, const GCodeReader::GCodeLine& lineG1, GCodeTimeEstimator::EUnits units, bool is_relative, float current_absolute_position)
|
|
||||||
{
|
|
||||||
float lengthsScaleFactor = (units == GCodeTimeEstimator::Inches) ? INCHES_TO_MM : 1.0f;
|
|
||||||
if (lineG1.has(Slic3r::Axis(axis)))
|
|
||||||
{
|
|
||||||
float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor;
|
|
||||||
return is_relative ? current_absolute_position + ret : ret;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return current_absolute_position;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GCodeTimeEstimator::_processG1(const GCodeReader::GCodeLine& line)
|
void GCodeTimeEstimator::_processG1(const GCodeReader::GCodeLine& line)
|
||||||
{
|
{
|
||||||
|
auto axis_absolute_position = [this](GCodeTimeEstimator::EAxis axis, const GCodeReader::GCodeLine& lineG1) -> float
|
||||||
|
{
|
||||||
|
float current_absolute_position = get_axis_position(axis);
|
||||||
|
float current_origin = get_axis_origin(axis);
|
||||||
|
float lengthsScaleFactor = (get_units() == GCodeTimeEstimator::Inches) ? INCHES_TO_MM : 1.0f;
|
||||||
|
|
||||||
|
bool is_relative = (get_global_positioning_type() == Relative);
|
||||||
|
if (axis == E)
|
||||||
|
is_relative |= (get_e_local_positioning_type() == Relative);
|
||||||
|
|
||||||
|
if (lineG1.has(Slic3r::Axis(axis)))
|
||||||
|
{
|
||||||
|
float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor;
|
||||||
|
return is_relative ? current_absolute_position + ret : ret + current_origin;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return current_absolute_position;
|
||||||
|
};
|
||||||
|
|
||||||
PROFILE_FUNC();
|
PROFILE_FUNC();
|
||||||
increment_g1_line_id();
|
increment_g1_line_id();
|
||||||
|
|
||||||
// updates axes positions from line
|
// updates axes positions from line
|
||||||
EUnits units = get_units();
|
|
||||||
float new_pos[Num_Axis];
|
float new_pos[Num_Axis];
|
||||||
for (unsigned char a = X; a < Num_Axis; ++a)
|
for (unsigned char a = X; a < Num_Axis; ++a)
|
||||||
{
|
{
|
||||||
bool is_relative = (get_global_positioning_type() == Relative);
|
new_pos[a] = axis_absolute_position((EAxis)a, line);
|
||||||
if (a == E)
|
|
||||||
is_relative |= (get_e_local_positioning_type() == Relative);
|
|
||||||
|
|
||||||
new_pos[a] = axis_absolute_position_from_G1_line((EAxis)a, line, units, is_relative, get_axis_position((EAxis)a));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// updates feedrate from line, if present
|
// updates feedrate from line, if present
|
||||||
|
@ -1225,25 +1243,25 @@ namespace Slic3r {
|
||||||
|
|
||||||
if (line.has_x())
|
if (line.has_x())
|
||||||
{
|
{
|
||||||
set_axis_position(X, line.x() * lengthsScaleFactor);
|
set_axis_origin(X, get_axis_position(X) - line.x() * lengthsScaleFactor);
|
||||||
anyFound = true;
|
anyFound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.has_y())
|
if (line.has_y())
|
||||||
{
|
{
|
||||||
set_axis_position(Y, line.y() * lengthsScaleFactor);
|
set_axis_origin(Y, get_axis_position(Y) - line.y() * lengthsScaleFactor);
|
||||||
anyFound = true;
|
anyFound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.has_z())
|
if (line.has_z())
|
||||||
{
|
{
|
||||||
set_axis_position(Z, line.z() * lengthsScaleFactor);
|
set_axis_origin(Z, get_axis_position(Z) - line.z() * lengthsScaleFactor);
|
||||||
anyFound = true;
|
anyFound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line.has_e())
|
if (line.has_e())
|
||||||
{
|
{
|
||||||
set_axis_position(E, line.e() * lengthsScaleFactor);
|
set_axis_origin(E, get_axis_position(E) - line.e() * lengthsScaleFactor);
|
||||||
anyFound = true;
|
anyFound = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1253,7 +1271,7 @@ namespace Slic3r {
|
||||||
{
|
{
|
||||||
for (unsigned char a = X; a < Num_Axis; ++a)
|
for (unsigned char a = X; a < Num_Axis; ++a)
|
||||||
{
|
{
|
||||||
set_axis_position((EAxis)a, 0.0f);
|
set_axis_origin((EAxis)a, get_axis_position((EAxis)a));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@ namespace Slic3r {
|
||||||
struct Axis
|
struct Axis
|
||||||
{
|
{
|
||||||
float position; // mm
|
float position; // mm
|
||||||
|
float origin; // mm
|
||||||
float max_feedrate; // mm/s
|
float max_feedrate; // mm/s
|
||||||
float max_acceleration; // mm/s^2
|
float max_acceleration; // mm/s^2
|
||||||
float max_jerk; // mm/s
|
float max_jerk; // mm/s
|
||||||
|
@ -282,6 +283,8 @@ namespace Slic3r {
|
||||||
|
|
||||||
// Set current position on the given axis with the given value
|
// Set current position on the given axis with the given value
|
||||||
void set_axis_position(EAxis axis, float position);
|
void set_axis_position(EAxis axis, float position);
|
||||||
|
// Set current origin on the given axis with the given value
|
||||||
|
void set_axis_origin(EAxis axis, float position);
|
||||||
|
|
||||||
void set_axis_max_feedrate(EAxis axis, float feedrate_mm_sec);
|
void set_axis_max_feedrate(EAxis axis, float feedrate_mm_sec);
|
||||||
void set_axis_max_acceleration(EAxis axis, float acceleration);
|
void set_axis_max_acceleration(EAxis axis, float acceleration);
|
||||||
|
@ -289,6 +292,8 @@ namespace Slic3r {
|
||||||
|
|
||||||
// Returns current position on the given axis
|
// Returns current position on the given axis
|
||||||
float get_axis_position(EAxis axis) const;
|
float get_axis_position(EAxis axis) const;
|
||||||
|
// Returns current origin on the given axis
|
||||||
|
float get_axis_origin(EAxis axis) const;
|
||||||
|
|
||||||
float get_axis_max_feedrate(EAxis axis) const;
|
float get_axis_max_feedrate(EAxis axis) const;
|
||||||
float get_axis_max_acceleration(EAxis axis) const;
|
float get_axis_max_acceleration(EAxis axis) const;
|
||||||
|
|
|
@ -70,7 +70,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
|
||||||
fill_surfaces
|
fill_surfaces
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this->layer()->lower_layer != NULL)
|
if (this->layer()->lower_layer != nullptr)
|
||||||
// Cummulative sum of polygons over all the regions.
|
// Cummulative sum of polygons over all the regions.
|
||||||
g.lower_slices = &this->layer()->lower_layer->slices;
|
g.lower_slices = &this->layer()->lower_layer->slices;
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
||||||
bridges.emplace_back(surface);
|
bridges.emplace_back(surface);
|
||||||
}
|
}
|
||||||
if (surface.is_internal()) {
|
if (surface.is_internal()) {
|
||||||
assert(surface.surface_type == stInternal);
|
assert(surface.surface_type == stInternal || surface.surface_type == stInternalSolid);
|
||||||
if (! has_infill && lower_layer != nullptr)
|
if (! has_infill && lower_layer != nullptr)
|
||||||
polygons_append(voids, surface.expolygon);
|
polygons_append(voids, surface.expolygon);
|
||||||
internal.emplace_back(std::move(surface));
|
internal.emplace_back(std::move(surface));
|
||||||
|
|
|
@ -433,6 +433,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
"If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. "
|
"If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. "
|
||||||
"If expressed as percentage (for example 200%), it will be computed over layer height.");
|
"If expressed as percentage (for example 200%), it will be computed over layer height.");
|
||||||
def->sidetext = L("mm or %");
|
def->sidetext = L("mm or %");
|
||||||
|
def->min = 0;
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||||
|
|
||||||
|
@ -541,6 +542,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
"(see the tooltips for perimeter extrusion width, infill extrusion width etc). "
|
"(see the tooltips for perimeter extrusion width, infill extrusion width etc). "
|
||||||
"If expressed as percentage (for example: 230%), it will be computed over layer height.");
|
"If expressed as percentage (for example: 230%), it will be computed over layer height.");
|
||||||
def->sidetext = L("mm or %");
|
def->sidetext = L("mm or %");
|
||||||
|
def->min = 0;
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||||
|
|
||||||
|
@ -863,6 +865,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
"If set to zero, it will use the default extrusion width.");
|
"If set to zero, it will use the default extrusion width.");
|
||||||
def->sidetext = L("mm or %");
|
def->sidetext = L("mm or %");
|
||||||
def->ratio_over = "first_layer_height";
|
def->ratio_over = "first_layer_height";
|
||||||
|
def->min = 0;
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionFloatOrPercent(200, true));
|
def->set_default_value(new ConfigOptionFloatOrPercent(200, true));
|
||||||
|
|
||||||
|
@ -994,6 +997,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
"You may want to use fatter extrudates to speed up the infill and make your parts stronger. "
|
"You may want to use fatter extrudates to speed up the infill and make your parts stronger. "
|
||||||
"If expressed as percentage (for example 90%) it will be computed over layer height.");
|
"If expressed as percentage (for example 90%) it will be computed over layer height.");
|
||||||
def->sidetext = L("mm or %");
|
def->sidetext = L("mm or %");
|
||||||
|
def->min = 0;
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||||
|
|
||||||
|
@ -1406,6 +1410,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
"If expressed as percentage (for example 200%) it will be computed over layer height.");
|
"If expressed as percentage (for example 200%) it will be computed over layer height.");
|
||||||
def->sidetext = L("mm or %");
|
def->sidetext = L("mm or %");
|
||||||
def->aliases = { "perimeters_extrusion_width" };
|
def->aliases = { "perimeters_extrusion_width" };
|
||||||
|
def->min = 0;
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||||
|
|
||||||
|
@ -1743,6 +1748,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
"If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. "
|
"If left zero, default extrusion width will be used if set, otherwise 1.125 x nozzle diameter will be used. "
|
||||||
"If expressed as percentage (for example 90%) it will be computed over layer height.");
|
"If expressed as percentage (for example 90%) it will be computed over layer height.");
|
||||||
def->sidetext = L("mm or %");
|
def->sidetext = L("mm or %");
|
||||||
|
def->min = 0;
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||||
|
|
||||||
|
@ -1917,6 +1923,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
"If left zero, default extrusion width will be used if set, otherwise nozzle diameter will be used. "
|
"If left zero, default extrusion width will be used if set, otherwise nozzle diameter will be used. "
|
||||||
"If expressed as percentage (for example 90%) it will be computed over layer height.");
|
"If expressed as percentage (for example 90%) it will be computed over layer height.");
|
||||||
def->sidetext = L("mm or %");
|
def->sidetext = L("mm or %");
|
||||||
|
def->min = 0;
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||||
|
|
||||||
|
@ -2076,6 +2083,7 @@ void PrintConfigDef::init_fff_params()
|
||||||
"If left zero, default extrusion width will be used if set, otherwise nozzle diameter will be used. "
|
"If left zero, default extrusion width will be used if set, otherwise nozzle diameter will be used. "
|
||||||
"If expressed as percentage (for example 90%) it will be computed over layer height.");
|
"If expressed as percentage (for example 90%) it will be computed over layer height.");
|
||||||
def->sidetext = L("mm or %");
|
def->sidetext = L("mm or %");
|
||||||
|
def->min = 0;
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
def->set_default_value(new ConfigOptionFloatOrPercent(0, false));
|
||||||
|
|
||||||
|
|
1
tests/data/test_config/new_from_ini.ini
Normal file
1
tests/data/test_config/new_from_ini.ini
Normal file
|
@ -0,0 +1 @@
|
||||||
|
filament_colour = #ABCD
|
1
tests/data/test_stl/ASCII/20mmbox-CR.stl
Normal file
1
tests/data/test_stl/ASCII/20mmbox-CR.stl
Normal file
|
@ -0,0 +1 @@
|
||||||
|
solid STL generated by MeshLab
facet normal 0.000000e+00 -0.000000e+00 -1.000000e+00
outer loop
vertex 2.000000e+01 2.000000e+01 0.000000e+00
vertex 2.000000e+01 0.000000e+00 0.000000e+00
vertex 0.000000e+00 0.000000e+00 0.000000e+00
endloop
endfacet
facet normal -0.000000e+00 0.000000e+00 -1.000000e+00
outer loop
vertex 2.000000e+01 2.000000e+01 0.000000e+00
vertex 0.000000e+00 0.000000e+00 0.000000e+00
vertex 0.000000e+00 2.000000e+01 0.000000e+00
endloop
endfacet
facet normal 0.000000e+00 0.000000e+00 1.000000e+00
outer loop
vertex 2.000000e+01 2.000000e+01 2.000000e+01
vertex 0.000000e+00 2.000000e+01 2.000000e+01
vertex 0.000000e+00 0.000000e+00 2.000000e+01
endloop
endfacet
facet normal 0.000000e+00 0.000000e+00 1.000000e+00
outer loop
vertex 2.000000e+01 2.000000e+01 2.000000e+01
vertex 0.000000e+00 0.000000e+00 2.000000e+01
vertex 2.000000e+01 0.000000e+00 2.000000e+01
endloop
endfacet
facet normal 1.000000e+00 0.000000e+00 -0.000000e+00
outer loop
vertex 2.000000e+01 2.000000e+01 0.000000e+00
vertex 2.000000e+01 2.000000e+01 2.000000e+01
vertex 2.000000e+01 0.000000e+00 2.000000e+01
endloop
endfacet
facet normal 1.000000e+00 0.000000e+00 0.000000e+00
outer loop
vertex 2.000000e+01 2.000000e+01 0.000000e+00
vertex 2.000000e+01 0.000000e+00 2.000000e+01
vertex 2.000000e+01 0.000000e+00 0.000000e+00
endloop
endfacet
facet normal 0.000000e+00 -1.000000e+00 0.000000e+00
outer loop
vertex 2.000000e+01 0.000000e+00 0.000000e+00
vertex 2.000000e+01 0.000000e+00 2.000000e+01
vertex 0.000000e+00 0.000000e+00 2.000000e+01
endloop
endfacet
facet normal 0.000000e+00 -1.000000e+00 0.000000e+00
outer loop
vertex 2.000000e+01 0.000000e+00 0.000000e+00
vertex 0.000000e+00 0.000000e+00 2.000000e+01
vertex 0.000000e+00 0.000000e+00 0.000000e+00
endloop
endfacet
facet normal -1.000000e+00 0.000000e+00 0.000000e+00
outer loop
vertex 0.000000e+00 0.000000e+00 0.000000e+00
vertex 0.000000e+00 0.000000e+00 2.000000e+01
vertex 0.000000e+00 2.000000e+01 2.000000e+01
endloop
endfacet
facet normal -1.000000e+00 0.000000e+00 0.000000e+00
outer loop
vertex 0.000000e+00 0.000000e+00 0.000000e+00
vertex 0.000000e+00 2.000000e+01 2.000000e+01
vertex 0.000000e+00 2.000000e+01 0.000000e+00
endloop
endfacet
facet normal 0.000000e+00 1.000000e+00 0.000000e+00
outer loop
vertex 2.000000e+01 2.000000e+01 2.000000e+01
vertex 2.000000e+01 2.000000e+01 0.000000e+00
vertex 0.000000e+00 2.000000e+01 0.000000e+00
endloop
endfacet
facet normal 0.000000e+00 1.000000e+00 0.000000e+00
outer loop
vertex 2.000000e+01 2.000000e+01 2.000000e+01
vertex 0.000000e+00 2.000000e+01 0.000000e+00
vertex 0.000000e+00 2.000000e+01 2.000000e+01
endloop
endfacet
endsolid vcg
|
86
tests/data/test_stl/ASCII/20mmbox-CRLF.stl
Normal file
86
tests/data/test_stl/ASCII/20mmbox-CRLF.stl
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
solid STL generated by MeshLab
|
||||||
|
facet normal 0.000000e+00 -0.000000e+00 -1.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 0.000000e+00
|
||||||
|
vertex 2.000000e+01 0.000000e+00 0.000000e+00
|
||||||
|
vertex 0.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal -0.000000e+00 0.000000e+00 -1.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 0.000000e+00
|
||||||
|
vertex 0.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
vertex 0.000000e+00 2.000000e+01 0.000000e+00
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.000000e+00 0.000000e+00 1.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 2.000000e+01
|
||||||
|
vertex 0.000000e+00 2.000000e+01 2.000000e+01
|
||||||
|
vertex 0.000000e+00 0.000000e+00 2.000000e+01
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.000000e+00 0.000000e+00 1.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 2.000000e+01
|
||||||
|
vertex 0.000000e+00 0.000000e+00 2.000000e+01
|
||||||
|
vertex 2.000000e+01 0.000000e+00 2.000000e+01
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 1.000000e+00 0.000000e+00 -0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 0.000000e+00
|
||||||
|
vertex 2.000000e+01 2.000000e+01 2.000000e+01
|
||||||
|
vertex 2.000000e+01 0.000000e+00 2.000000e+01
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 1.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 0.000000e+00
|
||||||
|
vertex 2.000000e+01 0.000000e+00 2.000000e+01
|
||||||
|
vertex 2.000000e+01 0.000000e+00 0.000000e+00
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.000000e+00 -1.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 0.000000e+00 0.000000e+00
|
||||||
|
vertex 2.000000e+01 0.000000e+00 2.000000e+01
|
||||||
|
vertex 0.000000e+00 0.000000e+00 2.000000e+01
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.000000e+00 -1.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 0.000000e+00 0.000000e+00
|
||||||
|
vertex 0.000000e+00 0.000000e+00 2.000000e+01
|
||||||
|
vertex 0.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal -1.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 0.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
vertex 0.000000e+00 0.000000e+00 2.000000e+01
|
||||||
|
vertex 0.000000e+00 2.000000e+01 2.000000e+01
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal -1.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 0.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
vertex 0.000000e+00 2.000000e+01 2.000000e+01
|
||||||
|
vertex 0.000000e+00 2.000000e+01 0.000000e+00
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.000000e+00 1.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 2.000000e+01
|
||||||
|
vertex 2.000000e+01 2.000000e+01 0.000000e+00
|
||||||
|
vertex 0.000000e+00 2.000000e+01 0.000000e+00
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.000000e+00 1.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 2.000000e+01
|
||||||
|
vertex 0.000000e+00 2.000000e+01 0.000000e+00
|
||||||
|
vertex 0.000000e+00 2.000000e+01 2.000000e+01
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
endsolid vcg
|
86
tests/data/test_stl/ASCII/20mmbox-LF.stl
Normal file
86
tests/data/test_stl/ASCII/20mmbox-LF.stl
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
solid STL generated by MeshLab
|
||||||
|
facet normal 0.000000e+00 -0.000000e+00 -1.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 0.000000e+00
|
||||||
|
vertex 2.000000e+01 0.000000e+00 0.000000e+00
|
||||||
|
vertex 0.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal -0.000000e+00 0.000000e+00 -1.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 0.000000e+00
|
||||||
|
vertex 0.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
vertex 0.000000e+00 2.000000e+01 0.000000e+00
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.000000e+00 0.000000e+00 1.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 2.000000e+01
|
||||||
|
vertex 0.000000e+00 2.000000e+01 2.000000e+01
|
||||||
|
vertex 0.000000e+00 0.000000e+00 2.000000e+01
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.000000e+00 0.000000e+00 1.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 2.000000e+01
|
||||||
|
vertex 0.000000e+00 0.000000e+00 2.000000e+01
|
||||||
|
vertex 2.000000e+01 0.000000e+00 2.000000e+01
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 1.000000e+00 0.000000e+00 -0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 0.000000e+00
|
||||||
|
vertex 2.000000e+01 2.000000e+01 2.000000e+01
|
||||||
|
vertex 2.000000e+01 0.000000e+00 2.000000e+01
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 1.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 0.000000e+00
|
||||||
|
vertex 2.000000e+01 0.000000e+00 2.000000e+01
|
||||||
|
vertex 2.000000e+01 0.000000e+00 0.000000e+00
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.000000e+00 -1.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 0.000000e+00 0.000000e+00
|
||||||
|
vertex 2.000000e+01 0.000000e+00 2.000000e+01
|
||||||
|
vertex 0.000000e+00 0.000000e+00 2.000000e+01
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.000000e+00 -1.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 0.000000e+00 0.000000e+00
|
||||||
|
vertex 0.000000e+00 0.000000e+00 2.000000e+01
|
||||||
|
vertex 0.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal -1.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 0.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
vertex 0.000000e+00 0.000000e+00 2.000000e+01
|
||||||
|
vertex 0.000000e+00 2.000000e+01 2.000000e+01
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal -1.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 0.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
vertex 0.000000e+00 2.000000e+01 2.000000e+01
|
||||||
|
vertex 0.000000e+00 2.000000e+01 0.000000e+00
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.000000e+00 1.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 2.000000e+01
|
||||||
|
vertex 2.000000e+01 2.000000e+01 0.000000e+00
|
||||||
|
vertex 0.000000e+00 2.000000e+01 0.000000e+00
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.000000e+00 1.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 2.000000e+01
|
||||||
|
vertex 0.000000e+00 2.000000e+01 0.000000e+00
|
||||||
|
vertex 0.000000e+00 2.000000e+01 2.000000e+01
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
endsolid
|
86
tests/data/test_stl/ASCII/20mmbox-nonstandard.stl
Normal file
86
tests/data/test_stl/ASCII/20mmbox-nonstandard.stl
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
solid STL generated by MeshLab
|
||||||
|
facet normal 0.000000e+00 -0.000000e+00 -1.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 0.000000e+00
|
||||||
|
vertex 2.000000e+01 0.000000e+00 0.000000e+00
|
||||||
|
vertex 0.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal -0.000000e+00 0.000000e+00 -1.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 0.000000e+00
|
||||||
|
vertex 0.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
vertex 0.000000e+00 2.000000e+01 0.000000e+00
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.000000e+00 0.000000e+00 1.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 2.000000e+01
|
||||||
|
vertex 0.000000e+00 2.000000e+01 2.000000e+01
|
||||||
|
vertex 0.000000e+00 0.000000e+00 2.000000e+01
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.000000e+00 0.000000e+00 1.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 2.000000e+01
|
||||||
|
vertex 0.000000e+00 0.000000e+00 2.000000e+01
|
||||||
|
vertex 2.000000e+01 0.000000e+00 2.000000e+01
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 1.000000e+00 0.000000e+00 -0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 0.000000e+00
|
||||||
|
vertex 2.000000e+01 2.000000e+01 2.000000e+01
|
||||||
|
vertex 2.000000e+01 0.000000e+00 2.000000e+01
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 1.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 0.000000e+00
|
||||||
|
vertex 2.000000e+01 0.000000e+00 2.000000e+01
|
||||||
|
vertex 2.000000e+01 0.000000e+00 0.000000e+00
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.000000e+00 -1.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 0.000000e+00 0.000000e+00
|
||||||
|
vertex 2.000000e+01 0.000000e+00 2.000000e+01
|
||||||
|
vertex 0.000000e+00 0.000000e+00 2.000000e+01
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal 0.000000e+00 -1.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 0.000000e+00 0.000000e+00
|
||||||
|
vertex 0.000000e+00 0.000000e+00 2.000000e+01
|
||||||
|
vertex 0.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal +inf -inf weirdvalue
|
||||||
|
outer loop
|
||||||
|
vertex 0.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
vertex 0.000000e+00 0.000000e+00 2.000000e+01
|
||||||
|
vertex 0.000000e+00 2.000000e+01 2.000000e+01
|
||||||
|
endloop
|
||||||
|
endfacet
|
||||||
|
facet normal -1.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 0.000000e+00 0.000000e+00 0.000000e+00
|
||||||
|
vertex 0.000000e+00 2.000000e+01 2.000000e+01
|
||||||
|
vertex 0.000000e+00 2.000000e+01 0.000000e+00
|
||||||
|
endloop
|
||||||
|
endfacet blah
|
||||||
|
facet normal 0.000000e+00 1.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 2.000000e+01
|
||||||
|
vertex 2.000000e+01 2.000000e+01 0.000000e+00
|
||||||
|
vertex 0.000000e+00 2.000000e+01 0.000000e+00
|
||||||
|
endloop foo
|
||||||
|
endfacet bar
|
||||||
|
facet normal 0.000000e+00 1.000000e+00 0.000000e+00
|
||||||
|
outer loop
|
||||||
|
vertex 2.000000e+01 2.000000e+01 2.000000e+01
|
||||||
|
vertex 0.000000e+00 2.000000e+01 0.000000e+00
|
||||||
|
vertex 0.000000e+00 2.000000e+01 2.000000e+01
|
||||||
|
endloop foo
|
||||||
|
endfacet bar
|
||||||
|
endsolid some blah blah
|
BIN
tests/data/test_stl/Geräte/20mmbox-čřšřěá.stl
Normal file
BIN
tests/data/test_stl/Geräte/20mmbox-čřšřěá.stl
Normal file
Binary file not shown.
|
@ -3,6 +3,7 @@ add_executable(${_TEST_NAME}_tests
|
||||||
${_TEST_NAME}_tests.cpp
|
${_TEST_NAME}_tests.cpp
|
||||||
test_data.cpp
|
test_data.cpp
|
||||||
test_data.hpp
|
test_data.hpp
|
||||||
|
test_extrusion_entity.cpp
|
||||||
test_fill.cpp
|
test_fill.cpp
|
||||||
test_flow.cpp
|
test_flow.cpp
|
||||||
test_gcodewriter.cpp
|
test_gcodewriter.cpp
|
||||||
|
@ -11,6 +12,7 @@ add_executable(${_TEST_NAME}_tests
|
||||||
test_printgcode.cpp
|
test_printgcode.cpp
|
||||||
test_printobject.cpp
|
test_printobject.cpp
|
||||||
test_skirt_brim.cpp
|
test_skirt_brim.cpp
|
||||||
|
test_support_material.cpp
|
||||||
test_trianglemesh.cpp
|
test_trianglemesh.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
|
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,12 +1,12 @@
|
||||||
#ifndef SLIC3R_TEST_DATA_HPP
|
#ifndef SLIC3R_TEST_DATA_HPP
|
||||||
#define SLIC3R_TEST_DATA_HPP
|
#define SLIC3R_TEST_DATA_HPP
|
||||||
|
|
||||||
#include "libslic3r/Point.hpp"
|
#include "libslic3r/Config.hpp"
|
||||||
#include "libslic3r/TriangleMesh.hpp"
|
|
||||||
#include "libslic3r/Geometry.hpp"
|
#include "libslic3r/Geometry.hpp"
|
||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
|
#include "libslic3r/Point.hpp"
|
||||||
#include "libslic3r/Print.hpp"
|
#include "libslic3r/Print.hpp"
|
||||||
#include "libslic3r/Config.hpp"
|
#include "libslic3r/TriangleMesh.hpp"
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
@ -61,15 +61,24 @@ bool _equiv(const T& a, const T& b) { return std::abs(a - b) < EPSILON; }
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool _equiv(const T& a, const T& b, double epsilon) { return abs(a - b) < epsilon; }
|
bool _equiv(const T& a, const T& b, double epsilon) { return abs(a - b) < epsilon; }
|
||||||
|
|
||||||
//Slic3r::Model model(const std::string& model_name, TestMesh m, Vec3d translate = Vec3d(0,0,0), Vec3d scale = Vec3d(1.0,1.0,1.0));
|
|
||||||
//Slic3r::Model model(const std::string& model_name, TestMesh m, Vec3d translate = Vec3d(0,0,0), double scale = 1.0);
|
|
||||||
|
|
||||||
Slic3r::Model model(const std::string& model_name, TriangleMesh&& _mesh);
|
Slic3r::Model model(const std::string& model_name, TriangleMesh&& _mesh);
|
||||||
|
void init_print(std::vector<TriangleMesh> &&meshes, Slic3r::Print &print, Slic3r::Model& model, const DynamicPrintConfig &config_in, bool comments = false);
|
||||||
|
void init_print(std::initializer_list<TestMesh> meshes, Slic3r::Print &print, Slic3r::Model& model, const Slic3r::DynamicPrintConfig &config_in = Slic3r::DynamicPrintConfig::full_print_config(), bool comments = false);
|
||||||
|
void init_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Print &print, Slic3r::Model& model, const Slic3r::DynamicPrintConfig &config_in = Slic3r::DynamicPrintConfig::full_print_config(), bool comments = false);
|
||||||
|
void init_print(std::initializer_list<TestMesh> meshes, Slic3r::Print &print, Slic3r::Model& model, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items, bool comments = false);
|
||||||
|
void init_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Print &print, Slic3r::Model& model, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items, bool comments = false);
|
||||||
|
|
||||||
std::shared_ptr<Print> init_print(std::initializer_list<TestMesh> meshes, Slic3r::Model& model, const Slic3r::DynamicPrintConfig &config_in = Slic3r::DynamicPrintConfig::full_print_config(), bool comments = false);
|
void init_and_process_print(std::initializer_list<TestMesh> meshes, Slic3r::Print &print, const DynamicPrintConfig& config, bool comments = false);
|
||||||
std::shared_ptr<Print> init_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Model& model, const Slic3r::DynamicPrintConfig &config_in = Slic3r::DynamicPrintConfig::full_print_config(), bool comments = false);
|
void init_and_process_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Print &print, const DynamicPrintConfig& config, bool comments = false);
|
||||||
|
void init_and_process_print(std::initializer_list<TestMesh> meshes, Slic3r::Print &print, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items, bool comments = false);
|
||||||
|
void init_and_process_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Print &print, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items, bool comments = false);
|
||||||
|
|
||||||
std::string gcode(std::shared_ptr<Print> print);
|
std::string gcode(Print& print);
|
||||||
|
|
||||||
|
std::string slice(std::initializer_list<TestMesh> meshes, const DynamicPrintConfig &config, bool comments = false);
|
||||||
|
std::string slice(std::initializer_list<TriangleMesh> meshes, const DynamicPrintConfig &config, bool comments = false);
|
||||||
|
std::string slice(std::initializer_list<TestMesh> meshes, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items, bool comments = false);
|
||||||
|
std::string slice(std::initializer_list<TriangleMesh> meshes, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items, bool comments = false);
|
||||||
|
|
||||||
} } // namespace Slic3r::Test
|
} } // namespace Slic3r::Test
|
||||||
|
|
||||||
|
|
85
tests/fff_print/test_extrusion_entity.cpp
Normal file
85
tests/fff_print/test_extrusion_entity.cpp
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include "libslic3r/ExtrusionEntityCollection.hpp"
|
||||||
|
#include "libslic3r/ExtrusionEntity.hpp"
|
||||||
|
#include "libslic3r/Point.hpp"
|
||||||
|
#include "libslic3r/libslic3r.h"
|
||||||
|
|
||||||
|
#include "test_data.hpp"
|
||||||
|
|
||||||
|
using namespace Slic3r;
|
||||||
|
|
||||||
|
static inline Slic3r::Point random_point(float LO=-50, float HI=50)
|
||||||
|
{
|
||||||
|
Vec2f pt = Vec2f(LO, LO) + (Vec2d(rand(), rand()) * (HI-LO) / RAND_MAX).cast<float>();
|
||||||
|
return pt.cast<coord_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// build a sample extrusion entity collection with random start and end points.
|
||||||
|
static Slic3r::ExtrusionPath random_path(size_t length = 20, float LO = -50, float HI = 50)
|
||||||
|
{
|
||||||
|
ExtrusionPath t {erPerimeter, 1.0, 1.0, 1.0};
|
||||||
|
for (size_t j = 0; j < length; ++ j)
|
||||||
|
t.polyline.append(random_point(LO, HI));
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Slic3r::ExtrusionPaths random_paths(size_t count = 10, size_t length = 20, float LO = -50, float HI = 50)
|
||||||
|
{
|
||||||
|
Slic3r::ExtrusionPaths p;
|
||||||
|
for (size_t i = 0; i < count; ++ i)
|
||||||
|
p.push_back(random_path(length, LO, HI));
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
SCENARIO("ExtrusionEntityCollection: Polygon flattening", "[ExtrusionEntity]") {
|
||||||
|
srand(0xDEADBEEF); // consistent seed for test reproducibility.
|
||||||
|
|
||||||
|
// Generate one specific random path set and save it for later comparison
|
||||||
|
Slic3r::ExtrusionPaths nosort_path_set = random_paths();
|
||||||
|
|
||||||
|
Slic3r::ExtrusionEntityCollection sub_nosort;
|
||||||
|
sub_nosort.append(nosort_path_set);
|
||||||
|
sub_nosort.no_sort = true;
|
||||||
|
|
||||||
|
Slic3r::ExtrusionEntityCollection sub_sort;
|
||||||
|
sub_sort.no_sort = false;
|
||||||
|
sub_sort.append(random_paths());
|
||||||
|
|
||||||
|
GIVEN("A Extrusion Entity Collection with a child that has one child that is marked as no-sort") {
|
||||||
|
Slic3r::ExtrusionEntityCollection sample;
|
||||||
|
Slic3r::ExtrusionEntityCollection output;
|
||||||
|
|
||||||
|
sample.append(sub_sort);
|
||||||
|
sample.append(sub_nosort);
|
||||||
|
sample.append(sub_sort);
|
||||||
|
|
||||||
|
WHEN("The EEC is flattened with default options (preserve_order=false)") {
|
||||||
|
output = sample.flatten();
|
||||||
|
THEN("The output EEC contains no Extrusion Entity Collections") {
|
||||||
|
CHECK(std::count_if(output.entities.cbegin(), output.entities.cend(), [=](const ExtrusionEntity* e) {return e->is_collection();}) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("The EEC is flattened with preservation (preserve_order=true)") {
|
||||||
|
output = sample.flatten(true);
|
||||||
|
THEN("The output EECs contains one EEC.") {
|
||||||
|
CHECK(std::count_if(output.entities.cbegin(), output.entities.cend(), [=](const ExtrusionEntity* e) {return e->is_collection();}) == 1);
|
||||||
|
}
|
||||||
|
AND_THEN("The ordered EEC contains the same order of elements than the original") {
|
||||||
|
// find the entity in the collection
|
||||||
|
for (auto e : output.entities)
|
||||||
|
if (e->is_collection()) {
|
||||||
|
ExtrusionEntityCollection *temp = dynamic_cast<ExtrusionEntityCollection*>(e);
|
||||||
|
// check each Extrusion path against nosort_path_set to see if the first and last match the same
|
||||||
|
CHECK(nosort_path_set.size() == temp->entities.size());
|
||||||
|
for (size_t i = 0; i < nosort_path_set.size(); ++ i) {
|
||||||
|
CHECK(temp->entities[i]->first_point() == nosort_path_set[i].first_point());
|
||||||
|
CHECK(temp->entities[i]->last_point() == nosort_path_set[i].last_point());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,20 +15,23 @@
|
||||||
using namespace Slic3r::Test;
|
using namespace Slic3r::Test;
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
|
|
||||||
SCENARIO("Extrusion width specifics", "[!mayfail]") {
|
SCENARIO("Extrusion width specifics", "[Flow]") {
|
||||||
GIVEN("A config with a skirt, brim, some fill density, 3 perimeters, and 1 bottom solid layer and a 20mm cube mesh") {
|
GIVEN("A config with a skirt, brim, some fill density, 3 perimeters, and 1 bottom solid layer and a 20mm cube mesh") {
|
||||||
// this is a sharedptr
|
// this is a sharedptr
|
||||||
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||||
config.opt_int("skirts") = 1;
|
config.set_deserialize({
|
||||||
config.opt_float("brim_width") = 2.;
|
{ "brim_width", 2 },
|
||||||
config.opt_int("perimeters") = 3;
|
{ "skirts", 1 },
|
||||||
config.set_deserialize("fill_density", "40%");
|
{ "perimeters", 3 },
|
||||||
config.set_deserialize("first_layer_height", "100%");
|
{ "fill_density", "40%" },
|
||||||
|
{ "first_layer_height", "100%" }
|
||||||
|
});
|
||||||
|
|
||||||
WHEN("first layer width set to 2mm") {
|
WHEN("first layer width set to 2mm") {
|
||||||
Slic3r::Model model;
|
Slic3r::Model model;
|
||||||
config.set_deserialize("first_layer_extrusion_width", "2");
|
config.set("first_layer_extrusion_width", 2);
|
||||||
std::shared_ptr<Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
Slic3r::Print print;
|
||||||
|
Slic3r::Test::init_print({TestMesh::cube_20x20x20}, print, model, config);
|
||||||
|
|
||||||
std::vector<double> E_per_mm_bottom;
|
std::vector<double> E_per_mm_bottom;
|
||||||
std::string gcode = Test::gcode(print);
|
std::string gcode = Test::gcode(print);
|
||||||
|
@ -56,7 +59,7 @@ SCENARIO("Extrusion width specifics", "[!mayfail]") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// needs gcode export
|
// needs gcode export
|
||||||
SCENARIO(" Bridge flow specifics.", "[!mayfail]") {
|
SCENARIO(" Bridge flow specifics.", "[Flow]") {
|
||||||
GIVEN("A default config with no cooling and a fixed bridge speed, flow ratio and an overhang mesh.") {
|
GIVEN("A default config with no cooling and a fixed bridge speed, flow ratio and an overhang mesh.") {
|
||||||
WHEN("bridge_flow_ratio is set to 1.0") {
|
WHEN("bridge_flow_ratio is set to 1.0") {
|
||||||
THEN("Output flow is as expected.") {
|
THEN("Output flow is as expected.") {
|
||||||
|
@ -89,54 +92,53 @@ SCENARIO(" Bridge flow specifics.", "[!mayfail]") {
|
||||||
|
|
||||||
/// Test the expected behavior for auto-width,
|
/// Test the expected behavior for auto-width,
|
||||||
/// spacing, etc
|
/// spacing, etc
|
||||||
SCENARIO("Flow: Flow math for non-bridges", "[!mayfail]") {
|
SCENARIO("Flow: Flow math for non-bridges", "[Flow]") {
|
||||||
GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") {
|
GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") {
|
||||||
ConfigOptionFloatOrPercent width(1.0, false);
|
ConfigOptionFloatOrPercent width(1.0, false);
|
||||||
float spacing {0.4};
|
float spacing = 0.4f;
|
||||||
float nozzle_diameter {0.4};
|
float nozzle_diameter = 0.4f;
|
||||||
float bridge_flow {1.0};
|
float bridge_flow = 0.f;
|
||||||
float layer_height {0.5};
|
float layer_height = 0.5f;
|
||||||
|
|
||||||
// Spacing for non-bridges is has some overlap
|
// Spacing for non-bridges is has some overlap
|
||||||
THEN("External perimeter flow has spacing fixed to 1.1*nozzle_diameter") {
|
THEN("External perimeter flow has spacing fixed to 1.125 * nozzle_diameter") {
|
||||||
auto flow = Flow::new_from_config_width(frExternalPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, 0.0f);
|
auto flow = Flow::new_from_config_width(frExternalPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, bridge_flow);
|
||||||
REQUIRE(flow.spacing() == Approx((1.1*nozzle_diameter) - layer_height * (1.0 - PI / 4.0)));
|
REQUIRE(flow.spacing() == Approx(1.125 * nozzle_diameter - layer_height * (1.0 - PI / 4.0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
THEN("Internal perimeter flow has spacing of 1.05 (minimum)") {
|
THEN("Internal perimeter flow has spacing fixed to 1.125 * nozzle_diameter") {
|
||||||
auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, 0.0f);
|
auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, bridge_flow);
|
||||||
REQUIRE(flow.spacing() == Approx((1.05*nozzle_diameter) - layer_height * (1.0 - PI / 4.0)));
|
REQUIRE(flow.spacing() == Approx(1.125 *nozzle_diameter - layer_height * (1.0 - PI / 4.0)));
|
||||||
}
|
}
|
||||||
THEN("Spacing for supplied width is 0.8927f") {
|
THEN("Spacing for supplied width is 0.8927f") {
|
||||||
auto flow = Flow::new_from_config_width(frExternalPerimeter, width, nozzle_diameter, layer_height, 0.0f);
|
auto flow = Flow::new_from_config_width(frExternalPerimeter, width, nozzle_diameter, layer_height, bridge_flow);
|
||||||
REQUIRE(flow.spacing() == Approx(width.value - layer_height * (1.0 - PI / 4.0)));
|
REQUIRE(flow.spacing() == Approx(width.value - layer_height * (1.0 - PI / 4.0)));
|
||||||
flow = Flow::new_from_config_width(frPerimeter, width, nozzle_diameter, layer_height, 0.0f);
|
flow = Flow::new_from_config_width(frPerimeter, width, nozzle_diameter, layer_height, bridge_flow);
|
||||||
REQUIRE(flow.spacing() == Approx(width.value - layer_height * (1.0 - PI / 4.0)));
|
REQUIRE(flow.spacing() == Approx(width.value - layer_height * (1.0 - PI / 4.0)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Check the min/max
|
/// Check the min/max
|
||||||
GIVEN("Nozzle Diameter of 0.25") {
|
GIVEN("Nozzle Diameter of 0.25") {
|
||||||
float spacing {0.4};
|
float spacing = 0.4f;
|
||||||
float nozzle_diameter {0.25};
|
float nozzle_diameter = 0.25f;
|
||||||
float bridge_flow {0.0};
|
float bridge_flow = 0.f;
|
||||||
float layer_height {0.5};
|
float layer_height = 0.5f;
|
||||||
WHEN("layer height is set to 0.2") {
|
WHEN("layer height is set to 0.2") {
|
||||||
layer_height = 0.15f;
|
layer_height = 0.15f;
|
||||||
THEN("Max width is set.") {
|
THEN("Max width is set.") {
|
||||||
auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, 0.0f);
|
auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, bridge_flow);
|
||||||
REQUIRE(flow.width == Approx(1.4*nozzle_diameter));
|
REQUIRE(flow.width == Approx(1.125 * nozzle_diameter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("Layer height is set to 0.2") {
|
WHEN("Layer height is set to 0.2") {
|
||||||
layer_height = 0.3f;
|
layer_height = 0.3f;
|
||||||
THEN("Min width is set.") {
|
THEN("Min width is set.") {
|
||||||
auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, 0.0f);
|
auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, bridge_flow);
|
||||||
REQUIRE(flow.width == Approx(1.05*nozzle_diameter));
|
REQUIRE(flow.width == Approx(1.125 * nozzle_diameter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/// Check for an edge case in the maths where the spacing could be 0; original
|
/// Check for an edge case in the maths where the spacing could be 0; original
|
||||||
/// math is 0.99. Slic3r issue #4654
|
/// math is 0.99. Slic3r issue #4654
|
||||||
|
@ -156,13 +158,13 @@ SCENARIO("Flow: Flow math for non-bridges", "[!mayfail]") {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spacing, width calculation for bridge extrusions
|
/// Spacing, width calculation for bridge extrusions
|
||||||
SCENARIO("Flow: Flow math for bridges", "[!mayfail]") {
|
SCENARIO("Flow: Flow math for bridges", "[Flow]") {
|
||||||
GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") {
|
GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") {
|
||||||
auto width = ConfigOptionFloatOrPercent(1.0, false);
|
auto width = ConfigOptionFloatOrPercent(1.0, false);
|
||||||
double spacing = 0.4;
|
float spacing = 0.4f;
|
||||||
double nozzle_diameter = 0.4;
|
float nozzle_diameter = 0.4f;
|
||||||
double bridge_flow = 1.0;
|
float bridge_flow = 1.0f;
|
||||||
double layer_height = 0.5;
|
float layer_height = 0.5f;
|
||||||
WHEN("Flow role is frExternalPerimeter") {
|
WHEN("Flow role is frExternalPerimeter") {
|
||||||
auto flow = Flow::new_from_config_width(frExternalPerimeter, width, nozzle_diameter, layer_height, bridge_flow);
|
auto flow = Flow::new_from_config_width(frExternalPerimeter, width, nozzle_diameter, layer_height, bridge_flow);
|
||||||
THEN("Bridge width is same as nozzle diameter") {
|
THEN("Bridge width is same as nozzle diameter") {
|
||||||
|
|
|
@ -6,36 +6,7 @@
|
||||||
|
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
|
|
||||||
SCENARIO("lift() and unlift() behavior with large values of Z", "[!shouldfail]") {
|
SCENARIO("lift() is not ignored after unlift() at normal values of Z", "[GCodeWriter]") {
|
||||||
GIVEN("A config from a file and a single extruder.") {
|
|
||||||
GCodeWriter writer;
|
|
||||||
GCodeConfig &config = writer.config;
|
|
||||||
config.load(std::string(TEST_DATA_DIR) + "/fff_print_tests/test_gcodewriter/config_lift_unlift.ini");
|
|
||||||
|
|
||||||
std::vector<unsigned int> extruder_ids {0};
|
|
||||||
writer.set_extruders(extruder_ids);
|
|
||||||
writer.set_extruder(0);
|
|
||||||
|
|
||||||
WHEN("Z is set to 9007199254740992") {
|
|
||||||
double trouble_Z = 9007199254740992;
|
|
||||||
writer.travel_to_z(trouble_Z);
|
|
||||||
AND_WHEN("GcodeWriter::Lift() is called") {
|
|
||||||
REQUIRE(writer.lift().size() > 0);
|
|
||||||
AND_WHEN("Z is moved post-lift to the same delta as the config Z lift") {
|
|
||||||
REQUIRE(writer.travel_to_z(trouble_Z + config.retract_lift.values[0]).size() == 0);
|
|
||||||
AND_WHEN("GCodeWriter::Unlift() is called") {
|
|
||||||
REQUIRE(writer.unlift().size() == 0); // we're the same height so no additional move happens.
|
|
||||||
THEN("GCodeWriter::Lift() emits gcode.") {
|
|
||||||
REQUIRE(writer.lift().size() > 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SCENARIO("lift() is not ignored after unlift() at normal values of Z") {
|
|
||||||
GIVEN("A config from a file and a single extruder.") {
|
GIVEN("A config from a file and a single extruder.") {
|
||||||
GCodeWriter writer;
|
GCodeWriter writer;
|
||||||
GCodeConfig &config = writer.config;
|
GCodeConfig &config = writer.config;
|
||||||
|
@ -93,10 +64,11 @@ SCENARIO("lift() is not ignored after unlift() at normal values of Z") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// The test above will fail for trouble_Z == 9007199254740992, where trouble_Z + 1.5 will be rounded to trouble_Z + 2.0 due to double mantisa overflow.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SCENARIO("set_speed emits values with fixed-point output.") {
|
SCENARIO("set_speed emits values with fixed-point output.", "[GCodeWriter]") {
|
||||||
|
|
||||||
GIVEN("GCodeWriter instance") {
|
GIVEN("GCodeWriter instance") {
|
||||||
GCodeWriter writer;
|
GCodeWriter writer;
|
||||||
|
|
|
@ -40,7 +40,7 @@ SCENARIO("Model construction", "[Model]") {
|
||||||
REQUIRE((p2 - p1).norm() < EPSILON);
|
REQUIRE((p2 - p1).norm() < EPSILON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Slic3r::ModelInstance *model_instance = model_object->add_instance();
|
model_object->add_instance();
|
||||||
model.arrange_objects(PrintConfig::min_object_distance(&config));
|
model.arrange_objects(PrintConfig::min_object_distance(&config));
|
||||||
model.center_instances_around_point(Slic3r::Vec2d(100, 100));
|
model.center_instances_around_point(Slic3r::Vec2d(100, 100));
|
||||||
model_object->ensure_on_bed();
|
model_object->ensure_on_bed();
|
||||||
|
|
|
@ -10,31 +10,20 @@ using namespace Slic3r::Test;
|
||||||
|
|
||||||
SCENARIO("PrintObject: Perimeter generation", "[PrintObject]") {
|
SCENARIO("PrintObject: Perimeter generation", "[PrintObject]") {
|
||||||
GIVEN("20mm cube and default config") {
|
GIVEN("20mm cube and default config") {
|
||||||
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
|
||||||
TestMesh m = TestMesh::cube_20x20x20;
|
|
||||||
Slic3r::Model model;
|
|
||||||
size_t event_counter = 0;
|
|
||||||
std::string stage;
|
|
||||||
int value = 0;
|
|
||||||
auto callback = [&event_counter, &stage, &value] (int a, const char* b) { stage = std::string(b); ++ event_counter; value = a; };
|
|
||||||
config.set_deserialize("fill_density", "0");
|
|
||||||
|
|
||||||
WHEN("make_perimeters() is called") {
|
WHEN("make_perimeters() is called") {
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
|
Slic3r::Print print;
|
||||||
print->process();
|
Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, { { "fill_density", 0 } });
|
||||||
const PrintObject& object = *(print->objects().at(0));
|
const PrintObject &object = *print.objects().front();
|
||||||
THEN("67 layers exist in the model") {
|
THEN("67 layers exist in the model") {
|
||||||
REQUIRE(object.layers().size() == 66);
|
REQUIRE(object.layers().size() == 66);
|
||||||
}
|
}
|
||||||
THEN("Every layer in region 0 has 1 island of perimeters") {
|
THEN("Every layer in region 0 has 1 island of perimeters") {
|
||||||
for (const Layer *layer : object.layers()) {
|
for (const Layer *layer : object.layers())
|
||||||
REQUIRE(layer->regions().front()->perimeters.entities.size() == 1);
|
REQUIRE(layer->regions().front()->perimeters.entities.size() == 1);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
THEN("Every layer in region 0 has 3 paths in its perimeters list.") {
|
THEN("Every layer in region 0 has 3 paths in its perimeters list.") {
|
||||||
for (const Layer *layer : object.layers()) {
|
for (const Layer *layer : object.layers())
|
||||||
REQUIRE(layer->regions().front()->perimeters.items_count() == 3);
|
REQUIRE(layer->regions().front()->perimeters.items_count() == 3);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,66 +31,59 @@ SCENARIO("PrintObject: Perimeter generation", "[PrintObject]") {
|
||||||
|
|
||||||
SCENARIO("Print: Skirt generation", "[Print]") {
|
SCENARIO("Print: Skirt generation", "[Print]") {
|
||||||
GIVEN("20mm cube and default config") {
|
GIVEN("20mm cube and default config") {
|
||||||
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
|
||||||
TestMesh m = TestMesh::cube_20x20x20;
|
|
||||||
Slic3r::Model model;
|
|
||||||
std::string stage;
|
|
||||||
int value = 0;
|
|
||||||
config.opt_int("skirt_height") = 1;
|
|
||||||
config.opt_float("skirt_distance") = 1.f;
|
|
||||||
WHEN("Skirts is set to 2 loops") {
|
WHEN("Skirts is set to 2 loops") {
|
||||||
config.opt_int("skirts") = 2;
|
Slic3r::Print print;
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
|
Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, {
|
||||||
print->process();
|
{ "skirt_height", 1 },
|
||||||
|
{ "skirt_distance", 1 },
|
||||||
|
{ "skirts", 2 }
|
||||||
|
});
|
||||||
THEN("Skirt Extrusion collection has 2 loops in it") {
|
THEN("Skirt Extrusion collection has 2 loops in it") {
|
||||||
REQUIRE(print->skirt().items_count() == 2);
|
REQUIRE(print.skirt().items_count() == 2);
|
||||||
REQUIRE(print->skirt().flatten().entities.size() == 2);
|
REQUIRE(print.skirt().flatten().entities.size() == 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_is_solid_infill(std::shared_ptr<Slic3r::Print> p, size_t obj_id, size_t layer_id ) {
|
|
||||||
const PrintObject &obj = *(p->objects().at(obj_id));
|
|
||||||
const Layer &layer = *(obj.get_layer((int)layer_id));
|
|
||||||
|
|
||||||
// iterate over all of the regions in the layer
|
|
||||||
for (const LayerRegion *reg : layer.regions()) {
|
|
||||||
// for each region, iterate over the fill surfaces
|
|
||||||
for (const Surface& s : reg->fill_surfaces.surfaces) {
|
|
||||||
CHECK(s.is_solid());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SCENARIO("Print: Changing number of solid surfaces does not cause all surfaces to become internal.", "[Print]") {
|
SCENARIO("Print: Changing number of solid surfaces does not cause all surfaces to become internal.", "[Print]") {
|
||||||
GIVEN("sliced 20mm cube and config with top_solid_surfaces = 2 and bottom_solid_surfaces = 1") {
|
GIVEN("sliced 20mm cube and config with top_solid_surfaces = 2 and bottom_solid_surfaces = 1") {
|
||||||
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||||
TestMesh m = TestMesh::cube_20x20x20;
|
config.set_deserialize({
|
||||||
config.opt_int("top_solid_layers") = 2;
|
{ "top_solid_layers", 2 },
|
||||||
config.opt_int("bottom_solid_layers") = 1;
|
{ "bottom_solid_layers", 1 },
|
||||||
config.opt_float("layer_height") = 0.5; // get a known number of layers
|
{ "layer_height", 0.5 }, // get a known number of layers
|
||||||
config.set_deserialize("first_layer_height", "0.5");
|
{ "first_layer_height", 0.5 }
|
||||||
|
});
|
||||||
|
Slic3r::Print print;
|
||||||
Slic3r::Model model;
|
Slic3r::Model model;
|
||||||
std::string stage;
|
Slic3r::Test::init_print({TestMesh::cube_20x20x20}, print, model, config);
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
|
|
||||||
print->process();
|
|
||||||
// Precondition: Ensure that the model has 2 solid top layers (39, 38)
|
// Precondition: Ensure that the model has 2 solid top layers (39, 38)
|
||||||
// and one solid bottom layer (0).
|
// and one solid bottom layer (0).
|
||||||
test_is_solid_infill(print, 0, 0); // should be solid
|
auto test_is_solid_infill = [&print](size_t obj_id, size_t layer_id) {
|
||||||
test_is_solid_infill(print, 0, 39); // should be solid
|
const Layer &layer = *(print.objects().at(obj_id)->get_layer((int)layer_id));
|
||||||
test_is_solid_infill(print, 0, 38); // should be solid
|
// iterate over all of the regions in the layer
|
||||||
|
for (const LayerRegion *region : layer.regions()) {
|
||||||
|
// for each region, iterate over the fill surfaces
|
||||||
|
for (const Surface &surface : region->fill_surfaces.surfaces)
|
||||||
|
CHECK(surface.is_solid());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
print.process();
|
||||||
|
test_is_solid_infill(0, 0); // should be solid
|
||||||
|
test_is_solid_infill(0, 39); // should be solid
|
||||||
|
test_is_solid_infill(0, 38); // should be solid
|
||||||
WHEN("Model is re-sliced with top_solid_layers == 3") {
|
WHEN("Model is re-sliced with top_solid_layers == 3") {
|
||||||
config.opt_int("top_solid_layers") = 3;
|
config.set("top_solid_layers", 3);
|
||||||
print->apply(model, config);
|
print.apply(model, config);
|
||||||
print->process();
|
print.process();
|
||||||
THEN("Print object does not have 0 solid bottom layers.") {
|
THEN("Print object does not have 0 solid bottom layers.") {
|
||||||
test_is_solid_infill(print, 0, 0);
|
test_is_solid_infill(0, 0);
|
||||||
}
|
}
|
||||||
AND_THEN("Print object has 3 top solid layers") {
|
AND_THEN("Print object has 3 top solid layers") {
|
||||||
test_is_solid_infill(print, 0, 39);
|
test_is_solid_infill(0, 39);
|
||||||
test_is_solid_infill(print, 0, 38);
|
test_is_solid_infill(0, 38);
|
||||||
test_is_solid_infill(print, 0, 37);
|
test_is_solid_infill(0, 37);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,35 +91,36 @@ SCENARIO("Print: Changing number of solid surfaces does not cause all surfaces t
|
||||||
|
|
||||||
SCENARIO("Print: Brim generation", "[Print]") {
|
SCENARIO("Print: Brim generation", "[Print]") {
|
||||||
GIVEN("20mm cube and default config, 1mm first layer width") {
|
GIVEN("20mm cube and default config, 1mm first layer width") {
|
||||||
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
|
||||||
TestMesh m = TestMesh::cube_20x20x20;
|
|
||||||
Slic3r::Model model;
|
|
||||||
std::string stage;
|
|
||||||
int value = 0;
|
|
||||||
config.set_deserialize("first_layer_extrusion_width", "1");
|
|
||||||
WHEN("Brim is set to 3mm") {
|
WHEN("Brim is set to 3mm") {
|
||||||
config.opt_float("brim_width") = 3;
|
Slic3r::Print print;
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
|
Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, {
|
||||||
print->process();
|
{ "first_layer_extrusion_width", 1 },
|
||||||
|
{ "brim_width", 3 }
|
||||||
|
});
|
||||||
THEN("Brim Extrusion collection has 3 loops in it") {
|
THEN("Brim Extrusion collection has 3 loops in it") {
|
||||||
REQUIRE(print->brim().items_count() == 3);
|
REQUIRE(print.brim().items_count() == 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("Brim is set to 6mm") {
|
WHEN("Brim is set to 6mm") {
|
||||||
config.opt_float("brim_width") = 6;
|
Slic3r::Print print;
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
|
Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, {
|
||||||
print->process();
|
{ "first_layer_extrusion_width", 1 },
|
||||||
|
{ "brim_width", 6 }
|
||||||
|
});
|
||||||
THEN("Brim Extrusion collection has 6 loops in it") {
|
THEN("Brim Extrusion collection has 6 loops in it") {
|
||||||
REQUIRE(print->brim().items_count() == 6);
|
REQUIRE(print.brim().items_count() == 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("Brim is set to 6mm, extrusion width 0.5mm") {
|
WHEN("Brim is set to 6mm, extrusion width 0.5mm") {
|
||||||
config.opt_float("brim_width") = 6;
|
Slic3r::Print print;
|
||||||
config.set_deserialize("first_layer_extrusion_width", "0.5");
|
Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, {
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
|
{ "first_layer_extrusion_width", 1 },
|
||||||
print->process();
|
{ "brim_width", 6 },
|
||||||
|
{ "first_layer_extrusion_width", 0.5 }
|
||||||
|
});
|
||||||
|
print.process();
|
||||||
THEN("Brim Extrusion collection has 12 loops in it") {
|
THEN("Brim Extrusion collection has 12 loops in it") {
|
||||||
REQUIRE(print->brim().items_count() == 14);
|
REQUIRE(print.brim().items_count() == 14);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,27 +6,27 @@
|
||||||
#include "test_data.hpp"
|
#include "test_data.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <regex>
|
#include <boost/regex.hpp>
|
||||||
|
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
using namespace Slic3r::Test;
|
using namespace Slic3r::Test;
|
||||||
|
|
||||||
std::regex perimeters_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; perimeter");
|
boost::regex perimeters_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; perimeter");
|
||||||
std::regex infill_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; infill");
|
boost::regex infill_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; infill");
|
||||||
std::regex skirt_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; skirt");
|
boost::regex skirt_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; skirt");
|
||||||
|
|
||||||
SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
|
SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
|
||||||
GIVEN("A default configuration and a print test object") {
|
GIVEN("A default configuration and a print test object") {
|
||||||
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
|
||||||
|
|
||||||
WHEN("the output is executed with no support material") {
|
WHEN("the output is executed with no support material") {
|
||||||
config.set_deserialize("layer_height", "0.2");
|
Slic3r::Print print;
|
||||||
config.set_deserialize("first_layer_height", "0.2");
|
|
||||||
config.set_deserialize("first_layer_extrusion_width", "0");
|
|
||||||
config.set_deserialize("gcode_comments", "1");
|
|
||||||
config.set_deserialize("start_gcode", "");
|
|
||||||
Slic3r::Model model;
|
Slic3r::Model model;
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
Slic3r::Test::init_print({TestMesh::cube_20x20x20}, print, model, {
|
||||||
|
{ "layer_height", 0.2 },
|
||||||
|
{ "first_layer_height", 0.2 },
|
||||||
|
{ "first_layer_extrusion_width", 0 },
|
||||||
|
{ "gcode_comments", true },
|
||||||
|
{ "start_gcode", "" }
|
||||||
|
});
|
||||||
std::string gcode = Slic3r::Test::gcode(print);
|
std::string gcode = Slic3r::Test::gcode(print);
|
||||||
THEN("Some text output is generated.") {
|
THEN("Some text output is generated.") {
|
||||||
REQUIRE(gcode.size() > 0);
|
REQUIRE(gcode.size() > 0);
|
||||||
|
@ -61,21 +61,21 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
|
||||||
REQUIRE(gcode.find("; fill_density") != std::string::npos);
|
REQUIRE(gcode.find("; fill_density") != std::string::npos);
|
||||||
}
|
}
|
||||||
THEN("Infill is emitted.") {
|
THEN("Infill is emitted.") {
|
||||||
std::smatch has_match;
|
boost::smatch has_match;
|
||||||
REQUIRE(std::regex_search(gcode, has_match, infill_regex));
|
REQUIRE(boost::regex_search(gcode, has_match, infill_regex));
|
||||||
}
|
}
|
||||||
THEN("Perimeters are emitted.") {
|
THEN("Perimeters are emitted.") {
|
||||||
std::smatch has_match;
|
boost::smatch has_match;
|
||||||
REQUIRE(std::regex_search(gcode, has_match, perimeters_regex));
|
REQUIRE(boost::regex_search(gcode, has_match, perimeters_regex));
|
||||||
}
|
}
|
||||||
THEN("Skirt is emitted.") {
|
THEN("Skirt is emitted.") {
|
||||||
std::smatch has_match;
|
boost::smatch has_match;
|
||||||
REQUIRE(std::regex_search(gcode, has_match, skirt_regex));
|
REQUIRE(boost::regex_search(gcode, has_match, skirt_regex));
|
||||||
}
|
}
|
||||||
THEN("final Z height is 20mm") {
|
THEN("final Z height is 20mm") {
|
||||||
double final_z = 0.0;
|
double final_z = 0.0;
|
||||||
GCodeReader reader;
|
GCodeReader reader;
|
||||||
reader.apply_config(print->config());
|
reader.apply_config(print.config());
|
||||||
reader.parse_buffer(gcode, [&final_z] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
|
reader.parse_buffer(gcode, [&final_z] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
|
||||||
final_z = std::max<double>(final_z, static_cast<double>(self.z())); // record the highest Z point we reach
|
final_z = std::max<double>(final_z, static_cast<double>(self.z())); // record the highest Z point we reach
|
||||||
});
|
});
|
||||||
|
@ -83,31 +83,33 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("output is executed with complete objects and two differently-sized meshes") {
|
WHEN("output is executed with complete objects and two differently-sized meshes") {
|
||||||
|
Slic3r::Print print;
|
||||||
Slic3r::Model model;
|
Slic3r::Model model;
|
||||||
config.set_deserialize("first_layer_extrusion_width", "0");
|
Slic3r::Test::init_print({TestMesh::cube_20x20x20,TestMesh::cube_20x20x20}, print, model, {
|
||||||
config.set_deserialize("first_layer_height", "0.3");
|
{ "first_layer_extrusion_width", 0 },
|
||||||
config.set_deserialize("layer_height", "0.2");
|
{ "first_layer_height", 0.3 },
|
||||||
config.set_deserialize("support_material", "0");
|
{ "layer_height", 0.2 },
|
||||||
config.set_deserialize("raft_layers", "0");
|
{ "support_material", false },
|
||||||
config.set_deserialize("complete_objects", "1");
|
{ "raft_layers", 0 },
|
||||||
config.set_deserialize("gcode_comments", "1");
|
{ "complete_objects", true },
|
||||||
config.set_deserialize("between_objects_gcode", "; between-object-gcode");
|
{ "gcode_comments", true },
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20,TestMesh::cube_20x20x20}, model, config);
|
{ "between_objects_gcode", "; between-object-gcode" }
|
||||||
|
});
|
||||||
std::string gcode = Slic3r::Test::gcode(print);
|
std::string gcode = Slic3r::Test::gcode(print);
|
||||||
THEN("Some text output is generated.") {
|
THEN("Some text output is generated.") {
|
||||||
REQUIRE(gcode.size() > 0);
|
REQUIRE(gcode.size() > 0);
|
||||||
}
|
}
|
||||||
THEN("Infill is emitted.") {
|
THEN("Infill is emitted.") {
|
||||||
std::smatch has_match;
|
boost::smatch has_match;
|
||||||
REQUIRE(std::regex_search(gcode, has_match, infill_regex));
|
REQUIRE(boost::regex_search(gcode, has_match, infill_regex));
|
||||||
}
|
}
|
||||||
THEN("Perimeters are emitted.") {
|
THEN("Perimeters are emitted.") {
|
||||||
std::smatch has_match;
|
boost::smatch has_match;
|
||||||
REQUIRE(std::regex_search(gcode, has_match, perimeters_regex));
|
REQUIRE(boost::regex_search(gcode, has_match, perimeters_regex));
|
||||||
}
|
}
|
||||||
THEN("Skirt is emitted.") {
|
THEN("Skirt is emitted.") {
|
||||||
std::smatch has_match;
|
boost::smatch has_match;
|
||||||
REQUIRE(std::regex_search(gcode, has_match, skirt_regex));
|
REQUIRE(boost::regex_search(gcode, has_match, skirt_regex));
|
||||||
}
|
}
|
||||||
THEN("Between-object-gcode is emitted.") {
|
THEN("Between-object-gcode is emitted.") {
|
||||||
REQUIRE(gcode.find("; between-object-gcode") != std::string::npos);
|
REQUIRE(gcode.find("; between-object-gcode") != std::string::npos);
|
||||||
|
@ -115,7 +117,7 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
|
||||||
THEN("final Z height is 20.1mm") {
|
THEN("final Z height is 20.1mm") {
|
||||||
double final_z = 0.0;
|
double final_z = 0.0;
|
||||||
GCodeReader reader;
|
GCodeReader reader;
|
||||||
reader.apply_config(print->config());
|
reader.apply_config(print.config());
|
||||||
reader.parse_buffer(gcode, [&final_z] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
|
reader.parse_buffer(gcode, [&final_z] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
|
||||||
final_z = std::max(final_z, static_cast<double>(self.z())); // record the highest Z point we reach
|
final_z = std::max(final_z, static_cast<double>(self.z())); // record the highest Z point we reach
|
||||||
});
|
});
|
||||||
|
@ -125,7 +127,7 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
|
||||||
double final_z = 0.0;
|
double final_z = 0.0;
|
||||||
bool reset = false;
|
bool reset = false;
|
||||||
GCodeReader reader;
|
GCodeReader reader;
|
||||||
reader.apply_config(print->config());
|
reader.apply_config(print.config());
|
||||||
reader.parse_buffer(gcode, [&final_z, &reset] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
|
reader.parse_buffer(gcode, [&final_z, &reset] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
|
||||||
if (final_z > 0 && std::abs(self.z() - 0.3) < 0.01 ) { // saw higher Z before this, now it's lower
|
if (final_z > 0 && std::abs(self.z() - 0.3) < 0.01 ) { // saw higher Z before this, now it's lower
|
||||||
reset = true;
|
reset = true;
|
||||||
|
@ -139,7 +141,7 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
|
||||||
double final_z = 0.0;
|
double final_z = 0.0;
|
||||||
bool reset = false;
|
bool reset = false;
|
||||||
GCodeReader reader;
|
GCodeReader reader;
|
||||||
reader.apply_config(print->config());
|
reader.apply_config(print.config());
|
||||||
reader.parse_buffer(gcode, [&final_z, &reset] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
|
reader.parse_buffer(gcode, [&final_z, &reset] (GCodeReader& self, const GCodeReader::GCodeLine& line) {
|
||||||
if (final_z > 0 && std::abs(self.z() - 0.3) < 0.01 ) {
|
if (final_z > 0 && std::abs(self.z() - 0.3) < 0.01 ) {
|
||||||
reset = (final_z > 20.0);
|
reset = (final_z > 20.0);
|
||||||
|
@ -151,13 +153,12 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("the output is executed with support material") {
|
WHEN("the output is executed with support material") {
|
||||||
Slic3r::Model model;
|
std::string gcode = ::Test::slice({TestMesh::cube_20x20x20}, {
|
||||||
config.set_deserialize("first_layer_extrusion_width", "0");
|
{ "first_layer_extrusion_width", 0 },
|
||||||
config.set_deserialize("support_material", "1");
|
{ "support_material", true },
|
||||||
config.set_deserialize("raft_layers", "3");
|
{ "raft_layers", 3 },
|
||||||
config.set_deserialize("gcode_comments", "1");
|
{ "gcode_comments", true }
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
});
|
||||||
std::string gcode = Slic3r::Test::gcode(print);
|
|
||||||
THEN("Some text output is generated.") {
|
THEN("Some text output is generated.") {
|
||||||
REQUIRE(gcode.size() > 0);
|
REQUIRE(gcode.size() > 0);
|
||||||
}
|
}
|
||||||
|
@ -175,10 +176,9 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("the output is executed with a separate first layer extrusion width") {
|
WHEN("the output is executed with a separate first layer extrusion width") {
|
||||||
Slic3r::Model model;
|
std::string gcode = ::Test::slice({ TestMesh::cube_20x20x20 }, {
|
||||||
config.set_deserialize("first_layer_extrusion_width", "0.5");
|
{ "first_layer_extrusion_width", "0.5" }
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
});
|
||||||
std::string gcode = Slic3r::Test::gcode(print);
|
|
||||||
THEN("Some text output is generated.") {
|
THEN("Some text output is generated.") {
|
||||||
REQUIRE(gcode.size() > 0);
|
REQUIRE(gcode.size() > 0);
|
||||||
}
|
}
|
||||||
|
@ -193,48 +193,46 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("Cooling is enabled and the fan is disabled.") {
|
WHEN("Cooling is enabled and the fan is disabled.") {
|
||||||
config.set_deserialize("cooling", "1");
|
std::string gcode = ::Test::slice({ TestMesh::cube_20x20x20 }, {
|
||||||
config.set_deserialize("disable_fan_first_layers", "5");
|
{ "cooling", true },
|
||||||
Slic3r::Model model;
|
{ "disable_fan_first_layers", 5 }
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
});
|
||||||
std::string gcode = Slic3r::Test::gcode(print);
|
|
||||||
THEN("GCode to disable fan is emitted."){
|
THEN("GCode to disable fan is emitted."){
|
||||||
REQUIRE(gcode.find("M107") != std::string::npos);
|
REQUIRE(gcode.find("M107") != std::string::npos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("end_gcode exists with layer_num and layer_z") {
|
WHEN("end_gcode exists with layer_num and layer_z") {
|
||||||
config.set_deserialize("end_gcode", "; Layer_num [layer_num]\n; Layer_z [layer_z]");
|
std::string gcode = ::Test::slice({ TestMesh::cube_20x20x20 }, {
|
||||||
config.set_deserialize("layer_height", "0.1");
|
{ "end_gcode", "; Layer_num [layer_num]\n; Layer_z [layer_z]" },
|
||||||
config.set_deserialize("first_layer_height", "0.1");
|
{ "layer_height", 0.1 },
|
||||||
|
{ "first_layer_height", 0.1 }
|
||||||
Slic3r::Model model;
|
});
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
|
||||||
std::string gcode = Slic3r::Test::gcode(print);
|
|
||||||
THEN("layer_num and layer_z are processed in the end gcode") {
|
THEN("layer_num and layer_z are processed in the end gcode") {
|
||||||
REQUIRE(gcode.find("; Layer_num 199") != std::string::npos);
|
REQUIRE(gcode.find("; Layer_num 199") != std::string::npos);
|
||||||
REQUIRE(gcode.find("; Layer_z 20") != std::string::npos);
|
REQUIRE(gcode.find("; Layer_z 20") != std::string::npos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("current_extruder exists in start_gcode") {
|
WHEN("current_extruder exists in start_gcode") {
|
||||||
config.set_deserialize("start_gcode", "; Extruder [current_extruder]");
|
|
||||||
{
|
{
|
||||||
Slic3r::Model model;
|
std::string gcode = ::Test::slice({ TestMesh::cube_20x20x20 }, {
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
{ "start_gcode", "; Extruder [current_extruder]" }
|
||||||
std::string gcode = Slic3r::Test::gcode(print);
|
});
|
||||||
THEN("current_extruder is processed in the start gcode and set for first extruder") {
|
THEN("current_extruder is processed in the start gcode and set for first extruder") {
|
||||||
REQUIRE(gcode.find("; Extruder 0") != std::string::npos);
|
REQUIRE(gcode.find("; Extruder 0") != std::string::npos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
config.set_num_extruders(4);
|
|
||||||
config.set_deserialize("infill_extruder", "2");
|
|
||||||
config.set_deserialize("solid_infill_extruder", "2");
|
|
||||||
config.set_deserialize("perimeter_extruder", "2");
|
|
||||||
config.set_deserialize("support_material_extruder", "2");
|
|
||||||
config.set_deserialize("support_material_interface_extruder", "2");
|
|
||||||
{
|
{
|
||||||
Slic3r::Model model;
|
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
config.set_num_extruders(4);
|
||||||
std::string gcode = Slic3r::Test::gcode(print);
|
config.set_deserialize({
|
||||||
|
{ "start_gcode", "; Extruder [current_extruder]" },
|
||||||
|
{ "infill_extruder", 2 },
|
||||||
|
{ "solid_infill_extruder", 2 },
|
||||||
|
{ "perimeter_extruder", 2 },
|
||||||
|
{ "support_material_extruder", 2 },
|
||||||
|
{ "support_material_interface_extruder", 2 }
|
||||||
|
});
|
||||||
|
std::string gcode = Slic3r::Test::slice({TestMesh::cube_20x20x20}, config);
|
||||||
THEN("current_extruder is processed in the start gcode and set for second extruder") {
|
THEN("current_extruder is processed in the start gcode and set for second extruder") {
|
||||||
REQUIRE(gcode.find("; Extruder 1") != std::string::npos);
|
REQUIRE(gcode.find("; Extruder 1") != std::string::npos);
|
||||||
}
|
}
|
||||||
|
@ -242,15 +240,13 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
|
||||||
}
|
}
|
||||||
|
|
||||||
WHEN("layer_num represents the layer's index from z=0") {
|
WHEN("layer_num represents the layer's index from z=0") {
|
||||||
config.set_deserialize("complete_objects", "1");
|
std::string gcode = ::Test::slice({ TestMesh::cube_20x20x20, TestMesh::cube_20x20x20 }, {
|
||||||
config.set_deserialize("gcode_comments", "1");
|
{ "complete_objects", true },
|
||||||
config.set_deserialize("layer_gcode", ";Layer:[layer_num] ([layer_z] mm)");
|
{ "gcode_comments", true },
|
||||||
config.set_deserialize("layer_height", "1.0");
|
{ "layer_gcode", ";Layer:[layer_num] ([layer_z] mm)" },
|
||||||
config.set_deserialize("first_layer_height", "1.0");
|
{ "layer_height", 1.0 },
|
||||||
|
{ "first_layer_height", 1.0 }
|
||||||
Slic3r::Model model;
|
});
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({TestMesh::cube_20x20x20,TestMesh::cube_20x20x20}, model, config);
|
|
||||||
std::string gcode = Slic3r::Test::gcode(print);
|
|
||||||
// End of the 1st object.
|
// End of the 1st object.
|
||||||
size_t pos = gcode.find(";Layer:19 ");
|
size_t pos = gcode.find(";Layer:19 ");
|
||||||
THEN("First and second object last layer is emitted") {
|
THEN("First and second object last layer is emitted") {
|
||||||
|
|
|
@ -10,18 +10,14 @@ using namespace Slic3r::Test;
|
||||||
|
|
||||||
SCENARIO("PrintObject: object layer heights", "[PrintObject]") {
|
SCENARIO("PrintObject: object layer heights", "[PrintObject]") {
|
||||||
GIVEN("20mm cube and default initial config, initial layer height of 2mm") {
|
GIVEN("20mm cube and default initial config, initial layer height of 2mm") {
|
||||||
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
|
||||||
TestMesh m = TestMesh::cube_20x20x20;
|
|
||||||
Slic3r::Model model;
|
|
||||||
|
|
||||||
config.set_deserialize("first_layer_height", "2");
|
|
||||||
|
|
||||||
WHEN("generate_object_layers() is called for 2mm layer heights and nozzle diameter of 3mm") {
|
WHEN("generate_object_layers() is called for 2mm layer heights and nozzle diameter of 3mm") {
|
||||||
config.opt_float("nozzle_diameter", 0) = 3;
|
Slic3r::Print print;
|
||||||
config.opt_float("layer_height") = 2.0;
|
Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, {
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
|
{ "first_layer_height", 2 },
|
||||||
print->process();
|
{ "layer_height", 2 },
|
||||||
const std::vector<Slic3r::Layer*> &layers = print->objects().front()->layers();
|
{ "nozzle_diameter", 3 }
|
||||||
|
});
|
||||||
|
const std::vector<Slic3r::Layer*> &layers = print.objects().front()->layers();
|
||||||
THEN("The output vector has 10 entries") {
|
THEN("The output vector has 10 entries") {
|
||||||
REQUIRE(layers.size() == 10);
|
REQUIRE(layers.size() == 10);
|
||||||
}
|
}
|
||||||
|
@ -34,11 +30,13 @@ SCENARIO("PrintObject: object layer heights", "[PrintObject]") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("generate_object_layers() is called for 10mm layer heights and nozzle diameter of 11mm") {
|
WHEN("generate_object_layers() is called for 10mm layer heights and nozzle diameter of 11mm") {
|
||||||
config.opt_float("nozzle_diameter", 0) = 11;
|
Slic3r::Print print;
|
||||||
config.opt_float("layer_height") = 10;
|
Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, {
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
|
{ "first_layer_height", 2 },
|
||||||
print->process();
|
{ "layer_height", 10 },
|
||||||
const std::vector<Slic3r::Layer*> &layers = print->objects().front()->layers();
|
{ "nozzle_diameter", 11 }
|
||||||
|
});
|
||||||
|
const std::vector<Slic3r::Layer*> &layers = print.objects().front()->layers();
|
||||||
THEN("The output vector has 3 entries") {
|
THEN("The output vector has 3 entries") {
|
||||||
REQUIRE(layers.size() == 3);
|
REQUIRE(layers.size() == 3);
|
||||||
}
|
}
|
||||||
|
@ -50,11 +48,13 @@ SCENARIO("PrintObject: object layer heights", "[PrintObject]") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("generate_object_layers() is called for 15mm layer heights and nozzle diameter of 16mm") {
|
WHEN("generate_object_layers() is called for 15mm layer heights and nozzle diameter of 16mm") {
|
||||||
config.opt_float("nozzle_diameter", 0) = 16;
|
Slic3r::Print print;
|
||||||
config.opt_float("layer_height") = 15.0;
|
Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, {
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
|
{ "first_layer_height", 2 },
|
||||||
print->process();
|
{ "layer_height", 15 },
|
||||||
const std::vector<Slic3r::Layer*> &layers = print->objects().front()->layers();
|
{ "nozzle_diameter", 16 }
|
||||||
|
});
|
||||||
|
const std::vector<Slic3r::Layer*> &layers = print.objects().front()->layers();
|
||||||
THEN("The output vector has 2 entries") {
|
THEN("The output vector has 2 entries") {
|
||||||
REQUIRE(layers.size() == 2);
|
REQUIRE(layers.size() == 2);
|
||||||
}
|
}
|
||||||
|
@ -67,11 +67,13 @@ SCENARIO("PrintObject: object layer heights", "[PrintObject]") {
|
||||||
}
|
}
|
||||||
#if 0
|
#if 0
|
||||||
WHEN("generate_object_layers() is called for 15mm layer heights and nozzle diameter of 5mm") {
|
WHEN("generate_object_layers() is called for 15mm layer heights and nozzle diameter of 5mm") {
|
||||||
config.opt_float("nozzle_diameter", 0) = 5;
|
Slic3r::Print print;
|
||||||
config.opt_float("layer_height") = 15.0;
|
Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, {
|
||||||
std::shared_ptr<Slic3r::Print> print = Slic3r::Test::init_print({m}, model, config);
|
{ "first_layer_height", 2 },
|
||||||
print->process();
|
{ "layer_height", 15 },
|
||||||
const std::vector<Slic3r::Layer*> &layers = print->objects().front()->layers();
|
{ "nozzle_diameter", 5 }
|
||||||
|
});
|
||||||
|
const std::vector<Slic3r::Layer*> &layers = print.objects().front()->layers();
|
||||||
THEN("The layer height is limited to 5mm.") {
|
THEN("The layer height is limited to 5mm.") {
|
||||||
CHECK(layers.size() == 5);
|
CHECK(layers.size() == 5);
|
||||||
coordf_t last = 2.0;
|
coordf_t last = 2.0;
|
||||||
|
|
|
@ -12,251 +12,241 @@ using namespace Slic3r::Test;
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
|
|
||||||
/// Helper method to find the tool used for the brim (always the first extrusion)
|
/// Helper method to find the tool used for the brim (always the first extrusion)
|
||||||
int get_brim_tool(std::string &gcode, Slic3r::GCodeReader& parser) {
|
static int get_brim_tool(const std::string &gcode)
|
||||||
|
{
|
||||||
int brim_tool = -1;
|
int brim_tool = -1;
|
||||||
int tool = -1;
|
int tool = -1;
|
||||||
|
GCodeReader parser;
|
||||||
parser.parse_buffer(gcode, [&tool, &brim_tool] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
|
parser.parse_buffer(gcode, [&tool, &brim_tool] (Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line)
|
||||||
{
|
{
|
||||||
// if the command is a T command, set the the current tool
|
// if the command is a T command, set the the current tool
|
||||||
if (boost::starts_with(line.cmd(), "T")) {
|
if (boost::starts_with(line.cmd(), "T")) {
|
||||||
tool = atoi(line.cmd().data() + 1);
|
tool = atoi(line.cmd().data() + 1);
|
||||||
} else if (line.cmd() == "G1" && line.extruding(self) && line.dist_XY(self) > 0 && brim_tool < 0) {
|
} else if (line.cmd() == "G1" && line.extruding(self) && line.dist_XY(self) > 0 && brim_tool < 0) {
|
||||||
brim_tool = tool;
|
brim_tool = tool;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return brim_tool;
|
return brim_tool;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Skirt height is honored") {
|
TEST_CASE("Skirt height is honored") {
|
||||||
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||||
config.opt_int("skirts") = 1;
|
config.set_deserialize({
|
||||||
config.opt_int("skirt_height") = 5;
|
{ "skirts", 1 },
|
||||||
config.opt_int("perimeters") = 0;
|
{ "skirt_height", 5 },
|
||||||
config.opt_float("support_material_speed") = 99;
|
{ "perimeters", 0 },
|
||||||
|
{ "support_material_speed", 99 },
|
||||||
|
// avoid altering speeds unexpectedly
|
||||||
|
{ "cooling", false },
|
||||||
|
{ "first_layer_speed", "100%" }
|
||||||
|
});
|
||||||
|
|
||||||
// avoid altering speeds unexpectedly
|
std::string gcode;
|
||||||
config.set_deserialize("cooling", "0");
|
SECTION("printing a single object") {
|
||||||
config.set_deserialize("first_layer_speed", "100%");
|
gcode = Slic3r::Test::slice({TestMesh::cube_20x20x20}, config);
|
||||||
double support_speed = config.opt<Slic3r::ConfigOptionFloat>("support_material_speed")->value * MM_PER_MIN;
|
}
|
||||||
|
SECTION("printing multiple objects") {
|
||||||
|
gcode = Slic3r::Test::slice({TestMesh::cube_20x20x20, TestMesh::cube_20x20x20}, config);
|
||||||
|
}
|
||||||
|
|
||||||
std::map<double, bool> layers_with_skirt;
|
std::map<double, bool> layers_with_skirt;
|
||||||
std::string gcode;
|
double support_speed = config.opt<Slic3r::ConfigOptionFloat>("support_material_speed")->value * MM_PER_MIN;
|
||||||
GCodeReader parser;
|
GCodeReader parser;
|
||||||
Slic3r::Model model;
|
parser.parse_buffer(gcode, [&layers_with_skirt, &support_speed] (Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) {
|
||||||
|
|
||||||
SECTION("printing a single object") {
|
|
||||||
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
|
||||||
gcode = Slic3r::Test::gcode(print);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("printing multiple objects") {
|
|
||||||
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20, TestMesh::cube_20x20x20}, model, config);
|
|
||||||
gcode = Slic3r::Test::gcode(print);
|
|
||||||
}
|
|
||||||
parser.parse_buffer(gcode, [&layers_with_skirt, &support_speed] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
|
|
||||||
{
|
|
||||||
if (line.extruding(self) && self.f() == Approx(support_speed)) {
|
if (line.extruding(self) && self.f() == Approx(support_speed)) {
|
||||||
layers_with_skirt[self.z()] = 1;
|
layers_with_skirt[self.z()] = 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
REQUIRE(layers_with_skirt.size() == (size_t)config.opt_int("skirt_height"));
|
REQUIRE(layers_with_skirt.size() == (size_t)config.opt_int("skirt_height"));
|
||||||
}
|
}
|
||||||
|
|
||||||
SCENARIO("Original Slic3r Skirt/Brim tests", "[!mayfail]") {
|
SCENARIO("Original Slic3r Skirt/Brim tests", "[!mayfail]") {
|
||||||
Slic3r::GCodeReader parser;
|
|
||||||
Slic3r::Model model;
|
|
||||||
std::string gcode;
|
|
||||||
GIVEN("A default configuration") {
|
GIVEN("A default configuration") {
|
||||||
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||||
config.set_num_extruders(4);
|
config.set_num_extruders(4);
|
||||||
config.opt_float("support_material_speed") = 99;
|
config.set_deserialize({
|
||||||
config.set_deserialize("first_layer_height", "0.3");
|
{ "support_material_speed", 99 },
|
||||||
config.set_deserialize("gcode_comments", "1");
|
{ "first_layer_height", 0.3 },
|
||||||
|
{ "gcode_comments", true },
|
||||||
// avoid altering speeds unexpectedly
|
// avoid altering speeds unexpectedly
|
||||||
config.set_deserialize("cooling", "0");
|
{ "cooling", false },
|
||||||
config.set_deserialize("first_layer_speed", "100%");
|
{ "first_layer_speed", "100%" },
|
||||||
// remove noise from top/solid layers
|
// remove noise from top/solid layers
|
||||||
config.opt_int("top_solid_layers") = 0;
|
{ "top_solid_layers", 0 },
|
||||||
config.opt_int("bottom_solid_layers") = 1;
|
{ "bottom_solid_layers", 1 }
|
||||||
|
});
|
||||||
|
|
||||||
WHEN("Brim width is set to 5") {
|
WHEN("Brim width is set to 5") {
|
||||||
config.opt_int("perimeters") = 0;
|
config.set_deserialize({
|
||||||
config.opt_int("skirts") = 0;
|
{ "perimeters", 0 },
|
||||||
config.opt_float("brim_width") = 5;
|
{ "skirts", 0 },
|
||||||
THEN("Brim is generated") {
|
{ "brim_width", 5 }
|
||||||
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
});
|
||||||
gcode = Slic3r::Test::gcode(print);
|
THEN("Brim is generated") {
|
||||||
|
std::string gcode = Slic3r::Test::slice({TestMesh::cube_20x20x20}, config);
|
||||||
bool brim_generated = false;
|
bool brim_generated = false;
|
||||||
double support_speed = config.opt<Slic3r::ConfigOptionFloat>("support_material_speed")->value * MM_PER_MIN;
|
double support_speed = config.opt<Slic3r::ConfigOptionFloat>("support_material_speed")->value * MM_PER_MIN;
|
||||||
parser.parse_buffer(gcode, [&brim_generated, support_speed] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
|
Slic3r::GCodeReader parser;
|
||||||
{
|
parser.parse_buffer(gcode, [&brim_generated, support_speed] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line) {
|
||||||
if (self.z() == Approx(0.3) || line.new_Z(self) == Approx(0.3)) {
|
if (self.z() == Approx(0.3) || line.new_Z(self) == Approx(0.3)) {
|
||||||
if (line.extruding(self) && self.f() == Approx(support_speed)) {
|
if (line.extruding(self) && self.f() == Approx(support_speed)) {
|
||||||
brim_generated = true;
|
brim_generated = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
});
|
||||||
REQUIRE(brim_generated);
|
REQUIRE(brim_generated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WHEN("Skirt area is smaller than the brim") {
|
WHEN("Skirt area is smaller than the brim") {
|
||||||
config.opt_int("skirts") = 1;
|
config.set_deserialize({
|
||||||
config.opt_float("brim_width") = 10;
|
{ "skirts", 1 },
|
||||||
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
{ "brim_width", 10}
|
||||||
|
});
|
||||||
THEN("Gcode generates") {
|
THEN("Gcode generates") {
|
||||||
REQUIRE(! Slic3r::Test::gcode(print).empty());
|
REQUIRE(! Slic3r::Test::slice({TestMesh::cube_20x20x20}, config).empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WHEN("Skirt height is 0 and skirts > 0") {
|
WHEN("Skirt height is 0 and skirts > 0") {
|
||||||
config.opt_int("skirts") = 2;
|
config.set_deserialize({
|
||||||
config.opt_int("skirt_height") = 0;
|
{ "skirts", 2 },
|
||||||
|
{ "skirt_height", 0 }
|
||||||
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
});
|
||||||
THEN("Gcode generates") {
|
THEN("Gcode generates") {
|
||||||
REQUIRE(! Slic3r::Test::gcode(print).empty());
|
REQUIRE(! Slic3r::Test::slice({TestMesh::cube_20x20x20}, config).empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WHEN("Perimeter extruder = 2 and support extruders = 3") {
|
WHEN("Perimeter extruder = 2 and support extruders = 3") {
|
||||||
config.opt_int("skirts") = 0;
|
|
||||||
config.opt_float("brim_width") = 5;
|
|
||||||
config.opt_int("perimeter_extruder") = 2;
|
|
||||||
config.opt_int("support_material_extruder") = 3;
|
|
||||||
THEN("Brim is printed with the extruder used for the perimeters of first object") {
|
THEN("Brim is printed with the extruder used for the perimeters of first object") {
|
||||||
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
std::string gcode = Slic3r::Test::slice({TestMesh::cube_20x20x20}, {
|
||||||
gcode = Slic3r::Test::gcode(print);
|
{ "skirts", 0 },
|
||||||
int tool = get_brim_tool(gcode, parser);
|
{ "brim_width", 5 },
|
||||||
|
{ "perimeter_extruder", 2 },
|
||||||
|
{ "support_material_extruder", 3 }
|
||||||
|
});
|
||||||
|
int tool = get_brim_tool(gcode);
|
||||||
REQUIRE(tool == config.opt_int("perimeter_extruder") - 1);
|
REQUIRE(tool == config.opt_int("perimeter_extruder") - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("Perimeter extruder = 2, support extruders = 3, raft is enabled") {
|
WHEN("Perimeter extruder = 2, support extruders = 3, raft is enabled") {
|
||||||
config.opt_int("skirts") = 0;
|
|
||||||
config.opt_float("brim_width") = 5;
|
|
||||||
config.opt_int("perimeter_extruder") = 2;
|
|
||||||
config.opt_int("support_material_extruder") = 3;
|
|
||||||
config.opt_int("raft_layers") = 1;
|
|
||||||
THEN("brim is printed with same extruder as skirt") {
|
THEN("brim is printed with same extruder as skirt") {
|
||||||
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
std::string gcode = Slic3r::Test::slice({TestMesh::cube_20x20x20}, {
|
||||||
gcode = Slic3r::Test::gcode(print);
|
{ "skirts", 0 },
|
||||||
int tool = get_brim_tool(gcode, parser);
|
{ "brim_width", 5 },
|
||||||
|
{ "perimeter_extruder", 2 },
|
||||||
|
{ "support_material_extruder", 3 },
|
||||||
|
{ "raft_layers", 1 }
|
||||||
|
});
|
||||||
|
int tool = get_brim_tool(gcode);
|
||||||
REQUIRE(tool == config.opt_int("support_material_extruder") - 1);
|
REQUIRE(tool == config.opt_int("support_material_extruder") - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("brim width to 1 with layer_width of 0.5") {
|
WHEN("brim width to 1 with layer_width of 0.5") {
|
||||||
config.opt_int("skirts") = 0;
|
config.set_deserialize({
|
||||||
config.set_deserialize("first_layer_extrusion_width", "0.5");
|
{ "skirts", 0 },
|
||||||
config.opt_float("brim_width") = 1;
|
{ "first_layer_extrusion_width", 0.5 },
|
||||||
|
{ "brim_width", 1 }
|
||||||
|
});
|
||||||
THEN("2 brim lines") {
|
THEN("2 brim lines") {
|
||||||
Slic3r::Model model;
|
Slic3r::Print print;
|
||||||
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, config);
|
||||||
print->process();
|
REQUIRE(print.brim().entities.size() == 2);
|
||||||
REQUIRE(print->brim().entities.size() == 2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
WHEN("brim ears on a square") {
|
WHEN("brim ears on a square") {
|
||||||
config.opt_int("skirts") = 0);
|
config.set_deserialize({
|
||||||
config.set_deserialize("first_layer_extrusion_width", "0.5");
|
{ "skirts", 0 },
|
||||||
config.opt_float("brim_width") = 1;
|
{ "first_layer_extrusion_width", 0.5 },
|
||||||
config.set("brim_ears", true);
|
{ "brim_width", 1 },
|
||||||
config.set("brim_ears_max_angle", 91);
|
{ "brim_ears", 1 },
|
||||||
|
{ "brim_ears_max_angle", 91 }
|
||||||
Slic3r::Model model;
|
});
|
||||||
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
Slic3r::Print print;
|
||||||
print->process();
|
Slic3r::Test::init_and_process_print({TestMesh::cube_20x20x20}, print, config);
|
||||||
|
|
||||||
THEN("Four brim ears") {
|
THEN("Four brim ears") {
|
||||||
REQUIRE(print->brim.size() == 4);
|
REQUIRE(print.brim().entities.size() == 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WHEN("brim ears on a square but with a too small max angle") {
|
WHEN("brim ears on a square but with a too small max angle") {
|
||||||
config.set("skirts", 0);
|
config.set_deserialize({
|
||||||
config.set("first_layer_extrusion_width", 0.5);
|
{ "skirts", 0 },
|
||||||
config.set("brim_width", 1);
|
{ "first_layer_extrusion_width", 0.5 },
|
||||||
config.set("brim_ears", true);
|
{ "brim_width", 1 },
|
||||||
config.set("brim_ears_max_angle", 89);
|
{ "brim_ears", 1 },
|
||||||
|
{ "brim_ears_max_angle", 89 }
|
||||||
|
});
|
||||||
THEN("no brim") {
|
THEN("no brim") {
|
||||||
Slic3r::Model model;
|
Slic3r::Print print;
|
||||||
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
Slic3r::Test::init_and_process_print({ TestMesh::cube_20x20x20 }, print, config);
|
||||||
print->process();
|
REQUIRE(print.brim().entities.size() == 0);
|
||||||
REQUIRE(print->brim.size() == 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
WHEN("Object is plated with overhang support and a brim") {
|
WHEN("Object is plated with overhang support and a brim") {
|
||||||
config.opt_float("layer_height") = 0.4;
|
config.set_deserialize({
|
||||||
config.set_deserialize("first_layer_height", "0.4");
|
{ "layer_height", 0.4 },
|
||||||
config.opt_int("skirts") = 1;
|
{ "first_layer_height", 0.4 },
|
||||||
config.opt_float("skirt_distance") = 0;
|
{ "skirts", 1 },
|
||||||
config.opt_float("support_material_speed") = 99;
|
{ "skirt_distance", 0 },
|
||||||
config.opt_int("perimeter_extruder") = 1;
|
{ "support_material_speed", 99 },
|
||||||
config.opt_int("support_material_extruder") = 2;
|
{ "perimeter_extruder", 1 },
|
||||||
config.opt_int("infill_extruder") = 3; // ensure that a tool command gets emitted.
|
{ "support_material_extruder", 2 },
|
||||||
config.set_deserialize("cooling", "0"); // to prevent speeds to be altered
|
{ "infill_extruder", 3 }, // ensure that a tool command gets emitted.
|
||||||
config.set_deserialize("first_layer_speed", "100%"); // to prevent speeds to be altered
|
{ "cooling", false }, // to prevent speeds to be altered
|
||||||
|
{ "first_layer_speed", "100%" }, // to prevent speeds to be altered
|
||||||
|
});
|
||||||
|
|
||||||
Slic3r::Model model;
|
THEN("overhang generates?") {
|
||||||
auto print = Slic3r::Test::init_print({TestMesh::overhang}, model, config);
|
//FIXME does it make sense?
|
||||||
print->process();
|
REQUIRE(! Slic3r::Test::slice({TestMesh::overhang}, config).empty());
|
||||||
|
}
|
||||||
|
|
||||||
// config.set("support_material", true); // to prevent speeds to be altered
|
// config.set("support_material", true); // to prevent speeds to be altered
|
||||||
|
|
||||||
THEN("skirt length is large enough to contain object with support") {
|
THEN("skirt length is large enough to contain object with support") {
|
||||||
CHECK(config.opt_bool("support_material")); // test is not valid if support material is off
|
CHECK(config.opt_bool("support_material")); // test is not valid if support material is off
|
||||||
double skirt_length = 0.0;
|
std::string gcode = Slic3r::Test::slice({TestMesh::cube_20x20x20}, config);
|
||||||
Points extrusion_points;
|
|
||||||
int tool = -1;
|
|
||||||
|
|
||||||
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
|
||||||
std::string gcode = Slic3r::Test::gcode(print);
|
|
||||||
|
|
||||||
double support_speed = config.opt<ConfigOptionFloat>("support_material_speed")->value * MM_PER_MIN;
|
double support_speed = config.opt<ConfigOptionFloat>("support_material_speed")->value * MM_PER_MIN;
|
||||||
parser.parse_buffer(gcode, [config, &extrusion_points, &tool, &skirt_length, support_speed] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line)
|
double skirt_length = 0.0;
|
||||||
{
|
Points extrusion_points;
|
||||||
// std::cerr << line.cmd() << "\n";
|
int tool = -1;
|
||||||
if (boost::starts_with(line.cmd(), "T")) {
|
GCodeReader parser;
|
||||||
tool = atoi(line.cmd().data() + 1);
|
parser.parse_buffer(gcode, [config, &extrusion_points, &tool, &skirt_length, support_speed] (Slic3r::GCodeReader& self, const Slic3r::GCodeReader::GCodeLine& line) {
|
||||||
} else if (self.z() == Approx(config.opt<ConfigOptionFloat>("first_layer_height")->value)) {
|
// std::cerr << line.cmd() << "\n";
|
||||||
// on first layer
|
if (boost::starts_with(line.cmd(), "T")) {
|
||||||
if (line.extruding(self) && line.dist_XY(self) > 0) {
|
tool = atoi(line.cmd().data() + 1);
|
||||||
float speed = ( self.f() > 0 ? self.f() : line.new_F(self));
|
} else if (self.z() == Approx(config.opt<ConfigOptionFloat>("first_layer_height")->value)) {
|
||||||
// std::cerr << "Tool " << tool << "\n";
|
// on first layer
|
||||||
if (speed == Approx(support_speed) && tool == config.opt_int("perimeter_extruder") - 1) {
|
if (line.extruding(self) && line.dist_XY(self) > 0) {
|
||||||
// Skirt uses first material extruder, support material speed.
|
float speed = ( self.f() > 0 ? self.f() : line.new_F(self));
|
||||||
skirt_length += line.dist_XY(self);
|
// std::cerr << "Tool " << tool << "\n";
|
||||||
} else {
|
if (speed == Approx(support_speed) && tool == config.opt_int("perimeter_extruder") - 1) {
|
||||||
extrusion_points.push_back(Slic3r::Point::new_scale(line.new_X(self), line.new_Y(self)));
|
// Skirt uses first material extruder, support material speed.
|
||||||
}
|
skirt_length += line.dist_XY(self);
|
||||||
}
|
} else
|
||||||
|
extrusion_points.push_back(Slic3r::Point::new_scale(line.new_X(self), line.new_Y(self)));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (self.z() == Approx(0.3) || line.new_Z(self) == Approx(0.3)) {
|
if (self.z() == Approx(0.3) || line.new_Z(self) == Approx(0.3)) {
|
||||||
if (line.extruding(self) && self.f() == Approx(support_speed)) {
|
if (line.extruding(self) && self.f() == Approx(support_speed)) {
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
});
|
||||||
Slic3r::Polygon convex_hull = Slic3r::Geometry::convex_hull(extrusion_points);
|
Slic3r::Polygon convex_hull = Slic3r::Geometry::convex_hull(extrusion_points);
|
||||||
double hull_perimeter = unscale<double>(convex_hull.split_at_first_point().length());
|
double hull_perimeter = unscale<double>(convex_hull.split_at_first_point().length());
|
||||||
REQUIRE(skirt_length > hull_perimeter);
|
REQUIRE(skirt_length > hull_perimeter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("Large minimum skirt length is used.") {
|
WHEN("Large minimum skirt length is used.") {
|
||||||
config.opt_float("min_skirt_length") = 20;
|
config.set("min_skirt_length", 20);
|
||||||
Slic3r::Model model;
|
|
||||||
auto print = Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config);
|
|
||||||
THEN("Gcode generation doesn't crash") {
|
THEN("Gcode generation doesn't crash") {
|
||||||
REQUIRE(! Slic3r::Test::gcode(print).empty());
|
REQUIRE(! Slic3r::Test::slice({TestMesh::cube_20x20x20}, config).empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
233
tests/fff_print/test_support_material.cpp
Normal file
233
tests/fff_print/test_support_material.cpp
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
#include "libslic3r/GCodeReader.hpp"
|
||||||
|
|
||||||
|
#include "test_data.hpp" // get access to init_print, etc
|
||||||
|
|
||||||
|
using namespace Slic3r::Test;
|
||||||
|
using namespace Slic3r;
|
||||||
|
|
||||||
|
TEST_CASE("SupportMaterial: Three raft layers created", "[SupportMaterial]")
|
||||||
|
{
|
||||||
|
Slic3r::Print print;
|
||||||
|
Slic3r::Test::init_and_process_print({ TestMesh::cube_20x20x20 }, print, {
|
||||||
|
{ "support_material", 1 },
|
||||||
|
{ "raft_layers", 3 }
|
||||||
|
});
|
||||||
|
REQUIRE(print.objects().front()->support_layers().size() == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
SCENARIO("SupportMaterial: support_layers_z and contact_distance", "[SupportMaterial]")
|
||||||
|
{
|
||||||
|
// Box h = 20mm, hole bottom at 5mm, hole height 10mm (top edge at 15mm).
|
||||||
|
TriangleMesh mesh = Slic3r::Test::mesh(Slic3r::Test::TestMesh::cube_with_hole);
|
||||||
|
mesh.rotate_x(float(M_PI / 2));
|
||||||
|
|
||||||
|
auto check = [](Slic3r::Print &print, bool &first_support_layer_height_ok, bool &layer_height_minimum_ok, bool &layer_height_maximum_ok, bool &top_spacing_ok)
|
||||||
|
{
|
||||||
|
const std::vector<Slic3r::SupportLayer*> &support_layers = print.objects().front()->support_layers();
|
||||||
|
|
||||||
|
first_support_layer_height_ok = support_layers.front()->print_z == print.default_object_config().first_layer_height.value;
|
||||||
|
|
||||||
|
layer_height_minimum_ok = true;
|
||||||
|
layer_height_maximum_ok = true;
|
||||||
|
double min_layer_height = print.config().min_layer_height.values.front();
|
||||||
|
double max_layer_height = print.config().nozzle_diameter.values.front();
|
||||||
|
if (print.config().max_layer_height.values.front() > EPSILON)
|
||||||
|
max_layer_height = std::min(max_layer_height, print.config().max_layer_height.values.front());
|
||||||
|
for (size_t i = 1; i < support_layers.size(); ++ i) {
|
||||||
|
if (support_layers[i]->print_z - support_layers[i - 1]->print_z < min_layer_height - EPSILON)
|
||||||
|
layer_height_minimum_ok = false;
|
||||||
|
if (support_layers[i]->print_z - support_layers[i - 1]->print_z > max_layer_height + EPSILON)
|
||||||
|
layer_height_maximum_ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
double expected_top_spacing = print.default_object_config().layer_height + print.config().nozzle_diameter.get_at(0);
|
||||||
|
bool wrong_top_spacing = 0;
|
||||||
|
std::vector<coordf_t> top_z { 1.1 };
|
||||||
|
for (coordf_t top_z_el : top_z) {
|
||||||
|
// find layer index of this top surface.
|
||||||
|
size_t layer_id = -1;
|
||||||
|
for (size_t i = 0; i < support_z.size(); ++ i) {
|
||||||
|
if (abs(support_z[i] - top_z_el) < EPSILON) {
|
||||||
|
layer_id = i;
|
||||||
|
i = static_cast<int>(support_z.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that first support layer above this top surface (or the next one) is spaced with nozzle diameter
|
||||||
|
if (abs(support_z[layer_id + 1] - support_z[layer_id] - expected_top_spacing) > EPSILON &&
|
||||||
|
abs(support_z[layer_id + 2] - support_z[layer_id] - expected_top_spacing) > EPSILON) {
|
||||||
|
wrong_top_spacing = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d = ! wrong_top_spacing;
|
||||||
|
#else
|
||||||
|
top_spacing_ok = true;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
GIVEN("A print object having one modelObject") {
|
||||||
|
WHEN("First layer height = 0.4") {
|
||||||
|
Slic3r::Print print;
|
||||||
|
Slic3r::Test::init_and_process_print({ mesh }, print, {
|
||||||
|
{ "support_material", 1 },
|
||||||
|
{ "layer_height", 0.2 },
|
||||||
|
{ "first_layer_height", 0.4 },
|
||||||
|
});
|
||||||
|
bool a, b, c, d;
|
||||||
|
check(print, a, b, c, d);
|
||||||
|
THEN("First layer height is honored") { REQUIRE(a == true); }
|
||||||
|
THEN("No null or negative support layers") { REQUIRE(b == true); }
|
||||||
|
THEN("No layers thicker than nozzle diameter") { REQUIRE(c == true); }
|
||||||
|
// THEN("Layers above top surfaces are spaced correctly") { REQUIRE(d == true); }
|
||||||
|
}
|
||||||
|
WHEN("Layer height = 0.2 and, first layer height = 0.3") {
|
||||||
|
Slic3r::Print print;
|
||||||
|
Slic3r::Test::init_and_process_print({ mesh }, print, {
|
||||||
|
{ "support_material", 1 },
|
||||||
|
{ "layer_height", 0.2 },
|
||||||
|
{ "first_layer_height", 0.3 },
|
||||||
|
});
|
||||||
|
bool a, b, c, d;
|
||||||
|
check(print, a, b, c, d);
|
||||||
|
THEN("First layer height is honored") { REQUIRE(a == true); }
|
||||||
|
THEN("No null or negative support layers") { REQUIRE(b == true); }
|
||||||
|
THEN("No layers thicker than nozzle diameter") { REQUIRE(c == true); }
|
||||||
|
// THEN("Layers above top surfaces are spaced correctly") { REQUIRE(d == true); }
|
||||||
|
}
|
||||||
|
WHEN("Layer height = nozzle_diameter[0]") {
|
||||||
|
Slic3r::Print print;
|
||||||
|
Slic3r::Test::init_and_process_print({ mesh }, print, {
|
||||||
|
{ "support_material", 1 },
|
||||||
|
{ "layer_height", 0.2 },
|
||||||
|
{ "first_layer_height", 0.3 },
|
||||||
|
});
|
||||||
|
bool a, b, c, d;
|
||||||
|
check(print, a, b, c, d);
|
||||||
|
THEN("First layer height is honored") { REQUIRE(a == true); }
|
||||||
|
THEN("No null or negative support layers") { REQUIRE(b == true); }
|
||||||
|
THEN("No layers thicker than nozzle diameter") { REQUIRE(c == true); }
|
||||||
|
// THEN("Layers above top surfaces are spaced correctly") { REQUIRE(d == true); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// Test 8.
|
||||||
|
TEST_CASE("SupportMaterial: forced support is generated", "[SupportMaterial]")
|
||||||
|
{
|
||||||
|
// Create a mesh & modelObject.
|
||||||
|
TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20);
|
||||||
|
|
||||||
|
Model model = Model();
|
||||||
|
ModelObject *object = model.add_object();
|
||||||
|
object->add_volume(mesh);
|
||||||
|
model.add_default_instances();
|
||||||
|
model.align_instances_to_origin();
|
||||||
|
|
||||||
|
Print print = Print();
|
||||||
|
|
||||||
|
std::vector<coordf_t> contact_z = {1.9};
|
||||||
|
std::vector<coordf_t> top_z = {1.1};
|
||||||
|
print.default_object_config.support_material_enforce_layers = 100;
|
||||||
|
print.default_object_config.support_material = 0;
|
||||||
|
print.default_object_config.layer_height = 0.2;
|
||||||
|
print.default_object_config.set_deserialize("first_layer_height", "0.3");
|
||||||
|
|
||||||
|
print.add_model_object(model.objects[0]);
|
||||||
|
print.objects.front()->_slice();
|
||||||
|
|
||||||
|
SupportMaterial *support = print.objects.front()->_support_material();
|
||||||
|
auto support_z = support->support_layers_z(contact_z, top_z, print.default_object_config.layer_height);
|
||||||
|
|
||||||
|
bool check = true;
|
||||||
|
for (size_t i = 1; i < support_z.size(); i++) {
|
||||||
|
if (support_z[i] - support_z[i - 1] <= 0)
|
||||||
|
check = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(check == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
bool test_6_checks(Print& print)
|
||||||
|
{
|
||||||
|
bool has_bridge_speed = true;
|
||||||
|
|
||||||
|
// Pre-Processing.
|
||||||
|
PrintObject* print_object = print.objects.front();
|
||||||
|
print_object->infill();
|
||||||
|
SupportMaterial* support_material = print.objects.front()->_support_material();
|
||||||
|
support_material->generate(print_object);
|
||||||
|
// TODO but not needed in test 6 (make brims and make skirts).
|
||||||
|
|
||||||
|
// Exporting gcode.
|
||||||
|
// TODO validation found in Simple.pm
|
||||||
|
|
||||||
|
|
||||||
|
return has_bridge_speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 6.
|
||||||
|
SCENARIO("SupportMaterial: Checking bridge speed", "[SupportMaterial]")
|
||||||
|
{
|
||||||
|
GIVEN("Print object") {
|
||||||
|
// Create a mesh & modelObject.
|
||||||
|
TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20);
|
||||||
|
|
||||||
|
Model model = Model();
|
||||||
|
ModelObject *object = model.add_object();
|
||||||
|
object->add_volume(mesh);
|
||||||
|
model.add_default_instances();
|
||||||
|
model.align_instances_to_origin();
|
||||||
|
|
||||||
|
Print print = Print();
|
||||||
|
print.config.brim_width = 0;
|
||||||
|
print.config.skirts = 0;
|
||||||
|
print.config.skirts = 0;
|
||||||
|
print.default_object_config.support_material = 1;
|
||||||
|
print.default_region_config.top_solid_layers = 0; // so that we don't have the internal bridge over infill.
|
||||||
|
print.default_region_config.bridge_speed = 99;
|
||||||
|
print.config.cooling = 0;
|
||||||
|
print.config.set_deserialize("first_layer_speed", "100%");
|
||||||
|
|
||||||
|
WHEN("support_material_contact_distance = 0.2") {
|
||||||
|
print.default_object_config.support_material_contact_distance = 0.2;
|
||||||
|
print.add_model_object(model.objects[0]);
|
||||||
|
|
||||||
|
bool check = test_6_checks(print);
|
||||||
|
REQUIRE(check == true); // bridge speed is used.
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("support_material_contact_distance = 0") {
|
||||||
|
print.default_object_config.support_material_contact_distance = 0;
|
||||||
|
print.add_model_object(model.objects[0]);
|
||||||
|
|
||||||
|
bool check = test_6_checks(print);
|
||||||
|
REQUIRE(check == true); // bridge speed is not used.
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("support_material_contact_distance = 0.2 & raft_layers = 5") {
|
||||||
|
print.default_object_config.support_material_contact_distance = 0.2;
|
||||||
|
print.default_object_config.raft_layers = 5;
|
||||||
|
print.add_model_object(model.objects[0]);
|
||||||
|
|
||||||
|
bool check = test_6_checks(print);
|
||||||
|
REQUIRE(check == true); // bridge speed is used.
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("support_material_contact_distance = 0 & raft_layers = 5") {
|
||||||
|
print.default_object_config.support_material_contact_distance = 0;
|
||||||
|
print.default_object_config.raft_layers = 5;
|
||||||
|
print.add_model_object(model.objects[0]);
|
||||||
|
|
||||||
|
bool check = test_6_checks(print);
|
||||||
|
|
||||||
|
REQUIRE(check == true); // bridge speed is not used.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -392,12 +392,13 @@ TEST_CASE("Regression test for issue #4486 - files take forever to slice") {
|
||||||
config.set("first_layer_height", 250);
|
config.set("first_layer_height", 250);
|
||||||
config.set("nozzle_diameter", 500);
|
config.set("nozzle_diameter", 500);
|
||||||
|
|
||||||
|
Slic3r::Print print;
|
||||||
Slic3r::Model model;
|
Slic3r::Model model;
|
||||||
auto print = Slic3r::Test::init_print({mesh}, model, config);
|
Slic3r::Test::init_print({mesh}, print, model, config);
|
||||||
|
|
||||||
print->status_cb = [] (int ln, const std::string& msg) { Slic3r::Log::info("Print") << ln << " " << msg << "\n";};
|
print.status_cb = [] (int ln, const std::string& msg) { Slic3r::Log::info("Print") << ln << " " << msg << "\n";};
|
||||||
|
|
||||||
std::future<void> fut = std::async([&print] () { print->process(); });
|
std::future<void> fut = std::async([&print] () { print.process(); });
|
||||||
std::chrono::milliseconds span {120000};
|
std::chrono::milliseconds span {120000};
|
||||||
bool timedout {false};
|
bool timedout {false};
|
||||||
if(fut.wait_for(span) == std::future_status::timeout) {
|
if(fut.wait_for(span) == std::future_status::timeout) {
|
||||||
|
@ -420,12 +421,13 @@ TEST_CASE("Profile test for issue #4486 - files take forever to slice") {
|
||||||
config.set("nozzle_diameter", 500);
|
config.set("nozzle_diameter", 500);
|
||||||
config.set("fill_density", "5%");
|
config.set("fill_density", "5%");
|
||||||
|
|
||||||
|
Slic3r::Print print;
|
||||||
Slic3r::Model model;
|
Slic3r::Model model;
|
||||||
auto print = Slic3r::Test::init_print({mesh}, model, config);
|
Slic3r::Test::init_print({mesh}, print, model, config);
|
||||||
|
|
||||||
print->status_cb = [] (int ln, const std::string& msg) { Slic3r::Log::info("Print") << ln << " " << msg << "\n";};
|
print.status_cb = [] (int ln, const std::string& msg) { Slic3r::Log::info("Print") << ln << " " << msg << "\n";};
|
||||||
|
|
||||||
print->process();
|
print.process();
|
||||||
|
|
||||||
REQUIRE(true);
|
REQUIRE(true);
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,10 @@ get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
|
||||||
add_executable(${_TEST_NAME}_tests
|
add_executable(${_TEST_NAME}_tests
|
||||||
${_TEST_NAME}_tests.cpp
|
${_TEST_NAME}_tests.cpp
|
||||||
test_3mf.cpp
|
test_3mf.cpp
|
||||||
|
test_config.cpp
|
||||||
test_geometry.cpp
|
test_geometry.cpp
|
||||||
test_polygon.cpp
|
test_polygon.cpp
|
||||||
|
test_stl.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
|
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
|
||||||
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
|
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
|
|
||||||
SCENARIO("Reading 3mf file") {
|
SCENARIO("Reading 3mf file", "[3mf]") {
|
||||||
GIVEN("umlauts in the path of the file") {
|
GIVEN("umlauts in the path of the file") {
|
||||||
Slic3r::Model model;
|
Slic3r::Model model;
|
||||||
WHEN("3mf model is read") {
|
WHEN("3mf model is read") {
|
||||||
|
|
203
tests/libslic3r/test_config.cpp
Normal file
203
tests/libslic3r/test_config.cpp
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
|
|
||||||
|
using namespace Slic3r;
|
||||||
|
|
||||||
|
SCENARIO("Generic config validation performs as expected.", "[Config]") {
|
||||||
|
GIVEN("A config generated from default options") {
|
||||||
|
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||||
|
WHEN( "perimeter_extrusion_width is set to 250%, a valid value") {
|
||||||
|
config.set_deserialize("perimeter_extrusion_width", "250%");
|
||||||
|
THEN( "The config is read as valid.") {
|
||||||
|
REQUIRE(config.validate().empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN( "perimeter_extrusion_width is set to -10, an invalid value") {
|
||||||
|
config.set("perimeter_extrusion_width", -10);
|
||||||
|
THEN( "Validate returns error") {
|
||||||
|
REQUIRE(! config.validate().empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN( "perimeters is set to -10, an invalid value") {
|
||||||
|
config.set("perimeters", -10);
|
||||||
|
THEN( "Validate returns error") {
|
||||||
|
REQUIRE(! config.validate().empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SCENARIO("Config accessor functions perform as expected.", "[Config]") {
|
||||||
|
GIVEN("A config generated from default options") {
|
||||||
|
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||||
|
WHEN("A boolean option is set to a boolean value") {
|
||||||
|
REQUIRE_NOTHROW(config.set("gcode_comments", true));
|
||||||
|
THEN("The underlying value is set correctly.") {
|
||||||
|
REQUIRE(config.opt<ConfigOptionBool>("gcode_comments")->getBool() == true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("A boolean option is set to a string value representing a 0 or 1") {
|
||||||
|
CHECK_NOTHROW(config.set_deserialize("gcode_comments", "1"));
|
||||||
|
THEN("The underlying value is set correctly.") {
|
||||||
|
REQUIRE(config.opt<ConfigOptionBool>("gcode_comments")->getBool() == true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("A boolean option is set to a string value representing something other than 0 or 1") {
|
||||||
|
THEN("A BadOptionTypeException exception is thrown.") {
|
||||||
|
REQUIRE_THROWS_AS(config.set("gcode_comments", "Z"), BadOptionTypeException);
|
||||||
|
}
|
||||||
|
AND_THEN("Value is unchanged.") {
|
||||||
|
REQUIRE(config.opt<ConfigOptionBool>("gcode_comments")->getBool() == false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("A boolean option is set to an int value") {
|
||||||
|
THEN("A BadOptionTypeException exception is thrown.") {
|
||||||
|
REQUIRE_THROWS_AS(config.set("gcode_comments", 1), BadOptionTypeException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("A numeric option is set from serialized string") {
|
||||||
|
config.set_deserialize("bed_temperature", "100");
|
||||||
|
THEN("The underlying value is set correctly.") {
|
||||||
|
REQUIRE(config.opt<ConfigOptionInts>("bed_temperature")->get_at(0) == 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
//FIXME better design accessors for vector elements.
|
||||||
|
WHEN("An integer-based option is set through the integer interface") {
|
||||||
|
config.set("bed_temperature", 100);
|
||||||
|
THEN("The underlying value is set correctly.") {
|
||||||
|
REQUIRE(config.opt<ConfigOptionInts>("bed_temperature")->get_at(0) == 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
WHEN("An floating-point option is set through the integer interface") {
|
||||||
|
config.set("perimeter_speed", 10);
|
||||||
|
THEN("The underlying value is set correctly.") {
|
||||||
|
REQUIRE(config.opt<ConfigOptionFloat>("perimeter_speed")->getFloat() == 10.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("A floating-point option is set through the double interface") {
|
||||||
|
config.set("perimeter_speed", 5.5);
|
||||||
|
THEN("The underlying value is set correctly.") {
|
||||||
|
REQUIRE(config.opt<ConfigOptionFloat>("perimeter_speed")->getFloat() == 5.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("An integer-based option is set through the double interface") {
|
||||||
|
THEN("A BadOptionTypeException exception is thrown.") {
|
||||||
|
REQUIRE_THROWS_AS(config.set("bed_temperature", 5.5), BadOptionTypeException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("A numeric option is set to a non-numeric value.") {
|
||||||
|
THEN("A BadOptionTypeException exception is thown.") {
|
||||||
|
REQUIRE_THROWS_AS(config.set_deserialize("perimeter_speed", "zzzz"), BadOptionTypeException);
|
||||||
|
}
|
||||||
|
THEN("The value does not change.") {
|
||||||
|
REQUIRE(config.opt<ConfigOptionFloat>("perimeter_speed")->getFloat() == 60.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("A string option is set through the string interface") {
|
||||||
|
config.set("printhost_apikey", "100");
|
||||||
|
THEN("The underlying value is set correctly.") {
|
||||||
|
REQUIRE(config.opt<ConfigOptionString>("printhost_apikey")->value == "100");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("A string option is set through the integer interface") {
|
||||||
|
config.set("printhost_apikey", 100);
|
||||||
|
THEN("The underlying value is set correctly.") {
|
||||||
|
REQUIRE(config.opt<ConfigOptionString>("printhost_apikey")->value == "100");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("A string option is set through the double interface") {
|
||||||
|
config.set("printhost_apikey", 100.5);
|
||||||
|
THEN("The underlying value is set correctly.") {
|
||||||
|
REQUIRE(config.opt<ConfigOptionString>("printhost_apikey")->value == std::to_string(100.5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("A float or percent is set as a percent through the string interface.") {
|
||||||
|
config.set_deserialize("first_layer_extrusion_width", "100%");
|
||||||
|
THEN("Value and percent flag are 100/true") {
|
||||||
|
auto tmp = config.opt<ConfigOptionFloatOrPercent>("first_layer_extrusion_width");
|
||||||
|
REQUIRE(tmp->percent == true);
|
||||||
|
REQUIRE(tmp->value == 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("A float or percent is set as a float through the string interface.") {
|
||||||
|
config.set_deserialize("first_layer_extrusion_width", "100");
|
||||||
|
THEN("Value and percent flag are 100/false") {
|
||||||
|
auto tmp = config.opt<ConfigOptionFloatOrPercent>("first_layer_extrusion_width");
|
||||||
|
REQUIRE(tmp->percent == false);
|
||||||
|
REQUIRE(tmp->value == 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("A float or percent is set as a float through the int interface.") {
|
||||||
|
config.set("first_layer_extrusion_width", 100);
|
||||||
|
THEN("Value and percent flag are 100/false") {
|
||||||
|
auto tmp = config.opt<ConfigOptionFloatOrPercent>("first_layer_extrusion_width");
|
||||||
|
REQUIRE(tmp->percent == false);
|
||||||
|
REQUIRE(tmp->value == 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("A float or percent is set as a float through the double interface.") {
|
||||||
|
config.set("first_layer_extrusion_width", 100.5);
|
||||||
|
THEN("Value and percent flag are 100.5/false") {
|
||||||
|
auto tmp = config.opt<ConfigOptionFloatOrPercent>("first_layer_extrusion_width");
|
||||||
|
REQUIRE(tmp->percent == false);
|
||||||
|
REQUIRE(tmp->value == 100.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("An invalid option is requested during set.") {
|
||||||
|
THEN("A BadOptionTypeException exception is thrown.") {
|
||||||
|
REQUIRE_THROWS_AS(config.set("deadbeef_invalid_option", 1), UnknownOptionException);
|
||||||
|
REQUIRE_THROWS_AS(config.set("deadbeef_invalid_option", 1.0), UnknownOptionException);
|
||||||
|
REQUIRE_THROWS_AS(config.set("deadbeef_invalid_option", "1"), UnknownOptionException);
|
||||||
|
REQUIRE_THROWS_AS(config.set("deadbeef_invalid_option", true), UnknownOptionException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("An invalid option is requested during get.") {
|
||||||
|
THEN("A UnknownOptionException exception is thrown.") {
|
||||||
|
REQUIRE_THROWS_AS(config.option_throw<ConfigOptionString>("deadbeef_invalid_option", false), UnknownOptionException);
|
||||||
|
REQUIRE_THROWS_AS(config.option_throw<ConfigOptionFloat>("deadbeef_invalid_option", false), UnknownOptionException);
|
||||||
|
REQUIRE_THROWS_AS(config.option_throw<ConfigOptionInt>("deadbeef_invalid_option", false), UnknownOptionException);
|
||||||
|
REQUIRE_THROWS_AS(config.option_throw<ConfigOptionBool>("deadbeef_invalid_option", false), UnknownOptionException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("An invalid option is requested during opt.") {
|
||||||
|
THEN("A UnknownOptionException exception is thrown.") {
|
||||||
|
REQUIRE_THROWS_AS(config.option_throw<ConfigOptionString>("deadbeef_invalid_option", false), UnknownOptionException);
|
||||||
|
REQUIRE_THROWS_AS(config.option_throw<ConfigOptionFloat>("deadbeef_invalid_option", false), UnknownOptionException);
|
||||||
|
REQUIRE_THROWS_AS(config.option_throw<ConfigOptionInt>("deadbeef_invalid_option", false), UnknownOptionException);
|
||||||
|
REQUIRE_THROWS_AS(config.option_throw<ConfigOptionBool>("deadbeef_invalid_option", false), UnknownOptionException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("getX called on an unset option.") {
|
||||||
|
THEN("The default is returned.") {
|
||||||
|
REQUIRE(config.opt_float("layer_height") == 0.3);
|
||||||
|
REQUIRE(config.opt_int("raft_layers") == 0);
|
||||||
|
REQUIRE(config.opt_bool("support_material") == false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("getFloat called on an option that has been set.") {
|
||||||
|
config.set("layer_height", 0.5);
|
||||||
|
THEN("The set value is returned.") {
|
||||||
|
REQUIRE(config.opt_float("layer_height") == 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SCENARIO("Config ini load/save interface", "[Config]") {
|
||||||
|
WHEN("new_from_ini is called") {
|
||||||
|
Slic3r::DynamicPrintConfig config;
|
||||||
|
std::string path = std::string(TEST_DATA_DIR) + "/test_config/new_from_ini.ini";
|
||||||
|
config.load_from_ini(path);
|
||||||
|
THEN("Config object contains ini file options.") {
|
||||||
|
REQUIRE(config.option_throw<ConfigOptionStrings>("filament_colour", false)->values.size() == 1);
|
||||||
|
REQUIRE(config.option_throw<ConfigOptionStrings>("filament_colour", false)->values.front() == "#ABCD");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
tests/libslic3r/test_stl.cpp
Normal file
57
tests/libslic3r/test_stl.cpp
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
#include "libslic3r/Model.hpp"
|
||||||
|
#include "libslic3r/Format/STL.hpp"
|
||||||
|
|
||||||
|
using namespace Slic3r;
|
||||||
|
|
||||||
|
static inline std::string stl_path(const char* path)
|
||||||
|
{
|
||||||
|
return std::string(TEST_DATA_DIR) + "/test_stl/" + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
SCENARIO("Reading an STL file", "[stl]") {
|
||||||
|
GIVEN("umlauts in the path of a binary STL file, Czech characters in the file name") {
|
||||||
|
WHEN("STL file is read") {
|
||||||
|
Slic3r::Model model;
|
||||||
|
THEN("load should succeed") {
|
||||||
|
REQUIRE(Slic3r::load_stl(stl_path("Geräte/20mmbox-čřšřěá.stl").c_str(), &model));
|
||||||
|
REQUIRE(is_approx(model.objects.front()->volumes.front()->mesh().size(), Vec3d(20, 20, 20)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GIVEN("in ASCII format") {
|
||||||
|
WHEN("line endings LF") {
|
||||||
|
Slic3r::Model model;
|
||||||
|
THEN("load should succeed") {
|
||||||
|
REQUIRE(Slic3r::load_stl(stl_path("ASCII/20mmbox-LF.stl").c_str(), &model));
|
||||||
|
REQUIRE(is_approx(model.objects.front()->volumes.front()->mesh().size(), Vec3d(20, 20, 20)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WHEN("line endings CRLF") {
|
||||||
|
Slic3r::Model model;
|
||||||
|
THEN("load should succeed") {
|
||||||
|
REQUIRE(Slic3r::load_stl(stl_path("ASCII/20mmbox-CRLF.stl").c_str(), &model));
|
||||||
|
REQUIRE(is_approx(model.objects.front()->volumes.front()->mesh().size(), Vec3d(20, 20, 20)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
// ASCII STLs ending with just carriage returns are not supported. These were used by the old Macs, while the Unix based MacOS uses LFs as any other Unix.
|
||||||
|
WHEN("line endings CR") {
|
||||||
|
Slic3r::Model model;
|
||||||
|
THEN("load should succeed") {
|
||||||
|
REQUIRE(Slic3r::load_stl(stl_path("ASCII/20mmbox-CR.stl").c_str(), &model));
|
||||||
|
REQUIRE(is_approx(model.objects.front()->volumes.front()->mesh().size(), Vec3d(20, 20, 20)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
WHEN("nonstandard STL file (text after ending tags, invalid normals, for example infinities)") {
|
||||||
|
Slic3r::Model model;
|
||||||
|
THEN("load should succeed") {
|
||||||
|
REQUIRE(Slic3r::load_stl(stl_path("ASCII/20mmbox-nonstandard.stl").c_str(), &model));
|
||||||
|
REQUIRE(is_approx(model.objects.front()->volumes.front()->mesh().size(), Vec3d(20, 20, 20)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -299,7 +299,7 @@ bool ConfigBase__set_deserialize(ConfigBase* THIS, const t_config_option_key &op
|
||||||
size_t len;
|
size_t len;
|
||||||
const char * c = SvPV(str, len);
|
const char * c = SvPV(str, len);
|
||||||
std::string value(c, len);
|
std::string value(c, len);
|
||||||
return THIS->set_deserialize(opt_key, value);
|
return THIS->set_deserialize_nothrow(opt_key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigBase__set_ifndef(ConfigBase* THIS, const t_config_option_key &opt_key, SV* value, bool deserialize)
|
void ConfigBase__set_ifndef(ConfigBase* THIS, const t_config_option_key &opt_key, SV* value, bool deserialize)
|
||||||
|
|
Loading…
Add table
Reference in a new issue