mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-17 02:31:50 +08:00
Merge branch 'lm_250' into dev_250
This commit is contained in:
commit
d8877973e3
@ -225,8 +225,13 @@ static std::string appconfig_md5_hash_line(const std::string_view data)
|
|||||||
return "# MD5 checksum " + md5_digest_str + "\n";
|
return "# MD5 checksum " + md5_digest_str + "\n";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ConfigFileInfo {
|
||||||
|
bool correct_checksum {false};
|
||||||
|
bool contains_null {false};
|
||||||
|
};
|
||||||
|
|
||||||
// Assume that the last line with the comment inside the config file contains a checksum and that the user didn't modify the config file.
|
// Assume that the last line with the comment inside the config file contains a checksum and that the user didn't modify the config file.
|
||||||
static bool verify_config_file_checksum(boost::nowide::ifstream &ifs)
|
static ConfigFileInfo check_config_file_and_verify_checksum(boost::nowide::ifstream &ifs)
|
||||||
{
|
{
|
||||||
auto read_whole_config_file = [&ifs]() -> std::string {
|
auto read_whole_config_file = [&ifs]() -> std::string {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
@ -235,7 +240,8 @@ static bool verify_config_file_checksum(boost::nowide::ifstream &ifs)
|
|||||||
};
|
};
|
||||||
|
|
||||||
ifs.seekg(0, boost::nowide::ifstream::beg);
|
ifs.seekg(0, boost::nowide::ifstream::beg);
|
||||||
std::string whole_config = read_whole_config_file();
|
const std::string whole_config = read_whole_config_file();
|
||||||
|
const bool contains_null = whole_config.find_first_of('\0') != std::string::npos;
|
||||||
|
|
||||||
// The checksum should be on the last line in the config file.
|
// The checksum should be on the last line in the config file.
|
||||||
if (size_t last_comment_pos = whole_config.find_last_of('#'); last_comment_pos != std::string::npos) {
|
if (size_t last_comment_pos = whole_config.find_last_of('#'); last_comment_pos != std::string::npos) {
|
||||||
@ -244,9 +250,9 @@ static bool verify_config_file_checksum(boost::nowide::ifstream &ifs)
|
|||||||
// When the checksum isn't found, the checksum was not saved correctly, it was removed or it is an older config file without the checksum.
|
// When the checksum isn't found, the checksum was not saved correctly, it was removed or it is an older config file without the checksum.
|
||||||
// If the checksum is incorrect, then the file was either not saved correctly or modified.
|
// If the checksum is incorrect, then the file was either not saved correctly or modified.
|
||||||
if (std::string_view(whole_config.c_str() + last_comment_pos, whole_config.size() - last_comment_pos) == appconfig_md5_hash_line({ whole_config.data(), last_comment_pos }))
|
if (std::string_view(whole_config.c_str() + last_comment_pos, whole_config.size() - last_comment_pos) == appconfig_md5_hash_line({ whole_config.data(), last_comment_pos }))
|
||||||
return true;
|
return {true, contains_null};
|
||||||
}
|
}
|
||||||
return false;
|
return {false, contains_null};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -264,14 +270,25 @@ std::string AppConfig::load(const std::string &path)
|
|||||||
ifs.open(path);
|
ifs.open(path);
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
// Verify the checksum of the config file without taking just for debugging purpose.
|
// Verify the checksum of the config file without taking just for debugging purpose.
|
||||||
if (!verify_config_file_checksum(ifs))
|
const ConfigFileInfo config_file_info = check_config_file_and_verify_checksum(ifs);
|
||||||
BOOST_LOG_TRIVIAL(info) << "The configuration file " << path <<
|
if (!config_file_info.correct_checksum)
|
||||||
" has a wrong MD5 checksum or the checksum is missing. This may indicate a file corruption or a harmless user edit.";
|
BOOST_LOG_TRIVIAL(info)
|
||||||
|
<< "The configuration file " << path
|
||||||
|
<< " has a wrong MD5 checksum or the checksum is missing. This may indicate a file corruption or a harmless user edit.";
|
||||||
|
|
||||||
|
if (!config_file_info.correct_checksum && config_file_info.contains_null) {
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "The configuration file " + path + " is corrupted, because it is contains null characters.";
|
||||||
|
throw Slic3r::CriticalException("The configuration file contains null characters.");
|
||||||
|
}
|
||||||
|
|
||||||
ifs.seekg(0, boost::nowide::ifstream::beg);
|
ifs.seekg(0, boost::nowide::ifstream::beg);
|
||||||
#endif
|
#endif
|
||||||
pt::read_ini(ifs, tree);
|
try {
|
||||||
} catch (pt::ptree_error& ex) {
|
pt::read_ini(ifs, tree);
|
||||||
|
} catch (pt::ptree_error &ex) {
|
||||||
|
throw Slic3r::CriticalException(ex.what());
|
||||||
|
}
|
||||||
|
} catch (Slic3r::CriticalException &ex) {
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
// The configuration file is corrupted, try replacing it with the backup configuration.
|
// The configuration file is corrupted, try replacing it with the backup configuration.
|
||||||
ifs.close();
|
ifs.close();
|
||||||
@ -279,29 +296,29 @@ std::string AppConfig::load(const std::string &path)
|
|||||||
if (boost::filesystem::exists(backup_path)) {
|
if (boost::filesystem::exists(backup_path)) {
|
||||||
// Compute checksum of the configuration backup file and try to load configuration from it when the checksum is correct.
|
// Compute checksum of the configuration backup file and try to load configuration from it when the checksum is correct.
|
||||||
boost::nowide::ifstream backup_ifs(backup_path);
|
boost::nowide::ifstream backup_ifs(backup_path);
|
||||||
if (!verify_config_file_checksum(backup_ifs)) {
|
if (const ConfigFileInfo config_file_info = check_config_file_and_verify_checksum(backup_ifs); !config_file_info.correct_checksum || config_file_info.contains_null) {
|
||||||
BOOST_LOG_TRIVIAL(error) << format("Both \"%1%\" and \"%2%\" are corrupted. It isn't possible to restore configuration from the backup.", path, backup_path);
|
BOOST_LOG_TRIVIAL(error) << format(R"(Both "%1%" and "%2%" are corrupted. It isn't possible to restore configuration from the backup.)", path, backup_path);
|
||||||
backup_ifs.close();
|
backup_ifs.close();
|
||||||
boost::filesystem::remove(backup_path);
|
boost::filesystem::remove(backup_path);
|
||||||
} else if (std::string error_message; copy_file(backup_path, path, error_message, false) != SUCCESS) {
|
} else if (std::string error_message; copy_file(backup_path, path, error_message, false) != SUCCESS) {
|
||||||
BOOST_LOG_TRIVIAL(error) << format("Configuration file \"%1%\" is corrupted. Failed to restore from backup \"%2%\": %3%", path, backup_path, error_message);
|
BOOST_LOG_TRIVIAL(error) << format(R"(Configuration file "%1%" is corrupted. Failed to restore from backup "%2%": %3%)", path, backup_path, error_message);
|
||||||
backup_ifs.close();
|
backup_ifs.close();
|
||||||
boost::filesystem::remove(backup_path);
|
boost::filesystem::remove(backup_path);
|
||||||
} else {
|
} else {
|
||||||
BOOST_LOG_TRIVIAL(info) << format("Configuration file \"%1%\" was corrupted. It has been succesfully restored from the backup \"%2%\".", path, backup_path);
|
BOOST_LOG_TRIVIAL(info) << format(R"(Configuration file "%1%" was corrupted. It has been successfully restored from the backup "%2%".)", path, backup_path);
|
||||||
// Try parse configuration file after restore from backup.
|
// Try parse configuration file after restore from backup.
|
||||||
try {
|
try {
|
||||||
ifs.open(path);
|
ifs.open(path);
|
||||||
pt::read_ini(ifs, tree);
|
pt::read_ini(ifs, tree);
|
||||||
recovered = true;
|
recovered = true;
|
||||||
} catch (pt::ptree_error& ex) {
|
} catch (pt::ptree_error& ex) {
|
||||||
BOOST_LOG_TRIVIAL(info) << format("Failed to parse configuration file \"%1%\" after it has been restored from backup: %2%", path, ex.what());
|
BOOST_LOG_TRIVIAL(info) << format(R"(Failed to parse configuration file "%1%" after it has been restored from backup: %2%)", path, ex.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
BOOST_LOG_TRIVIAL(info) << format("Failed to parse configuration file \"%1%\": %2%", path, ex.what());
|
BOOST_LOG_TRIVIAL(info) << format(R"(Failed to parse configuration file "%1%": %2%)", path, ex.what());
|
||||||
if (! recovered) {
|
if (!recovered) {
|
||||||
// Report the initial error of parsing PrusaSlicer.ini.
|
// Report the initial error of parsing PrusaSlicer.ini.
|
||||||
// Error while parsing config file. We'll customize the error message and rethrow to be displayed.
|
// Error while parsing config file. We'll customize the error message and rethrow to be displayed.
|
||||||
// ! But to avoid the use of _utf8 (related to use of wxWidgets)
|
// ! But to avoid the use of _utf8 (related to use of wxWidgets)
|
||||||
|
@ -84,7 +84,7 @@ public:
|
|||||||
virtual ExtrusionEntity* clone() const = 0;
|
virtual ExtrusionEntity* clone() const = 0;
|
||||||
// Create a new object, initialize it with this object using the move semantics.
|
// Create a new object, initialize it with this object using the move semantics.
|
||||||
virtual ExtrusionEntity* clone_move() = 0;
|
virtual ExtrusionEntity* clone_move() = 0;
|
||||||
virtual ~ExtrusionEntity() {}
|
virtual ~ExtrusionEntity() = default;
|
||||||
virtual void reverse() = 0;
|
virtual void reverse() = 0;
|
||||||
virtual const Point& first_point() const = 0;
|
virtual const Point& first_point() const = 0;
|
||||||
virtual const Point& last_point() const = 0;
|
virtual const Point& last_point() const = 0;
|
||||||
|
@ -36,9 +36,13 @@ public:
|
|||||||
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->clear();
|
||||||
~ExtrusionEntityCollection() { clear(); }
|
this->entities = std::move(other.entities);
|
||||||
|
this->no_sort = other.no_sort;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
~ExtrusionEntityCollection() override { clear(); }
|
||||||
explicit operator ExtrusionPaths() const;
|
explicit operator ExtrusionPaths() const;
|
||||||
|
|
||||||
bool is_collection() const override { return true; }
|
bool is_collection() const override { return true; }
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
#include "SVG.hpp"
|
#include "SVG.hpp"
|
||||||
|
|
||||||
#include <tbb/parallel_for.h>
|
#include <tbb/parallel_for.h>
|
||||||
|
#include <tbb/task_scheduler_observer.h>
|
||||||
|
#include <tbb/enumerable_thread_specific.h>
|
||||||
|
|
||||||
// Intel redesigned some TBB interface considerably when merging TBB with their oneAPI set of libraries, see GH #7332.
|
// Intel redesigned some TBB interface considerably when merging TBB with their oneAPI set of libraries, see GH #7332.
|
||||||
// We are using quite an old TBB 2017 U7. Before we update our build servers, let's use the old API, which is deprecated in up to date TBB.
|
// We are using quite an old TBB 2017 U7. Before we update our build servers, let's use the old API, which is deprecated in up to date TBB.
|
||||||
@ -455,7 +457,7 @@ namespace Slic3r {
|
|||||||
bool ignore_sparse = false;
|
bool ignore_sparse = false;
|
||||||
if (gcodegen.config().wipe_tower_no_sparse_layers.value) {
|
if (gcodegen.config().wipe_tower_no_sparse_layers.value) {
|
||||||
wipe_tower_z = m_last_wipe_tower_print_z;
|
wipe_tower_z = m_last_wipe_tower_print_z;
|
||||||
ignore_sparse = (m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool);
|
ignore_sparse = (m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool && m_layer_idx != 0);
|
||||||
if (m_tool_change_idx == 0 && !ignore_sparse)
|
if (m_tool_change_idx == 0 && !ignore_sparse)
|
||||||
wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height;
|
wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height;
|
||||||
}
|
}
|
||||||
@ -1537,6 +1539,32 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||||||
print.throw_if_canceled();
|
print.throw_if_canceled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For unknown reasons and in sporadic cases when GCode export is processing, some participating thread
|
||||||
|
// in tbb::parallel_pipeline has not set locales to "C", probably because this thread is newly spawned.
|
||||||
|
// So in this class method on_scheduler_entry is called for every thread before it starts participating
|
||||||
|
// in tbb::parallel_pipeline to ensure that locales are set correctly
|
||||||
|
|
||||||
|
// For tbb::parallel_pipeline, it seems that on_scheduler_entry is called for every layer and every filter.
|
||||||
|
// We ensure using thread-local storage that locales will be set to "C" just once for any participating thread.
|
||||||
|
class TBBLocalesSetter : public tbb::task_scheduler_observer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TBBLocalesSetter() { this->observe(true); }
|
||||||
|
~TBBLocalesSetter() override = default;
|
||||||
|
|
||||||
|
void on_scheduler_entry(bool is_worker) override
|
||||||
|
{
|
||||||
|
if (bool &is_locales_sets = m_is_locales_sets.local(); !is_locales_sets) {
|
||||||
|
// Set locales of the worker thread to "C".
|
||||||
|
set_c_locales();
|
||||||
|
is_locales_sets = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
tbb::enumerable_thread_specific<bool, tbb::cache_aligned_allocator<bool>, tbb::ets_key_usage_type::ets_key_per_instance> m_is_locales_sets;
|
||||||
|
};
|
||||||
|
|
||||||
// Process all layers of all objects (non-sequential mode) with a parallel pipeline:
|
// Process all layers of all objects (non-sequential mode) with a parallel pipeline:
|
||||||
// Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
|
// Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
|
||||||
// and export G-code into file.
|
// and export G-code into file.
|
||||||
@ -1580,6 +1608,10 @@ void GCode::process_layers(
|
|||||||
[&output_stream](std::string s) { output_stream.write(s); }
|
[&output_stream](std::string s) { output_stream.write(s); }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// It registers a handler that sets locales to "C" before any TBB thread starts participating in tbb::parallel_pipeline.
|
||||||
|
// Handler is unregistered when the destructor is called.
|
||||||
|
TBBLocalesSetter locales_setter;
|
||||||
|
|
||||||
// The pipeline elements are joined using const references, thus no copying is performed.
|
// The pipeline elements are joined using const references, thus no copying is performed.
|
||||||
output_stream.find_replace_supress();
|
output_stream.find_replace_supress();
|
||||||
if (m_spiral_vase && m_find_replace)
|
if (m_spiral_vase && m_find_replace)
|
||||||
@ -1633,6 +1665,10 @@ void GCode::process_layers(
|
|||||||
[&output_stream](std::string s) { output_stream.write(s); }
|
[&output_stream](std::string s) { output_stream.write(s); }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// It registers a handler that sets locales to "C" before any TBB thread starts participating in tbb::parallel_pipeline.
|
||||||
|
// Handler is unregistered when the destructor is called.
|
||||||
|
TBBLocalesSetter locales_setter;
|
||||||
|
|
||||||
// The pipeline elements are joined using const references, thus no copying is performed.
|
// The pipeline elements are joined using const references, thus no copying is performed.
|
||||||
output_stream.find_replace_supress();
|
output_stream.find_replace_supress();
|
||||||
if (m_spiral_vase && m_find_replace)
|
if (m_spiral_vase && m_find_replace)
|
||||||
|
@ -1180,7 +1180,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
|
|||||||
|
|
||||||
// Ask our writer about how much material was consumed.
|
// Ask our writer about how much material was consumed.
|
||||||
// Skip this in case the layer is sparse and config option to not print sparse layers is enabled.
|
// Skip this in case the layer is sparse and config option to not print sparse layers is enabled.
|
||||||
if (! m_no_sparse_layers || toolchanges_on_layer)
|
if (! m_no_sparse_layers || toolchanges_on_layer || first_layer)
|
||||||
if (m_current_tool < m_used_filament_length.size())
|
if (m_current_tool < m_used_filament_length.size())
|
||||||
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||||
|
|
||||||
@ -1196,7 +1196,7 @@ void WipeTower::plan_toolchange(float z_par, float layer_height_par, unsigned in
|
|||||||
if (m_plan.empty() || m_plan.back().z + WT_EPSILON < z_par) // if we moved to a new layer, we'll add it to m_plan first
|
if (m_plan.empty() || m_plan.back().z + WT_EPSILON < z_par) // if we moved to a new layer, we'll add it to m_plan first
|
||||||
m_plan.push_back(WipeTowerInfo(z_par, layer_height_par));
|
m_plan.push_back(WipeTowerInfo(z_par, layer_height_par));
|
||||||
|
|
||||||
if (m_first_layer_idx == size_t(-1) && (! m_no_sparse_layers || old_tool != new_tool))
|
if (m_first_layer_idx == size_t(-1) && (! m_no_sparse_layers || old_tool != new_tool || m_plan.size() == 1))
|
||||||
m_first_layer_idx = m_plan.size() - 1;
|
m_first_layer_idx = m_plan.size() - 1;
|
||||||
|
|
||||||
if (old_tool == new_tool) // new layer without toolchanges - we are done
|
if (old_tool == new_tool) // new layer without toolchanges - we are done
|
||||||
|
@ -43,6 +43,25 @@ std::string float_to_string_decimal_point(double value, int precision = -1);
|
|||||||
//std::string float_to_string_decimal_point(float value, int precision = -1);
|
//std::string float_to_string_decimal_point(float value, int precision = -1);
|
||||||
double string_to_double_decimal_point(const std::string_view str, size_t* pos = nullptr);
|
double string_to_double_decimal_point(const std::string_view str, size_t* pos = nullptr);
|
||||||
|
|
||||||
|
// Set locales to "C".
|
||||||
|
inline void set_c_locales()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
|
||||||
|
std::setlocale(LC_ALL, "C");
|
||||||
|
#else
|
||||||
|
// We are leaking some memory here, because the newlocale() produced memory will never be released.
|
||||||
|
// This is not a problem though, as there will be a maximum one worker thread created per physical thread.
|
||||||
|
uselocale(newlocale(
|
||||||
|
#ifdef __APPLE__
|
||||||
|
LC_ALL_MASK
|
||||||
|
#else // some Unix / Linux / BSD
|
||||||
|
LC_ALL
|
||||||
|
#endif
|
||||||
|
, "C", nullptr));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
#endif // slic3r_LocalesUtils_hpp_
|
#endif // slic3r_LocalesUtils_hpp_
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "GCode.hpp"
|
#include "GCode.hpp"
|
||||||
#include "GCode/WipeTower.hpp"
|
#include "GCode/WipeTower.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
#include "BuildVolume.hpp"
|
||||||
|
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
|
|
||||||
@ -386,7 +387,7 @@ bool Print::sequential_print_horizontal_clearance_valid(const Print& print, Poly
|
|||||||
Geometry::assemble_transform({ 0.0, 0.0, model_instance0->get_offset().z() }, model_instance0->get_rotation(), model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
|
Geometry::assemble_transform({ 0.0, 0.0, model_instance0->get_offset().z() }, model_instance0->get_rotation(), model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
|
||||||
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
|
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
|
||||||
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
|
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
|
||||||
float(scale_(0.5 * print.config().extruder_clearance_radius.value - EPSILON)),
|
float(scale_(0.5 * print.config().extruder_clearance_radius.value - BuildVolume::BedEpsilon)),
|
||||||
jtRound, scale_(0.1)).front());
|
jtRound, scale_(0.1)).front());
|
||||||
}
|
}
|
||||||
// Make a copy, so it may be rotated for instances.
|
// Make a copy, so it may be rotated for instances.
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include "Thread.hpp"
|
#include "Thread.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
#include "LocalesUtils.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -234,21 +235,8 @@ void name_tbb_thread_pool_threads_set_locale()
|
|||||||
std::ostringstream name;
|
std::ostringstream name;
|
||||||
name << "slic3r_tbb_" << range.begin();
|
name << "slic3r_tbb_" << range.begin();
|
||||||
set_current_thread_name(name.str().c_str());
|
set_current_thread_name(name.str().c_str());
|
||||||
// Set locales of the worker thread to "C".
|
// Set locales of the worker thread to "C".
|
||||||
#ifdef _WIN32
|
set_c_locales();
|
||||||
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
|
|
||||||
std::setlocale(LC_ALL, "C");
|
|
||||||
#else
|
|
||||||
// We are leaking some memory here, because the newlocale() produced memory will never be released.
|
|
||||||
// This is not a problem though, as there will be a maximum one worker thread created per physical thread.
|
|
||||||
uselocale(newlocale(
|
|
||||||
#ifdef __APPLE__
|
|
||||||
LC_ALL_MASK
|
|
||||||
#else // some Unix / Linux / BSD
|
|
||||||
LC_ALL
|
|
||||||
#endif
|
|
||||||
, "C", nullptr));
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -3448,9 +3448,8 @@ void GCodeViewer::render_legend(float& legend_height)
|
|||||||
PartialTimes items;
|
PartialTimes items;
|
||||||
|
|
||||||
std::vector<CustomGCode::Item> custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes : m_custom_gcode_per_print_z;
|
std::vector<CustomGCode::Item> custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes : m_custom_gcode_per_print_z;
|
||||||
int extruders_count = wxGetApp().extruders_edited_cnt();
|
std::vector<Color> last_color(m_extruders_count);
|
||||||
std::vector<Color> last_color(extruders_count);
|
for (int i = 0; i < m_extruders_count; ++i) {
|
||||||
for (int i = 0; i < extruders_count; ++i) {
|
|
||||||
last_color[i] = m_tool_colors[i];
|
last_color[i] = m_tool_colors[i];
|
||||||
}
|
}
|
||||||
int last_extruder_id = 1;
|
int last_extruder_id = 1;
|
||||||
|
@ -6419,12 +6419,13 @@ bool GLCanvas3D::_is_any_volume_outside() const
|
|||||||
void GLCanvas3D::_update_selection_from_hover()
|
void GLCanvas3D::_update_selection_from_hover()
|
||||||
{
|
{
|
||||||
bool ctrl_pressed = wxGetKeyState(WXK_CONTROL);
|
bool ctrl_pressed = wxGetKeyState(WXK_CONTROL);
|
||||||
|
bool selection_changed = false;
|
||||||
|
|
||||||
if (m_hover_volume_idxs.empty()) {
|
if (m_hover_volume_idxs.empty()) {
|
||||||
if (!ctrl_pressed && (m_rectangle_selection.get_state() == GLSelectionRectangle::Select))
|
if (!ctrl_pressed && (m_rectangle_selection.get_state() == GLSelectionRectangle::Select)) {
|
||||||
|
selection_changed = ! m_selection.is_empty();
|
||||||
m_selection.remove_all();
|
m_selection.remove_all();
|
||||||
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GLSelectionRectangle::EState state = m_rectangle_selection.get_state();
|
GLSelectionRectangle::EState state = m_rectangle_selection.get_state();
|
||||||
@ -6437,7 +6438,6 @@ void GLCanvas3D::_update_selection_from_hover()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool selection_changed = false;
|
|
||||||
if (state == GLSelectionRectangle::Select) {
|
if (state == GLSelectionRectangle::Select) {
|
||||||
bool contains_all = true;
|
bool contains_all = true;
|
||||||
for (int i : m_hover_volume_idxs) {
|
for (int i : m_hover_volume_idxs) {
|
||||||
|
@ -92,16 +92,18 @@ void RotoptimizeJob::finalize()
|
|||||||
|
|
||||||
auto trmatrix = oi->get_transformation().get_matrix();
|
auto trmatrix = oi->get_transformation().get_matrix();
|
||||||
Polygon trchull = o->convex_hull_2d(trmatrix);
|
Polygon trchull = o->convex_hull_2d(trmatrix);
|
||||||
|
|
||||||
MinAreaBoundigBox rotbb(trchull, MinAreaBoundigBox::pcConvex);
|
if (!trchull.empty()) {
|
||||||
double phi = rotbb.angle_to_X();
|
MinAreaBoundigBox rotbb(trchull, MinAreaBoundigBox::pcConvex);
|
||||||
|
double phi = rotbb.angle_to_X();
|
||||||
// The box should be landscape
|
|
||||||
if(rotbb.width() < rotbb.height()) phi += PI / 2;
|
// The box should be landscape
|
||||||
|
if(rotbb.width() < rotbb.height()) phi += PI / 2;
|
||||||
Vec3d rt = oi->get_rotation(); rt(Z) += phi;
|
|
||||||
|
Vec3d rt = oi->get_rotation(); rt(Z) += phi;
|
||||||
oi->set_rotation(rt);
|
|
||||||
|
oi->set_rotation(rt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Correct the z offset of the object which was corrupted be
|
// Correct the z offset of the object which was corrupted be
|
||||||
|
@ -140,7 +140,7 @@ static bool check_internet_connection_win()
|
|||||||
{
|
{
|
||||||
bool internet = true; // return true if COM object creation fails.
|
bool internet = true; // return true if COM object creation fails.
|
||||||
|
|
||||||
if (CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) == S_OK) {
|
if (SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED))) {
|
||||||
{
|
{
|
||||||
CComPtr<INetworkListManager> pNLM;
|
CComPtr<INetworkListManager> pNLM;
|
||||||
if (pNLM.CoCreateInstance(CLSID_NetworkListManager) == S_OK) {
|
if (pNLM.CoCreateInstance(CLSID_NetworkListManager) == S_OK) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user