mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-14 09:41:47 +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";
|
||||
};
|
||||
|
||||
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.
|
||||
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 {
|
||||
std::stringstream ss;
|
||||
@ -235,7 +240,8 @@ static bool verify_config_file_checksum(boost::nowide::ifstream &ifs)
|
||||
};
|
||||
|
||||
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.
|
||||
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.
|
||||
// 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 }))
|
||||
return true;
|
||||
return {true, contains_null};
|
||||
}
|
||||
return false;
|
||||
return {false, contains_null};
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -264,14 +270,25 @@ std::string AppConfig::load(const std::string &path)
|
||||
ifs.open(path);
|
||||
#ifdef WIN32
|
||||
// Verify the checksum of the config file without taking just for debugging purpose.
|
||||
if (!verify_config_file_checksum(ifs))
|
||||
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.";
|
||||
const ConfigFileInfo config_file_info = check_config_file_and_verify_checksum(ifs);
|
||||
if (!config_file_info.correct_checksum)
|
||||
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);
|
||||
#endif
|
||||
pt::read_ini(ifs, tree);
|
||||
} catch (pt::ptree_error& ex) {
|
||||
try {
|
||||
pt::read_ini(ifs, tree);
|
||||
} catch (pt::ptree_error &ex) {
|
||||
throw Slic3r::CriticalException(ex.what());
|
||||
}
|
||||
} catch (Slic3r::CriticalException &ex) {
|
||||
#ifdef WIN32
|
||||
// The configuration file is corrupted, try replacing it with the backup configuration.
|
||||
ifs.close();
|
||||
@ -279,29 +296,29 @@ std::string AppConfig::load(const std::string &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.
|
||||
boost::nowide::ifstream backup_ifs(backup_path);
|
||||
if (!verify_config_file_checksum(backup_ifs)) {
|
||||
BOOST_LOG_TRIVIAL(error) << format("Both \"%1%\" and \"%2%\" are corrupted. It isn't possible to restore configuration from the backup.", path, backup_path);
|
||||
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(R"(Both "%1%" and "%2%" are corrupted. It isn't possible to restore configuration from the backup.)", path, backup_path);
|
||||
backup_ifs.close();
|
||||
boost::filesystem::remove(backup_path);
|
||||
} 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();
|
||||
boost::filesystem::remove(backup_path);
|
||||
} 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 {
|
||||
ifs.open(path);
|
||||
pt::read_ini(ifs, tree);
|
||||
recovered = true;
|
||||
} 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
|
||||
#endif // WIN32
|
||||
BOOST_LOG_TRIVIAL(info) << format("Failed to parse configuration file \"%1%\": %2%", path, ex.what());
|
||||
if (! recovered) {
|
||||
BOOST_LOG_TRIVIAL(info) << format(R"(Failed to parse configuration file "%1%": %2%)", path, ex.what());
|
||||
if (!recovered) {
|
||||
// Report the initial error of parsing PrusaSlicer.ini.
|
||||
// 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)
|
||||
|
@ -84,7 +84,7 @@ public:
|
||||
virtual ExtrusionEntity* clone() const = 0;
|
||||
// Create a new object, initialize it with this object using the move semantics.
|
||||
virtual ExtrusionEntity* clone_move() = 0;
|
||||
virtual ~ExtrusionEntity() {}
|
||||
virtual ~ExtrusionEntity() = default;
|
||||
virtual void reverse() = 0;
|
||||
virtual const Point& first_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) {}
|
||||
explicit ExtrusionEntityCollection(const ExtrusionPaths &paths);
|
||||
ExtrusionEntityCollection& operator=(const ExtrusionEntityCollection &other);
|
||||
ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other)
|
||||
{ this->entities = std::move(other.entities); this->no_sort = other.no_sort; return *this; }
|
||||
~ExtrusionEntityCollection() { clear(); }
|
||||
ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other) {
|
||||
this->clear();
|
||||
this->entities = std::move(other.entities);
|
||||
this->no_sort = other.no_sort;
|
||||
return *this;
|
||||
}
|
||||
~ExtrusionEntityCollection() override { clear(); }
|
||||
explicit operator ExtrusionPaths() const;
|
||||
|
||||
bool is_collection() const override { return true; }
|
||||
|
@ -35,6 +35,8 @@
|
||||
#include "SVG.hpp"
|
||||
|
||||
#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.
|
||||
// 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;
|
||||
if (gcodegen.config().wipe_tower_no_sparse_layers.value) {
|
||||
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)
|
||||
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();
|
||||
}
|
||||
|
||||
// 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:
|
||||
// Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
|
||||
// and export G-code into file.
|
||||
@ -1580,6 +1608,10 @@ void GCode::process_layers(
|
||||
[&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.
|
||||
output_stream.find_replace_supress();
|
||||
if (m_spiral_vase && m_find_replace)
|
||||
@ -1633,6 +1665,10 @@ void GCode::process_layers(
|
||||
[&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.
|
||||
output_stream.find_replace_supress();
|
||||
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.
|
||||
// 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())
|
||||
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
|
||||
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;
|
||||
|
||||
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);
|
||||
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
|
||||
|
||||
#endif // slic3r_LocalesUtils_hpp_
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "GCode.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "BuildVolume.hpp"
|
||||
|
||||
#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())),
|
||||
// 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.
|
||||
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());
|
||||
}
|
||||
// Make a copy, so it may be rotated for instances.
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "Thread.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "LocalesUtils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
@ -234,21 +235,8 @@ void name_tbb_thread_pool_threads_set_locale()
|
||||
std::ostringstream name;
|
||||
name << "slic3r_tbb_" << range.begin();
|
||||
set_current_thread_name(name.str().c_str());
|
||||
// Set locales of the worker thread to "C".
|
||||
#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
|
||||
// Set locales of the worker thread to "C".
|
||||
set_c_locales();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -3448,9 +3448,8 @@ void GCodeViewer::render_legend(float& legend_height)
|
||||
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;
|
||||
int extruders_count = wxGetApp().extruders_edited_cnt();
|
||||
std::vector<Color> last_color(extruders_count);
|
||||
for (int i = 0; i < extruders_count; ++i) {
|
||||
std::vector<Color> last_color(m_extruders_count);
|
||||
for (int i = 0; i < m_extruders_count; ++i) {
|
||||
last_color[i] = m_tool_colors[i];
|
||||
}
|
||||
int last_extruder_id = 1;
|
||||
|
@ -6419,12 +6419,13 @@ bool GLCanvas3D::_is_any_volume_outside() const
|
||||
void GLCanvas3D::_update_selection_from_hover()
|
||||
{
|
||||
bool ctrl_pressed = wxGetKeyState(WXK_CONTROL);
|
||||
bool selection_changed = false;
|
||||
|
||||
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();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
bool contains_all = true;
|
||||
for (int i : m_hover_volume_idxs) {
|
||||
|
@ -92,16 +92,18 @@ void RotoptimizeJob::finalize()
|
||||
|
||||
auto trmatrix = oi->get_transformation().get_matrix();
|
||||
Polygon trchull = o->convex_hull_2d(trmatrix);
|
||||
|
||||
MinAreaBoundigBox rotbb(trchull, MinAreaBoundigBox::pcConvex);
|
||||
double phi = rotbb.angle_to_X();
|
||||
|
||||
// The box should be landscape
|
||||
if(rotbb.width() < rotbb.height()) phi += PI / 2;
|
||||
|
||||
Vec3d rt = oi->get_rotation(); rt(Z) += phi;
|
||||
|
||||
oi->set_rotation(rt);
|
||||
|
||||
if (!trchull.empty()) {
|
||||
MinAreaBoundigBox rotbb(trchull, MinAreaBoundigBox::pcConvex);
|
||||
double phi = rotbb.angle_to_X();
|
||||
|
||||
// The box should be landscape
|
||||
if(rotbb.width() < rotbb.height()) phi += PI / 2;
|
||||
|
||||
Vec3d rt = oi->get_rotation(); rt(Z) += phi;
|
||||
|
||||
oi->set_rotation(rt);
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
if (CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) == S_OK) {
|
||||
if (SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED))) {
|
||||
{
|
||||
CComPtr<INetworkListManager> pNLM;
|
||||
if (pNLM.CoCreateInstance(CLSID_NetworkListManager) == S_OK) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user