mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-17 02:22:17 +00:00
Fix tests (#10906)
* Get libslic3r tests closer to passing
I can't get geometry tests to do anything useful. I've added extra
output, but it hasn't helped me figure out why they don't work
yet. That's also probably the last broken 3mf test doesn't work.
The config tests were mostly broken because of config name changes.
The placeholder_parser tests have some things that may-or-may-not
still apply to Orca.
* Vendor a 3.x version of Catch2
Everything is surely broken at this point.
* Allow building tests separately from Orca with build_linux.sh
* Remove unnecessary log message screwing up ctest
Same solution as Prusaslicer
* Make 2 TriangleMesh methods const
Since they can be.
* Move method comment to the header where it belongsc
* Add indirectly-included header directly
Transform3d IIRC
* libslic3r tests converted to Catch2 v3
Still has 3 failing tests, but builds and runs.
* Disable 2D convex hull test and comment what I've learned
Not sure the best way to solve this yet.
* Add diff compare method for DynamicConfig
Help the unit test report errors better.
* Perl no longer used, remove comment line
* Clang-format Config.?pp
So difficult to work with ATM
* Remove cpp17 unit tests
Who gives a shit
* Don't need explicit "example" test
We have lots of tests to serve as examples.
* Leave breadcrumb to enable sla_print tests
* Fix serialization of DynamicConfig
Add comments to test, because these code paths might not be even used
anymore.
* Update run_unit_tests to run all the tests
By the time I'm done with the PR all tests will either excluded by
default or passing, so just do all.
* Update how-to-test now that build_linux.sh builds tests separately
* Update cmake regenerate instructions
Read this online; hopefully works.
* Enable slic3rutils test with Catch2 v3
* Port libnest2d and fff_print to Catch2 v3
They build. Many failing.
* Add slightly more info to Objects not fit on bed exception
* Disable failing fff_print tests from running
They're mostly failing for "objects don't fit on bed" for an
infinite-sized bed. Given infinite bed is probably only used in tests,
it probably was incidentally broken long ago.
* Must checkout tests directory in GH Actions
So we get the test data
* Missed a failing fff_print test
* Disable (most/all) broken libnest2d tests
Trying all, not checking yet though
* Fix Polygon convex/concave detection tests
Document the implementation too. Reorganize the tests to be cleaner.
* Update the test script to run tests in parallel
* Get sla_print tests to build
Probably not passing
* Don't cause full project rebuild when updating test CMakeLists.txts
* Revert "Clang-format Config.?pp"
This reverts commit 771e4c0ad2.
---------
Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
@@ -1703,6 +1703,81 @@ t_config_option_keys DynamicConfig::keys() const
|
||||
return keys;
|
||||
}
|
||||
|
||||
DynamicConfig::DynamicConfigDifference DynamicConfig::diff_report(const DynamicConfig& rhs) const {
|
||||
DynamicConfig::DynamicConfigDifference result;
|
||||
|
||||
std::set<t_config_option_key> all_keys;
|
||||
|
||||
for (const auto& kvp : this->options) {
|
||||
all_keys.insert(kvp.first);
|
||||
}
|
||||
for (const auto& kvp : rhs.options) {
|
||||
all_keys.insert(kvp.first);
|
||||
}
|
||||
|
||||
for (const auto& key : all_keys) {
|
||||
auto left_it = this->options.find(key);
|
||||
auto right_it = rhs.options.find(key);
|
||||
|
||||
bool left_has = (left_it != this->options.end());
|
||||
bool right_has = (right_it != rhs.options.end());
|
||||
|
||||
if (left_has && right_has) {
|
||||
if (*left_it->second != *right_it->second) {
|
||||
result.differences[key] = {
|
||||
left_it->second->serialize(),
|
||||
right_it->second->serialize()
|
||||
};
|
||||
}
|
||||
} else if (left_has) {
|
||||
result.differences[key] = {
|
||||
left_it->second->serialize(),
|
||||
std::nullopt
|
||||
};
|
||||
} else if (right_has) {
|
||||
result.differences[key] = {
|
||||
std::nullopt,
|
||||
right_it->second->serialize()
|
||||
};
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const DynamicConfig::DynamicConfigDifference& diff) {
|
||||
if (!diff.is_different()) {
|
||||
os << "Configurations are identical.\n";
|
||||
return os;
|
||||
}
|
||||
|
||||
int missing_right=0, missing_left=0, differ=0;
|
||||
os << "DynamicConfig Differences Found (" << diff.differences.size() << " keys):\n";
|
||||
for (const auto& kvp : diff.differences) {
|
||||
const auto& key = kvp.first;
|
||||
const auto& detail = kvp.second;
|
||||
|
||||
os << " Key: **" << key << "**\n";
|
||||
|
||||
if (detail.is_missing_key()) {
|
||||
// Determine which side is missing the key
|
||||
if (detail.left_value.has_value()) {
|
||||
os << " - **Missing in Right**: Key exists in left config. Value: " << detail.left_value.value() << "\n";
|
||||
missing_right++;
|
||||
} else {
|
||||
os << " - **Missing in Left**: Key exists in right config. Value: " << detail.right_value.value() << "\n";
|
||||
missing_left++;
|
||||
}
|
||||
} else if (detail.is_different_value()) {
|
||||
differ++;
|
||||
os << " - **Value Differs**:\n";
|
||||
os << " -> Left Value: " << detail.left_value.value() << "\n";
|
||||
os << " -> Right Value: " << detail.right_value.value() << "\n";
|
||||
}
|
||||
}
|
||||
os << "Summary: " << missing_right << " missing on right, " << missing_left << " missing on left, and " << differ << " have differing values\n";
|
||||
return os;
|
||||
}
|
||||
|
||||
void StaticConfig::set_defaults()
|
||||
{
|
||||
// use defaults from definition
|
||||
|
||||
@@ -345,15 +345,15 @@ public:
|
||||
virtual void set(const std::vector<const ConfigOption*> &rhs) = 0;
|
||||
// Set a single vector item from either a scalar option or the first value of a vector option.vector of ConfigOptions.
|
||||
// This function is useful to split values from multiple extrder / filament settings into separate configurations.
|
||||
virtual void set_at(const ConfigOption *rhs, size_t i, size_t j) = 0;
|
||||
//BBS
|
||||
virtual void set_at_to_nil(size_t i) = 0;
|
||||
virtual void append(const ConfigOption *rhs) = 0;
|
||||
virtual void set(const ConfigOption* rhs, size_t start, size_t len) = 0;
|
||||
virtual void set_with_restore(const ConfigOptionVectorBase* rhs, std::vector<int>& restore_index, int stride) = 0;
|
||||
virtual void set_at(const ConfigOption* rhs, size_t i, size_t j) = 0;
|
||||
// BBS
|
||||
virtual void set_at_to_nil(size_t i) = 0;
|
||||
virtual void append(const ConfigOption* rhs) = 0;
|
||||
virtual void set(const ConfigOption* rhs, size_t start, size_t len) = 0;
|
||||
virtual void set_with_restore(const ConfigOptionVectorBase* rhs, std::vector<int>& restore_index, int stride) = 0;
|
||||
virtual void set_with_restore_2(const ConfigOptionVectorBase* rhs, std::vector<int>& restore_index, int start, int len, bool skip_error = false) = 0;
|
||||
virtual void set_only_diff(const ConfigOptionVectorBase* rhs, std::vector<int>& diff_index, int stride) = 0;
|
||||
virtual void set_with_nil(const ConfigOptionVectorBase* rhs, const ConfigOptionVectorBase* inherits, int stride) = 0;
|
||||
virtual void set_only_diff(const ConfigOptionVectorBase* rhs, std::vector<int>& diff_index, int stride) = 0;
|
||||
virtual void set_with_nil(const ConfigOptionVectorBase* rhs, const ConfigOptionVectorBase* inherits, int stride) = 0;
|
||||
// Resize the vector of values, copy the newly added values from opt_default if provided.
|
||||
virtual void resize(size_t n, const ConfigOption *opt_default = nullptr) = 0;
|
||||
// Clear the values vector.
|
||||
@@ -504,11 +504,11 @@ public:
|
||||
throw ConfigurationError("ConfigOptionVector::set_with_restore(): Assigning an incompatible type");
|
||||
}
|
||||
|
||||
//set a item related with extruder variants when loading config from filament json, replace the original filament items
|
||||
//rhs: item from seperate filament config
|
||||
//restore_index: which index in this vector need to be restored
|
||||
//start: which index in this vector need to be replaced
|
||||
//count: how many items in this vector need to be replaced
|
||||
// set a item related with extruder variants when loading config from filament json, replace the original filament items
|
||||
// rhs: item from seperate filament config
|
||||
// restore_index: which index in this vector need to be restored
|
||||
// start: which index in this vector need to be replaced
|
||||
// count: how many items in this vector need to be replaced
|
||||
virtual void set_with_restore_2(const ConfigOptionVectorBase* rhs, std::vector<int>& restore_index, int start, int len, bool skip_error = false) override
|
||||
{
|
||||
if (rhs->type() == this->type()) {
|
||||
@@ -565,10 +565,9 @@ public:
|
||||
|
||||
for (size_t i = 0; i < diff_index.size(); i++) {
|
||||
if (diff_index[i] != -1) {
|
||||
for (size_t j = 0; j < stride; j++)
|
||||
{
|
||||
for (size_t j = 0; j < stride; j++) {
|
||||
if (!other->is_nil(diff_index[i] * stride))
|
||||
this->values[i * stride +j] = other->values[diff_index[i] * stride +j];
|
||||
this->values[i * stride + j] = other->values[diff_index[i] * stride + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2223,41 +2222,148 @@ public:
|
||||
|
||||
bool is_scalar() const { return (int(this->type) & int(coVectorType)) == 0; }
|
||||
|
||||
template<class Archive> ConfigOption* load_option_from_archive(Archive &archive) const {
|
||||
if (this->nullable) {
|
||||
switch (this->type) {
|
||||
case coFloats: { auto opt = new ConfigOptionFloatsNullable(); archive(*opt); return opt; }
|
||||
case coInts: { auto opt = new ConfigOptionIntsNullable(); archive(*opt); return opt; }
|
||||
case coPercents: { auto opt = new ConfigOptionPercentsNullable();archive(*opt); return opt; }
|
||||
case coBools: { auto opt = new ConfigOptionBoolsNullable(); archive(*opt); return opt; }
|
||||
case coFloatsOrPercents:{ auto opt = new ConfigOptionFloatsOrPercentsNullable();archive(*opt); return opt; }
|
||||
default: throw ConfigurationError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key);
|
||||
}
|
||||
} else {
|
||||
switch (this->type) {
|
||||
case coFloat: { auto opt = new ConfigOptionFloat(); archive(*opt); return opt; }
|
||||
case coFloats: { auto opt = new ConfigOptionFloats(); archive(*opt); return opt; }
|
||||
case coInt: { auto opt = new ConfigOptionInt(); archive(*opt); return opt; }
|
||||
case coInts: { auto opt = new ConfigOptionInts(); archive(*opt); return opt; }
|
||||
case coString: { auto opt = new ConfigOptionString(); archive(*opt); return opt; }
|
||||
case coStrings: { auto opt = new ConfigOptionStrings(); archive(*opt); return opt; }
|
||||
case coPercent: { auto opt = new ConfigOptionPercent(); archive(*opt); return opt; }
|
||||
case coPercents: { auto opt = new ConfigOptionPercents(); archive(*opt); return opt; }
|
||||
case coFloatOrPercent: { auto opt = new ConfigOptionFloatOrPercent(); archive(*opt); return opt; }
|
||||
case coFloatsOrPercents: { auto opt = new ConfigOptionFloatsOrPercents(); archive(*opt); return opt; }
|
||||
case coPoint: { auto opt = new ConfigOptionPoint(); archive(*opt); return opt; }
|
||||
case coPoints: { auto opt = new ConfigOptionPoints(); archive(*opt); return opt; }
|
||||
case coPoint3: { auto opt = new ConfigOptionPoint3(); archive(*opt); return opt; }
|
||||
case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; }
|
||||
case coBools: { auto opt = new ConfigOptionBools(); archive(*opt); return opt; }
|
||||
case coEnum: { auto opt = new ConfigOptionEnumGeneric(this->enum_keys_map); archive(*opt); return opt; }
|
||||
template<class Archive> ConfigOption* load_option_from_archive(Archive& archive) const
|
||||
{
|
||||
if (this->nullable) {
|
||||
switch (this->type) {
|
||||
case coFloats: {
|
||||
auto opt = new ConfigOptionFloatsNullable();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coInts: {
|
||||
auto opt = new ConfigOptionIntsNullable();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coPercents: {
|
||||
auto opt = new ConfigOptionPercentsNullable();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coBools: {
|
||||
auto opt = new ConfigOptionBoolsNullable();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coFloatsOrPercents: {
|
||||
auto opt = new ConfigOptionFloatsOrPercentsNullable();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coEnums: {
|
||||
auto opt = new ConfigOptionEnumsGenericNullable(this->enum_keys_map);
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
default:
|
||||
throw ConfigurationError(
|
||||
std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key);
|
||||
}
|
||||
} else {
|
||||
switch (this->type) {
|
||||
case coFloat: {
|
||||
auto opt = new ConfigOptionFloat();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coFloats: {
|
||||
auto opt = new ConfigOptionFloats();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coInt: {
|
||||
auto opt = new ConfigOptionInt();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coInts: {
|
||||
auto opt = new ConfigOptionInts();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coString: {
|
||||
auto opt = new ConfigOptionString();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coStrings: {
|
||||
auto opt = new ConfigOptionStrings();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coPercent: {
|
||||
auto opt = new ConfigOptionPercent();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coPercents: {
|
||||
auto opt = new ConfigOptionPercents();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coFloatOrPercent: {
|
||||
auto opt = new ConfigOptionFloatOrPercent();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coFloatsOrPercents: {
|
||||
auto opt = new ConfigOptionFloatsOrPercents();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coPoint: {
|
||||
auto opt = new ConfigOptionPoint();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coPoints: {
|
||||
auto opt = new ConfigOptionPoints();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coPoint3: {
|
||||
auto opt = new ConfigOptionPoint3();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coBool: {
|
||||
auto opt = new ConfigOptionBool();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coBools: {
|
||||
auto opt = new ConfigOptionBools();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coEnum: {
|
||||
auto opt = new ConfigOptionEnumGeneric(this->enum_keys_map);
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
// BBS
|
||||
case coEnums: { auto opt = new ConfigOptionEnumsGeneric(this->enum_keys_map); archive(*opt); return opt; }
|
||||
case coIntsGroups: { auto opt = new ConfigOptionIntsGroups(); archive(*opt); return opt; }
|
||||
default: throw ConfigurationError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
case coEnums: {
|
||||
auto opt = new ConfigOptionEnumsGeneric(this->enum_keys_map);
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coIntsGroups: {
|
||||
auto opt = new ConfigOptionIntsGroups();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
case coPointsGroups: {
|
||||
auto opt = new ConfigOptionPointsGroups();
|
||||
archive(*opt);
|
||||
return opt;
|
||||
}
|
||||
default:
|
||||
throw ConfigurationError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") +
|
||||
this->opt_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class Archive> ConfigOption* save_option_to_archive(Archive &archive, const ConfigOption *opt) const {
|
||||
if (this->nullable) {
|
||||
@@ -2267,35 +2373,41 @@ public:
|
||||
case coPercents: archive(*static_cast<const ConfigOptionPercentsNullable*>(opt));break;
|
||||
case coBools: archive(*static_cast<const ConfigOptionBoolsNullable*>(opt)); break;
|
||||
case coFloatsOrPercents: archive(*static_cast<const ConfigOptionFloatsOrPercentsNullable*>(opt)); break;
|
||||
default: throw ConfigurationError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key);
|
||||
}
|
||||
} else {
|
||||
switch (this->type) {
|
||||
case coFloat: archive(*static_cast<const ConfigOptionFloat*>(opt)); break;
|
||||
case coFloats: archive(*static_cast<const ConfigOptionFloats*>(opt)); break;
|
||||
case coInt: archive(*static_cast<const ConfigOptionInt*>(opt)); break;
|
||||
case coInts: archive(*static_cast<const ConfigOptionInts*>(opt)); break;
|
||||
case coString: archive(*static_cast<const ConfigOptionString*>(opt)); break;
|
||||
case coStrings: archive(*static_cast<const ConfigOptionStrings*>(opt)); break;
|
||||
case coPercent: archive(*static_cast<const ConfigOptionPercent*>(opt)); break;
|
||||
case coPercents: archive(*static_cast<const ConfigOptionPercents*>(opt)); break;
|
||||
case coFloatOrPercent: archive(*static_cast<const ConfigOptionFloatOrPercent *>(opt)); break;
|
||||
case coFloatsOrPercents: archive(*static_cast<const ConfigOptionFloatsOrPercents *>(opt)); break;
|
||||
case coPoint: archive(*static_cast<const ConfigOptionPoint*>(opt)); break;
|
||||
case coPoints: archive(*static_cast<const ConfigOptionPoints*>(opt)); break;
|
||||
case coPoint3: archive(*static_cast<const ConfigOptionPoint3*>(opt)); break;
|
||||
case coBool: archive(*static_cast<const ConfigOptionBool*>(opt)); break;
|
||||
case coBools: archive(*static_cast<const ConfigOptionBools*>(opt)); break;
|
||||
case coEnum: archive(*static_cast<const ConfigOptionEnumGeneric*>(opt)); break;
|
||||
case coEnums: archive(*static_cast<const ConfigOptionEnumsGenericNullable*>(opt)); break;
|
||||
default:
|
||||
throw ConfigurationError(
|
||||
std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key);
|
||||
}
|
||||
} else {
|
||||
switch (this->type) {
|
||||
case coFloat: archive(*static_cast<const ConfigOptionFloat*>(opt)); break;
|
||||
case coFloats: archive(*static_cast<const ConfigOptionFloats*>(opt)); break;
|
||||
case coInt: archive(*static_cast<const ConfigOptionInt*>(opt)); break;
|
||||
case coInts: archive(*static_cast<const ConfigOptionInts*>(opt)); break;
|
||||
case coString: archive(*static_cast<const ConfigOptionString*>(opt)); break;
|
||||
case coStrings: archive(*static_cast<const ConfigOptionStrings*>(opt)); break;
|
||||
case coPercent: archive(*static_cast<const ConfigOptionPercent*>(opt)); break;
|
||||
case coPercents: archive(*static_cast<const ConfigOptionPercents*>(opt)); break;
|
||||
case coFloatOrPercent: archive(*static_cast<const ConfigOptionFloatOrPercent*>(opt)); break;
|
||||
case coFloatsOrPercents: archive(*static_cast<const ConfigOptionFloatsOrPercents*>(opt)); break;
|
||||
case coPoint: archive(*static_cast<const ConfigOptionPoint*>(opt)); break;
|
||||
case coPoints: archive(*static_cast<const ConfigOptionPoints*>(opt)); break;
|
||||
case coPoint3: archive(*static_cast<const ConfigOptionPoint3*>(opt)); break;
|
||||
case coBool: archive(*static_cast<const ConfigOptionBool*>(opt)); break;
|
||||
case coBools: archive(*static_cast<const ConfigOptionBools*>(opt)); break;
|
||||
case coEnum: archive(*static_cast<const ConfigOptionEnumGeneric*>(opt)); break;
|
||||
// BBS
|
||||
case coEnums: archive(*static_cast<const ConfigOptionEnumsGeneric*>(opt)); break;
|
||||
case coIntsGroups: archive(*static_cast<const ConfigOptionIntsGroups *>(opt)); break;
|
||||
default: throw ConfigurationError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key);
|
||||
}
|
||||
}
|
||||
// Make the compiler happy, shut up the warnings.
|
||||
return nullptr;
|
||||
}
|
||||
case coEnums: archive(*static_cast<const ConfigOptionEnumsGeneric*>(opt)); break;
|
||||
case coIntsGroups: archive(*static_cast<const ConfigOptionIntsGroups*>(opt)); break;
|
||||
case coPointsGroups: archive(*static_cast<const ConfigOptionPointsGroups*>(opt)); break;
|
||||
default:
|
||||
throw ConfigurationError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") +
|
||||
this->opt_key);
|
||||
}
|
||||
}
|
||||
// Make the compiler happy, shut up the warnings.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Usually empty.
|
||||
// Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection,
|
||||
@@ -2514,7 +2626,7 @@ public:
|
||||
template<typename TYPE>
|
||||
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);
|
||||
if (opt != nullptr && opt->type() != TYPE::static_type()) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": attempt to access option with wrong type: " << opt_key;
|
||||
return nullptr;
|
||||
@@ -2794,6 +2906,40 @@ public:
|
||||
std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator cend() const { return options.cend(); }
|
||||
size_t size() const { return options.size(); }
|
||||
|
||||
/**
|
||||
* @brief Detailed information about the difference found for a single key.
|
||||
*/
|
||||
struct KeyDifference {
|
||||
std::optional<std::string> left_value;
|
||||
std::optional<std::string> right_value;
|
||||
|
||||
bool is_missing_key() const {
|
||||
return !left_value.has_value() || !right_value.has_value();
|
||||
}
|
||||
bool is_different_value() const {
|
||||
return left_value.has_value() && right_value.has_value() && (left_value.value() != right_value.value());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The full report object containing all detected differences.
|
||||
*/
|
||||
struct DynamicConfigDifference {
|
||||
std::map<t_config_option_key, KeyDifference> differences;
|
||||
|
||||
bool is_different() const {
|
||||
return !differences.empty();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Computes the symmetric difference between this DynamicConfig (left)
|
||||
* and another DynamicConfig (rhs).
|
||||
* @param rhs The right-hand side config to compare against.
|
||||
* @return DynamicConfigDifference report.
|
||||
*/
|
||||
DynamicConfigDifference diff_report(const DynamicConfig& rhs) const;
|
||||
|
||||
private:
|
||||
std::map<t_config_option_key, std::unique_ptr<ConfigOption>> options;
|
||||
|
||||
@@ -2801,6 +2947,8 @@ private:
|
||||
template<class Archive> void serialize(Archive &ar) { ar(options); }
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const DynamicConfig::DynamicConfigDifference& diff);
|
||||
|
||||
// Configuration store with a static definition of configuration values.
|
||||
// In Slic3r, the static configuration stores are during the slicing / g-code generation for efficiency reasons,
|
||||
// because the configuration values could be accessed directly.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "libslic3r.h"
|
||||
#include "BoundingBox.hpp"
|
||||
#include "ExPolygon.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "Polygon.hpp"
|
||||
#include "Polyline.hpp"
|
||||
|
||||
|
||||
@@ -647,7 +647,6 @@ ModelMaterial* Model::add_material(t_model_material_id material_id, const ModelM
|
||||
return material;
|
||||
}
|
||||
|
||||
// makes sure all objects have at least one instance
|
||||
bool Model::add_default_instances()
|
||||
{
|
||||
// apply a default position to all objects not having one
|
||||
@@ -1641,17 +1640,31 @@ Polygon ModelObject::convex_hull_2d(const Transform3d& trafo_instance) const
|
||||
Points pts;
|
||||
|
||||
for (const ModelVolume* v : volumes) {
|
||||
if (v->is_model_part())
|
||||
if (v->is_model_part()) {
|
||||
//BBS: use convex hull vertex instead of all
|
||||
append(pts, its_convex_hull_2d_above(v->get_convex_hull().its, (trafo_instance * v->get_matrix()).cast<float>(), 0.0f).points);
|
||||
// The next commented line instead of the previous + the rest of this #if0 section is the same as PrusaSlicer until https://github.com/prusa3d/PrusaSlicer/commit/2f7f3578d531f2d34f7732a64449606d86bb4aaa where it was parallelised.
|
||||
//append(pts, its_convex_hull_2d_above(v->mesh().its, (trafo_instance * v->get_matrix()).cast<float>(), 0.0f).points);
|
||||
// its_convex_hull_2d_above calls its_collect_mesh_projection_points_above
|
||||
// The latter multiplies each vertex by the full matrix
|
||||
// For every vector which crosses the Z plane, the intersection is used instead of any point below. Consecutive points below the Z plane are ignored.
|
||||
}
|
||||
}
|
||||
return Geometry::convex_hull(std::move(pts));
|
||||
#else
|
||||
// This seems to differ from PrusaSlicer (and the old code above) in that
|
||||
// points below the Z plane aren't treated specially.
|
||||
Points pts;
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part()) {
|
||||
const Polygon& volume_hull = v->get_convex_hull_2d(trafo_instance);
|
||||
// In comparison to the old code above, get_convex_hull_2d starts with:
|
||||
// new_matrix = trafo_instance * m_transformation.get_matrix();
|
||||
// which is the same matrix multiplication as above.
|
||||
// Then checks caches, maybe calling ModelVolume::calculate_convex_hull_2d(const Geometry::Transformation &) if no hit
|
||||
// That method accesses v->get_convex_hull().its (also used above).
|
||||
// It multiplies each point by the matrix w/o translate, then calls convex_hull(pts)
|
||||
// Then translates polygon in X & Y
|
||||
|
||||
pts.insert(pts.end(), volume_hull.points.begin(), volume_hull.points.end());
|
||||
}
|
||||
|
||||
@@ -1632,6 +1632,7 @@ public:
|
||||
|
||||
void delete_material(t_model_material_id material_id);
|
||||
void clear_materials();
|
||||
// Make sure all objects have at least one instance
|
||||
bool add_default_instances();
|
||||
// Returns approximate axis aligned bounding box of this model.
|
||||
BoundingBoxf3 bounding_box_approx() const;
|
||||
|
||||
@@ -17,9 +17,9 @@ using arrangement::CircleBed;
|
||||
// Do something with ArrangePolygons in virtual beds
|
||||
using VirtualBedFn = std::function<void(arrangement::ArrangePolygon&)>;
|
||||
|
||||
[[noreturn]] inline void throw_if_out_of_bed(arrangement::ArrangePolygon&)
|
||||
[[noreturn]] inline void throw_if_out_of_bed(arrangement::ArrangePolygon& ap)
|
||||
{
|
||||
throw Slic3r::RuntimeError("Objects could not fit on the bed");
|
||||
throw Slic3r::RuntimeError("Objects could not fit on the bed; bed_idx==" + std::to_string(ap.bed_idx));
|
||||
}
|
||||
|
||||
ArrangePolygons get_arrange_polys(const Model &model, ModelInstancePtrs &instances);
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "Polygon.hpp"
|
||||
#include "Polyline.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
double Polygon::length() const
|
||||
@@ -207,6 +209,7 @@ bool Polygon::intersections(const Line &line, Points *intersections) const
|
||||
}
|
||||
return intersections->size() > intersections_size;
|
||||
}
|
||||
|
||||
bool Polygon::overlaps(const Polygons& other) const
|
||||
{
|
||||
if (this->empty() || other.empty())
|
||||
@@ -219,6 +222,7 @@ bool Polygon::overlaps(const Polygons& other) const
|
||||
// If *this is completely inside other, then pl_out is empty, but the expolygons overlap. Test for that situation.
|
||||
std::any_of(other.begin(), other.end(), [this](auto& poly) {return poly.contains(this->points.front()); });
|
||||
}
|
||||
|
||||
// Filter points from poly to the output with the help of FilterFn.
|
||||
// filter function receives two vectors:
|
||||
// v1: this_point - previous_point
|
||||
@@ -236,8 +240,10 @@ Points filter_points_by_vectors(const Points &poly, FilterFn filter)
|
||||
for (Point p2 : poly) {
|
||||
// p2 is next point to the currently visited point p1.
|
||||
Vec2d v2 = (p2 - p1).cast<double>();
|
||||
// std::cerr << ((void*) &poly) << ": p1=" << p1 << "\tp2=" << p2 << "\tv1="<<v1<<"\tv2="<<v2;
|
||||
if (filter(v1, v2))
|
||||
out.emplace_back(p1);
|
||||
// std::cerr << "\n";
|
||||
v1 = v2;
|
||||
p1 = p2;
|
||||
}
|
||||
@@ -245,19 +251,56 @@ Points filter_points_by_vectors(const Points &poly, FilterFn filter)
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Filters points in a polygon based on a minimum angle threshold and a convex/concave criterion.
|
||||
*
|
||||
* This function iterates through the vertices of the input polygon and selects
|
||||
* points where the internal angle meets or exceeds the specified \p angle_threshold
|
||||
* and the point satisfies the condition defined by the \p convex_concave_filter.
|
||||
*
|
||||
* @tparam ConvexConcaveFilterFn The type of the callable filter object (e.g., function pointer, lambda, functor)
|
||||
* that determines if a point is considered convex or concave.
|
||||
* First vector is incoming line segment (ending at point), second vector is leaving current point.
|
||||
* It should have the signature `bool(const Vec2d&, const Vec2d&)`
|
||||
* @param poly Vertices of the input polygon.
|
||||
* @param angle_threshold The **minimum** angle (in radians) that the internal angle at a vertex must meet or exceed. Must be less than Pi (because angle is always positive) and greater than zero.
|
||||
* @param convex_concave_filter A callable object that returns `true` if the point should be included
|
||||
* (e.g., if it's convex), and `false` otherwise.
|
||||
* @return Points Point objects that meet both the angle threshold and the convex/concave filter criterion.
|
||||
*/
|
||||
template<typename ConvexConcaveFilterFn>
|
||||
Points filter_convex_concave_points_by_angle_threshold(const Points &poly, double angle_threshold, ConvexConcaveFilterFn convex_concave_filter)
|
||||
{
|
||||
// The filter function is typically cross2(v1, v2) {>,<} 0
|
||||
assert(angle_threshold >= 0.);
|
||||
assert(angle_threshold < M_PI);
|
||||
if (angle_threshold > EPSILON) {
|
||||
double cos_angle = cos(angle_threshold);
|
||||
return filter_points_by_vectors(poly, [convex_concave_filter, cos_angle](const Vec2d &v1, const Vec2d &v2){
|
||||
return convex_concave_filter(v1, v2) && v1.normalized().dot(v2.normalized()) < cos_angle;
|
||||
// The methods con{cave,vex}_points are documented as
|
||||
// "with the angle at the vertex larger than a threshold."
|
||||
// Due to the imprecision of floating point, this is difficult to get exactly right.
|
||||
// So I'm adding just enough here that an input of (M_PI/2) does not match a right angle.
|
||||
// Which doesn't mean it'll be correct for all values.
|
||||
// And we might learn people actually want "at or larger than threshold" instead.
|
||||
double cos_threshold = cos(std::nextafter(angle_threshold, +INFINITY));
|
||||
return filter_points_by_vectors(poly, [convex_concave_filter, cos_threshold](const Vec2d &v1, const Vec2d &v2){
|
||||
if (!convex_concave_filter(v1, v2)) { /*std::cerr << "FIL_FALS";*/ return false; }
|
||||
// Math lesson: Dot product is the product of the magnitudes and the cos(angle) between them.
|
||||
// So if we normalize both, their magnitudes are 1 and, thus, the dot product is cos(angle)
|
||||
// So we want to ensure we only pick angles *bigger* than our angle_threshold
|
||||
// cos(θ) goes 1->-1 as θ=0->Pi , the opposite direction
|
||||
// So if we want angle_vectors > angle_threshold
|
||||
// we must check dot_product_of_vectors < cos(angle_threshold)
|
||||
auto vec_dot = v1.normalized().dot(v2.normalized());
|
||||
// std::cerr << "\tvec_dot="<<vec_dot<<"\tcos_threshold="<<cos_threshold;
|
||||
if ( vec_dot < cos_threshold ) {
|
||||
// std::cerr << "TRUE";
|
||||
return true;
|
||||
}
|
||||
// std::cerr <<"DOT_FALS";
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
return filter_points_by_vectors(poly, [convex_concave_filter](const Vec2d &v1, const Vec2d &v2){
|
||||
return convex_concave_filter(v1, v2);
|
||||
});
|
||||
return filter_points_by_vectors(poly, convex_concave_filter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -712,4 +755,4 @@ Polygon make_circle_num_segments(double radius, size_t num_segments)
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "PrintConfig.hpp"
|
||||
#include "PrintConfigConstants.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Config.hpp"
|
||||
#include "MaterialType.hpp"
|
||||
@@ -686,7 +687,7 @@ void PrintConfigDef::init_common_params()
|
||||
def->tooltip = L("Slicing height for each layer. Smaller layer height means more accurate and more printing time.");
|
||||
def->sidetext = L("mm"); // milimeters, CIS languages need translation
|
||||
def->min = 0;
|
||||
def->set_default_value(new ConfigOptionFloat(0.2));
|
||||
def->set_default_value(new ConfigOptionFloat(INITIAL_LAYER_HEIGHT));
|
||||
|
||||
def = this->add("printable_height", coFloat);
|
||||
def->label = L("Printable height");
|
||||
@@ -833,7 +834,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->category = L("Quality");
|
||||
def->tooltip = L("Detour to avoid traveling across walls, which may cause blobs on the surface.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
def->set_default_value(new ConfigOptionBool(INITIAL_REDUCE_CROSSING_WALL));
|
||||
|
||||
def = this->add("max_travel_detour_distance", coFloatOrPercent);
|
||||
def->label = L("Avoid crossing walls - Max detour length");
|
||||
@@ -4598,7 +4599,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->min = 0;
|
||||
def->max = 100;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInt(0));
|
||||
def->set_default_value(new ConfigOptionInt(INITIAL_RAFT_LAYERS));
|
||||
|
||||
def = this->add("resolution", coFloat);
|
||||
def->label = L("Resolution");
|
||||
@@ -9519,10 +9520,10 @@ std::map<std::string, std::string> validate(const FullPrintConfig &cfg, bool und
|
||||
#define PRINT_CONFIG_CACHE_INITIALIZE(CLASSES_SEQ) \
|
||||
BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CACHE_ELEMENT_DEFINITION, _, BOOST_PP_TUPLE_TO_SEQ(CLASSES_SEQ)) \
|
||||
int print_config_static_initializer() { \
|
||||
/* Putting a trace here to avoid the compiler to optimize out this function. */ \
|
||||
BOOST_LOG_TRIVIAL(trace) << "Initializing StaticPrintConfigs"; \
|
||||
/* For some reason it's important this function doesn't get optimized out, so this should work. */ \
|
||||
static volatile int ret = 1; \
|
||||
BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CACHE_ELEMENT_INITIALIZATION, _, BOOST_PP_TUPLE_TO_SEQ(CLASSES_SEQ)) \
|
||||
return 1; \
|
||||
return ret; \
|
||||
}
|
||||
PRINT_CONFIG_CACHE_INITIALIZE((
|
||||
PrintObjectConfig, PrintRegionConfig, MachineEnvelopeConfig, GCodeConfig, PrintConfig, FullPrintConfig,
|
||||
|
||||
@@ -512,7 +512,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType)
|
||||
|
||||
class DynamicPrintConfig;
|
||||
|
||||
// Defines each and every confiuration option of Slic3r, including the properties of the GUI dialogs.
|
||||
// Defines each and every configuration option of Slic3r, including the properties of the GUI dialogs.
|
||||
// Does not store the actual values, but defines default values.
|
||||
class PrintConfigDef : public ConfigDef
|
||||
{
|
||||
@@ -563,7 +563,6 @@ double min_object_distance(const ConfigBase &cfg);
|
||||
// The dynamic configuration is also used to store user modifications of the print global parameters,
|
||||
// so the modified configuration values may be diffed against the active configuration
|
||||
// to invalidate the proper slicing resp. g-code generation processing steps.
|
||||
// This object is mapped to Perl as Slic3r::Config.
|
||||
class DynamicPrintConfig : public DynamicConfig
|
||||
{
|
||||
public:
|
||||
|
||||
7
src/libslic3r/PrintConfigConstants.hpp
Normal file
7
src/libslic3r/PrintConfigConstants.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
// These are here just for unit testing.
|
||||
|
||||
#define INITIAL_LAYER_HEIGHT 0.2
|
||||
#define INITIAL_RAFT_LAYERS 0
|
||||
#define INITIAL_REDUCE_CROSSING_WALL false
|
||||
@@ -217,12 +217,12 @@ bool TriangleMesh::ReadSTLFile(const char *input_file, bool repair, ImportstlPro
|
||||
return from_stl(stl, repair);
|
||||
}
|
||||
|
||||
bool TriangleMesh::write_ascii(const char* output_file)
|
||||
bool TriangleMesh::write_ascii(const char* output_file) const
|
||||
{
|
||||
return its_write_stl_ascii(output_file, "", this->its);
|
||||
}
|
||||
|
||||
bool TriangleMesh::write_binary(const char* output_file)
|
||||
bool TriangleMesh::write_binary(const char* output_file) const
|
||||
{
|
||||
return its_write_stl_binary(output_file, "", this->its);
|
||||
}
|
||||
|
||||
@@ -95,8 +95,8 @@ public:
|
||||
void clear() { this->its.clear(); this->m_stats.clear(); }
|
||||
bool from_stl(stl_file& stl, bool repair = true);
|
||||
bool ReadSTLFile(const char *input_file, bool repair = true, ImportstlProgressFn stlFn = nullptr, int custom_header_length = 80);
|
||||
bool write_ascii(const char* output_file);
|
||||
bool write_binary(const char* output_file);
|
||||
bool write_ascii(const char* output_file) const;
|
||||
bool write_binary(const char* output_file) const;
|
||||
float volume();
|
||||
void WriteOBJFile(const char* output_file) const;
|
||||
void scale(float factor);
|
||||
|
||||
Reference in New Issue
Block a user