diff --git a/src/libslic3r/Extruder.cpp b/src/libslic3r/Extruder.cpp index d2ff650974..b1a089d085 100644 --- a/src/libslic3r/Extruder.cpp +++ b/src/libslic3r/Extruder.cpp @@ -37,6 +37,7 @@ std::pair Extruder::extrude(double dE) value supplied will overwrite the previous one if any. */ std::pair Extruder::retract(double retract_length, double restart_extra) { + assert(restart_extra >= 0); // in case of relative E distances we always reset to 0 before any output if (m_config->use_relative_e_distances) m_E = 0.; @@ -64,6 +65,24 @@ std::pair Extruder::unretract() return std::make_pair(dE, emitE); } +// Setting the retract state from the script. +// Sets current retraction value & restart extra filament amount if retracted > 0. +void Extruder::set_retracted(double retracted, double restart_extra) +{ + if (retracted < - EPSILON) + throw Slic3r::RuntimeError("Custom G-code reports negative z_retracted."); + if (restart_extra < - EPSILON) + throw Slic3r::RuntimeError("Custom G-code reports negative z_restart_extra."); + + if (retracted > EPSILON) { + m_retracted = retracted; + m_restart_extra = restart_extra < EPSILON ? 0 : restart_extra; + } else { + m_retracted = 0; + m_restart_extra = 0; + } +} + // Used filament volume in mm^3. double Extruder::extruded_volume() const { diff --git a/src/libslic3r/Extruder.hpp b/src/libslic3r/Extruder.hpp index 7491b1c8f5..7c37f1934c 100644 --- a/src/libslic3r/Extruder.hpp +++ b/src/libslic3r/Extruder.hpp @@ -36,6 +36,19 @@ public: double extruded_volume() const; // Used filament length in mm. double used_filament() const; + + // Getters for the PlaceholderParser. + // Get current extruder position. Only applicable with absolute extruder addressing. + double position() const { return m_E; } + // Get current retraction value. Only non-negative values. + double retracted() const { return m_retracted; } + // Get extra retraction planned after + double restart_extra() const { return m_restart_extra; } + // Setters for the PlaceholderParser. + // Set current extruder position. Only applicable with absolute extruder addressing. + void set_position(double e) { m_E = e; } + // Sets current retraction value & restart extra filament amount if retracted > 0. + void set_retracted(double retracted, double restart_extra); double filament_diameter() const; double filament_crossection() const { return this->filament_diameter() * this->filament_diameter() * 0.25 * PI; } diff --git a/src/libslic3r/ExtrusionRole.cpp b/src/libslic3r/ExtrusionRole.cpp index 01ec73ed14..a7ec319496 100644 --- a/src/libslic3r/ExtrusionRole.cpp +++ b/src/libslic3r/ExtrusionRole.cpp @@ -1,4 +1,5 @@ #include "ExtrusionRole.hpp" +#include "I18N.hpp" #include #include diff --git a/src/libslic3r/Format/SLAArchiveReader.cpp b/src/libslic3r/Format/SLAArchiveReader.cpp index f80b9a2ba6..b931ea0e4d 100644 --- a/src/libslic3r/Format/SLAArchiveReader.cpp +++ b/src/libslic3r/Format/SLAArchiveReader.cpp @@ -1,6 +1,7 @@ #include "SLAArchiveReader.hpp" #include "SL1.hpp" #include "SL1_SVG.hpp" +#include "I18N.hpp" #include "libslic3r/SlicesToTriangleMesh.hpp" @@ -36,8 +37,8 @@ static const std::map REGISTERED_ARCHIVES { [] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) { return std::make_unique(fname, quality, progr); } } }, { - "SL2", - { L("SL2 archive files"), {"sl2", "sl1_svg"/*, "zip"*/}, // also a zip but unnecessary hassle to implement single extension for multiple archives + "SL1SVG", + { L("SL1SVG archive files"), {"sl1_svg"/*, "zip"*/}, // also a zip but unnecessary hassle to implement single extension for multiple archives [] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) { return std::make_unique(fname, quality, progr); }} }, // TODO: pwmx and future others. diff --git a/src/libslic3r/Format/SLAArchiveWriter.cpp b/src/libslic3r/Format/SLAArchiveWriter.cpp index babf92d0db..b28c2c6806 100644 --- a/src/libslic3r/Format/SLAArchiveWriter.cpp +++ b/src/libslic3r/Format/SLAArchiveWriter.cpp @@ -25,9 +25,13 @@ static const std::map REGISTERED_ARCHIVES { "SL1", { "sl1", [] (const auto &cfg) { return std::make_unique(cfg); } } }, + { + "SL1SVG", + { "sl1_svg", [] (const auto &cfg) { return std::make_unique(cfg); } } + }, { "SL2", - { "sl2", [] (const auto &cfg) { return std::make_unique(cfg); } } + { "sl1_svg", [] (const auto &cfg) { return std::make_unique(cfg); } } }, { "pwmx", diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index d4278ed749..22c26b3a55 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -419,7 +419,7 @@ namespace Slic3r { std::string WipeTowerIntegration::finalize(GCode& gcodegen) { std::string gcode; - if (std::abs(gcodegen.writer().get_position()(2) - m_final_purge.print_z) > EPSILON) + if (std::abs(gcodegen.writer().get_position().z() - m_final_purge.print_z) > EPSILON) gcode += gcodegen.change_layer(m_final_purge.print_z); gcode += append_tcr(gcodegen, m_final_purge, -1); return gcode; @@ -429,6 +429,90 @@ namespace Slic3r { #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id()) +void GCode::PlaceholderParserIntegration::reset() +{ + this->failed_templates.clear(); + this->output_config.clear(); + this->opt_position = nullptr; + this->opt_zhop = nullptr; + this->opt_e_position = nullptr; + this->opt_e_retracted = nullptr; + this->opt_e_restart_extra = nullptr; + this->num_extruders = 0; + this->position.clear(); + this->e_position.clear(); + this->e_retracted.clear(); + this->e_restart_extra.clear(); +} + +void GCode::PlaceholderParserIntegration::init(const GCodeWriter &writer) +{ + this->reset(); + const std::vector &extruders = writer.extruders(); + if (! extruders.empty()) { + this->num_extruders = extruders.back().id() + 1; + this->e_retracted.assign(num_extruders, 0); + this->e_restart_extra.assign(num_extruders, 0); + this->opt_e_retracted = new ConfigOptionFloats(e_retracted); + this->opt_e_restart_extra = new ConfigOptionFloats(e_restart_extra); + this->output_config.set_key_value("e_retracted", this->opt_e_retracted); + this->output_config.set_key_value("e_restart_extra", this->opt_e_restart_extra); + if (! writer.config.use_relative_e_distances) { + e_position.assign(num_extruders, 0); + opt_e_position = new ConfigOptionFloats(e_position); + this->output_config.set_key_value("e_position", opt_e_position); + } + } + + // Reserve buffer for current position. + this->position.assign(3, 0); + this->opt_position = new ConfigOptionFloats(this->position); + // Store zhop variable into the parser itself, it is a read-only variable to the script. + this->opt_zhop = new ConfigOptionFloat(writer.get_zhop()); + this->parser.set("zhop", this->opt_zhop); +} + +void GCode::PlaceholderParserIntegration::update_from_gcodewriter(const GCodeWriter &writer) +{ + memcpy(this->position.data(), writer.get_position().data(), sizeof(double) * 3); + this->opt_position->values = this->position; + this->opt_zhop->value = writer.get_zhop(); + + if (this->num_extruders > 0) { + const std::vector &extruders = writer.extruders(); + assert(! extruders.empty() && num_extruders == extruders.back().id() + 1); + this->e_retracted.assign(num_extruders, 0); + this->e_restart_extra.assign(num_extruders, 0); + for (const Extruder &e : extruders) { + this->e_retracted[e.id()] = e.retracted(); + this->e_restart_extra[e.id()] = e.restart_extra(); + } + opt_e_retracted->values = this->e_retracted; + opt_e_restart_extra->values = this->e_restart_extra; + if (! writer.config.use_relative_e_distances) { + this->e_position.assign(num_extruders, 0); + for (const Extruder &e : extruders) + this->e_position[e.id()] = e.position(); + this->opt_e_position->values = this->e_position; + } + } +} + +// Throw if any of the output vector variables were resized by the script. +void GCode::PlaceholderParserIntegration::validate_output_vector_variables() +{ + if (this->opt_position->values.size() != 3) + throw Slic3r::RuntimeError("\"position\" output variable must not be resized by the script."); + if (this->num_extruders > 0) { + if (this->opt_e_position && this->opt_e_position->values.size() != this->num_extruders) + throw Slic3r::RuntimeError("\"e_position\" output variable must not be resized by the script."); + if (this->opt_e_retracted->values.size() != this->num_extruders) + throw Slic3r::RuntimeError("\"e_retracted\" output variable must not be resized by the script."); + if (this->opt_e_restart_extra->values.size() != this->num_extruders) + throw Slic3r::RuntimeError("\"e_restart_extra\" output variable must not be resized by the script."); + } +} + // Collect pairs of object_layer + support_layer sorted by print_z. // object_layer & support_layer are considered to be on the same print_z, if they are not further than EPSILON. GCode::ObjectsLayerToPrint GCode::collect_layers_to_print(const PrintObject& object) @@ -728,7 +812,6 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); try { - m_placeholder_parser_failed_templates.clear(); this->_do_export(*print, file, thumbnail_cb); file.flush(); if (file.is_error()) { @@ -745,11 +828,11 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu } file.close(); - if (! m_placeholder_parser_failed_templates.empty()) { + if (! m_placeholder_parser_integration.failed_templates.empty()) { // G-code export proceeded, but some of the PlaceholderParser substitutions failed. //FIXME localize! std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n"; - for (const auto &name_and_error : m_placeholder_parser_failed_templates) + for (const auto &name_and_error : m_placeholder_parser_integration.failed_templates) msg += name_and_error.first + "\n" + name_and_error.second + "\n"; msg += "\nPlease inspect the file "; msg += path_tmp + " for error messages enclosed between\n"; @@ -1078,12 +1161,12 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.find_replace_enable(); // Prepare the helper object for replacing placeholders in custom G-code and output filename. - m_placeholder_parser = print.placeholder_parser(); - m_placeholder_parser.update_timestamp(); - m_placeholder_parser_context.rng = std::mt19937(std::chrono::high_resolution_clock::now().time_since_epoch().count()); + m_placeholder_parser_integration.parser = print.placeholder_parser(); + m_placeholder_parser_integration.parser.update_timestamp(); + m_placeholder_parser_integration.context.rng = std::mt19937(std::chrono::high_resolution_clock::now().time_since_epoch().count()); // Enable passing global variables between PlaceholderParser invocations. - m_placeholder_parser_context.global_config = std::make_unique(); - print.update_object_placeholders(m_placeholder_parser.config_writable(), ".gcode"); + m_placeholder_parser_integration.context.global_config = std::make_unique(); + print.update_object_placeholders(m_placeholder_parser_integration.parser.config_writable(), ".gcode"); // Get optimal tool ordering to minimize tool switches of a multi-exruder print. // For a print by objects, find the 1st printing object. @@ -1147,23 +1230,25 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Emit machine envelope limits for the Marlin firmware. this->print_machine_envelope(file, print); + // Update output variables after the extruders were initialized. + m_placeholder_parser_integration.init(m_writer); // Let the start-up script prime the 1st printing tool. - m_placeholder_parser.set("initial_tool", initial_extruder_id); - m_placeholder_parser.set("initial_extruder", initial_extruder_id); - m_placeholder_parser.set("current_extruder", initial_extruder_id); + this->placeholder_parser().set("initial_tool", initial_extruder_id); + this->placeholder_parser().set("initial_extruder", initial_extruder_id); + this->placeholder_parser().set("current_extruder", initial_extruder_id); //Set variable for total layer count so it can be used in custom gcode. - m_placeholder_parser.set("total_layer_count", m_layer_count); + this->placeholder_parser().set("total_layer_count", m_layer_count); // Useful for sequential prints. - m_placeholder_parser.set("current_object_idx", 0); + this->placeholder_parser().set("current_object_idx", 0); // For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided. - m_placeholder_parser.set("has_wipe_tower", has_wipe_tower); - m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming); - m_placeholder_parser.set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change). + this->placeholder_parser().set("has_wipe_tower", has_wipe_tower); + this->placeholder_parser().set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming); + this->placeholder_parser().set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change). { BoundingBoxf bbox(print.config().bed_shape.values); - m_placeholder_parser.set("print_bed_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); - m_placeholder_parser.set("print_bed_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); - m_placeholder_parser.set("print_bed_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); + this->placeholder_parser().set("print_bed_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); + this->placeholder_parser().set("print_bed_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); + this->placeholder_parser().set("print_bed_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); } { // Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line. @@ -1176,15 +1261,15 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato for (const Point &pt : print.first_layer_convex_hull().points) pts->values.emplace_back(unscale(pt)); BoundingBoxf bbox(pts->values); - m_placeholder_parser.set("first_layer_print_convex_hull", pts.release()); - m_placeholder_parser.set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); - m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); - m_placeholder_parser.set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); + this->placeholder_parser().set("first_layer_print_convex_hull", pts.release()); + this->placeholder_parser().set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); + this->placeholder_parser().set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); + this->placeholder_parser().set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); std::vector is_extruder_used(print.config().nozzle_diameter.size(), 0); for (unsigned int extruder_id : tool_ordering.all_extruders()) is_extruder_used[extruder_id] = true; - m_placeholder_parser.set("is_extruder_used", new ConfigOptionBools(is_extruder_used)); + this->placeholder_parser().set("is_extruder_used", new ConfigOptionBools(is_extruder_used)); } // Enable ooze prevention if configured so. @@ -1251,7 +1336,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Ff we are printing the bottom layer of an object, and we have already finished // another one, set first layer temperatures. This happens before the Z move // is triggered, so machine has more time to reach such temperatures. - m_placeholder_parser.set("current_object_idx", int(finished_objects)); + this->placeholder_parser().set("current_object_idx", int(finished_objects)); std::string between_objects_gcode = this->placeholder_parser_process("between_objects_gcode", print.config().between_objects_gcode.value, initial_extruder_id); // Set first layer bed and extruder temperatures, don't wait for it to reach the temperature. this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false); @@ -1337,7 +1422,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato { DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position()(2) - m_config.z_offset.value)); + config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position().z() - m_config.z_offset.value)); config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); if (print.config().single_extruder_multi_material) { // Process the end_filament_gcode for the active filament only. @@ -1560,17 +1645,46 @@ void GCode::process_layers( output_stream.find_replace_enable(); } -std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) +std::string GCode::placeholder_parser_process( + const std::string &name, + const std::string &templ, + unsigned int current_extruder_id, + const DynamicConfig *config_override) { + PlaceholderParserIntegration &ppi = m_placeholder_parser_integration; try { - return m_placeholder_parser.process(templ, current_extruder_id, config_override, &m_placeholder_parser_context); - } catch (std::runtime_error &err) { + ppi.update_from_gcodewriter(m_writer); + std::string output = ppi.parser.process(templ, current_extruder_id, config_override, &ppi.output_config, &ppi.context); + ppi.validate_output_vector_variables(); + + if (const std::vector &pos = ppi.opt_position->values; ppi.position != pos) { + // Update G-code writer. + m_writer.update_position({ pos[0], pos[1], pos[2] }); + this->set_last_pos(this->gcode_to_point({ pos[0], pos[1] })); + } + + for (const Extruder &e : m_writer.extruders()) { + unsigned int eid = e.id(); + assert(eid < ppi.num_extruders); + if ( eid < ppi.num_extruders) { + if (! m_writer.config.use_relative_e_distances && ! is_approx(ppi.e_position[eid], ppi.opt_e_position->values[eid])) + const_cast(e).set_position(ppi.opt_e_position->values[eid]); + if (! is_approx(ppi.e_retracted[eid], ppi.opt_e_retracted->values[eid]) || + ! is_approx(ppi.e_restart_extra[eid], ppi.opt_e_restart_extra->values[eid])) + const_cast(e).set_retracted(ppi.opt_e_retracted->values[eid], ppi.opt_e_restart_extra->values[eid]); + } + } + + return output; + } + catch (std::runtime_error &err) + { // Collect the names of failed template substitutions for error reporting. - auto it = m_placeholder_parser_failed_templates.find(name); - if (it == m_placeholder_parser_failed_templates.end()) + auto it = ppi.failed_templates.find(name); + if (it == ppi.failed_templates.end()) // Only if there was no error reported for this template, store the first error message into the map to be reported. // We don't want to collect error message for each and every occurence of a single custom G-code section. - m_placeholder_parser_failed_templates.insert(it, std::make_pair(name, std::string(err.what()))); + ppi.failed_templates.insert(it, std::make_pair(name, std::string(err.what()))); // Insert the macro error message into the G-code. return std::string("\n!!!!! Failed to process the custom G-code template ") + name + "\n" + @@ -3160,7 +3274,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) // if we are running a single-extruder setup, just set the extruder and return nothing if (!m_writer.multiple_extruders) { - m_placeholder_parser.set("current_extruder", extruder_id); + this->placeholder_parser().set("current_extruder", extruder_id); std::string gcode; // Append the filament start G-code. @@ -3169,7 +3283,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) // Process the start_filament_gcode for the filament. DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position()(2) - m_config.z_offset.value)); + config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position().z() - m_config.z_offset.value)); config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(extruder_id))); gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id, &config); @@ -3233,7 +3347,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) gcode += m_writer.set_temperature(temp, false); } - m_placeholder_parser.set("current_extruder", extruder_id); + this->placeholder_parser().set("current_extruder", extruder_id); // Append the filament start G-code. const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id); @@ -3241,7 +3355,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) // Process the start_filament_gcode for the new filament. DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position()(2) - m_config.z_offset.value)); + config.set_key_value("layer_z", new ConfigOptionFloat(this->writer().get_position().z() - m_config.z_offset.value)); config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(extruder_id))); gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id, &config); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index ee50aefccc..98f49095dc 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -173,8 +173,8 @@ public: const Layer* layer() const { return m_layer; } GCodeWriter& writer() { return m_writer; } const GCodeWriter& writer() const { return m_writer; } - PlaceholderParser& placeholder_parser() { return m_placeholder_parser; } - const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; } + PlaceholderParser& placeholder_parser() { return m_placeholder_parser_integration.parser; } + const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser_integration.parser; } // Process a template through the placeholder parser, collect error messages to be reported // inside the generated string and after the G-code export finishes. std::string placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr); @@ -343,11 +343,33 @@ private: // scaled G-code resolution double m_scaled_resolution; GCodeWriter m_writer; - PlaceholderParser m_placeholder_parser; - // For random number generator etc. - PlaceholderParser::ContextData m_placeholder_parser_context; - // Collection of templates, on which the placeholder substitution failed. - std::map m_placeholder_parser_failed_templates; + + struct PlaceholderParserIntegration { + void reset(); + void init(const GCodeWriter &config); + void update_from_gcodewriter(const GCodeWriter &writer); + void validate_output_vector_variables(); + + PlaceholderParser parser; + // For random number generator etc. + PlaceholderParser::ContextData context; + // Collection of templates, on which the placeholder substitution failed. + std::map failed_templates; + // Input/output from/to custom G-code block, for returning position, retraction etc. + DynamicConfig output_config; + ConfigOptionFloats *opt_position { nullptr }; + ConfigOptionFloat *opt_zhop { nullptr }; + ConfigOptionFloats *opt_e_position { nullptr }; + ConfigOptionFloats *opt_e_retracted { nullptr }; + ConfigOptionFloats *opt_e_restart_extra { nullptr }; + // Caches of the data passed to the script. + size_t num_extruders; + std::vector position; + std::vector e_position; + std::vector e_retracted; + std::vector e_restart_extra; + } m_placeholder_parser_integration; + OozePrevention m_ooze_prevention; Wipe m_wipe; AvoidCrossingPerimeters m_avoid_crossing_perimeters; @@ -403,15 +425,15 @@ private: // Index of a last object copy extruded. std::pair m_last_obj_copy; - bool m_silent_time_estimator_enabled; + bool m_silent_time_estimator_enabled; // Processor - GCodeProcessor m_processor; + GCodeProcessor m_processor; - std::string _extrude(const ExtrusionPath &path, const std::string_view description, double speed = -1); - void print_machine_envelope(GCodeOutputStream &file, Print &print); - void _print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); - void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); + std::string _extrude(const ExtrusionPath &path, const std::string_view description, double speed = -1); + void print_machine_envelope(GCodeOutputStream &file, Print &print); + void _print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); + void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); // On the first printing layer. This flag triggers first layer speeds. bool on_first_layer() const { return m_layer != nullptr && m_layer->id() == 0; } // To control print speed of 1st object layer over raft interface. diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 5080fabb4c..9c330c38e9 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -486,6 +486,20 @@ std::string GCodeWriter::unlift() return gcode; } +void GCodeWriter::update_position(const Vec3d &new_pos) +{ + assert(this->m_lifted >= 0); + const double nominal_z = m_pos.z() - m_lifted; + m_lifted = new_pos.z() - nominal_z; + if (m_lifted < - EPSILON) + throw Slic3r::RuntimeError("Custom G-code reports negative Z-hop. Final Z position is below the print_z height."); + // In case that retract_lift == layer_height we could end up with almost zero in_m_lifted + // and a retract could be skipped (https://github.com/prusa3d/PrusaSlicer/issues/2154 + if (m_lifted < EPSILON) + m_lifted = 0.; + m_pos = new_pos; +} + std::string GCodeWriter::set_fan(const GCodeFlavor gcode_flavor, bool gcode_comments, unsigned int speed) { std::ostringstream gcode; diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 9e5fce7023..0d376cb159 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -68,7 +68,18 @@ public: std::string unretract(); std::string lift(); std::string unlift(); + + // Current position of the printer, in G-code coordinates. + // Z coordinate of current position contains zhop. If zhop is applied (this->zhop() > 0), + // then the print_z = this->get_position().z() - this->zhop(). Vec3d get_position() const { return m_pos; } + // Current Z hop value. + double get_zhop() const { return m_lifted; } + // Update position of the print head based on the final position returned by a custom G-code block. + // The new position Z coordinate contains the Z-hop. + // GCodeWriter expects the custom script to NOT change print_z, only Z-hop, thus the print_z is maintained + // by this function while the current Z-hop accumulator is updated. + void update_position(const Vec3d &new_pos); // Returns whether this flavor supports separate print and travel acceleration. static bool supports_separate_travel_acceleration(GCodeFlavor flavor); diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 6374766cd0..d722f1e9ce 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -727,7 +727,7 @@ void LayerRegion::prepare_fill_surfaces() if (! spiral_vase && this->region().config().top_solid_layers == 0) { for (Surface &surface : m_fill_surfaces) if (surface.is_top()) - surface.surface_type = this->layer()->object()->config().infill_only_where_needed && this->region().config().fill_pattern != ipLightning ? stInternalVoid : stInternal; + surface.surface_type = /*this->layer()->object()->config().infill_only_where_needed && this->region().config().fill_pattern != ipLightning ? stInternalVoid :*/ stInternal; } if (this->region().config().bottom_solid_layers == 0) { for (Surface &surface : m_fill_surfaces) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index fb9fdbea0e..11ffd4a542 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -177,7 +177,7 @@ namespace client bool writable { false }; // -1 means it is a scalar variable, or it is a vector variable and index was not assigned yet or the whole vector is considered. int index { -1 }; - IteratorRange it_range; + IteratorRange it_range; bool empty() const { return opt == nullptr; } bool has_index() const { return index != -1; } @@ -1862,7 +1862,7 @@ namespace client // Allow back tracking after '{' in case of a text_block embedded inside a condition. // In that case the inner-most {else} wins and the {if}/{elsif}/{else} shall be paired. // {elsif}/{else} without an {if} will be allowed to back track from the embedded text_block. - | (lit('{') >> macro(_r1)[_val+=_1] > *(+lit(';') >> macro(_r1)[_val+=_1]) > *lit(';') > '}') + | (lit('{') >> (macros(_r1)[_val += _1] > '}') | '}') | (lit('[') > legacy_variable_expansion(_r1) [_val+=_1] > ']') ); text_block.name("text_block"); @@ -1874,26 +1874,45 @@ namespace client // New style of macro expansion. // The macro expansion may contain numeric or string expressions, ifs and cases. - macro = - (kw["if"] > if_else_output(_r1) [_val = _1]) -// | (kw["switch"] > switch_output(_r1) [_val = _1]) - | (assignment_statement(_r1) [_val = _1]) - | (new_variable_statement(_r1) [_val = _1]) - | (conditional_expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ]) + macros = + +(block(_r1)[_val += _1] | (statement(_r1) > (+lit(';') | &lit('}')))[_val += _1] | +lit(';')); + macros.name("macro"); + // if_macros and else_macros only differ by the look-ahead ending condition, which is to not have to repeat the last semicolon + // at the end of the block. + if_macros = kw["then"] > *(block(_r1)[_val += _1] | (statement(_r1) > (+lit(';') | &(kw["elsif"] | kw["else"] | kw["endif"])))[_val += _1] | +lit(';')); + if_macros.name("if_macros"); + else_macros = *(block(_r1)[_val += _1] | (statement(_r1) > (+lit(';') | &kw["endif"]))[_val += _1] | +lit(';')); + else_macros.name("else_macros"); + + // Blocks do not require a separating semicolon. + block = + (kw["if"] > if_else_output(_r1)[_val = _1]) + // (kw["switch"] ... + ; + block.name("block"); + + // Statements require a separating semicolon. + statement = + (assignment_statement(_r1) [_val = _1]) + | (new_variable_statement(_r1)[_val = _1]) + | (conditional_expression(_r1)[px::bind(&expr::to_string2, _1, _val)]) ; - macro.name("macro"); // An if expression enclosed in {} (the outmost {} are already parsed by the caller). + // Also }{ could be replaced with ; to simplify writing of pure code. if_else_output = eps[_a=true] > - (bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1)] > '}' > - text_block(_r1))[px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)] > '{' > - *((kw["elsif"] > bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1 && _a)] > '}' > - text_block(_r1))[px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)] > '{') > - -(kw["else"] > eps[px::bind(&MyContext::block_enter, _r1, _a)] > lit('}') > - text_block(_r1)[px::bind(&MyContext::block_exit, _r1, _a, _a, _1, _val)] > '{') > + (bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1)] > (if_text_block(_r1) | if_macros(_r1))) + [px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)] > + *((kw["elsif"] > bool_expr_eval(_r1)[px::bind(&MyContext::block_enter, _r1, _1 && _a)] > (if_text_block(_r1) | if_macros(_r1))) + [px::bind(&MyContext::block_exit, _r1, _1, _a, _2, _val)]) > + -(kw["else"] > eps[px::bind(&MyContext::block_enter, _r1, _a)] > (if_text_block(_r1) | else_macros(_r1))) + [px::bind(&MyContext::block_exit, _r1, _a, _a, _1, _val)] > kw["endif"]; if_else_output.name("if_else_output"); + if_text_block = (lit('}') > text_block(_r1) > '{'); + if_text_block.name("if_text_block"); + // A switch expression enclosed in {} (the outmost {} are already parsed by the caller). /* switch_output = @@ -2119,7 +2138,7 @@ namespace client debug(start); debug(text); debug(text_block); - debug(macro); + debug(macros); debug(if_else_output); debug(interpolate_table); // debug(switch_output); @@ -2155,7 +2174,7 @@ namespace client // A free-form text, possibly empty, possibly containing macro expansions. qi::rule text_block; // Statements enclosed in curely braces {} - qi::rule macro; + qi::rule block, statement, macros, if_text_block, if_macros, else_macros; // Legacy variable expansion of the original Slic3r, in the form of [scalar_variable] or [vector_variable_index]. qi::rule legacy_variable_expansion; // Parsed identifier name. diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index a584920622..23f1438c4c 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -3,6 +3,7 @@ #include "Exception.hpp" #include "Preset.hpp" #include "AppConfig.hpp" +#include "I18N.hpp" #ifdef _MSC_VER #define WIN32_LEAN_AND_MEAN @@ -424,7 +425,7 @@ static std::vector s_Preset_print_options { "top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness", "extra_perimeters", "extra_perimeters_on_overhangs", "avoid_crossing_curled_overhangs", "avoid_crossing_perimeters", "thin_walls", "overhangs", "seam_position","staggered_inner_seams", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern", - "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle", + "infill_every_layers", /*"infill_only_where_needed",*/ "solid_infill_every_layers", "fill_angle", "bridge_angle", "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", "ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing", "max_print_speed", "max_volumetric_speed", "avoid_crossing_perimeters_max_detour", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 0089ce4571..2fb2b877a3 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1560,14 +1560,14 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionBool(false)); - def = this->add("infill_only_where_needed", coBool); - def->label = L("Only infill where needed"); - def->category = L("Infill"); - def->tooltip = L("This option will limit infill to the areas actually needed for supporting ceilings " - "(it will act as internal support material). If enabled, slows down the G-code generation " - "due to the multiple checks involved."); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); + // def = this->add("infill_only_where_needed", coBool); + // def->label = L("Only infill where needed"); + // def->category = L("Infill"); + // def->tooltip = L("This option will limit infill to the areas actually needed for supporting ceilings " + // "(it will act as internal support material). If enabled, slows down the G-code generation " + // "due to the multiple checks involved."); + // def->mode = comAdvanced; + // def->set_default_value(new ConfigOptionBool(false)); def = this->add("infill_overlap", coFloatOrPercent); def->label = L("Infill/perimeters overlap"); @@ -4154,6 +4154,8 @@ static std::set PrintConfigDef_ignore = { "wall_add_middle_threshold", "wall_split_middle_threshold", // Replaced by new concentric ensuring in 2.6.0-alpha5 "ensure_vertical_shell_thickness", + // Disabled in 2.6.0-alpha6, this option is problematic + "infill_only_where_needed", }; void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value) @@ -4552,12 +4554,19 @@ std::string validate(const FullPrintConfig &cfg) } case coFloats: case coPercents: - for (double v : static_cast*>(opt)->values) + { + const auto* vec = static_cast*>(opt); + for (size_t i = 0; i < vec->size(); ++i) { + if (vec->is_nil(i)) + continue; + double v = vec->values[i]; if (v < optdef->min || v > optdef->max) { out_of_range = true; break; } + } break; + } case coInt: { auto *iopt = static_cast(opt); @@ -4565,12 +4574,19 @@ std::string validate(const FullPrintConfig &cfg) break; } case coInts: - for (int v : static_cast*>(opt)->values) + { + const auto* vec = static_cast*>(opt); + for (size_t i = 0; i < vec->size(); ++i) { + if (vec->is_nil(i)) + continue; + int v = vec->values[i]; if (v < optdef->min || v > optdef->max) { out_of_range = true; break; } + } break; + } default:; } if (out_of_range) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index d29afe5e71..08f3bcd2fd 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -495,7 +495,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloatOrPercent, extrusion_width)) ((ConfigOptionFloat, first_layer_acceleration_over_raft)) ((ConfigOptionFloatOrPercent, first_layer_speed_over_raft)) - ((ConfigOptionBool, infill_only_where_needed)) + // ((ConfigOptionBool, infill_only_where_needed)) // Force the generation of solid shells between adjacent materials/volumes. ((ConfigOptionBool, interface_shells)) ((ConfigOptionFloat, layer_height)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 7136617d82..61067ed18d 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -348,8 +348,8 @@ void PrintObject::prepare_infill() //FIXME The surfaces are supported by a sparse infill, but the sparse infill is only as large as the area to support. // Likely the sparse infill will not be anchored correctly, so it will not work as intended. // Also one wishes the perimeters to be supported by a full infill. - this->clip_fill_surfaces(); - m_print->throw_if_canceled(); + // this->clip_fill_surfaces(); + // m_print->throw_if_canceled(); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { @@ -2107,7 +2107,7 @@ void PrintObject::bridge_over_infill() total_fill_area = closing(total_fill_area, SCALED_EPSILON); expansion_area = closing(expansion_area, SCALED_EPSILON); expansion_area = intersection(expansion_area, deep_infill_area); - Polylines anchors = intersection_pl(infill_lines[lidx - 1], expansion_area); + Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing)); #ifdef DEBUG_BRIDGE_OVER_INFILL debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + @@ -2419,98 +2419,98 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c // Also one wishes the perimeters to be supported by a full infill. // Idempotence of this method is guaranteed by the fact that we don't remove things from // fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries. -void PrintObject::clip_fill_surfaces() -{ - bool has_lightning_infill = false; - for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) - if (const PrintRegionConfig &config = this->printing_region(region_id).config(); config.fill_density > 0 && config.fill_pattern == ipLightning) - has_lightning_infill = true; +// void PrintObject::clip_fill_surfaces() +// { +// bool has_lightning_infill = false; +// for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) +// if (const PrintRegionConfig &config = this->printing_region(region_id).config(); config.fill_density > 0 && config.fill_pattern == ipLightning) +// has_lightning_infill = true; - // For Lightning infill, infill_only_where_needed is ignored because both - // do a similar thing, and their combination doesn't make much sense. - if (! m_config.infill_only_where_needed.value || has_lightning_infill) - return; - bool has_infill = false; - for (size_t i = 0; i < this->num_printing_regions(); ++ i) - if (this->printing_region(i).config().fill_density > 0) { - has_infill = true; - break; - } - if (! has_infill) - return; +// // For Lightning infill, infill_only_where_needed is ignored because both +// // do a similar thing, and their combination doesn't make much sense. +// if (! m_config.infill_only_where_needed.value || has_lightning_infill) +// return; +// bool has_infill = false; +// for (size_t i = 0; i < this->num_printing_regions(); ++ i) +// if (this->printing_region(i).config().fill_density > 0) { +// has_infill = true; +// break; +// } +// if (! has_infill) +// return; - // We only want infill under ceilings; this is almost like an - // internal support material. - // Proceed top-down, skipping the bottom layer. - Polygons upper_internal; - for (int layer_id = int(m_layers.size()) - 1; layer_id > 0; -- layer_id) { - Layer *layer = m_layers[layer_id]; - Layer *lower_layer = m_layers[layer_id - 1]; - // Detect things that we need to support. - // Cummulative fill surfaces. - Polygons fill_surfaces; - // Solid surfaces to be supported. - Polygons overhangs; - for (const LayerRegion *layerm : layer->m_regions) - for (const Surface &surface : layerm->fill_surfaces()) { - Polygons polygons = to_polygons(surface.expolygon); - if (surface.is_solid()) - polygons_append(overhangs, polygons); - polygons_append(fill_surfaces, std::move(polygons)); - } - Polygons lower_layer_fill_surfaces; - Polygons lower_layer_internal_surfaces; - for (const LayerRegion *layerm : lower_layer->m_regions) - for (const Surface &surface : layerm->fill_surfaces()) { - Polygons polygons = to_polygons(surface.expolygon); - if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) - polygons_append(lower_layer_internal_surfaces, polygons); - polygons_append(lower_layer_fill_surfaces, std::move(polygons)); - } - // We also need to support perimeters when there's at least one full unsupported loop - { - // Get perimeters area as the difference between slices and fill_surfaces - // Only consider the area that is not supported by lower perimeters - Polygons perimeters = intersection(diff(layer->lslices, fill_surfaces), lower_layer_fill_surfaces); - // Only consider perimeter areas that are at least one extrusion width thick. - //FIXME Offset2 eats out from both sides, while the perimeters are create outside in. - //Should the pw not be half of the current value? - float pw = FLT_MAX; - for (const LayerRegion *layerm : layer->m_regions) - pw = std::min(pw, (float)layerm->flow(frPerimeter).scaled_width()); - // Append such thick perimeters to the areas that need support - polygons_append(overhangs, opening(perimeters, pw)); - } - // Merge the new overhangs, find new internal infill. - polygons_append(upper_internal, std::move(overhangs)); - static constexpr const auto closing_radius = scaled(2.f); - upper_internal = intersection( - // Regularize the overhang regions, so that the infill areas will not become excessively jagged. - smooth_outward( - closing(upper_internal, closing_radius, ClipperLib::jtSquare, 0.), - scaled(0.1)), - lower_layer_internal_surfaces); - // Apply new internal infill to regions. - for (LayerRegion *layerm : lower_layer->m_regions) { - if (layerm->region().config().fill_density.value == 0) - continue; - Polygons internal; - for (Surface &surface : layerm->m_fill_surfaces.surfaces) - if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) - polygons_append(internal, std::move(surface.expolygon)); - layerm->m_fill_surfaces.remove_types({ stInternal, stInternalVoid }); - layerm->m_fill_surfaces.append(intersection_ex(internal, upper_internal, ApplySafetyOffset::Yes), stInternal); - layerm->m_fill_surfaces.append(diff_ex (internal, upper_internal, ApplySafetyOffset::Yes), stInternalVoid); - // If there are voids it means that our internal infill is not adjacent to - // perimeters. In this case it would be nice to add a loop around infill to - // make it more robust and nicer. TODO. -#ifdef SLIC3R_DEBUG_SLICE_PROCESSING - layerm->export_region_fill_surfaces_to_svg_debug("6_clip_fill_surfaces"); -#endif - } - m_print->throw_if_canceled(); - } -} // void PrintObject::clip_fill_surfaces() +// // We only want infill under ceilings; this is almost like an +// // internal support material. +// // Proceed top-down, skipping the bottom layer. +// Polygons upper_internal; +// for (int layer_id = int(m_layers.size()) - 1; layer_id > 0; -- layer_id) { +// Layer *layer = m_layers[layer_id]; +// Layer *lower_layer = m_layers[layer_id - 1]; +// // Detect things that we need to support. +// // Cummulative fill surfaces. +// Polygons fill_surfaces; +// // Solid surfaces to be supported. +// Polygons overhangs; +// for (const LayerRegion *layerm : layer->m_regions) +// for (const Surface &surface : layerm->fill_surfaces()) { +// Polygons polygons = to_polygons(surface.expolygon); +// if (surface.is_solid()) +// polygons_append(overhangs, polygons); +// polygons_append(fill_surfaces, std::move(polygons)); +// } +// Polygons lower_layer_fill_surfaces; +// Polygons lower_layer_internal_surfaces; +// for (const LayerRegion *layerm : lower_layer->m_regions) +// for (const Surface &surface : layerm->fill_surfaces()) { +// Polygons polygons = to_polygons(surface.expolygon); +// if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) +// polygons_append(lower_layer_internal_surfaces, polygons); +// polygons_append(lower_layer_fill_surfaces, std::move(polygons)); +// } +// // We also need to support perimeters when there's at least one full unsupported loop +// { +// // Get perimeters area as the difference between slices and fill_surfaces +// // Only consider the area that is not supported by lower perimeters +// Polygons perimeters = intersection(diff(layer->lslices, fill_surfaces), lower_layer_fill_surfaces); +// // Only consider perimeter areas that are at least one extrusion width thick. +// //FIXME Offset2 eats out from both sides, while the perimeters are create outside in. +// //Should the pw not be half of the current value? +// float pw = FLT_MAX; +// for (const LayerRegion *layerm : layer->m_regions) +// pw = std::min(pw, (float)layerm->flow(frPerimeter).scaled_width()); +// // Append such thick perimeters to the areas that need support +// polygons_append(overhangs, opening(perimeters, pw)); +// } +// // Merge the new overhangs, find new internal infill. +// polygons_append(upper_internal, std::move(overhangs)); +// static constexpr const auto closing_radius = scaled(2.f); +// upper_internal = intersection( +// // Regularize the overhang regions, so that the infill areas will not become excessively jagged. +// smooth_outward( +// closing(upper_internal, closing_radius, ClipperLib::jtSquare, 0.), +// scaled(0.1)), +// lower_layer_internal_surfaces); +// // Apply new internal infill to regions. +// for (LayerRegion *layerm : lower_layer->m_regions) { +// if (layerm->region().config().fill_density.value == 0) +// continue; +// Polygons internal; +// for (Surface &surface : layerm->m_fill_surfaces.surfaces) +// if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid) +// polygons_append(internal, std::move(surface.expolygon)); +// layerm->m_fill_surfaces.remove_types({ stInternal, stInternalVoid }); +// layerm->m_fill_surfaces.append(intersection_ex(internal, upper_internal, ApplySafetyOffset::Yes), stInternal); +// layerm->m_fill_surfaces.append(diff_ex (internal, upper_internal, ApplySafetyOffset::Yes), stInternalVoid); +// // If there are voids it means that our internal infill is not adjacent to +// // perimeters. In this case it would be nice to add a loop around infill to +// // make it more robust and nicer. TODO. +// #ifdef SLIC3R_DEBUG_SLICE_PROCESSING +// layerm->export_region_fill_surfaces_to_svg_debug("6_clip_fill_surfaces"); +// #endif +// } +// m_print->throw_if_canceled(); +// } +// } // void PrintObject::clip_fill_surfaces() void PrintObject::discover_horizontal_shells() { diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 6dc4a46206..8238a266ee 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -588,6 +588,15 @@ std::string SLAPrint::validate(std::string*) const return ""; } +void SLAPrint::export_print(const std::string &fname, const ThumbnailsList &thumbnails, const std::string &projectname) +{ + if (m_archiver) + m_archiver->export_print(fname, *this, thumbnails, projectname); + else { + throw ExportError(format(_u8L("Unknown archive format: %s"), m_printer_config.sla_archive_format.value)); + } +} + bool SLAPrint::invalidate_step(SLAPrintStep step) { bool invalidated = Inherited::invalidate_step(step); diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index bd8424ca78..126941d820 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -546,10 +546,7 @@ public: void export_print(const std::string &fname, const ThumbnailsList &thumbnails, - const std::string &projectname = "") - { - m_archiver->export_print(fname, *this, thumbnails, projectname); - } + const std::string &projectname = ""); private: diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 5ef8a27096..b73a8c4fb1 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -199,7 +199,13 @@ void SLAPrint::Steps::generate_preview(SLAPrintObject &po, SLAPrintObjectStep st m = csgmesh_merge_positive_parts(r); handled = true; } else if (csg::check_csgmesh_booleans(r) == r.end()) { - auto cgalmeshptr = csg::perform_csgmesh_booleans(r); + MeshBoolean::cgal::CGALMeshPtr cgalmeshptr; + try { + cgalmeshptr = csg::perform_csgmesh_booleans(r); + } catch (...) { + // leaves cgalmeshptr as nullptr + } + if (cgalmeshptr) { m = MeshBoolean::cgal::cgal_to_indexed_triangle_set(*cgalmeshptr); handled = true; diff --git a/src/slic3r/GUI/CoordAxes.cpp b/src/slic3r/GUI/CoordAxes.cpp index 277c8ad300..fcb434f3b2 100644 --- a/src/slic3r/GUI/CoordAxes.cpp +++ b/src/slic3r/GUI/CoordAxes.cpp @@ -44,17 +44,23 @@ void CoordAxes::render(const Transform3d& trafo, float emission_factor) shader->start_using(); shader->set_uniform("emission_factor", emission_factor); + // Scale the axes if the camera is close to them to avoid issues + // such as https://github.com/prusa3d/PrusaSlicer/issues/9483 + const Camera& camera = wxGetApp().plater()->get_camera(); + Transform3d scale_tr = Transform3d::Identity(); + scale_tr.scale(std::min(1., camera.get_inv_zoom() * 10.)); + // x axis m_arrow.set_color(ColorRGBA::X()); - render_axis(*shader, trafo * Geometry::translation_transform(m_origin) * Geometry::rotation_transform({ 0.0, 0.5 * M_PI, 0.0 })); + render_axis(*shader, trafo * Geometry::translation_transform(m_origin) * Geometry::rotation_transform({ 0.0, 0.5 * M_PI, 0.0 }) * scale_tr); // y axis m_arrow.set_color(ColorRGBA::Y()); - render_axis(*shader, trafo * Geometry::translation_transform(m_origin) * Geometry::rotation_transform({ -0.5 * M_PI, 0.0, 0.0 })); + render_axis(*shader, trafo * Geometry::translation_transform(m_origin) * Geometry::rotation_transform({ -0.5 * M_PI, 0.0, 0.0 }) * scale_tr); // z axis m_arrow.set_color(ColorRGBA::Z()); - render_axis(*shader, trafo * Geometry::translation_transform(m_origin)); + render_axis(*shader, trafo * Geometry::translation_transform(m_origin) * scale_tr); shader->stop_using(); if (curr_shader != nullptr) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index dcc85dc65f..08f68356f1 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1572,8 +1572,7 @@ void GLCanvas3D::render() _render_objects(GLVolumeCollection::ERenderType::Opaque); _render_sla_slices(); _render_selection(); - if (m_show_bed_axes) - _render_bed_axes(); + _render_bed_axes(); if (is_looking_downward) _render_bed(camera.get_view_matrix(), camera.get_projection_matrix(), false); if (!m_main_toolbar.is_enabled()) @@ -2351,15 +2350,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) #endif /* __APPLE__ */ post_event(SimpleEvent(EVT_GLTOOLBAR_COPY)); break; -#ifdef __APPLE__ - case 'd': - case 'D': -#else /* __APPLE__ */ - case WXK_CONTROL_D: -#endif /* __APPLE__ */ - m_show_bed_axes = !m_show_bed_axes; - m_dirty = true; - break; #ifdef __APPLE__ case 'f': case 'F': diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 6ebe7bcc62..a79bcff2ed 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -522,7 +522,6 @@ private: ECursorType m_cursor_type; GLSelectionRectangle m_rectangle_selection; std::vector m_hover_volume_idxs; - bool m_show_bed_axes{ true }; // Following variable is obsolete and it should be safe to remove it. // I just don't want to do it now before a release (Lukas Matena 24.3.2019) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 1d4a53fd9a..e03df09bb1 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1322,7 +1322,7 @@ bool GUI_App::on_init_inner() if (!delayed_error_load_presets.empty()) show_error(nullptr, delayed_error_load_presets); - mainframe = new MainFrame(); + mainframe = new MainFrame(app_config->has("font_size") ? atoi(app_config->get("font_size").c_str()) : -1); // hide settings tabs after first Layout if (is_editor()) mainframe->select_tab(size_t(0)); @@ -1810,7 +1810,7 @@ void GUI_App::recreate_GUI(const wxString& msg_name) dlg.Update(10, _L("Recreating") + dots); MainFrame *old_main_frame = mainframe; - mainframe = new MainFrame(); + mainframe = new MainFrame(app_config->has("font_size") ? atoi(app_config->get("font_size").c_str()) : -1); if (is_editor()) // hide settings tabs after first Layout mainframe->select_tab(size_t(0)); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index ad2758f9e8..5f8b9a75b5 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2939,6 +2939,16 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selectio if (obj_idx >= m_objects->size()) return; + wxDataViewItemArray sels; + if (!selections) { + GetSelections(sels); + for (wxDataViewItem item : sels) + if (item.IsOk() && m_objects_model->GetItemType(item) == itVolume) { + selections = &sels; + break; + } + } + const ModelObject* model_object = (*m_objects)[obj_idx]; wxDataViewItem item_obj = m_objects_model->GetItemById(obj_idx); assert(item_obj.IsOk()); diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index 55ca432480..624e7e1a34 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -82,7 +82,7 @@ template class DPIAware : public P { public: DPIAware(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &pos=wxDefaultPosition, - const wxSize &size=wxDefaultSize, long style=wxDEFAULT_FRAME_STYLE, const wxString &name=wxFrameNameStr) + const wxSize &size=wxDefaultSize, long style=wxDEFAULT_FRAME_STYLE, const wxString &name= wxFrameNameStr, const int font_point_size = -1) : P(parent, id, title, pos, size, style, name) { int dpi = get_dpi_for_window(this); @@ -90,6 +90,9 @@ public: m_prev_scale_factor = m_scale_factor; m_normal_font = get_default_font_for_dpi(this, dpi); + if (font_point_size > 0) + m_normal_font.SetPointSize(font_point_size); + /* Because of default window font is a primary display font, * We should set correct font for window before getting em_unit value. */ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 1bf593a415..8e747c01e4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -774,7 +774,7 @@ GLGizmoEmboss::GuiCfg GLGizmoEmboss::create_gui_configuration() tr.skew_ration = _u8L("Skew ratio"); tr.from_surface = _u8L("From surface"); tr.rotation = _u8L("Rotation"); - tr.keep_up = _u8L("Keep Up"); + tr.keep_up = "Keep Rotation"; tr.collection = _u8L("Collection"); float max_advanced_text_width = std::max({ @@ -2996,7 +2996,7 @@ void GLGizmoEmboss::draw_advanced() } } if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", _u8L("Keep text orientation during surface dragging.\nNot stable between horizontal and vertical alignment.").c_str()); + ImGui::SetTooltip("%s", _u8L("Lock the text's rotation when moving text along the object's surface.").c_str()); // when more collection add selector if (ff.font_file->infos.size() > 1) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 4720569f8f..007b959e72 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -13,6 +13,7 @@ #include #include +#include namespace Slic3r::GUI { diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index b2eb2c79da..66961b2cd4 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -151,7 +151,6 @@ void KBShortcutsDialog::fill_shortcuts() { L("Arrow Right"), L("Move selection 10 mm in positive X direction") }, { std::string("Shift+") + L("Any arrow"), L("Movement step set to 1 mm") }, { ctrl + L("Any arrow"), L("Movement in camera space") }, - { ctrl + "D", L("Show/hide reference axes") }, { L("Page Up"), L("Rotate selection 45 degrees CCW") }, { L("Page Down"), L("Rotate selection 45 degrees CW") }, { "M", L("Gizmo move") }, @@ -232,7 +231,6 @@ void KBShortcutsDialog::fill_shortcuts() { "X", L("On/Off one layer mode of the vertical slider") }, { "L", L("Show/Hide legend") }, { "C", L("Show/Hide G-code window") }, - { ctrl + "D", L("Show/hide reference axes") }, }; m_full_shortcuts.push_back({ { _L("Preview"), "" }, preview_shortcuts }); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 0c7805db80..4c9e8332ad 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -122,8 +122,8 @@ static wxIcon main_frame_icon(GUI_App::EAppMode app_mode) #endif // _WIN32 } -MainFrame::MainFrame() : -DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"), +MainFrame::MainFrame(const int font_point_size) : +DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe", font_point_size), m_printhost_queue_dlg(new PrintHostQueueDialog(this)) , m_recent_projects(9) , m_settings_dialog(this) @@ -1540,6 +1540,7 @@ void MainFrame::init_menubar_as_editor() // assign menubar to frame after appending items, otherwise special items // will not be handled correctly m_menubar = new wxMenuBar(); + m_menubar->SetFont(this->normal_font()); m_menubar->Append(fileMenu, _L("&File")); if (editMenu) m_menubar->Append(editMenu, _L("&Edit")); m_menubar->Append(windowMenu, _L("&Window")); @@ -2237,7 +2238,7 @@ std::string MainFrame::get_dir_name(const wxString &full_name) const // ---------------------------------------------------------------------------- SettingsDialog::SettingsDialog(MainFrame* mainframe) -:DPIFrame(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Settings"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "settings_dialog"), +:DPIFrame(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Settings"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "settings_dialog", mainframe->normal_font().GetPointSize()), //: DPIDialog(mainframe, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _L("Settings"), wxDefaultPosition, wxDefaultSize, // wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMINIMIZE_BOX | wxMAXIMIZE_BOX, "settings_dialog"), m_main_frame(mainframe) diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 78ec13f64e..c9fa601e1c 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -139,7 +139,7 @@ protected: virtual void on_sys_color_changed() override; public: - MainFrame(); + MainFrame(const int font_point_size); ~MainFrame() = default; void update_layout(); diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index 43e13841c5..cce159903e 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -136,7 +136,7 @@ static void add_msg_content(wxWindow* parent, wxBoxSizer* content_sizer, wxStrin msg_lines++; } - wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + wxFont font = wxGetApp().normal_font();//wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); wxFont monospace = wxGetApp().code_font(); wxColour text_clr = wxGetApp().get_label_clr_default(); wxColour bgr_clr = parent->GetBackgroundColour(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3595276af6..159a9693d2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -54,6 +54,10 @@ #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/miniz_extension.hpp" +// For stl export +#include "libslic3r/CSGMesh/ModelToCSGMesh.hpp" +#include "libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp" + #include "GUI.hpp" #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" @@ -6382,14 +6386,34 @@ void Plater::export_stl_obj(bool extended, bool selection_only) return; // Following lambda generates a combined mesh for export with normals pointing outwards. - auto mesh_to_export_fff = [](const ModelObject& mo, int instance_id) { + auto mesh_to_export_fff = [this](const ModelObject& mo, int instance_id) { TriangleMesh mesh; - for (const ModelVolume* v : mo.volumes) - if (v->is_model_part()) { - TriangleMesh vol_mesh(v->mesh()); - vol_mesh.transform(v->get_matrix(), true); - mesh.merge(vol_mesh); - } + + std::vector csgmesh; + csgmesh.reserve(2 * mo.volumes.size()); + csg::model_to_csgmesh(mo, Transform3d::Identity(), std::back_inserter(csgmesh), + csg::mpartsPositive | csg::mpartsNegative | csg::mpartsDoSplits); + + if (csg::check_csgmesh_booleans(range(csgmesh)) == csgmesh.end()) { + try { + auto cgalm = csg::perform_csgmesh_booleans(range(csgmesh)); + mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*cgalm); + } catch (...) {} + } + + if (mesh.empty()) { + get_notification_manager()->push_plater_error_notification( + _u8L("Unable to perform boolean operation on model meshes. " + "Only positive parts will be exported.")); + + for (const ModelVolume* v : mo.volumes) + if (v->is_model_part()) { + TriangleMesh vol_mesh(v->mesh()); + vol_mesh.transform(v->get_matrix(), true); + mesh.merge(vol_mesh); + } + } + if (instance_id == -1) { TriangleMesh vols_mesh(mesh); mesh = TriangleMesh(); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 492e3fc37b..bf1169d826 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -4,6 +4,7 @@ #include "Plater.hpp" #include "MsgDialog.hpp" #include "I18N.hpp" +#include "format.hpp" #include "libslic3r/AppConfig.hpp" #include #include "Notebook.hpp" @@ -580,6 +581,7 @@ void PreferencesDialog::build() activate_options_tab(m_optgroup_other); create_downloader_path_sizer(); +// create_settings_font_widget(); #if ENABLE_ENVIRONMENT_MAP // Add "Render" tab @@ -694,7 +696,7 @@ void PreferencesDialog::accept(wxEvent&) bool update_filament_sidebar = (m_values.find("no_templates") != m_values.end()); - std::vector options_to_recreate_GUI = { "no_defaults", "tabs_as_menu", "sys_menu_enabled" }; + std::vector options_to_recreate_GUI = { "no_defaults", "tabs_as_menu", "sys_menu_enabled", "font_size" }; for (const std::string& option : options_to_recreate_GUI) { if (m_values.find(option) != m_values.end()) { @@ -943,7 +945,6 @@ void PreferencesDialog::create_icon_size_slider() void PreferencesDialog::create_settings_mode_widget() { wxWindow* parent = m_optgroup_gui->parent(); - wxGetApp().UpdateDarkUI(parent); wxString title = L("Layout Options"); wxStaticBox* stb = new wxStaticBox(parent, wxID_ANY, _(title)); @@ -1045,6 +1046,74 @@ void PreferencesDialog::create_settings_mode_color_widget() append_preferences_option_to_searcher(m_optgroup_gui, opt_key, title); } +void PreferencesDialog::create_settings_font_widget() +{ + wxWindow* parent = m_optgroup_other->parent(); + wxGetApp().UpdateDarkUI(parent); + + const wxString title = L("Application font size"); + wxStaticBox* stb = new wxStaticBox(parent, wxID_ANY, _(title)); + if (!wxOSX) stb->SetBackgroundStyle(wxBG_STYLE_PAINT); + + const std::string opt_key = "font_size"; + m_blinkers[opt_key] = new BlinkingBitmap(parent); + + wxSizer* stb_sizer = new wxStaticBoxSizer(stb, wxHORIZONTAL); + + wxStaticText* font_example = new wxStaticText(parent, wxID_ANY, "Application text"); + int val = wxGetApp().normal_font().GetPointSize(); + wxSpinCtrl* size_sc = new wxSpinCtrl(parent, wxID_ANY, format_wxstr("%1%", val), wxDefaultPosition, wxSize(15*em_unit(), -1), wxTE_PROCESS_ENTER | wxSP_ARROW_KEYS +#ifdef _WIN32 + | wxBORDER_SIMPLE +#endif + , 8, 20); + wxGetApp().UpdateDarkUI(size_sc); + + auto apply_font = [this, font_example, opt_key](const int val, const wxFont& font) { + font_example->SetFont(font); + m_values[opt_key] = format("%1%", val); + refresh_og(m_optgroup_other); + }; + + auto change_value = [size_sc, apply_font](wxCommandEvent& evt) { + const int val = size_sc->GetValue(); + wxFont font = wxGetApp().normal_font(); + font.SetPointSize(val); + + apply_font(val, font); + }; + size_sc->Bind(wxEVT_SPINCTRL, change_value); + size_sc->Bind(wxEVT_TEXT_ENTER, change_value); + + auto revert_btn = new ScalableButton(parent, wxID_ANY, "undo"); + revert_btn->SetToolTip(_L("Revert font to default")); + revert_btn->Bind(wxEVT_BUTTON, [size_sc, apply_font](wxEvent& event) { + wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + const int val = font.GetPointSize(); + size_sc->SetValue(val); + apply_font(val, font); + }); + parent->Bind(wxEVT_UPDATE_UI, [size_sc](wxUpdateUIEvent& evt) { + const int def_size = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).GetPointSize(); + evt.Enable(def_size != size_sc->GetValue()); + }, revert_btn->GetId()); + + stb_sizer->Add(new wxStaticText(parent, wxID_ANY, _L("Font size") + ":"), 0, wxALIGN_CENTER_VERTICAL | wxLEFT, em_unit()); + stb_sizer->Add(size_sc, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT | wxLEFT, em_unit()); + stb_sizer->Add(revert_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, em_unit()); + wxBoxSizer* font_sizer = new wxBoxSizer(wxVERTICAL); + font_sizer->Add(font_example, 1, wxALIGN_CENTER_HORIZONTAL); + stb_sizer->Add(font_sizer, 1, wxALIGN_CENTER_VERTICAL); + + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(m_blinkers[opt_key], 0, wxRIGHT, 2); + sizer->Add(stb_sizer, 1, wxALIGN_CENTER_VERTICAL); + + m_optgroup_other->sizer->Add(sizer, 1, wxEXPAND | wxTOP, em_unit()); + + append_preferences_option_to_searcher(m_optgroup_other, opt_key, title); +} + void PreferencesDialog::create_downloader_path_sizer() { wxWindow* parent = m_optgroup_other->parent(); diff --git a/src/slic3r/GUI/Preferences.hpp b/src/slic3r/GUI/Preferences.hpp index 4ae93f6f9a..765447f89f 100644 --- a/src/slic3r/GUI/Preferences.hpp +++ b/src/slic3r/GUI/Preferences.hpp @@ -91,10 +91,12 @@ protected: void layout(); void clear_cache(); void refresh_og(std::shared_ptr og); + void refresh_og(ConfigOptionsGroup* og); void create_icon_size_slider(); void create_settings_mode_widget(); void create_settings_text_color_widget(); void create_settings_mode_color_widget(); + void create_settings_font_widget(); void create_downloader_path_sizer(); void init_highlighter(const t_config_option_key& opt_key); std::vector optgroups(); diff --git a/src/slic3r/GUI/SavePresetDialog.cpp b/src/slic3r/GUI/SavePresetDialog.cpp index 33f41b9c4e..a561a754e2 100644 --- a/src/slic3r/GUI/SavePresetDialog.cpp +++ b/src/slic3r/GUI/SavePresetDialog.cpp @@ -85,18 +85,17 @@ void SavePresetDialog::Item::init_input_name_ctrl(wxBoxSizer *input_name_sizer, } } -wxString SavePresetDialog::Item::get_top_label_text() const +static std::map TOP_LABELS = { - const std::string label_str = m_use_text_ctrl ? - // TRN %1% = "Preset" - L("Rename %1% to") : - // TRN %1% = "Preset" - L("Save %1% as"); - Tab* tab = wxGetApp().get_tab(m_type); - return format_wxstr(_(label_str) + ":", tab->title()); -} + // type Save settings + { Preset::Type::TYPE_PRINT, L("Save print settings as") }, + { Preset::Type::TYPE_SLA_PRINT, L("Save print settings as") }, + { Preset::Type::TYPE_FILAMENT, L("Save filament settings as")}, + { Preset::Type::TYPE_SLA_MATERIAL, L("Save material settings as")}, + { Preset::Type::TYPE_PRINTER, L("Save printer settings as") }, +}; -SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent): +SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent, bool is_for_multiple_save): m_type(type), m_use_text_ctrl(parent->is_for_rename()), m_parent(parent), @@ -105,14 +104,15 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string& suffix, wxBox { m_valid_label->SetFont(wxGetApp().bold_font()); - wxStaticText* label_top = new wxStaticText(m_parent, wxID_ANY, get_top_label_text()); + wxStaticText* label_top = is_for_multiple_save ? new wxStaticText(m_parent, wxID_ANY, _(TOP_LABELS.at(m_type)) + ":") : nullptr; wxBoxSizer* input_name_sizer = new wxBoxSizer(wxHORIZONTAL); input_name_sizer->Add(m_valid_bmp, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, BORDER_W); init_input_name_ctrl(input_name_sizer, get_init_preset_name(suffix)); - sizer->Add(label_top, 0, wxEXPAND | wxTOP| wxBOTTOM, BORDER_W); - sizer->Add(input_name_sizer,0, wxEXPAND | wxBOTTOM, BORDER_W); + if (label_top) + sizer->Add(label_top, 0, wxEXPAND | wxTOP| wxBOTTOM, BORDER_W); + sizer->Add(input_name_sizer,0, wxEXPAND | (label_top ? 0 : wxTOP) | wxBOTTOM, BORDER_W); sizer->Add(m_valid_label, 0, wxEXPAND | wxLEFT, 3*BORDER_W); if (m_type == Preset::TYPE_PRINTER) @@ -205,8 +205,6 @@ void SavePresetDialog::Item::update() if ((!m_use_text_ctrl && m_presets->get_edited_preset().is_dirty) || (dlg && dlg->get_preset_bundle())) // means that we save modifications from the DiffDialog info_line = _L("Save preset modifications to existing user profile"); - else - info_line = _L("Nothing changed"); m_valid_type = ValidationType::Valid; } else { @@ -266,8 +264,8 @@ void SavePresetDialog::Item::update() void SavePresetDialog::Item::update_valid_bmp() { - std::string bmp_name = m_valid_type == ValidationType::Warning ? "exclamation" : - m_valid_type == ValidationType::NoValid ? "cross" : "tick_mark" ; + std::string bmp_name = m_valid_type == ValidationType::Warning ? "exclamation_manifold" : + m_valid_type == ValidationType::NoValid ? "exclamation" : "tick_mark" ; m_valid_bmp->SetBitmap(*get_bmp_bundle(bmp_name)); } @@ -289,22 +287,17 @@ void SavePresetDialog::Item::Enable(bool enable /*= true*/) // SavePresetDialog //----------------------------------------------- -SavePresetDialog::SavePresetDialog(wxWindow* parent, Preset::Type type, std::string suffix, bool template_filament) - : DPIDialog(parent, wxID_ANY, _L("Save preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER) -{ - build(std::vector{type}, suffix, template_filament); -} - SavePresetDialog::SavePresetDialog(wxWindow* parent, std::vector types, std::string suffix, bool template_filament/* =false*/, PresetBundle* preset_bundle/* = nullptr*/) - : DPIDialog(parent, wxID_ANY, _L("Save presets"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER), + : DPIDialog(parent, wxID_ANY, types.size() == 1 ? _L("Save preset") : _L("Save presets"), + wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING), m_preset_bundle(preset_bundle) { build(types, suffix, template_filament); } -SavePresetDialog::SavePresetDialog(wxWindow* parent, Preset::Type type, bool rename, const wxString& info_line_extention) - : DPIDialog(parent, wxID_ANY, _L("Rename preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING | wxRESIZE_BORDER), - m_use_for_rename(rename), +SavePresetDialog::SavePresetDialog(wxWindow* parent, Preset::Type type, const wxString& info_line_extention) + : DPIDialog(parent, wxID_ANY, _L("Rename preset"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), 5 * wxGetApp().em_unit()), wxDEFAULT_DIALOG_STYLE | wxICON_WARNING), + m_use_for_rename(true), m_info_line_extention(info_line_extention) { build(std::vector{type}); @@ -318,11 +311,13 @@ SavePresetDialog::~SavePresetDialog() void SavePresetDialog::build(std::vector types, std::string suffix, bool template_filament) { + this->SetFont(wxGetApp().normal_font()); + #if defined(__WXMSW__) // ys_FIXME! temporary workaround for correct font scaling // Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts, // From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT - this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); +// this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); #else SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); #endif // __WXMSW__ @@ -335,9 +330,9 @@ void SavePresetDialog::build(std::vector types, std::string suffix m_presets_sizer = new wxBoxSizer(wxVERTICAL); - // Add first item - for (Preset::Type type : types) - AddItem(type, suffix); + const bool is_for_multiple_save = types.size() > 1; + for (const Preset::Type& type : types) + AddItem(type, suffix, is_for_multiple_save); // Add dialog's buttons wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); @@ -367,9 +362,9 @@ void SavePresetDialog::build(std::vector types, std::string suffix #endif } -void SavePresetDialog::AddItem(Preset::Type type, const std::string& suffix) +void SavePresetDialog::AddItem(Preset::Type type, const std::string& suffix, bool is_for_multiple_save) { - m_items.emplace_back(new Item{type, suffix, m_presets_sizer, this}); + m_items.emplace_back(new Item{type, suffix, m_presets_sizer, this, is_for_multiple_save}); } std::string SavePresetDialog::get_name() diff --git a/src/slic3r/GUI/SavePresetDialog.hpp b/src/slic3r/GUI/SavePresetDialog.hpp index 3088d457f6..1450ebbd24 100644 --- a/src/slic3r/GUI/SavePresetDialog.hpp +++ b/src/slic3r/GUI/SavePresetDialog.hpp @@ -36,7 +36,7 @@ public: Warning }; - Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent); + Item(Preset::Type type, const std::string& suffix, wxBoxSizer* sizer, SavePresetDialog* parent, bool is_for_multiple_save); Item(wxWindow* parent, wxBoxSizer* sizer, const std::string& def_name, PrinterTechnology pt = ptFFF); void update_valid_bmp(); @@ -65,7 +65,6 @@ public: std::string get_init_preset_name(const std::string &suffix); void init_input_name_ctrl(wxBoxSizer *input_name_sizer, std::string preset_name); const Preset* get_existing_preset() const ; - wxString get_top_label_text() const ; void update(); }; @@ -89,12 +88,11 @@ public: const wxString& get_info_line_extention() { return m_info_line_extention; } - SavePresetDialog(wxWindow* parent, Preset::Type type, std::string suffix = "", bool template_filament = false); SavePresetDialog(wxWindow* parent, std::vector types, std::string suffix = "", bool template_filament = false, PresetBundle* preset_bundle = nullptr); - SavePresetDialog(wxWindow* parent, Preset::Type type, bool rename, const wxString& info_line_extention); + SavePresetDialog(wxWindow* parent, Preset::Type type, const wxString& info_line_extention); ~SavePresetDialog() override; - void AddItem(Preset::Type type, const std::string& suffix); + void AddItem(Preset::Type type, const std::string& suffix, bool is_for_multiple_save); PresetBundle* get_preset_bundle() const { return m_preset_bundle; } std::string get_name(); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index b095d4ea28..d31f48913b 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -265,6 +265,7 @@ void Tab::create_preset_tab() // tree m_treectrl = new wxTreeCtrl(panel, wxID_ANY, wxDefaultPosition, wxSize(20 * m_em_unit, -1), wxTR_NO_BUTTONS | wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES | wxBORDER_SUNKEN | wxWANTS_CHARS); + m_treectrl->SetFont(wxGetApp().normal_font()); m_left_sizer->Add(m_treectrl, 1, wxEXPAND); // Index of the last icon inserted into m_treectrl m_icon_count = -1; @@ -1469,7 +1470,7 @@ void TabPrint::build() optgroup = page->new_optgroup(L("Reducing printing time")); category_path = "infill_42#"; optgroup->append_single_option_line("infill_every_layers", category_path + "combine-infill-every-x-layers"); - optgroup->append_single_option_line("infill_only_where_needed", category_path + "only-infill-where-needed"); + // optgroup->append_single_option_line("infill_only_where_needed", category_path + "only-infill-where-needed"); optgroup = page->new_optgroup(L("Advanced")); optgroup->append_single_option_line("solid_infill_every_layers", category_path + "solid-infill-every-x-layers"); @@ -3820,7 +3821,7 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach) } if (name.empty()) { - SavePresetDialog dlg(m_parent, m_type, detach ? _u8L("Detached") : "", from_template); + SavePresetDialog dlg(m_parent, { m_type }, detach ? _u8L("Detached") : "", from_template); if (dlg.ShowModal() != wxID_OK) return; name = dlg.get_name(); @@ -3931,7 +3932,7 @@ void Tab::rename_preset() // get new name - SavePresetDialog dlg(m_parent, m_type, true, msg); + SavePresetDialog dlg(m_parent, m_type, msg); if (dlg.ShowModal() != wxID_OK) return; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index d7d4798c20..5220b2115d 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -837,7 +837,8 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ // ys_FIXME! temporary workaround for correct font scaling // Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts, // From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT - this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); +// this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); + this->SetFont(wxGetApp().normal_font()); #endif // __WXMSW__ int border = 10; @@ -850,7 +851,8 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ add_new_value_column = false; m_action_line = new wxStaticText(this, wxID_ANY, ""); - m_action_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); +// m_action_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); + m_action_line->SetFont(wxGetApp().bold_font()); m_tree = new DiffViewCtrl(this, wxSize(em * (add_new_value_column ? 80 : 60), em * 30)); m_tree->AppendToggleColumn_(L"\u2714" , DiffModel::colToggle, wxLinux ? 9 : 6); @@ -910,7 +912,8 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ cancel_btn->Bind(wxEVT_BUTTON, [this](wxEvent&) { this->EndModal(wxID_CANCEL); }); m_info_line = new wxStaticText(this, wxID_ANY, ""); - m_info_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); +// m_info_line->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold()); + m_info_line->SetFont(wxGetApp().bold_font()); m_info_line->Hide(); if (!m_app_config_key.empty()) { @@ -1012,7 +1015,7 @@ bool UnsavedChangesDialog::save(PresetCollection* dependent_presets, bool show_s // for system/default/external presets we should take an edited name if (preset.is_system || preset.is_default || preset.is_external) { - SavePresetDialog save_dlg(this, preset.type); + SavePresetDialog save_dlg(this, { preset.type }); if (save_dlg.ShowModal() != wxID_OK) { m_exit_action = Action::Discard; return false; @@ -1362,6 +1365,7 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString : wxDialog(nullptr, wxID_ANY, option_name, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { wxGetApp().UpdateDarkUI(this); + this->SetFont(wxGetApp().normal_font()); int border = 10; bool has_new_value_column = !new_value_header.IsEmpty(); @@ -1528,7 +1532,8 @@ void DiffPresetDialog::create_show_all_presets_chb() void DiffPresetDialog::create_info_lines() { - const wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold(); +// const wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold(); + const wxFont font = GetFont().Bold(); m_top_info_line = new wxStaticText(this, wxID_ANY, _L("Select presets to compare")); m_top_info_line->SetFont(font); @@ -1668,7 +1673,8 @@ DiffPresetDialog::DiffPresetDialog(MainFrame* mainframe) // ys_FIXME! temporary workaround for correct font scaling // Because of from wxWidgets 3.1.3 auto rescaling is implemented for the Fonts, // From the very beginning set dialog font to the wxSYS_DEFAULT_GUI_FONT - this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); +// this->SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); + this->SetFont(mainframe->normal_font()); #endif // __WXMSW__ // Init bundles diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index 540852af89..3824c8ed4a 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -512,11 +512,8 @@ std::string OctoPrint::make_url(const std::string &path) const } } -SL1Host::SL1Host(DynamicPrintConfig *config) : - OctoPrint(config), - m_authorization_type(dynamic_cast*>(config->option("printhost_authorization_type"))->value), - m_username(config->opt_string("printhost_user")), - m_password(config->opt_string("printhost_password")) +SL1Host::SL1Host(DynamicPrintConfig *config) + : PrusaLink(config) { } @@ -538,22 +535,6 @@ bool SL1Host::validate_version_text(const boost::optional &version_ return version_text ? boost::starts_with(*version_text, "Prusa SLA") : false; } -void SL1Host::set_auth(Http &http) const -{ - switch (m_authorization_type) { - case atKeyPassword: - http.header("X-Api-Key", get_apikey()); - break; - case atUserPassword: - http.auth_digest(m_username, m_password); - break; - } - - if (! get_cafile().empty()) { - http.ca_file(get_cafile()); - } -} - // PrusaLink PrusaLink::PrusaLink(DynamicPrintConfig* config, bool show_after_message) : OctoPrint(config), diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 82c07f6f45..2daeab73f0 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -55,30 +55,6 @@ private: #endif }; -class SL1Host: public OctoPrint -{ -public: - SL1Host(DynamicPrintConfig *config); - ~SL1Host() override = default; - - const char* get_name() const override; - - wxString get_test_ok_msg() const override; - wxString get_test_failed_msg(wxString &msg) const override; - PrintHostPostUploadActions get_post_upload_actions() const override { return {}; } - -protected: - bool validate_version_text(const boost::optional &version_text) const override; - -private: - void set_auth(Http &http) const override; - - // Host authorization type. - AuthorizationType m_authorization_type; - // username and password for HTTP Digest Authentization (RFC RFC2617) - std::string m_username; - std::string m_password; -}; class PrusaLink : public OctoPrint { @@ -106,6 +82,12 @@ protected: bool upload_inner_with_resolved_ip(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn, const boost::asio::ip::address& resolved_addr) const override; #endif + // Host authorization type. + AuthorizationType m_authorization_type; + // username and password for HTTP Digest Authentization (RFC RFC2617) + std::string m_username; + std::string m_password; + private: bool test_with_method_check(wxString& curl_msg, bool& use_put) const; bool put_inner(PrintHostUpload upload_data, std::string url, const std::string& name, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const; @@ -113,11 +95,7 @@ private: #ifdef WIN32 bool test_with_resolved_ip_and_method_check(wxString& curl_msg, bool& use_put) const; #endif - // Host authorization type. - AuthorizationType m_authorization_type; - // username and password for HTTP Digest Authentization (RFC RFC2617) - std::string m_username; - std::string m_password; + bool m_show_after_message; #if 0 @@ -149,6 +127,22 @@ public: const char* get_name() const override { return "Mainsail/Fluidd"; } }; +class SL1Host : public PrusaLink +{ +public: + SL1Host(DynamicPrintConfig* config); + ~SL1Host() override = default; + + const char* get_name() const override; + + wxString get_test_ok_msg() const override; + wxString get_test_failed_msg(wxString& msg) const override; + PrintHostPostUploadActions get_post_upload_actions() const override { return {}; } + +protected: + bool validate_version_text(const boost::optional& version_text) const override; +}; + } #endif diff --git a/tests/fff_print/test_fill.cpp b/tests/fff_print/test_fill.cpp index 8f024102d3..6d8422c82d 100644 --- a/tests/fff_print/test_fill.cpp +++ b/tests/fff_print/test_fill.cpp @@ -252,85 +252,85 @@ SCENARIO("Infill does not exceed perimeters", "[Fill]") GIVEN("Concentric") { test("concentric"sv); } } -SCENARIO("Infill only where needed", "[Fill]") -{ - DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config(); - config.set_deserialize_strict({ - { "nozzle_diameter", "0.4, 0.4, 0.4, 0.4" }, - { "infill_only_where_needed", true }, - { "bottom_solid_layers", 0 }, - { "infill_extruder", 2 }, - { "infill_extrusion_width", 0.5 }, - { "wipe_into_infill", false }, - { "fill_density", 0.4 }, - // for preventing speeds from being altered - { "cooling", "0, 0, 0, 0" }, - // for preventing speeds from being altered - { "first_layer_speed", "100%" } - }); +// SCENARIO("Infill only where needed", "[Fill]") +// { +// DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config(); +// config.set_deserialize_strict({ +// { "nozzle_diameter", "0.4, 0.4, 0.4, 0.4" }, +// { "infill_only_where_needed", true }, +// { "bottom_solid_layers", 0 }, +// { "infill_extruder", 2 }, +// { "infill_extrusion_width", 0.5 }, +// { "wipe_into_infill", false }, +// { "fill_density", 0.4 }, +// // for preventing speeds from being altered +// { "cooling", "0, 0, 0, 0" }, +// // for preventing speeds from being altered +// { "first_layer_speed", "100%" } +// }); - auto test = [&config]() -> double { - TriangleMesh pyramid = Test::mesh(Slic3r::Test::TestMesh::pyramid); - // Arachne doesn't use "Detect thin walls," and because of this, it filters out tiny infill areas differently. - // So, for Arachne, we cut the pyramid model to achieve similar results. - if (config.opt_enum("perimeter_generator") == Slic3r::PerimeterGeneratorType::Arachne) { - indexed_triangle_set lower{}; - cut_mesh(pyramid.its, 35, nullptr, &lower); - pyramid = TriangleMesh(lower); - } - std::string gcode = Slic3r::Test::slice({ pyramid }, config); - THEN("gcode not empty") { - REQUIRE(! gcode.empty()); - } +// auto test = [&config]() -> double { +// TriangleMesh pyramid = Test::mesh(Slic3r::Test::TestMesh::pyramid); +// // Arachne doesn't use "Detect thin walls," and because of this, it filters out tiny infill areas differently. +// // So, for Arachne, we cut the pyramid model to achieve similar results. +// if (config.opt_enum("perimeter_generator") == Slic3r::PerimeterGeneratorType::Arachne) { +// indexed_triangle_set lower{}; +// cut_mesh(pyramid.its, 35, nullptr, &lower); +// pyramid = TriangleMesh(lower); +// } +// std::string gcode = Slic3r::Test::slice({ pyramid }, config); +// THEN("gcode not empty") { +// REQUIRE(! gcode.empty()); +// } - GCodeReader parser; - int tool = -1; - const int infill_extruder = config.opt_int("infill_extruder"); - Points infill_points; - parser.parse_buffer(gcode, [&tool, &infill_points, infill_extruder](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) - { - // if the command is a T command, set the the current tool - if (boost::starts_with(line.cmd(), "T")) { - tool = atoi(line.cmd().data() + 1) + 1; - } else if (line.cmd() == "G1" && line.extruding(self) && line.dist_XY(self) > 0) { - if (tool == infill_extruder) { - infill_points.emplace_back(self.xy_scaled()); - infill_points.emplace_back(line.new_XY_scaled(self)); - } - } - }); - // prevent calling convex_hull() with no points - THEN("infill not empty") { - REQUIRE(! infill_points.empty()); - } +// GCodeReader parser; +// int tool = -1; +// const int infill_extruder = config.opt_int("infill_extruder"); +// Points infill_points; +// parser.parse_buffer(gcode, [&tool, &infill_points, infill_extruder](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) +// { +// // if the command is a T command, set the the current tool +// if (boost::starts_with(line.cmd(), "T")) { +// tool = atoi(line.cmd().data() + 1) + 1; +// } else if (line.cmd() == "G1" && line.extruding(self) && line.dist_XY(self) > 0) { +// if (tool == infill_extruder) { +// infill_points.emplace_back(self.xy_scaled()); +// infill_points.emplace_back(line.new_XY_scaled(self)); +// } +// } +// }); +// // prevent calling convex_hull() with no points +// THEN("infill not empty") { +// REQUIRE(! infill_points.empty()); +// } - auto opt_width = config.opt("infill_extrusion_width"); - REQUIRE(! opt_width->percent); - Polygons convex_hull = expand(Geometry::convex_hull(infill_points), scaled(opt_width->value / 2)); - return SCALING_FACTOR * SCALING_FACTOR * std::accumulate(convex_hull.begin(), convex_hull.end(), 0., [](double acc, const Polygon &poly){ return acc + poly.area(); }); - }; +// auto opt_width = config.opt("infill_extrusion_width"); +// REQUIRE(! opt_width->percent); +// Polygons convex_hull = expand(Geometry::convex_hull(infill_points), scaled(opt_width->value / 2)); +// return SCALING_FACTOR * SCALING_FACTOR * std::accumulate(convex_hull.begin(), convex_hull.end(), 0., [](double acc, const Polygon &poly){ return acc + poly.area(); }); +// }; - double tolerance = 5; // mm^2 +// double tolerance = 5; // mm^2 - // GIVEN("solid_infill_below_area == 0") { - // config.opt_float("solid_infill_below_area") = 0; - // WHEN("pyramid is sliced ") { - // auto area = test(); - // THEN("no infill is generated when using infill_only_where_needed on a pyramid") { - // REQUIRE(area < tolerance); - // } - // } - // } - // GIVEN("solid_infill_below_area == 70") { - // config.opt_float("solid_infill_below_area") = 70; - // WHEN("pyramid is sliced ") { - // auto area = test(); - // THEN("infill is only generated under the forced solid shells") { - // REQUIRE(std::abs(area - 70) < tolerance); - // } - // } - // } -} +// // GIVEN("solid_infill_below_area == 0") { +// // config.opt_float("solid_infill_below_area") = 0; +// // WHEN("pyramid is sliced ") { +// // auto area = test(); +// // THEN("no infill is generated when using infill_only_where_needed on a pyramid") { +// // REQUIRE(area < tolerance); +// // } +// // } +// // } +// // GIVEN("solid_infill_below_area == 70") { +// // config.opt_float("solid_infill_below_area") = 70; +// // WHEN("pyramid is sliced ") { +// // auto area = test(); +// // THEN("infill is only generated under the forced solid shells") { +// // REQUIRE(std::abs(area - 70) < tolerance); +// // } +// // } +// // } +// } SCENARIO("Combine infill", "[Fill]") { diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index 0e3521a23f..a6da48bf34 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -245,10 +245,41 @@ SCENARIO("Placeholder parser variables", "[PlaceholderParser]") { "{size(myfloats)}"; REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7"); } + SECTION("nested if with new variables 2, mixing }{ with ;") { + std::string script = + "{if 1 == 0 then local myints = (5, 4, 3, 2, 1);else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif}" + "{size(myfloats)}"; + REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7"); + } SECTION("nested if with new variables, two level") { std::string script = "{if 1 == 1}{if 2 == 3}{nejaka / haluz}{else}{local myints = (6, 5, 4, 3, 2, 1)}{endif}{else}{if zase * haluz}{else}{local myfloats = (1., 2., 3., 4., 5., 6., 7.)}{endif}{endif}" "{size(myints)}"; REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); } + SECTION("if with empty block and ;") { + std::string script = + "{if false then else;local myfloats = (1., 2., 3., 4., 5., 6., 7.);endif}" + "{size(myfloats)}"; + REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "7"); + } + SECTION("nested if with new variables, two level, mixing }{ with ;") { + std::string script = + "{if 1 == 1 then if 2 == 3}nejaka / haluz{else local myints = (6, 5, 4, 3, 2, 1) endif else if zase * haluz then else local myfloats = (1., 2., 3., 4., 5., 6., 7.) endif endif}" + "{size(myints)}"; + REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); + } + SECTION("nested if with new variables, two level, mixing }{ with ; 2") { + std::string script = + "{if 1 == 1 then if 2 == 3 then nejaka / haluz else}{local myints = (6, 5, 4, 3, 2, 1)}{endif else if zase * haluz then else local myfloats = (1., 2., 3., 4., 5., 6., 7.) endif endif}" + "{size(myints)}"; + REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); + } + SECTION("nested if with new variables, two level, mixing }{ with ; 3") { + std::string script = + "{if 1 == 1 then if 2 == 3 then nejaka / haluz else}{local myints = (6, 5, 4, 3, 2, 1)}{endif else}{if zase * haluz}{else local myfloats = (1., 2., 3., 4., 5., 6., 7.) endif}{endif}" + "{size(myints)}"; + REQUIRE(parser.process(script, 0, nullptr, nullptr, nullptr) == "6"); + } + SECTION("if else completely empty") { REQUIRE(parser.process("{if false then elsif false then else endif}", 0, nullptr, nullptr, nullptr) == ""); } }