diff --git a/resources/icons/PrusaSlicer-gcodeviewer-mac_128px.png b/resources/icons/PrusaSlicer-gcodeviewer-mac_128px.png new file mode 100644 index 000000000..b62f3fddc Binary files /dev/null and b/resources/icons/PrusaSlicer-gcodeviewer-mac_128px.png differ diff --git a/resources/icons/PrusaSlicer-mac_128px.png b/resources/icons/PrusaSlicer-mac_128px.png new file mode 100644 index 000000000..ff0b093be Binary files /dev/null and b/resources/icons/PrusaSlicer-mac_128px.png differ diff --git a/src/libslic3r/BlacklistedLibraryCheck.cpp b/src/libslic3r/BlacklistedLibraryCheck.cpp index d9dea188f..76f675c70 100644 --- a/src/libslic3r/BlacklistedLibraryCheck.cpp +++ b/src/libslic3r/BlacklistedLibraryCheck.cpp @@ -33,22 +33,20 @@ std::wstring BlacklistedLibraryCheck::get_blacklisted_string() bool BlacklistedLibraryCheck::perform_check() { - // Get a handle to the process. - HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId()); - if (NULL == hProcess) - return false; + // Get the pseudo-handle for the current process. + HANDLE hCurrentProcess = GetCurrentProcess(); // Get a list of all the modules in this process. HMODULE hMods[1024]; DWORD cbNeeded; - if (EnumProcessModulesEx(hProcess, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL)) + if (EnumProcessModulesEx(hCurrentProcess, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL)) { //printf("Total Dlls: %d\n", cbNeeded / sizeof(HMODULE)); for (unsigned int i = 0; i < cbNeeded / sizeof(HMODULE); ++ i) { wchar_t szModName[MAX_PATH]; // Get the full path to the module's file. - if (GetModuleFileNameExW(hProcess, hMods[i], szModName, MAX_PATH)) + if (GetModuleFileNameExW(hCurrentProcess, hMods[i], szModName, MAX_PATH)) { // Add to list if blacklisted if (BlacklistedLibraryCheck::is_blacklisted(szModName)) { @@ -61,7 +59,6 @@ bool BlacklistedLibraryCheck::perform_check() } } - CloseHandle(hProcess); //printf("\n"); return !m_found.empty(); } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 19909b2bc..f2deba046 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -744,27 +744,27 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re std::string path_tmp(path); path_tmp += ".tmp"; - FILE *file = boost::nowide::fopen(path_tmp.c_str(), "wb"); - if (file == nullptr) + GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb")); + if (! file.is_open()) 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); - fflush(file); - if (ferror(file)) { - fclose(file); + file.flush(); + if (file.is_error()) { + file.close(); boost::nowide::remove(path_tmp.c_str()); throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed\nIs the disk full?\n"); } } catch (std::exception & /* ex */) { // Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown. // Close and remove the file. - fclose(file); + file.close(); boost::nowide::remove(path_tmp.c_str()); throw; } - fclose(file); + file.close(); if (! m_placeholder_parser_failed_templates.empty()) { // G-code export proceeded, but some of the PlaceholderParser substitutions failed. @@ -1046,7 +1046,7 @@ std::vector sort_object_instances_by_model_order(const Pri return instances; } -void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb) +void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb) { PROFILE_FUNC(); @@ -1111,10 +1111,10 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu #endif /* HAS_PRESSURE_EQUALIZER */ // Write information on the generator. - _write_format(file, "; %s\n\n", Slic3r::header_slic3r_generated().c_str()); + file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); DoExport::export_thumbnails_to_file(thumbnail_cb, print.full_print_config().option("thumbnails")->values, - [this, file](const char* sz) { this->_write(file, sz); }, + [&file](const char* sz) { file.write(sz); }, [&print]() { print.throw_if_canceled(); }); // Write notes (content of the Print Settings tab -> Notes) @@ -1125,10 +1125,10 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // Remove the trailing '\r' from the '\r\n' sequence. if (! line.empty() && line.back() == '\r') line.pop_back(); - _write_format(file, "; %s\n", line.c_str()); + file.write_format("; %s\n", line.c_str()); } if (! lines.empty()) - _write(file, "\n"); + file.write("\n"); } print.throw_if_canceled(); @@ -1139,22 +1139,22 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu const double first_layer_height = print.config().first_layer_height.value; for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) { const PrintRegion ®ion = print.get_print_region(region_id); - _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width()); - _write_format(file, "; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width()); - _write_format(file, "; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width()); - _write_format(file, "; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width()); - _write_format(file, "; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width()); + file.write_format("; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width()); + file.write_format("; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width()); + file.write_format("; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width()); + file.write_format("; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width()); + file.write_format("; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width()); if (print.has_support_material()) - _write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width()); + file.write_format("; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width()); if (print.config().first_layer_extrusion_width.value > 0) - _write_format(file, "; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width()); - _write_format(file, "\n"); + file.write_format("; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width()); + file.write_format("\n"); } print.throw_if_canceled(); // adds tags for time estimators if (print.config().remaining_times.value) - _write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::First_Line_M73_Placeholder).c_str()); + file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::First_Line_M73_Placeholder).c_str()); // Prepare the helper object for replacing placeholders in custom G-code and output filename. m_placeholder_parser = print.placeholder_parser(); @@ -1218,7 +1218,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // Disable fan. if (! print.config().cooling.get_at(initial_extruder_id) || print.config().disable_fan_first_layers.get_at(initial_extruder_id)) - _write(file, m_writer.set_fan(0, true)); + file.write(m_writer.set_fan(0, true)); // Let the start-up script prime the 1st printing tool. m_placeholder_parser.set("initial_tool", initial_extruder_id); @@ -1261,10 +1261,10 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false); // adds tag for processor - _write_format(file, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str()); + file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str()); // Write the custom start G-code - _writeln(file, start_gcode); + file.writeln(start_gcode); // Process filament-specific gcode. /* if (has_wipe_tower) { @@ -1272,14 +1272,14 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu } else { DynamicConfig config; config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id))); - _writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config)); + file.writeln(this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config)); } */ this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true); print.throw_if_canceled(); // Set other general things. - _write(file, this->preamble()); + file.write(this->preamble()); // Calculate wiping points if needed DoExport::init_ooze_prevention(print, m_ooze_prevention); @@ -1291,7 +1291,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu if (! (has_wipe_tower && print.config().single_extruder_multi_material_priming)) { // Set initial extruder only after custom start G-code. // Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed. - _write(file, this->set_extruder(initial_extruder_id, 0.)); + file.write(this->set_extruder(initial_extruder_id, 0.)); } // Do all objects for each layer. @@ -1317,8 +1317,8 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // This happens before Z goes down to layer 0 again, so that no collision happens hopefully. m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer m_avoid_crossing_perimeters.use_external_mp_once(); - _write(file, this->retract()); - _write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object")); + file.write(this->retract()); + file.write(this->travel_to(Point(0, 0), erNone, "move to origin position for next object")); m_enable_cooling_markers = true; // Disable motion planner when traveling to first object point. m_avoid_crossing_perimeters.disable_once(); @@ -1330,7 +1330,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // 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); this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false); - _writeln(file, between_objects_gcode); + file.writeln(between_objects_gcode); } // Reset the cooling buffer internal state (the current position, feed rate, accelerations). m_cooling_buffer->reset(); @@ -1346,7 +1346,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu } #ifdef HAS_PRESSURE_EQUALIZER if (m_pressure_equalizer) - _write(file, m_pressure_equalizer->process("", true)); + file.write(m_pressure_equalizer->process("", true)); #endif /* HAS_PRESSURE_EQUALIZER */ ++ finished_objects; // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed. @@ -1361,9 +1361,9 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // Prusa Multi-Material wipe tower. if (has_wipe_tower && ! layers_to_print.empty()) { m_wipe_tower.reset(new WipeTowerIntegration(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get())); - _write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height")); + file.write(m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height")); if (print.config().single_extruder_multi_material_priming) { - _write(file, m_wipe_tower->prime(*this)); + file.write(m_wipe_tower->prime(*this)); // Verify, whether the print overaps the priming extrusions. BoundingBoxf bbox_print(get_print_extrusions_extents(print)); coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON; @@ -1375,15 +1375,15 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu bool overlap = bbox_prime.overlap(bbox_print); if (print.config().gcode_flavor == gcfMarlinLegacy || print.config().gcode_flavor == gcfMarlinFirmware) { - _write(file, this->retract()); - _write(file, "M300 S800 P500\n"); // Beep for 500ms, tone 800Hz. + file.write(this->retract()); + file.write("M300 S800 P500\n"); // Beep for 500ms, tone 800Hz. if (overlap) { // Wait for the user to remove the priming extrusions. - _write(file, "M1 Remove priming towers and click button.\n"); + file.write("M1 Remove priming towers and click button.\n"); } else { // Just wait for a bit to let the user check, that the priming succeeded. //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix. - _write(file, "M1 S10\n"); + file.write("M1 S10\n"); } } else { // This is not Marlin, M1 command is probably not supported. @@ -1410,19 +1410,19 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu } #ifdef HAS_PRESSURE_EQUALIZER if (m_pressure_equalizer) - _write(file, m_pressure_equalizer->process("", true)); + file.write(m_pressure_equalizer->process("", true)); #endif /* HAS_PRESSURE_EQUALIZER */ if (m_wipe_tower) // Purge the extruder, pull out the active filament. - _write(file, m_wipe_tower->finalize(*this)); + file.write(m_wipe_tower->finalize(*this)); } // Write end commands to file. - _write(file, this->retract()); - _write(file, m_writer.set_fan(false)); + file.write(this->retract()); + file.write(m_writer.set_fan(false)); // adds tag for processor - _write_format(file, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str()); + file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str()); // Process filament-specific gcode in extruder order. { @@ -1434,48 +1434,48 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu // Process the end_filament_gcode for the active filament only. int extruder_id = m_writer.extruder()->id(); config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id)); - _writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(extruder_id), extruder_id, &config)); + file.writeln(this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(extruder_id), extruder_id, &config)); } else { for (const std::string &end_gcode : print.config().end_filament_gcode.values) { int extruder_id = (unsigned int)(&end_gcode - &print.config().end_filament_gcode.values.front()); config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id)); - _writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, extruder_id, &config)); + file.writeln(this->placeholder_parser_process("end_filament_gcode", end_gcode, extruder_id, &config)); } } - _writeln(file, this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.extruder()->id(), &config)); + file.writeln(this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.extruder()->id(), &config)); } - _write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100% - _write(file, m_writer.postamble()); + file.write(m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100% + file.write(m_writer.postamble()); // adds tags for time estimators if (print.config().remaining_times.value) - _write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Last_Line_M73_Placeholder).c_str()); + file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Last_Line_M73_Placeholder).c_str()); print.throw_if_canceled(); // Get filament stats. - _write(file, DoExport::update_print_stats_and_format_filament_stats( + file.write(DoExport::update_print_stats_and_format_filament_stats( // Const inputs has_wipe_tower, print.wipe_tower_data(), m_writer.extruders(), // Modifies print.m_print_statistics)); - _write(file, "\n"); - _write_format(file, "; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight); - _write_format(file, "; total filament cost = %.2lf\n", print.m_print_statistics.total_cost); + file.write("\n"); + file.write_format("; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight); + file.write_format("; total filament cost = %.2lf\n", print.m_print_statistics.total_cost); if (print.m_print_statistics.total_toolchanges > 0) - _write_format(file, "; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); - _write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); + file.write_format("; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); + file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); // Append full config, delimited by two 'phony' configuration keys prusaslicer_config = begin and prusaslicer_config = end. // The delimiters are structured as configuration key / value pairs to be parsable by older versions of PrusaSlicer G-code viewer. { - _write(file, "\n; prusaslicer_config = begin\n"); + file.write("\n; prusaslicer_config = begin\n"); std::string full_config; append_full_config(print, full_config); if (!full_config.empty()) - _write(file, full_config); - _write(file, "; prusaslicer_config = end\n"); + file.write(full_config); + file.write("; prusaslicer_config = end\n"); } print.throw_if_canceled(); } @@ -1565,16 +1565,16 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc // Print the machine envelope G-code for the Marlin firmware based on the "machine_max_xxx" parameters. // Do not process this piece of G-code by the time estimator, it already knows the values through another sources. -void GCode::print_machine_envelope(FILE *file, Print &print) +void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print) { if ((print.config().gcode_flavor.value == gcfMarlinLegacy || print.config().gcode_flavor.value == gcfMarlinFirmware) && print.config().machine_limits_usage.value == MachineLimitsUsage::EmitToGCode) { - fprintf(file, "M201 X%d Y%d Z%d E%d ; sets maximum accelerations, mm/sec^2\n", + file.write_format("M201 X%d Y%d Z%d E%d ; sets maximum accelerations, mm/sec^2\n", int(print.config().machine_max_acceleration_x.values.front() + 0.5), int(print.config().machine_max_acceleration_y.values.front() + 0.5), int(print.config().machine_max_acceleration_z.values.front() + 0.5), int(print.config().machine_max_acceleration_e.values.front() + 0.5)); - fprintf(file, "M203 X%d Y%d Z%d E%d ; sets maximum feedrates, mm/sec\n", + file.write_format("M203 X%d Y%d Z%d E%d ; sets maximum feedrates, mm/sec\n", int(print.config().machine_max_feedrate_x.values.front() + 0.5), int(print.config().machine_max_feedrate_y.values.front() + 0.5), int(print.config().machine_max_feedrate_z.values.front() + 0.5), @@ -1587,18 +1587,18 @@ void GCode::print_machine_envelope(FILE *file, Print &print) int travel_acc = print.config().gcode_flavor == gcfMarlinLegacy ? int(print.config().machine_max_acceleration_extruding.values.front() + 0.5) : int(print.config().machine_max_acceleration_travel.values.front() + 0.5); - fprintf(file, "M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n", + file.write_format("M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n", int(print.config().machine_max_acceleration_extruding.values.front() + 0.5), int(print.config().machine_max_acceleration_retracting.values.front() + 0.5), travel_acc); assert(is_decimal_separator_point()); - fprintf(file, "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n", + file.write_format("M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n", print.config().machine_max_jerk_x.values.front(), print.config().machine_max_jerk_y.values.front(), print.config().machine_max_jerk_z.values.front(), print.config().machine_max_jerk_e.values.front()); - fprintf(file, "M205 S%d T%d ; sets the minimum extruding and travel feed rate, mm/sec\n", + file.write_format("M205 S%d T%d ; sets the minimum extruding and travel feed rate, mm/sec\n", int(print.config().machine_min_extruding_rate.values.front() + 0.5), int(print.config().machine_min_travel_rate.values.front() + 0.5)); } @@ -1608,7 +1608,7 @@ void GCode::print_machine_envelope(FILE *file, Print &print) // Only do that if the start G-code does not already contain any M-code controlling an extruder temperature. // M140 - Set Extruder Temperature // M190 - Set Extruder Temperature and Wait -void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) +void GCode::_print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) { // Initial bed temperature based on the first extruder. int temp = print.config().first_layer_bed_temperature.get_at(first_printing_extruder_id); @@ -1621,7 +1621,7 @@ void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const s // the custom start G-code emited these. std::string set_temp_gcode = m_writer.set_bed_temperature(temp, wait); if (! temp_set_by_gcode) - _write(file, set_temp_gcode); + file.write(set_temp_gcode); } // Write 1st layer extruder temperatures into the G-code. @@ -1629,7 +1629,7 @@ void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const s // M104 - Set Extruder Temperature // M109 - Set Extruder Temperature and Wait // RepRapFirmware: G10 Sxx -void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) +void GCode::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) { // Is the bed temperature set by the provided custom G-code? int temp_by_gcode = -1; @@ -1646,7 +1646,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c // Set temperature of the first printing extruder only. int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id); if (temp > 0) - _write(file, m_writer.set_temperature(temp, wait, first_printing_extruder_id)); + file.write(m_writer.set_temperature(temp, wait, first_printing_extruder_id)); } else { // Set temperatures of all the printing extruders. for (unsigned int tool_id : print.extruders()) { @@ -1654,7 +1654,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c if (print.config().ooze_prevention.value) temp += print.config().standby_temperature_delta.value; if (temp > 0) - _write(file, m_writer.set_temperature(temp, wait, tool_id)); + file.write(m_writer.set_temperature(temp, wait, tool_id)); } } } @@ -1891,7 +1891,7 @@ namespace Skirt { // and performing the extruder specific extrusions together. void GCode::process_layer( // Write into the output file. - FILE *file, + GCodeOutputStream &file, const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. const std::vector &layers, @@ -2306,7 +2306,7 @@ void GCode::process_layer( // printf("G-code after filter:\n%s\n", out.c_str()); #endif /* HAS_PRESSURE_EQUALIZER */ - _write(file, gcode); + file.write(gcode); BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << log_memory_info(); } @@ -2642,22 +2642,40 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill return gcode; } -void GCode::_write(FILE* file, const char *what) +bool GCode::GCodeOutputStream::is_error() const +{ + return ::ferror(this->f); +} + +void GCode::GCodeOutputStream::flush() +{ + ::fflush(this->f); +} + +void GCode::GCodeOutputStream::close() +{ + if (this->f) { + ::fclose(this->f); + this->f = nullptr; + } +} + +void GCode::GCodeOutputStream::write(const char *what) { if (what != nullptr) { const char* gcode = what; // writes string to file - fwrite(gcode, 1, ::strlen(gcode), file); + fwrite(gcode, 1, ::strlen(gcode), this->f); } } -void GCode::_writeln(FILE* file, const std::string &what) +void GCode::GCodeOutputStream::writeln(const std::string &what) { if (! what.empty()) - _write(file, (what.back() == '\n') ? what : (what + '\n')); + this->write(what.back() == '\n' ? what : what + '\n'); } -void GCode::_write_format(FILE* file, const char* format, ...) +void GCode::GCodeOutputStream::write_format(const char* format, ...) { va_list args; va_start(args, format); @@ -2681,7 +2699,7 @@ void GCode::_write_format(FILE* file, const char* format, ...) char *bufptr = buffer_dynamic ? (char*)malloc(buflen) : buffer; int res = ::vsnprintf(bufptr, buflen, format, args); if (res > 0) - _write(file, bufptr); + this->write(bufptr); if (buffer_dynamic) free(bufptr); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index d2d241054..89e4570a9 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -184,13 +184,39 @@ public: }; private: - void _do_export(Print &print, FILE *file, ThumbnailsGeneratorCallback thumbnail_cb); + class GCodeOutputStream { + public: + GCodeOutputStream(FILE *f) : f(f) {} + ~GCodeOutputStream() { this->close(); } + + bool is_open() const { return f; } + bool is_error() const; + + void flush(); + void close(); + + // Write a string into a file. + void write(const std::string& what) { this->write(what.c_str()); } + void write(const char* what); + + // Write a string into a file. + // Add a newline, if the string does not end with a newline already. + // Used to export a custom G-code section processed by the PlaceholderParser. + void writeln(const std::string& what); + + // Formats and write into a file the given data. + void write_format(const char* format, ...); + + private: + FILE *f = nullptr; + }; + void _do_export(Print &print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb); static std::vector collect_layers_to_print(const PrintObject &object); static std::vector>> collect_layers_to_print(const Print &print); void process_layer( // Write into the output file. - FILE *file, + GCodeOutputStream &file, const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. const std::vector &layers, @@ -358,22 +384,10 @@ private: // Processor GCodeProcessor m_processor; - // Write a string into a file. - void _write(FILE* file, const std::string& what) { this->_write(file, what.c_str()); } - void _write(FILE* file, const char *what); - - // Write a string into a file. - // Add a newline, if the string does not end with a newline already. - // Used to export a custom G-code section processed by the PlaceholderParser. - void _writeln(FILE* file, const std::string& what); - - // Formats and write into a file the given data. - void _write_format(FILE* file, const char* format, ...); - std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1); - void print_machine_envelope(FILE *file, Print &print); - void _print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); - void _print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); + 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/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 0d71a19f5..60af399c0 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -21,6 +21,9 @@ #include +static const float DEFAULT_TOOLPATH_WIDTH = 0.4f; +static const float DEFAULT_TOOLPATH_HEIGHT = 0.2f; + static const float INCHES_TO_MM = 25.4f; static const float MMMIN_TO_MMSEC = 1.0f / 60.0f; static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2 @@ -344,20 +347,34 @@ void GCodeProcessor::TimeProcessor::reset() machines[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true; } -void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector& moves) +struct FilePtr { + FilePtr(FILE *f) : f(f) {} + ~FilePtr() { this->close(); } + void close() { + if (this->f) { + ::fclose(this->f); + this->f = nullptr; + } + } + FILE* f = nullptr; +}; + +void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector& moves, std::vector& lines_ends) { - boost::nowide::ifstream in(filename); - if (!in.good()) + FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") }; + if (in.f == nullptr) throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for reading.\n")); // temporary file to contain modified gcode std::string out_path = filename + ".postprocess"; - FILE* out = boost::nowide::fopen(out_path.c_str(), "wb"); - if (out == nullptr) + FilePtr out{ boost::nowide::fopen(out_path.c_str(), "wb") }; + if (out.f == nullptr) { throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for writing.\n")); + } auto time_in_minutes = [](float time_in_seconds) { - return int(::roundf(time_in_seconds / 60.0f)); + assert(time_in_seconds >= 0.f); + return int((time_in_seconds + 0.5f) / 60.0f); }; auto time_in_last_minute = [](float time_in_seconds) { @@ -389,7 +406,6 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st return std::string(line_M73); }; - GCodeReader parser; std::string gcode_line; size_t g1_lines_counter = 0; // keeps track of last exported pair @@ -408,11 +424,12 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st std::string export_line; // replace placeholder lines with the proper final value - auto process_placeholders = [&](const std::string& gcode_line) { + // gcode_line is in/out parameter, to reduce expensive memory allocation + auto process_placeholders = [&](std::string& gcode_line) { unsigned int extra_lines_count = 0; // remove trailing '\n' - std::string line = gcode_line.substr(0, gcode_line.length() - 1); + auto line = std::string_view(gcode_line).substr(0, gcode_line.length() - 1); std::string ret; if (line.length() > 1) { @@ -453,7 +470,10 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st } } - return std::tuple(!ret.empty(), ret.empty() ? gcode_line : ret, (extra_lines_count == 0) ? extra_lines_count : extra_lines_count - 1); + if (! ret.empty()) + // Not moving the move operator on purpose, so that the gcode_line allocation will grow and it will not be reallocated after handful of lines are processed. + gcode_line = ret; + return std::tuple(!ret.empty(), (extra_lines_count == 0) ? extra_lines_count : extra_lines_count - 1); }; // check for temporary lines @@ -476,11 +496,19 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st g1_times_cache_it.emplace_back(machine.g1_times_cache.begin()); // add lines M73 to exported gcode - auto process_line_G1 = [&]() { + auto process_line_G1 = [ + // Lambdas, mostly for string formatting, all with an empty capture block. + time_in_minutes, format_time_float, format_line_M73_main, format_line_M73_stop_int, format_line_M73_stop_float, time_in_last_minute, + &self = std::as_const(*this), + // Caches, to be modified + &g1_times_cache_it, &last_exported_main, &last_exported_stop, + // String output + &export_line] + (const size_t g1_lines_counter) { unsigned int exported_lines_count = 0; - if (export_remaining_time_enabled) { + if (self.export_remaining_time_enabled) { for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { - const TimeMachine& machine = machines[i]; + const TimeMachine& machine = self.machines[i]; if (machine.enabled) { // export pair // Skip all machine.g1_times_cache below g1_lines_counter. @@ -544,60 +572,81 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st }; // helper function to write to disk - auto write_string = [&](const std::string& str) { - fwrite((const void*)export_line.c_str(), 1, export_line.length(), out); - if (ferror(out)) { - in.close(); - fclose(out); + size_t out_file_pos = 0; + lines_ends.clear(); + auto write_string = [&export_line, &out, &out_path, &out_file_pos, &lines_ends](const std::string& str) { + fwrite((const void*)export_line.c_str(), 1, export_line.length(), out.f); + if (ferror(out.f)) { + out.close(); boost::nowide::remove(out_path.c_str()); throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nIs the disk full?\n")); } + for (size_t i = 0; i < export_line.size(); ++ i) + if (export_line[i] == '\n') + lines_ends.emplace_back(out_file_pos + i + 1); + out_file_pos += export_line.size(); export_line.clear(); }; unsigned int line_id = 0; std::vector> offsets; - while (std::getline(in, gcode_line)) { - if (!in.good()) { - fclose(out); - throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n")); - } + { + // Read the input stream 64kB at a time, extract lines and process them. + std::vector buffer(65536 * 10, 0); + // Line buffer. + assert(gcode_line.empty()); + for (;;) { + size_t cnt_read = ::fread(buffer.data(), 1, buffer.size(), in.f); + if (::ferror(in.f)) + throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n")); + bool eof = cnt_read == 0; + auto it = buffer.begin(); + auto it_bufend = buffer.begin() + cnt_read; + while (it != it_bufend || (eof && ! gcode_line.empty())) { + // Find end of line. + bool eol = false; + auto it_end = it; + for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end) ; + // End of line is indicated also if end of file was reached. + eol |= eof && it_end == it_bufend; + gcode_line.insert(gcode_line.end(), it, it_end); + if (eol) { + ++line_id; - ++line_id; - - gcode_line += "\n"; - // replace placeholder lines - auto [processed, result, lines_added_count] = process_placeholders(gcode_line); - if (processed && lines_added_count > 0) - offsets.push_back({ line_id, lines_added_count }); - gcode_line = result; - if (!processed) { - // remove temporary lines - if (is_temporary_decoration(gcode_line)) - continue; - - // add lines M73 where needed - parser.parse_line(gcode_line, - [&](GCodeReader& reader, const GCodeReader::GCodeLine& line) { - if (line.cmd_is("G1")) { - unsigned int extra_lines_count = process_line_G1(); - ++g1_lines_counter; + gcode_line += "\n"; + // replace placeholder lines + auto [processed, lines_added_count] = process_placeholders(gcode_line); + if (processed && lines_added_count > 0) + offsets.push_back({ line_id, lines_added_count }); + if (! processed && ! is_temporary_decoration(gcode_line) && GCodeReader::GCodeLine::cmd_is(gcode_line, "G1")) { + // remove temporary lines, add lines M73 where needed + unsigned int extra_lines_count = process_line_G1(g1_lines_counter ++); if (extra_lines_count > 0) offsets.push_back({ line_id, extra_lines_count }); } - }); - } - export_line += gcode_line; - if (export_line.length() > 65535) - write_string(export_line); + export_line += gcode_line; + if (export_line.length() > 65535) + write_string(export_line); + gcode_line.clear(); + } + // Skip EOL. + it = it_end; + if (it != it_bufend && *it == '\r') + ++ it; + if (it != it_bufend && *it == '\n') + ++ it; + } + if (eof) + break; + } } if (!export_line.empty()) write_string(export_line); - fclose(out); + out.close(); in.close(); // updates moves' gcode ids which have been modified by the insertion of the M73 lines @@ -698,7 +747,9 @@ void GCodeProcessor::Result::reset() { } #else void GCodeProcessor::Result::reset() { - moves = std::vector(); + + moves.clear(); + lines_ends.clear(); bed_shape = Pointfs(); settings_ids.reset(); extruders_count = 0; @@ -951,10 +1002,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config) } // replace missing values with default - std::string default_color = "#FF8000"; for (size_t i = 0; i < m_result.extruder_colors.size(); ++i) { if (m_result.extruder_colors[i].empty()) - m_result.extruder_colors[i] = default_color; + m_result.extruder_colors[i] = "#FF8000"; } m_extruder_colors.resize(m_result.extruder_colors.size()); @@ -1161,8 +1211,6 @@ void GCodeProcessor::reset() void GCodeProcessor::process_file(const std::string& filename, bool apply_postprocess, std::function cancel_callback) { - auto last_cancel_callback_time = std::chrono::high_resolution_clock::now(); - CNumericLocalesSetter locales_setter; #if ENABLE_GCODE_VIEWER_STATISTICS @@ -1174,7 +1222,7 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr if (m_producers_enabled) { m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { const std::string_view cmd = line.cmd(); - if (cmd.length() == 0) { + if (cmd.empty()) { const std::string_view comment = line.comment(); if (comment.length() > 1 && detect_producer(comment)) m_parser.quit_parsing(); @@ -1201,17 +1249,16 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr m_result.id = ++s_result_id; // 1st move must be a dummy move m_result.moves.emplace_back(MoveVertex()); - m_parser.parse_file(filename, [this, cancel_callback, &last_cancel_callback_time](GCodeReader& reader, const GCodeReader::GCodeLine& line) { - if (cancel_callback != nullptr) { - // call the cancel callback every 100 ms - auto curr_time = std::chrono::high_resolution_clock::now(); - if (std::chrono::duration_cast(curr_time - last_cancel_callback_time).count() > 100) { + size_t parse_line_callback_cntr = 10000; + m_parser.parse_file(filename, [this, cancel_callback, &parse_line_callback_cntr](GCodeReader& reader, const GCodeReader::GCodeLine& line) { + if (-- parse_line_callback_cntr == 0) { + // Don't call the cancel_callback() too often, do it every at every 10000'th line. + parse_line_callback_cntr = 10000; + if (cancel_callback) cancel_callback(); - last_cancel_callback_time = curr_time; - } } - process_gcode_line(line); - }); + this->process_gcode_line(line); + }); // update width/height of wipe moves for (MoveVertex& move : m_result.moves) { @@ -1236,7 +1283,7 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr // post-process to add M73 lines into the gcode if (apply_postprocess) - m_time_processor.post_process(filename, m_result.moves); + m_time_processor.post_process(filename, m_result.moves, m_result.lines_ends); #if ENABLE_GCODE_VIEWER_DATA_CHECKING std::cout << "\n"; @@ -1340,7 +1387,7 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename) if (pos != cmt.npos) { std::string data_str = cmt.substr(pos + 1); std::vector values_str; - boost::split(values_str, data_str, boost::is_any_of("|"), boost::token_compress_on); + boost::split(values_str, data_str, boost::is_any_of("|,"), boost::token_compress_on); for (const std::string& s : values_str) { out.emplace_back(static_cast(string_to_double_decimal_point(s))); } @@ -1364,10 +1411,16 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename) m_result.filament_densities.clear(); extract_floats(comment, "filamentDensities", m_result.filament_densities); } + else if (comment.find("extruderDiameter") != comment.npos) { + std::vector extruder_diameters; + extract_floats(comment, "extruderDiameter", extruder_diameters); + m_result.extruders_count = extruder_diameters.size(); + } } }); - m_result.extruders_count = std::max(1, std::min(m_result.filament_diameters.size(), m_result.filament_densities.size())); + if (m_result.extruders_count == 0) + m_result.extruders_count = std::max(1, std::min(m_result.filament_diameters.size(), m_result.filament_densities.size())); if (bed_size.is_defined()) { m_result.bed_shape = { @@ -1391,61 +1444,170 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) const std::string_view cmd = line.cmd(); if (cmd.length() > 1) { // process command lines - switch (::toupper(cmd[0])) + switch (cmd[0]) { + case 'g': case 'G': - { - switch (::atoi(&cmd[1])) - { - case 0: { process_G0(line); break; } // Move - case 1: { process_G1(line); break; } // Move - case 10: { process_G10(line); break; } // Retract - case 11: { process_G11(line); break; } // Unretract - case 20: { process_G20(line); break; } // Set Units to Inches - case 21: { process_G21(line); break; } // Set Units to Millimeters - case 22: { process_G22(line); break; } // Firmware controlled retract - case 23: { process_G23(line); break; } // Firmware controlled unretract - case 28: { process_G28(line); break; } // Move to origin - case 90: { process_G90(line); break; } // Set to Absolute Positioning - case 91: { process_G91(line); break; } // Set to Relative Positioning - case 92: { process_G92(line); break; } // Set Position - default: { break; } + switch (cmd.size()) { + case 2: + switch (cmd[1]) { + case '0': { process_G0(line); break; } // Move + case '1': { process_G1(line); break; } // Move + default: break; } break; + case 3: + switch (cmd[1]) { + case '1': + switch (cmd[2]) { + case '0': { process_G10(line); break; } // Retract + case '1': { process_G11(line); break; } // Unretract + default: break; + } + break; + case '2': + switch (cmd[2]) { + case '0': { process_G20(line); break; } // Set Units to Inches + case '1': { process_G21(line); break; } // Set Units to Millimeters + case '2': { process_G22(line); break; } // Firmware controlled retract + case '3': { process_G23(line); break; } // Firmware controlled unretract + case '8': { process_G28(line); break; } // Move to origin + default: break; + } + break; + case '9': + switch (cmd[2]) { + case '0': { process_G90(line); break; } // Set to Absolute Positioning + case '1': { process_G91(line); break; } // Set to Relative Positioning + case '2': { process_G92(line); break; } // Set Position + default: break; + } + break; + } + break; + default: + break; } + break; + case 'm': case 'M': - { - switch (::atoi(&cmd[1])) - { - case 1: { process_M1(line); break; } // Sleep or Conditional stop - case 82: { process_M82(line); break; } // Set extruder to absolute mode - case 83: { process_M83(line); break; } // Set extruder to relative mode - case 104: { process_M104(line); break; } // Set extruder temperature - case 106: { process_M106(line); break; } // Set fan speed - case 107: { process_M107(line); break; } // Disable fan - case 108: { process_M108(line); break; } // Set tool (Sailfish) - case 109: { process_M109(line); break; } // Set extruder temperature and wait - case 132: { process_M132(line); break; } // Recall stored home offsets - case 135: { process_M135(line); break; } // Set tool (MakerWare) - case 201: { process_M201(line); break; } // Set max printing acceleration - case 203: { process_M203(line); break; } // Set maximum feedrate - case 204: { process_M204(line); break; } // Set default acceleration - case 205: { process_M205(line); break; } // Advanced settings - case 221: { process_M221(line); break; } // Set extrude factor override percentage - case 401: { process_M401(line); break; } // Repetier: Store x, y and z position - case 402: { process_M402(line); break; } // Repetier: Go to stored position - case 566: { process_M566(line); break; } // Set allowable instantaneous speed change - case 702: { process_M702(line); break; } // Unload the current filament into the MK3 MMU2 unit at the end of print. - default: { break; } + switch (cmd.size()) { + case 2: + switch (cmd[1]) { + case '1': { process_M1(line); break; } // Sleep or Conditional stop + default: break; } break; - } - case 'T': - { - process_T(line); // Select Tool + case 3: + switch (cmd[1]) { + case '8': + switch (cmd[2]) { + case '2': { process_M82(line); break; } // Set extruder to absolute mode + case '3': { process_M83(line); break; } // Set extruder to relative mode + default: break; + } + break; + default: + break; + } + break; + case 4: + switch (cmd[1]) { + case '1': + switch (cmd[2]) { + case '0': + switch (cmd[3]) { + case '4': { process_M104(line); break; } // Set extruder temperature + case '6': { process_M106(line); break; } // Set fan speed + case '7': { process_M107(line); break; } // Disable fan + case '8': { process_M108(line); break; } // Set tool (Sailfish) + case '9': { process_M109(line); break; } // Set extruder temperature and wait + default: break; + } + break; + case '3': + switch (cmd[3]) { + case '2': { process_M132(line); break; } // Recall stored home offsets + case '5': { process_M135(line); break; } // Set tool (MakerWare) + default: break; + } + break; + default: + break; + } + break; + case '2': + switch (cmd[2]) { + case '0': + switch (cmd[3]) { + case '1': { process_M201(line); break; } // Set max printing acceleration + case '3': { process_M203(line); break; } // Set maximum feedrate + case '4': { process_M204(line); break; } // Set default acceleration + case '5': { process_M205(line); break; } // Advanced settings + default: break; + } + break; + case '2': + switch (cmd[3]) { + case '1': { process_M221(line); break; } // Set extrude factor override percentage + default: break; + } + break; + default: + break; + } + break; + case '4': + switch (cmd[2]) { + case '0': + switch (cmd[3]) { + case '1': { process_M401(line); break; } // Repetier: Store x, y and z position + case '2': { process_M402(line); break; } // Repetier: Go to stored position + default: break; + } + break; + default: + break; + } + break; + case '5': + switch (cmd[2]) { + case '6': + switch (cmd[3]) { + case '6': { process_M566(line); break; } // Set allowable instantaneous speed change + default: break; + } + break; + default: + break; + } + break; + case '7': + switch (cmd[2]) { + case '0': + switch (cmd[3]) { + case '2': { process_M702(line); break; } // Unload the current filament into the MK3 MMU2 unit at the end of print. + default: break; + } + break; + default: + break; + } + break; + default: + break; + } + break; + default: break; } - default: { break; } + break; + case 't': + case 'T': + process_T(line); // Select Tool + break; + default: + break; } } else { @@ -2210,9 +2372,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) return; EMoveType type = move_type(delta_pos); - if (type == EMoveType::Extrude && m_end_position[Z] == 0.0f) - type = EMoveType::Travel; - if (type == EMoveType::Extrude) { float delta_xyz = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z])); float volume_extruded_filament = area_filament_cross_section * delta_pos[E]; @@ -2236,6 +2395,12 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) } } + if (m_height == 0.0f) + m_height = DEFAULT_TOOLPATH_HEIGHT; + + if (m_end_position[Z] == 0.0f) + m_end_position[Z] = m_height; + #if ENABLE_GCODE_VIEWER_DATA_CHECKING m_height_compare.update(m_height, m_extrusion_role); #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING @@ -2252,17 +2417,17 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) // cross section: rectangle + 2 semicircles m_width = delta_pos[E] * static_cast(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast(1.0 - 0.25 * M_PI) * m_height; + if (m_width == 0.0f) + m_width = DEFAULT_TOOLPATH_WIDTH; + // clamp width to avoid artifacts which may arise from wrong values of m_height - m_width = std::min(m_width, std::max(1.0f, 4.0f * m_height)); + m_width = std::min(m_width, std::max(2.0f, 4.0f * m_height)); #if ENABLE_GCODE_VIEWER_DATA_CHECKING m_width_compare.update(m_width, m_extrusion_role); #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING } - if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f)) - type = EMoveType::Travel; - // time estimate section auto move_length = [](const AxisCoords& delta_pos) { float sq_xyz_length = sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]); @@ -2916,7 +3081,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type) m_line_id + 1 : ((type == EMoveType::Seam) ? m_last_line_id : m_line_id); - MoveVertex vertex = { + m_result.moves.push_back({ m_last_line_id, type, m_extrusion_role, @@ -2931,8 +3096,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type) m_fan_speed, m_extruder_temps[m_extruder_id], static_cast(m_result.moves.size()) - }; - m_result.moves.emplace_back(vertex); + }); // stores stop time placeholders for later use if (type == EMoveType::Color_change || type == EMoveType::Pause_Print) { diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 4fcdd8df3..bb9310888 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -306,7 +306,7 @@ namespace Slic3r { // post process the file with the given filename to add remaining time lines M73 // and updates moves' gcode ids accordingly - void post_process(const std::string& filename, std::vector& moves); + void post_process(const std::string& filename, std::vector& moves, std::vector& lines_ends); }; struct UsedFilaments // filaments per ColorChange @@ -350,6 +350,8 @@ namespace Slic3r { std::string filename; unsigned int id; std::vector moves; + // Positions of ends of lines of the final G-code this->filename after TimeProcessor::post_process() finalizes the G-code. + std::vector lines_ends; Pointfs bed_shape; SettingsIds settings_ids; size_t extruders_count; diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp index 7dc3c9a8a..4c4bebee4 100644 --- a/src/libslic3r/GCodeReader.cpp +++ b/src/libslic3r/GCodeReader.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -32,7 +33,7 @@ void GCodeReader::apply_config(const DynamicPrintConfig &config) m_extrusion_axis = get_extrusion_axis_char(m_config); } -const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline, std::pair &command) +const char* GCodeReader::parse_line_internal(const char *ptr, const char *end, GCodeLine &gline, std::pair &command) { PROFILE_FUNC(); @@ -70,9 +71,16 @@ const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline, } if (axis != NUM_AXES_WITH_UNKNOWN) { // Try to parse the numeric value. +#ifdef WIN32 + double v; + auto [pend, ec] = std::from_chars(++ c, end, v); + if (pend != c && is_end_of_word(*pend)) { +#else + // The older version of GCC and Clang support std::from_chars just for integers, so strtod we used it instead. char *pend = nullptr; double v = strtod(++ c, &pend); if (pend != nullptr && is_end_of_word(*pend)) { +#endif // The axis value has been parsed correctly. if (axis != UNKNOWN_AXIS) gline.m_axis[int(axis)] = float(v); @@ -125,13 +133,42 @@ void GCodeReader::update_coordinates(GCodeLine &gline, std::pair buffer(65536 * 10, 0); std::string line; m_parsing = true; - while (m_parsing && std::getline(f, line)) - this->parse_line(line, callback); + GCodeLine gline; + while (m_parsing && ! f.eof()) { + f.read(buffer.data(), buffer.size()); + if (! f.eof() && ! f.good()) + // Reading the input file failed. + return false; + auto it = buffer.begin(); + auto it_bufend = buffer.begin() + f.gcount(); + while (it != it_bufend) { + bool eol = false; + auto it_end = it; + for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end) ; + eol |= f.eof() && it_end == it_bufend; + if (eol) { + gline.reset(); + if (line.empty()) + this->parse_line(&(*it), &(*it_end), gline, callback); + else { + line.insert(line.end(), it, it_end); + this->parse_line(line.c_str(), line.c_str() + line.size(), gline, callback); + line.clear(); + } + } else + line.insert(line.end(), it, it_end); + // Skip all the empty lines. + for (it = it_end; it != it_bufend && (*it == '\r' || *it == '\n'); ++ it) ; + } + } + return true; } bool GCodeReader::GCodeLine::has(char axis) const diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp index 6b58608e6..8c820b95d 100644 --- a/src/libslic3r/GCodeReader.hpp +++ b/src/libslic3r/GCodeReader.hpp @@ -44,11 +44,7 @@ public: float y = this->has(Y) ? (this->y() - reader.y()) : 0; return sqrt(x*x + y*y); } - bool cmd_is(const char *cmd_test) const { - const char *cmd = GCodeReader::skip_whitespaces(m_raw.c_str()); - size_t len = strlen(cmd_test); - return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]); - } + bool cmd_is(const char *cmd_test) const { return cmd_is(m_raw, cmd_test); } bool extruding(const GCodeReader &reader) const { return this->cmd_is("G1") && this->dist_E(reader) > 0; } bool retracting(const GCodeReader &reader) const { return this->cmd_is("G1") && this->dist_E(reader) < 0; } bool travel() const { return this->cmd_is("G1") && ! this->has(E); } @@ -66,6 +62,12 @@ public: float e() const { return m_axis[E]; } float f() const { return m_axis[F]; } + static bool cmd_is(const std::string &gcode_line, const char *cmd_test) { + const char *cmd = GCodeReader::skip_whitespaces(gcode_line.c_str()); + size_t len = strlen(cmd_test); + return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]); + } + private: std::string m_raw; float m_axis[NUM_AXES]; @@ -83,11 +85,12 @@ public: void parse_buffer(const std::string &buffer, Callback callback) { const char *ptr = buffer.c_str(); + const char *end = ptr + buffer.size(); GCodeLine gline; m_parsing = true; while (m_parsing && *ptr != 0) { gline.reset(); - ptr = this->parse_line(ptr, gline, callback); + ptr = this->parse_line(ptr, end, gline, callback); } } @@ -95,20 +98,21 @@ public: { this->parse_buffer(buffer, [](GCodeReader&, const GCodeReader::GCodeLine&){}); } template - const char* parse_line(const char *ptr, GCodeLine &gline, Callback &callback) + const char* parse_line(const char *ptr, const char *end, GCodeLine &gline, Callback &callback) { std::pair cmd; - const char *end = parse_line_internal(ptr, gline, cmd); + const char *line_end = parse_line_internal(ptr, end, gline, cmd); callback(*this, gline); update_coordinates(gline, cmd); - return end; + return line_end; } template void parse_line(const std::string &line, Callback callback) - { GCodeLine gline; this->parse_line(line.c_str(), gline, callback); } + { GCodeLine gline; this->parse_line(line.c_str(), line.c_str() + line.size(), gline, callback); } - void parse_file(const std::string &file, callback_t callback); + // Returns false if reading the file failed. + bool parse_file(const std::string &file, callback_t callback); void quit_parsing() { m_parsing = false; } float& x() { return m_position[X]; } @@ -127,7 +131,7 @@ public: // void set_extrusion_axis(char axis) { m_extrusion_axis = axis; } private: - const char* parse_line_internal(const char *ptr, GCodeLine &gline, std::pair &command); + const char* parse_line_internal(const char *ptr, const char *end, GCodeLine &gline, std::pair &command); void update_coordinates(GCodeLine &gline, std::pair &command); static bool is_whitespace(char c) { return c == ' ' || c == '\t'; } diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 24549fd89..c97180982 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -1,17 +1,21 @@ #include "GCodeWriter.hpp" #include "CustomGCode.hpp" #include +#include #include #include #include #include +#define XYZF_EXPORT_DIGITS 3 +#define E_EXPORT_DIGITS 5 + #define FLAVOR_IS(val) this->config.gcode_flavor == val #define FLAVOR_IS_NOT(val) this->config.gcode_flavor != val #define COMMENT(comment) if (this->config.gcode_comments && !comment.empty()) gcode << " ; " << comment; #define PRECISION(val, precision) std::fixed << std::setprecision(precision) << (val) -#define XYZF_NUM(val) PRECISION(val, 3) -#define E_NUM(val) PRECISION(val, 5) +#define XYZF_NUM(val) PRECISION(val, XYZF_EXPORT_DIGITS) +#define E_NUM(val) PRECISION(val, E_EXPORT_DIGITS) namespace Slic3r { @@ -288,16 +292,89 @@ std::string GCodeWriter::toolchange(unsigned int extruder_id) return gcode.str(); } +class G1Writer { +private: + static constexpr const size_t buflen = 256; + char buf[buflen]; + char *buf_end; + std::to_chars_result ptr_err; + +public: + G1Writer() { + this->buf[0] = 'G'; + this->buf[1] = '1'; + this->buf_end = this->buf + buflen; + this->ptr_err.ptr = this->buf + 2; + } + + void emit_axis(const char axis, const double v, size_t digits) { + *ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = axis; +#ifdef WIN32 + this->ptr_err = std::to_chars(this->ptr_err.ptr, this->buf_end, v, std::chars_format::fixed, digits); +#else + int buf_capacity = int(this->buf_end - this->ptr_err.ptr); + int ret = snprintf(this->ptr_err.ptr, buf_capacity, "%.*lf", int(digits), v); + if (ret <= 0 || ret > buf_capacity) + ptr_err.ec = std::errc::value_too_large; + else + this->ptr_err.ptr = this->ptr_err.ptr + ret; +#endif + } + + void emit_xy(const Vec2d &point) { + this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS); + this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS); + } + + void emit_xyz(const Vec3d &point) { + this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS); + this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS); + this->emit_z(point.z()); + } + + void emit_z(const double z) { + this->emit_axis('Z', z, XYZF_EXPORT_DIGITS); + } + + void emit_e(const std::string &axis, double v) { + if (! axis.empty()) { + // not gcfNoExtrusion + this->emit_axis(axis[0], v, E_EXPORT_DIGITS); + } + } + + void emit_f(double speed) { + this->emit_axis('F', speed, XYZF_EXPORT_DIGITS); + } + + void emit_string(const std::string &s) { + strncpy(ptr_err.ptr, s.c_str(), s.size()); + ptr_err.ptr += s.size(); + } + + void emit_comment(bool allow_comments, const std::string &comment) { + if (allow_comments && ! comment.empty()) { + *ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = ';'; *ptr_err.ptr ++ = ' '; + this->emit_string(comment); + } + } + + std::string string() { + *ptr_err.ptr ++ = '\n'; + return std::string(this->buf, ptr_err.ptr - buf); + } +}; + std::string GCodeWriter::set_speed(double F, const std::string &comment, const std::string &cooling_marker) const { assert(F > 0.); assert(F < 100000.); - std::ostringstream gcode; - gcode << "G1 F" << XYZF_NUM(F); - COMMENT(comment); - gcode << cooling_marker; - gcode << "\n"; - return gcode.str(); + + G1Writer w; + w.emit_f(F); + w.emit_comment(this->config.gcode_comments, comment); + w.emit_string(cooling_marker); + return w.string(); } std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment) @@ -305,13 +382,11 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &com m_pos(0) = point(0); m_pos(1) = point(1); - std::ostringstream gcode; - gcode << "G1 X" << XYZF_NUM(point(0)) - << " Y" << XYZF_NUM(point(1)) - << " F" << XYZF_NUM(this->config.travel_speed.value * 60.0); - COMMENT(comment); - gcode << "\n"; - return gcode.str(); + G1Writer w; + w.emit_xy(point); + w.emit_f(this->config.travel_speed.value * 60.0); + w.emit_comment(this->config.gcode_comments, comment); + return w.string(); } std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &comment) @@ -340,14 +415,11 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co m_lifted = 0; m_pos = point; - std::ostringstream gcode; - gcode << "G1 X" << XYZF_NUM(point(0)) - << " Y" << XYZF_NUM(point(1)) - << " Z" << XYZF_NUM(point(2)) - << " F" << XYZF_NUM(this->config.travel_speed.value * 60.0); - COMMENT(comment); - gcode << "\n"; - return gcode.str(); + G1Writer w; + w.emit_xyz(point); + w.emit_f(this->config.travel_speed.value * 60.0); + w.emit_comment(this->config.gcode_comments, comment); + return w.string(); } std::string GCodeWriter::travel_to_z(double z, const std::string &comment) @@ -377,12 +449,11 @@ std::string GCodeWriter::_travel_to_z(double z, const std::string &comment) if (speed == 0.) speed = this->config.travel_speed.value; - std::ostringstream gcode; - gcode << "G1 Z" << XYZF_NUM(z) - << " F" << XYZF_NUM(speed * 60.0); - COMMENT(comment); - gcode << "\n"; - return gcode.str(); + G1Writer w; + w.emit_z(z); + w.emit_f(speed * 60.0); + w.emit_comment(this->config.gcode_comments, comment); + return w.string(); } bool GCodeWriter::will_move_z(double z) const @@ -402,16 +473,12 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std: m_pos(0) = point(0); m_pos(1) = point(1); m_extruder->extrude(dE); - - std::ostringstream gcode; - gcode << "G1 X" << XYZF_NUM(point(0)) - << " Y" << XYZF_NUM(point(1)); - if (! m_extrusion_axis.empty()) - // not gcfNoExtrusion - gcode << " " << m_extrusion_axis << E_NUM(m_extruder->E()); - COMMENT(comment); - gcode << "\n"; - return gcode.str(); + + G1Writer w; + w.emit_xy(point); + w.emit_e(m_extrusion_axis, m_extruder->E()); + w.emit_comment(this->config.gcode_comments, comment); + return w.string(); } std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment) @@ -420,16 +487,11 @@ std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std m_lifted = 0; m_extruder->extrude(dE); - std::ostringstream gcode; - gcode << "G1 X" << XYZF_NUM(point(0)) - << " Y" << XYZF_NUM(point(1)) - << " Z" << XYZF_NUM(point(2)); - if (! m_extrusion_axis.empty()) - // not gcfNoExtrusion - gcode << " " << m_extrusion_axis << E_NUM(m_extruder->E()); - COMMENT(comment); - gcode << "\n"; - return gcode.str(); + G1Writer w; + w.emit_xyz(point); + w.emit_e(m_extrusion_axis, m_extruder->E()); + w.emit_comment(this->config.gcode_comments, comment); + return w.string(); } std::string GCodeWriter::retract(bool before_wipe) diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index a6be64299..68c40fc20 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -120,7 +120,8 @@ void Polyline::simplify(double tolerance) this->points = MultiPoint::_douglas_peucker(this->points, tolerance); } -/* This method simplifies all *lines* contained in the supplied area */ +#if 0 +// This method simplifies all *lines* contained in the supplied area template void Polyline::simplify_by_visibility(const T &area) { @@ -141,6 +142,7 @@ void Polyline::simplify_by_visibility(const T &area) } template void Polyline::simplify_by_visibility(const ExPolygon &area); template void Polyline::simplify_by_visibility(const ExPolygonCollection &area); +#endif void Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const { diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 51dcf9d36..758fc38cd 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -72,7 +72,7 @@ public: void extend_start(double distance); Points equally_spaced_points(double distance) const; void simplify(double tolerance); - template void simplify_by_visibility(const T &area); +// template void simplify_by_visibility(const T &area); void split_at(const Point &point, Polyline* p1, Polyline* p2) const; bool is_straight() const; bool is_closed() const { return this->points.front() == this->points.back(); } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 526b7b874..d9a4f2670 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2293,7 +2293,7 @@ void PrintObject::project_and_append_custom_facets( const indexed_triangle_set custom_facets = seam ? mv->seam_facets.get_facets_strict(*mv, type) : mv->supported_facets.get_facets_strict(*mv, type); - if (! custom_facets.indices.empty()) + if (! custom_facets.indices.empty()) { if (seam) project_triangles_to_slabs(this->layers(), custom_facets, (this->trafo_centered() * mv->get_matrix()).cast(), @@ -2301,8 +2301,16 @@ void PrintObject::project_and_append_custom_facets( else { std::vector projected; slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, [](){}); - append(out, std::move(projected)); + // Merge these projections with the output, layer by layer. + assert(! projected.empty()); + assert(out.empty() || out.size() == projected.size()); + if (out.empty()) + out = std::move(projected); + else + for (size_t i = 0; i < out.size(); ++ i) + append(out[i], std::move(projected[i])); } + } } } diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index c330f34b2..c5dbdac9c 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -984,16 +984,11 @@ std::string log_memory_info(bool ignore_loglevel) } PROCESS_MEMORY_COUNTERS_EX, *PPROCESS_MEMORY_COUNTERS_EX; #endif /* PROCESS_MEMORY_COUNTERS_EX */ - - HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ::GetCurrentProcessId()); - if (hProcess != nullptr) { - PROCESS_MEMORY_COUNTERS_EX pmc; - if (GetProcessMemoryInfo(hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) - out = " WorkingSet: " + format_memsize_MB(pmc.WorkingSetSize) + "; PrivateBytes: " + format_memsize_MB(pmc.PrivateUsage) + "; Pagefile(peak): " + format_memsize_MB(pmc.PagefileUsage) + "(" + format_memsize_MB(pmc.PeakPagefileUsage) + ")"; - else - out += " Used memory: N/A"; - CloseHandle(hProcess); - } + PROCESS_MEMORY_COUNTERS_EX pmc; + if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) + out = " WorkingSet: " + format_memsize_MB(pmc.WorkingSetSize) + "; PrivateBytes: " + format_memsize_MB(pmc.PrivateUsage) + "; Pagefile(peak): " + format_memsize_MB(pmc.PagefileUsage) + "(" + format_memsize_MB(pmc.PeakPagefileUsage) + ")"; + else + out += " Used memory: N/A"; #elif defined(__linux__) or defined(__APPLE__) // Get current memory usage. #ifdef __APPLE__ diff --git a/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.GCodeViewer.desktop b/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.GCodeViewer.desktop deleted file mode 100644 index 317785473..000000000 --- a/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.GCodeViewer.desktop +++ /dev/null @@ -1,10 +0,0 @@ -[Desktop Entry] -Name=Prusa GCode viewer -Name[de]=Prusa GCode Vorschau -Exec=prusa-slicer --gcodeviewer %F -Icon=com.prusa3d.PrusaSlicer.GCodeViewer -Terminal=false -Type=Application -MimeType=text/x.gcode; -Categories=Graphics;3DGraphics; -Keywords=3D;Printing;Slicer; diff --git a/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.desktop b/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.desktop deleted file mode 100755 index 1ea3e157e..000000000 --- a/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.desktop +++ /dev/null @@ -1,20 +0,0 @@ -[Desktop Entry] -Name=PrusaSlicer -Name[de]=PrusaSlicer -GenericName=3D Printing Software -Icon=com.prusa3d.PrusaSlicer -Exec=prusa-slicer %F -Terminal=false -Type=Application -MimeType=model/stl;model/x-wavefront-obj;model/3mf;model/x-geomview-off;application/x-amf; -Categories=Graphics;3DGraphics;Engineering; -Keywords=3D;Printing;Slicer;slice;3D;printer;convert;gcode;stl;obj;amf;SLA -StartupNotify=false -StartupWMClass=prusa-slicer -Actions=GCodeViewer; - -[Desktop Action GCodeViewer] -Exec=prusa-slicer --gcodeviewer %F -Name=G-Code Viewer -Name[de]=G-Code Vorschau -Icon=com.prusa3d.PrusaSlicer.GCodeViewer diff --git a/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml b/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml deleted file mode 100755 index c37f43d69..000000000 --- a/src/platform/unix/flatpak/com.prusa3d.PrusaSlicer.metainfo.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - com.prusa3d.PrusaSlicer - com.prusa3d.PrusaSlicer.desktop - - prusa-slicer.desktop - - PrusaSlicer - Powerful 3D printing slicer optimized for Prusa printers - 0BSD - AGPL-3.0-only - -

- PrusaSlicer takes 3D models (STL, OBJ, AMF) and converts them into G-code - instructions for FFF printers or PNG layers for mSLA 3D printers. It's - compatible with any modern printer based on the RepRap toolchain, including all - those based on the Marlin, Prusa, Sprinter and Repetier firmware. It also works - with Mach3, LinuxCNC and Machinekit controllers. -

-

- PrusaSlicer is based on Slic3r by Alessandro Ranelucci and the RepRap community. -

-

- What are some of PrusaSlicer's main features? -

-
    -
  • multi-platform (Linux/Mac/Win) and packaged as standalone-app with no dependencies required
  • -
  • complete command-line interface to use it with no GUI
  • -
  • multi-material (multiple extruders) object printing
  • -
  • multiple G-code flavors supported (RepRap, Makerbot, Mach3, Machinekit etc.)
  • -
  • ability to plate multiple objects having distinct print settings
  • -
  • multithread processing
  • -
  • STL auto-repair (tolerance for broken models)
  • -
  • wide automated unit testing
  • -
-
- https://www.prusa3d.com/prusaslicer/ - https://help.prusa3d.com - https://github.com/prusa3d/PrusaSlicer/issues - - - https://user-images.githubusercontent.com/590307/78981854-24d07580-7b21-11ea-9441-77923534a659.png - - - https://user-images.githubusercontent.com/590307/78981860-2863fc80-7b21-11ea-8c2d-8ff79ced2578.png - - - https://user-images.githubusercontent.com/590307/78981862-28fc9300-7b21-11ea-9b0d-d03e16b709d3.png - - - - - - -

This is the first alpha release of PrusaSlicer 2.4.0, introducing Multi-material painting tool, improved FDM supports and raft, - Windows dark mode, Fuzzy Skin, per object Brim configuration, Negative volumes, automatic Color Print for signs, Shape Gallery, - "Tip of the day" notifications, Model simplification, support for Marlin 2 acceleration control, support of 3DLabPrint airplane models, - new 3rd party printer profiles and many more new features, improvements and bugfixes.

-
-
- - -

PrusaSlicer 2.3.3 is a patch release following PrusaSlicer 2.3.2 release, fixing an unfortunate bug in handling FDM multi-material - project and configuration files #6711.

-
-
- - -

This is a final release of PrusaSlicer 2.3.2, following 2.3.2-beta and 2.3.2-rc. - For the new features in the 2.3.2 series, please read the change logs of the beta and release candidate. - The final release is functionally equal to the release candidate, with a single additional improvement: - Before installing profile updates, a configuration snapshot is taken. In rare circumstances when the current configuration is not consistent, - taking a configuration snapshot fails. PrusaSlicer 2.3.2 newly informs about the issue and offers either to install the configuration updates - even if taking the configuration snapshot failed or to abort update of the profiles.

-
-
- - -

This a final release of PrusaSlicer 2.3.1, introducing native builds for the new Apple Silicon MacBooks, Chrome OS support, performance improvements - in G-code rendering, security fixes and new 3rd party printer profiles. For full changelog on PrusaSlicer 2.3.1 series, please check the PrusaSlicer - 2.3.1-rc change log.

-
-
- - -

This a final release of PrusaSlicer 2.3.0, introducing paint-on supports, ironing, monotonic infill, seam painting, adaptive and support cubic infill, - print time per feature analysis, standalone G-code viewer application, better auto-arrange with a customizable gap and rotation, - search function for the settings, reworked "Avoid crossing perimeters" function, physical printers (network settings), - many new 3rd party printer profiles and much, much more.

-
-
- - -

Development/Unstable Snapshot

-
-
- - -

This is final release of PrusaSlicer 2.2.0 introducing SLA hollowing and hole drilling, support for 3rd party printer vendors, 3Dconnexion support, - automatic variable layer height, macOS dark mode support, greatly improved ColorPrint feature and much, much more. - Several bugs found in the previous release candidate are fixed in this final release. See the respective change logs of the previous releases for all the - new features, improvements and bugfixes in the 2.2.0 series.

-
-
-
-
diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index eaf75ba5b..5b7218c87 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -163,12 +163,16 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c } std::string texture_filename = custom_texture.empty() ? texture : custom_texture; - if (!check_texture(texture_filename)) + if (! texture_filename.empty() && ! check_texture(texture_filename)) { + BOOST_LOG_TRIVIAL(error) << "Unable to load bed texture: " << texture_filename; texture_filename.clear(); + } std::string model_filename = custom_model.empty() ? model : custom_model; - if (!check_model(model_filename)) + if (! model_filename.empty() && ! check_model(model_filename)) { + BOOST_LOG_TRIVIAL(error) << "Unable to load bed model: " << model_filename; model_filename.clear(); + } if (m_shape == shape && m_type == type && m_texture_filename == texture_filename && m_model_filename == model_filename) // No change, no need to update the UI. diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 8a58e5ec9..b4de4a509 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -574,11 +574,8 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn // Some FFF status was invalidated, and the G-code was not exported yet. // Let the G-code preview UI know that the final G-code preview is not valid. // In addition, this early memory deallocation reduces memory footprint. - if (m_gcode_result != nullptr) { - //FIXME calling platter from here is not a staple of a good architecture. - GUI::wxGetApp().plater()->stop_mapping_gcode_window(); + if (m_gcode_result != nullptr) m_gcode_result->reset(); - } } return invalidated; } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 8bf5ba5d8..7e8e587a6 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -70,18 +70,9 @@ static std::vector> decode_colors(const std::vector &lines_ends) { - if (m_filename.empty()) - return; - + assert(! m_file.is_open()); if (m_file.is_open()) return; - try - { - // generate mapping for accessing data in file by line number - boost::nowide::ifstream f(m_filename); - - f.seekg(0, f.end); - uint64_t file_length = static_cast(f.tellg()); - f.seekg(0, f.beg); - - std::string line; - uint64_t offset = 0; - while (std::getline(f, line)) { - uint64_t line_length = static_cast(line.length()); - m_lines_map.push_back({ offset, line_length }); - offset += static_cast(line_length) + 1; - } - - if (offset != file_length) { - // if the final offset does not match with file length, lines are terminated with CR+LF - // so update all offsets accordingly - for (size_t i = 0; i < m_lines_map.size(); ++i) { - m_lines_map[i].first += static_cast(i); - } - } - } - catch (...) - { - BOOST_LOG_TRIVIAL(error) << "Unable to load data from " << m_filename << ". Cannot show G-code window."; - reset(); - return; - } + m_filename = filename; + m_lines_ends = std::move(lines_ends); m_selected_line_id = 0; m_last_lines_size = 0; @@ -354,7 +314,9 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u ret.reserve(end_id - start_id + 1); for (uint64_t id = start_id; id <= end_id; ++id) { // read line from file - std::string gline(m_file.data() + m_lines_map[id - 1].first, m_lines_map[id - 1].second); + const size_t start = id == 1 ? 0 : m_lines_ends[id - 2]; + const size_t len = m_lines_ends[id - 1] - start; + std::string gline(m_file.data() + start, len); std::string command; std::string parameters; @@ -388,7 +350,7 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u static const ImVec4 PARAMETERS_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f }; static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f }; - if (!m_visible || m_filename.empty() || m_lines_map.empty() || curr_line_id == 0) + if (!m_visible || m_filename.empty() || m_lines_ends.empty() || curr_line_id == 0) return; // window height @@ -406,8 +368,8 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u const uint64_t half_lines_count = lines_count / 2; uint64_t start_id = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 0; uint64_t end_id = start_id + lines_count - 1; - if (end_id >= static_cast(m_lines_map.size())) { - end_id = static_cast(m_lines_map.size()) - 1; + if (end_id >= static_cast(m_lines_ends.size())) { + end_id = static_cast(m_lines_ends.size()) - 1; start_id = end_id - lines_count + 1; } @@ -512,7 +474,7 @@ void GCodeViewer::SequentialView::render(float legend_height) const } const std::vector GCodeViewer::Extrusion_Role_Colors {{ - { 0.75f, 0.75f, 0.75f, 1.0f }, // erNone + { 0.90f, 0.70f, 0.70f, 1.0f }, // erNone { 1.00f, 0.90f, 0.30f, 1.0f }, // erPerimeter { 1.00f, 0.49f, 0.22f, 1.0f }, // erExternalPerimeter { 0.12f, 0.12f, 1.00f, 1.0f }, // erOverhangPerimeter @@ -615,8 +577,7 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& // release gpu memory, if used reset(); - m_sequential_view.gcode_window.set_filename(gcode_result.filename); - m_sequential_view.gcode_window.load_gcode(); + m_sequential_view.gcode_window.load_gcode(gcode_result.filename, gcode_result.lines_ends); #if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER if (wxGetApp().is_gcode_viewer()) @@ -704,8 +665,8 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: // update tool colors m_tool_colors = decode_colors(str_tool_colors); - // ensure at least one (default) color is defined - if (m_tool_colors.empty()) + // ensure there are enough colors defined + while (m_tool_colors.size() < std::max(size_t(1), gcode_result.extruders_count)) m_tool_colors.push_back(decode_color("#FF8000")); // update ranges for coloring / legend @@ -721,11 +682,11 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std: { case EMoveType::Extrude: { - m_extrusions.ranges.height.update_from(round_to_nearest(curr.height, 2)); - m_extrusions.ranges.width.update_from(round_to_nearest(curr.width, 2)); + m_extrusions.ranges.height.update_from(round_to_nearest_percent(curr.height)); + m_extrusions.ranges.width.update_from(round_to_nearest_percent(curr.width)); m_extrusions.ranges.fan_speed.update_from(curr.fan_speed); m_extrusions.ranges.temperature.update_from(curr.temperature); - m_extrusions.ranges.volumetric_rate.update_from(round_to_nearest(curr.volumetric_rate(), 2)); + m_extrusions.ranges.volumetric_rate.update_from(round_to_nearest_percent(curr.volumetric_rate())); [[fallthrough]]; } case EMoveType::Travel: @@ -1175,16 +1136,6 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const fclose(fp); } -void GCodeViewer::start_mapping_gcode_window() -{ - m_sequential_view.gcode_window.load_gcode(); -} - -void GCodeViewer::stop_mapping_gcode_window() -{ - m_sequential_view.gcode_window.stop_mapping_file(); -} - void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) { // max index buffer size, in bytes @@ -2563,7 +2514,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool buffer.model.instances.render_ranges.reset(); - if (!buffer.visible) + if (!buffer.visible || buffer.model.instances.s_ids.empty()) continue; buffer.model.instances.render_ranges.ranges.push_back({ 0, 0, 0, buffer.model.color }); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 86a7dcf85..3509bfbe5 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -695,18 +695,17 @@ public: std::string m_filename; boost::iostreams::mapped_file_source m_file; // map for accessing data in file by line number - std::vector> m_lines_map; + std::vector m_lines_ends; // current visible lines std::vector m_lines; public: GCodeWindow() = default; ~GCodeWindow() { stop_mapping_file(); } - void set_filename(const std::string& filename) { m_filename = filename; } - void load_gcode(); + void load_gcode(const std::string& filename, const std::vector &lines_ends); void reset() { stop_mapping_file(); - m_lines_map.clear(); + m_lines_ends.clear(); m_lines.clear(); m_filename.clear(); } @@ -833,8 +832,6 @@ public: void export_toolpaths_to_obj(const char* filename) const; - void start_mapping_gcode_window(); - void stop_mapping_gcode_window(); void toggle_gcode_window_visibility() { m_sequential_view.gcode_window.toggle_visibility(); } #if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e28189220..f50c7fe25 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1114,16 +1114,6 @@ int GLCanvas3D::check_volumes_outside_state() const return (int)state; } -void GLCanvas3D::start_mapping_gcode_window() -{ - m_gcode_viewer.start_mapping_gcode_window(); -} - -void GLCanvas3D::stop_mapping_gcode_window() -{ - m_gcode_viewer.stop_mapping_gcode_window(); -} - void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx) { m_render_sla_auxiliaries = visible; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 3750e3a3e..799fa11c7 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -621,9 +621,6 @@ public: const GCodeViewer::SequentialView& get_gcode_sequential_view() const { return m_gcode_viewer.get_sequential_view(); } void update_gcode_sequential_view_current(unsigned int first, unsigned int last) { m_gcode_viewer.update_sequential_view_current(first, last); } - void start_mapping_gcode_window(); - void stop_mapping_gcode_window(); - void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); void update_instance_printable_state_for_object(size_t obj_idx); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 096836f23..bf174cbf1 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1044,13 +1044,8 @@ void ObjectList::key_event(wxKeyEvent& event) { if (event.GetKeyCode() == WXK_TAB) Navigate(event.ShiftDown() ? wxNavigationKeyEvent::IsBackward : wxNavigationKeyEvent::IsForward); - else if (event.GetKeyCode() == WXK_DELETE -#ifdef __WXOSX__ - || event.GetKeyCode() == WXK_BACK -#endif //__WXOSX__ - ) { + else if (event.GetKeyCode() == WXK_DELETE || event.GetKeyCode() == WXK_BACK ) remove(); - } else if (event.GetKeyCode() == WXK_F5) wxGetApp().plater()->reload_all_from_disk(); else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL/*WXK_SHIFT*/)) @@ -4030,17 +4025,12 @@ void ObjectList::simplify() // Do not simplify when a gizmo is open. There might be issues with updates // and what is worse, the snapshot time would refer to the internal stack. - auto current_type = gizmos_mgr.get_current_type(); - if (current_type == GLGizmosManager::Simplify) { + if (! gizmos_mgr.check_gizmos_closed_except(GLGizmosManager::EType::Simplify)) + return; + + if (gizmos_mgr.get_current_type() == GLGizmosManager::Simplify) { // close first gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify); - }else if (current_type != GLGizmosManager::Undefined) { - plater->get_notification_manager()->push_notification( - NotificationType::CustomSupportsAndSeamRemovedAfterRepair, - NotificationManager::NotificationLevel::RegularNotification, - _u8L("ERROR: Please close all manipulators available from " - "the left toolbar before start simplify the mesh.")); - return; } gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index cc089e26e..64479a39e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -232,6 +232,20 @@ void GLGizmoBase::render_input_window(float x, float y, float bottom_limit) } } + + +std::string GLGizmoBase::get_name(bool include_shortcut) const +{ + int key = get_shortcut_key(); + assert( key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z); + std::string out = on_get_name(); + if (include_shortcut) + out += std::string(" [") + char(int('A') + key - int(WXK_CONTROL_A)) + "]"; + return out; +} + + + // Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components // were not interpolated by alpha blending or multi sampling. unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 8b033ce73..05f6adb5e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -120,7 +120,7 @@ public: void load(cereal::BinaryInputArchive& ar) { m_state = On; on_load(ar); } void save(cereal::BinaryOutputArchive& ar) const { on_save(ar); } - std::string get_name() const { return on_get_name(); } + std::string get_name(bool include_shortcut = true) const; int get_group_id() const { return m_group_id; } void set_group_id(int id) { m_group_id = id; } @@ -135,6 +135,7 @@ public: bool is_activable() const { return on_is_activable(); } bool is_selectable() const { return on_is_selectable(); } CommonGizmosDataID get_requirements() const { return on_get_requirements(); } + virtual bool wants_enter_leave_snapshots() const { return false; } void set_common_data_pool(CommonGizmosDataPool* ptr) { m_c = ptr; } unsigned int get_sprite_id() const { return m_sprite_id; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 641258ca4..3dcb9e2b1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -49,7 +49,7 @@ bool GLGizmoCut::on_init() std::string GLGizmoCut::on_get_name() const { - return (_L("Cut") + " [C]").ToUTF8().data(); + return _u8L("Cut"); } void GLGizmoCut::on_set_state() diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 99c010bf1..1ebba4d11 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -28,7 +28,7 @@ void GLGizmoFdmSupports::on_shutdown() std::string GLGizmoFdmSupports::on_get_name() const { - return (_L("Paint-on supports") + " [L]").ToUTF8().data(); + return _u8L("Paint-on supports"); } @@ -85,7 +85,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l y = std::min(y, bottom_limit - approx_height); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); - m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 3e5c12eec..b8c7d9f92 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -37,7 +37,7 @@ CommonGizmosDataID GLGizmoFlatten::on_get_requirements() const std::string GLGizmoFlatten::on_get_name() const { - return (_L("Place on face") + " [F]").ToUTF8().data(); + return _u8L("Place on face"); } bool GLGizmoFlatten::on_is_activable() const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 05e0be141..f405f457d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -505,7 +505,7 @@ RENDER_AGAIN: y = std::min(y, bottom_limit - approx_height); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); - m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: const float settings_sliders_left = @@ -773,7 +773,7 @@ bool GLGizmoHollow::on_is_selectable() const std::string GLGizmoHollow::on_get_name() const { - return (_(L("Hollow and drill")) + " [H]").ToUTF8().data(); + return _u8L("Hollow and drill"); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index a30ac377e..608e7bd1f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -42,7 +42,7 @@ void GLGizmoMmuSegmentation::on_shutdown() std::string GLGizmoMmuSegmentation::on_get_name() const { // FIXME Lukas H.: Discuss and change shortcut - return (_L("Multimaterial painting") + " [N]").ToUTF8().data(); + return _u8L("Multimaterial painting"); } bool GLGizmoMmuSegmentation::on_is_selectable() const @@ -239,7 +239,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott y = std::min(y, bottom_limit - approx_height); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); - m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index 9a056adcb..e7b6a0ad6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -52,7 +52,7 @@ bool GLGizmoMove3D::on_init() std::string GLGizmoMove3D::on_get_name() const { - return (_L("Move") + " [M]").ToUTF8().data(); + return _u8L("Move"); } bool GLGizmoMove3D::on_is_activable() const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 36759d2ec..54fcfc2c7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -463,7 +463,7 @@ bool GLGizmoRotate3D::on_init() std::string GLGizmoRotate3D::on_get_name() const { - return (_L("Rotate") + " [R]").ToUTF8().data(); + return _u8L("Rotate"); } bool GLGizmoRotate3D::on_is_activable() const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 894d844d8..b97507166 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -76,7 +76,7 @@ bool GLGizmoScale3D::on_init() std::string GLGizmoScale3D::on_get_name() const { - return (_L("Scale") + " [S]").ToUTF8().data(); + return _u8L("Scale"); } bool GLGizmoScale3D::on_is_activable() const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index 5b84bbaba..a2ee56d9a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -48,7 +48,7 @@ bool GLGizmoSeam::on_init() std::string GLGizmoSeam::on_get_name() const { - return (_L("Seam painting") + " [P]").ToUTF8().data(); + return _u8L("Seam painting"); } @@ -79,7 +79,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) const float approx_height = m_imgui->scaled(14.0f); y = std::min(y, bottom_limit - approx_height); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); - m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index f70e3f93e..9f49f9734 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -44,7 +44,7 @@ bool GLGizmoSimplify::on_init() std::string GLGizmoSimplify::on_get_name() const { - return (_L("Simplify")).ToUTF8().data(); + return _u8L("Simplify"); } void GLGizmoSimplify::on_render() {} @@ -97,7 +97,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse; - m_imgui->begin(on_get_name(), flag); + m_imgui->begin(get_name(), flag); size_t triangle_count = m_volume->mesh().its.indices.size(); // already reduced mesh diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 8c90d20d3..11446d6d7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -625,7 +625,7 @@ RENDER_AGAIN: //ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) )); //ImGui::SetNextWindowSize(ImVec2(window_size)); - m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); + m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse); // adjust window position to avoid overlap the view toolbar float win_h = ImGui::GetWindowHeight(); @@ -863,7 +863,7 @@ bool GLGizmoSlaSupports::on_is_selectable() const std::string GLGizmoSlaSupports::on_get_name() const { - return (_L("SLA Support Points") + " [L]").ToUTF8().data(); + return _u8L("SLA Support Points"); } CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const @@ -901,15 +901,6 @@ void GLGizmoSlaSupports::on_set_state() return; if (m_state == On && m_old_state != On) { // the gizmo was just turned on - if (! m_parent.get_gizmos_manager().is_serializing()) { - // Only take the snapshot when the USER opens the gizmo. Common gizmos - // data are not yet available, the CallAfter will postpone taking the - // snapshot until they are. No, it does not feel right. - wxGetApp().CallAfter([]() { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Entering SLA gizmo")); - }); - } - // Set default head diameter from config. const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; m_new_point_head_diameter = static_cast(cfg.option("support_head_front_diameter"))->value; @@ -925,8 +916,6 @@ void GLGizmoSlaSupports::on_set_state() else { // we are actually shutting down disable_editing_mode(); // so it is not active next time the gizmo opens - Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Leaving SLA gizmo")); - m_normal_cache.clear(); m_old_mo_id = -1; } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index cb60c0e25..8e30aa6b8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -67,6 +67,8 @@ public: bool has_backend_supports() const; void reslice_SLA_supports(bool postpone_error_messages = false) const; + bool wants_enter_leave_snapshots() const override { return true; } + private: bool on_init() override; void on_update(const UpdateData& data) override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 6d9a03977..93eba3042 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -22,6 +22,7 @@ #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp" +#include "libslic3r/format.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/PresetBundle.hpp" @@ -163,10 +164,8 @@ void GLGizmosManager::refresh_on_off_state() return; if (m_current != Undefined - && ! m_gizmos[m_current]->is_activable()) { - activate_gizmo(Undefined); + && ! m_gizmos[m_current]->is_activable() && activate_gizmo(Undefined)) update_data(); - } } void GLGizmosManager::reset_all_states() @@ -181,14 +180,28 @@ void GLGizmosManager::reset_all_states() bool GLGizmosManager::open_gizmo(EType type) { int idx = int(type); - if (m_gizmos[idx]->is_activable()) { - activate_gizmo(m_current == idx ? Undefined : (EType)idx); + if (m_gizmos[idx]->is_activable() + && activate_gizmo(m_current == idx ? Undefined : (EType)idx)) { update_data(); return true; } return false; } + +bool GLGizmosManager::check_gizmos_closed_except(EType type) const +{ + if (get_current_type() != type && get_current_type() != Undefined) { + wxGetApp().plater()->get_notification_manager()->push_notification( + NotificationType::CustomSupportsAndSeamRemovedAfterRepair, + NotificationManager::NotificationLevel::RegularNotification, + _u8L("ERROR: Please close all manipulators available from " + "the left toolbar first")); + return false; + } + return true; +} + void GLGizmosManager::set_hover_id(int id) { if (!m_enabled || m_current == Undefined) @@ -1193,31 +1206,35 @@ std::string GLGizmosManager::update_hover_state(const Vec2d& mouse_pos) return name; } -void GLGizmosManager::activate_gizmo(EType type) +bool GLGizmosManager::activate_gizmo(EType type) { if (m_gizmos.empty() || m_current == type) - return; + return true; - if (m_current != Undefined) { - m_gizmos[m_current]->set_state(GLGizmoBase::Off); - if (m_gizmos[m_current]->get_state() != GLGizmoBase::Off) - return; // gizmo refused to be turned off, do nothing. + GLGizmoBase* old_gizmo = m_current == Undefined ? nullptr : m_gizmos[m_current].get(); + GLGizmoBase* new_gizmo = type == Undefined ? nullptr : m_gizmos[type].get(); + + if (old_gizmo) { + old_gizmo->set_state(GLGizmoBase::Off); + if (old_gizmo->get_state() != GLGizmoBase::Off) + return false; // gizmo refused to be turned off, do nothing. + + if (! m_parent.get_gizmos_manager().is_serializing() + && old_gizmo->wants_enter_leave_snapshots()) + Plater::TakeSnapshot snapshot(wxGetApp().plater(), + Slic3r::format(_utf8("Leaving %1%"), old_gizmo->get_name(false))); } + if (new_gizmo && ! m_parent.get_gizmos_manager().is_serializing() + && new_gizmo->wants_enter_leave_snapshots()) + Plater::TakeSnapshot snapshot(wxGetApp().plater(), + Slic3r::format(_utf8("Entering %1%"), new_gizmo->get_name(false))); + m_current = type; - // Updating common data should be left to the update_data function, which - // is always called after this one. activate_gizmo can be called by undo/redo, - // when selection is not yet deserialized, so the common data would update - // incorrectly (or crash if relying on unempty selection). Undo/redo stack - // will also call update_data, after selection is restored. - - //m_common_gizmos_data->update(get_current() - // ? get_current()->get_requirements() - // : CommonGizmosDataID(0)); - - if (type != Undefined) - m_gizmos[type]->set_state(GLGizmoBase::On); + if (new_gizmo) + new_gizmo->set_state(GLGizmoBase::On); + return true; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 188c9e914..ed02574df 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -104,7 +104,7 @@ private: std::vector get_selectable_idxs() const; size_t get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const; - void activate_gizmo(EType type); + bool activate_gizmo(EType type); struct MouseCapture { @@ -176,6 +176,7 @@ public: void reset_all_states(); bool is_serializing() const { return m_serializing; } bool open_gizmo(EType type); + bool check_gizmos_closed_except(EType) const; void set_hover_id(int id); void enable_grabber(EType type, unsigned int id, bool enable); diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index 33ccdc4ae..fe5eeb67c 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -74,7 +74,12 @@ void write_used_binary(const std::vector& ids) } void read_used_binary(std::vector& ids) { - boost::filesystem::ifstream file((boost::filesystem::path(data_dir()) / "cache" / "hints.cereal")); + boost::filesystem::path path(boost::filesystem::path(data_dir()) / "cache" / "hints.cereal"); + if (!boost::filesystem::exists(path)) { + BOOST_LOG_TRIVIAL(warning) << "Failed to load to hints.cereal. File does not exists. " << path.string(); + return; + } + boost::filesystem::ifstream file(path); cereal::BinaryInputArchive archive(file); HintsCerealData cd; try diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index eb309b1af..51dbc201d 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -142,11 +142,11 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S default: case GUI_App::EAppMode::Editor: m_taskbar_icon = std::make_unique(wxTBI_DOCK); - m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer"); + m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer-mac_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer"); break; case GUI_App::EAppMode::GCodeViewer: m_taskbar_icon = std::make_unique(wxTBI_DOCK); - m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer-gcodeviewer_128px.png"), wxBITMAP_TYPE_PNG), "G-code Viewer"); + m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer-gcodeviewer-mac_128px.png"), wxBITMAP_TYPE_PNG), "G-code Viewer"); break; } #endif // __APPLE__ diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 9417364ef..88527e9fd 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -1361,7 +1361,8 @@ wxDataViewItem ObjectDataViewModel::ReorganizeChildren( const int current_volume if (!node_parent) // happens if item.IsOk()==false return ret_item; - const size_t shift = node_parent->GetChildren().Item(0)->m_type == itSettings ? 1 : 0; + size_t shift; + for (shift = 0; shift < node_parent->GetChildCount() && node_parent->GetNthChild(shift)->GetType() != itVolume; shift ++); ObjectDataViewModelNode *deleted_node = node_parent->GetNthChild(current_volume_id+shift); node_parent->GetChildren().Remove(deleted_node); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 4236ee450..2c77dcb7a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3186,6 +3186,9 @@ void Plater::priv::update_sla_scene() void Plater::priv::replace_with_stl() { + if (! q->canvas3D()->get_gizmos_manager().check_gizmos_closed_except(GLGizmosManager::EType::Undefined)) + return; + const Selection& selection = get_selection(); if (selection.is_wipe_tower() || get_selection().get_volume_idxs().size() != 1) @@ -3530,14 +3533,8 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = // Do not fix anything when a gizmo is open. There might be issues with updates // and what is worse, the snapshot time would refer to the internal stack. - if (q->canvas3D()->get_gizmos_manager().get_current_type() != GLGizmosManager::Undefined) { - notification_manager->push_notification( - NotificationType::CustomSupportsAndSeamRemovedAfterRepair, - NotificationManager::NotificationLevel::RegularNotification, - _u8L("ERROR: Please close all manipulators available from " - "the left toolbar before fixing the mesh.")); + if (! q->canvas3D()->get_gizmos_manager().check_gizmos_closed_except(GLGizmosManager::Undefined)) return; - } // size_t snapshot_time = undo_redo_stack().active_snapshot_time(); Plater::TakeSnapshot snapshot(q, _L("Fix through NetFabb")); @@ -6146,16 +6143,6 @@ BoundingBoxf Plater::bed_shape_bb() const return p->bed_shape_bb(); } -void Plater::start_mapping_gcode_window() -{ - p->preview->get_canvas3d()->start_mapping_gcode_window(); -} - -void Plater::stop_mapping_gcode_window() -{ - p->preview->get_canvas3d()->stop_mapping_gcode_window(); -} - void Plater::arrange() { p->m_ui_jobs.arrange(); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 99f7e3cda..f1a493b0f 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -282,9 +282,6 @@ public: GLCanvas3D* get_current_canvas3D(); BoundingBoxf bed_shape_bb() const; - void start_mapping_gcode_window(); - void stop_mapping_gcode_window(); - void arrange(); void find_new_position(const ModelInstancePtrs &instances); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 2b63bc79b..ef480f60f 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -696,7 +696,12 @@ void DiffViewCtrl::context_menu(wxDataViewEvent& event) auto it = m_items_map.find(item); if (it == m_items_map.end() || !it->second.is_long) return; - FullCompareDialog(it->second.opt_name, it->second.old_val, it->second.new_val).ShowModal(); + + size_t column_cnt = this->GetColumnCount(); + const wxString old_value_header = this->GetColumn(column_cnt - 2)->GetTitle(); + const wxString new_value_header = this->GetColumn(column_cnt - 1)->GetTitle(); + FullCompareDialog(it->second.opt_name, it->second.old_val, it->second.new_val, + old_value_header, new_value_header).ShowModal(); #ifdef __WXOSX__ wxWindow* parent = this->GetParent(); @@ -1281,7 +1286,8 @@ void UnsavedChangesDialog::on_sys_color_changed() // FullCompareDialog //------------------------------------------ -FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value) +FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value, + const wxString& old_value_header, const wxString& new_value_header) : wxDialog(nullptr, wxID_ANY, option_name, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { wxGetApp().UpdateDarkUI(this); @@ -1302,8 +1308,8 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString grid_sizer->Add(text, 0, wxALL, border); }; - add_header(_L("Old value")); - add_header(_L("New value")); + add_header(old_value_header); + add_header(new_value_header); auto get_set_from_val = [](wxString str) { if (str.Find("\n") == wxNOT_FOUND) @@ -1327,7 +1333,7 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString std::set_difference(new_set.begin(), new_set.end(), old_set.begin(), old_set.end(), std::inserter(new_old_diff_set, new_old_diff_set.begin())); auto add_value = [grid_sizer, border, this](wxString label, const std::set& diff_set, bool is_colored = false) { - wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(400, 400), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_SIMPLE | wxTE_RICH); + wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(400, 400), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_DEFAULT | wxTE_RICH); wxGetApp().UpdateDarkUI(text); text->SetStyle(0, label.Len(), wxTextAttr(is_colored ? wxColour(orange) : wxNullColour, wxNullColour, this->GetFont())); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index 966743033..9dbaf6e99 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -299,7 +299,8 @@ protected: class FullCompareDialog : public wxDialog { public: - FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value); + FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value, + const wxString& old_value_header, const wxString& new_value_header); ~FullCompareDialog() {} }; diff --git a/xs/t/09_polyline.t b/xs/t/09_polyline.t index 0a7b8dcb0..5203ec5ef 100644 --- a/xs/t/09_polyline.t +++ b/xs/t/09_polyline.t @@ -89,40 +89,40 @@ is_deeply $polyline->pp, [ @$points, @$points ], 'append_polyline'; } # disabled because we now use a more efficient but incomplete algorithm -if (0) { - my $polyline = Slic3r::Polyline->new( - map [$_,10], (0,10,20,30,40,50,60) - ); - { - my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new( - [25,0], [55,0], [55,30], [25,30], - )); - my $p = $polyline->clone; - $p->simplify_by_visibility($expolygon); - is_deeply $p->pp, [ - map [$_,10], (0,10,20,30,50,60) - ], 'simplify_by_visibility()'; - } - { - my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new( - [-15,0], [75,0], [75,30], [-15,30], - )); - my $p = $polyline->clone; - $p->simplify_by_visibility($expolygon); - is_deeply $p->pp, [ - map [$_,10], (0,60) - ], 'simplify_by_visibility()'; - } - { - my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new( - [-15,0], [25,0], [25,30], [-15,30], - )); - my $p = $polyline->clone; - $p->simplify_by_visibility($expolygon); - is_deeply $p->pp, [ - map [$_,10], (0,20,30,40,50,60) - ], 'simplify_by_visibility()'; - } -} +#if (0) { +# my $polyline = Slic3r::Polyline->new( +# map [$_,10], (0,10,20,30,40,50,60) +# ); +# { +# my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new( +# [25,0], [55,0], [55,30], [25,30], +# )); +# my $p = $polyline->clone; +# $p->simplify_by_visibility($expolygon); +# is_deeply $p->pp, [ +# map [$_,10], (0,10,20,30,50,60) +# ], 'simplify_by_visibility()'; +# } +# { +# my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new( +# [-15,0], [75,0], [75,30], [-15,30], +# )); +# my $p = $polyline->clone; +# $p->simplify_by_visibility($expolygon); +# is_deeply $p->pp, [ +# map [$_,10], (0,60) +# ], 'simplify_by_visibility()'; +# } +# { +# my $expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new( +# [-15,0], [25,0], [25,30], [-15,30], +# )); +# my $p = $polyline->clone; +# $p->simplify_by_visibility($expolygon); +# is_deeply $p->pp, [ +# map [$_,10], (0,20,30,40,50,60) +# ], 'simplify_by_visibility()'; +# } +#} __END__ diff --git a/xs/xsp/Polyline.xsp b/xs/xsp/Polyline.xsp index 10bbb263f..7846ea5f4 100644 --- a/xs/xsp/Polyline.xsp +++ b/xs/xsp/Polyline.xsp @@ -31,8 +31,6 @@ void extend_end(double distance); void extend_start(double distance); void simplify(double tolerance); - void simplify_by_visibility(ExPolygon* expolygon) - %code{% THIS->simplify_by_visibility(*expolygon); %}; void split_at(Point* point, Polyline* p1, Polyline* p2) %code{% THIS->split_at(*point, p1, p2); %}; bool is_straight();