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/resources/shaders/gouraud_light_instanced.vs b/resources/shaders/gouraud_light_instanced.vs index a8931361d..997b6a2bf 100644 --- a/resources/shaders/gouraud_light_instanced.vs +++ b/resources/shaders/gouraud_light_instanced.vs @@ -34,7 +34,9 @@ void main() float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; - vec4 world_position = vec4(v_position * vec3(vec2(i_scales.x), i_scales.y) + i_offset, 1.0); + float width = 1.5 * i_scales.x; + float height = 1.5 * i_scales.y; + vec4 world_position = vec4(v_position * vec3(vec2(width), height) + i_offset - vec3(0.0, 0.0, 0.5 * i_scales.y), 1.0); vec3 eye_position = (gl_ModelViewMatrix * world_position).xyz; intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(eye_position), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); 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..efea240e5 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -744,27 +744,28 @@ 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) + m_processor.initialize(path_tmp); + GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor); + 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. @@ -782,7 +783,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re } BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info(); - m_processor.process_file(path_tmp, true, [print]() { print->throw_if_canceled(); }); + m_processor.finalize(); // DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics); DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics); if (result != nullptr) { @@ -1046,7 +1047,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 +1112,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 +1126,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 +1140,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 +1219,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 +1262,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 +1273,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 +1292,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 +1318,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 +1331,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 +1347,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 +1362,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 +1376,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 +1411,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 +1435,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 +1566,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 +1588,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 +1609,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 +1622,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 +1630,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 +1647,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 +1655,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 +1892,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 +2307,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 +2643,42 @@ 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); + //FIXME don't allocate a string, maybe process a batch of lines? + m_processor.process_buffer(std::string(gcode)); } } -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 +2702,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..1dbc5b70a 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -184,13 +184,40 @@ public: }; private: - void _do_export(Print &print, FILE *file, ThumbnailsGeneratorCallback thumbnail_cb); + class GCodeOutputStream { + public: + GCodeOutputStream(FILE *f, GCodeProcessor &processor) : f(f), m_processor(processor) {} + ~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; + GCodeProcessor &m_processor; + }; + 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 +385,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 c4f262675..d16cfb9cf 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; @@ -777,6 +828,9 @@ bool GCodeProcessor::contains_reserved_tags(const std::string& gcode, unsigned i } GCodeProcessor::GCodeProcessor() +#if ENABLE_FIX_PREVIEW_OPTIONS_Z +: m_options_z_corrector(m_result) +#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z { reset(); m_time_processor.machines[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_main_mask = "M73 P%s R%s\n"; @@ -951,10 +1005,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()); @@ -1139,7 +1192,6 @@ void GCodeProcessor::reset() m_cp_color.reset(); m_producer = EProducer::Unknown; - m_producers_enabled = false; m_time_processor.reset(); m_used_filaments.reset(); @@ -1152,6 +1204,10 @@ void GCodeProcessor::reset() m_last_default_color_id = 0; #endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER +#if ENABLE_FIX_PREVIEW_OPTIONS_Z + m_options_z_corrector.reset(); +#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z + #if ENABLE_GCODE_VIEWER_DATA_CHECKING m_mm3_per_mm_compare.reset(); m_height_compare.reset(); @@ -1159,27 +1215,26 @@ void GCodeProcessor::reset() #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING } -void GCodeProcessor::process_file(const std::string& filename, bool apply_postprocess, std::function cancel_callback) +void GCodeProcessor::process_file(const std::string& filename, std::function cancel_callback) { - auto last_cancel_callback_time = std::chrono::high_resolution_clock::now(); - CNumericLocalesSetter locales_setter; #if ENABLE_GCODE_VIEWER_STATISTICS - auto start_time = std::chrono::high_resolution_clock::now(); + m_start_time = std::chrono::high_resolution_clock::now(); #endif // ENABLE_GCODE_VIEWER_STATISTICS // pre-processing // parse the gcode file to detect its producer - 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(); } }); + m_parser.reset(); // if the gcode was produced by PrusaSlicer, // extract the config from it @@ -1201,18 +1256,45 @@ 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, true); + }); + this->finalize(); +} + +void GCodeProcessor::initialize(const std::string& filename) +{ + assert(is_decimal_separator_point()); + +#if ENABLE_GCODE_VIEWER_STATISTICS + m_start_time = std::chrono::high_resolution_clock::now(); +#endif // ENABLE_GCODE_VIEWER_STATISTICS + + // process gcode + m_result.filename = filename; + m_result.id = ++s_result_id; + // 1st move must be a dummy move + m_result.moves.emplace_back(MoveVertex()); +} + +void GCodeProcessor::process_buffer(const std::string &buffer) +{ + //FIXME maybe cache GCodeLine gline to be over multiple parse_buffer() invocations. + m_parser.parse_buffer(buffer, [this](GCodeReader&, const GCodeReader::GCodeLine& line) { + this->process_gcode_line(line, false); + }); +} + +void GCodeProcessor::finalize() +{ // update width/height of wipe moves for (MoveVertex& move : m_result.moves) { if (move.type == EMoveType::Wipe) { @@ -1234,10 +1316,6 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr update_estimated_times_stats(); - // post-process to add M73 lines into the gcode - if (apply_postprocess) - m_time_processor.post_process(filename, m_result.moves); - #if ENABLE_GCODE_VIEWER_DATA_CHECKING std::cout << "\n"; m_mm3_per_mm_compare.output(); @@ -1245,8 +1323,9 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr m_width_compare.output(); #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING + m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends); #if ENABLE_GCODE_VIEWER_STATISTICS - m_result.time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start_time).count(); + m_result.time = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - m_start_time).count(); #endif // ENABLE_GCODE_VIEWER_STATISTICS } @@ -1340,7 +1419,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 +1443,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 = { @@ -1379,7 +1464,7 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename) } } -void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) +void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line, bool producers_enabled) { /* std::cout << line.raw() << std::endl; */ @@ -1391,61 +1476,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 { @@ -1453,7 +1647,7 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line) if (comment.length() > 2 && comment.front() == ';') // Process tags embedded into comments. Tag comments always start at the start of a line // with a comment and continue with a tag without any whitespace separator. - process_tags(comment.substr(1)); + process_tags(comment.substr(1), producers_enabled); } } @@ -1502,10 +1696,10 @@ template } } -void GCodeProcessor::process_tags(const std::string_view comment) +void GCodeProcessor::process_tags(const std::string_view comment, bool producers_enabled) { // producers tags - if (m_producers_enabled && process_producers_tags(comment)) + if (producers_enabled && process_producers_tags(comment)) return; // extrusion role tag @@ -1529,7 +1723,7 @@ void GCodeProcessor::process_tags(const std::string_view comment) return; } - if (!m_producers_enabled || m_producer == EProducer::PrusaSlicer) { + if (!producers_enabled || m_producer == EProducer::PrusaSlicer) { // height tag if (boost::starts_with(comment, reserved_tag(ETags::Height))) { if (!parse_number(comment.substr(reserved_tag(ETags::Height).size()), m_forced_height)) @@ -1619,6 +1813,9 @@ void GCodeProcessor::process_tags(const std::string_view comment) #if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER CustomGCode::Item item = { static_cast(m_end_position[2]), CustomGCode::ColorChange, extruder_id + 1, color, "" }; m_result.custom_gcode_per_print_z.emplace_back(item); +#if ENABLE_FIX_PREVIEW_OPTIONS_Z + m_options_z_corrector.set(); +#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z process_custom_gcode_time(CustomGCode::ColorChange); process_filaments(CustomGCode::ColorChange); #endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER @@ -1638,6 +1835,9 @@ void GCodeProcessor::process_tags(const std::string_view comment) #if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER CustomGCode::Item item = { static_cast(m_end_position[2]), CustomGCode::PausePrint, m_extruder_id + 1, "", "" }; m_result.custom_gcode_per_print_z.emplace_back(item); +#if ENABLE_FIX_PREVIEW_OPTIONS_Z + m_options_z_corrector.set(); +#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z #endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER process_custom_gcode_time(CustomGCode::PausePrint); return; @@ -1649,6 +1849,9 @@ void GCodeProcessor::process_tags(const std::string_view comment) #if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER CustomGCode::Item item = { static_cast(m_end_position[2]), CustomGCode::Custom, m_extruder_id + 1, "", "" }; m_result.custom_gcode_per_print_z.emplace_back(item); +#if ENABLE_FIX_PREVIEW_OPTIONS_Z + m_options_z_corrector.set(); +#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z #endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER return; } @@ -2210,9 +2413,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]; @@ -2232,10 +2432,23 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) else { if (m_end_position[Z] > m_extruded_last_z + EPSILON) { m_height = m_end_position[Z] - m_extruded_last_z; +#if !ENABLE_FIX_PREVIEW_OPTIONS_Z m_extruded_last_z = m_end_position[Z]; +#endif // !ENABLE_FIX_PREVIEW_OPTIONS_Z } } + 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_FIX_PREVIEW_OPTIONS_Z + m_extruded_last_z = m_end_position[Z]; + m_options_z_corrector.update(m_height); +#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z + #if ENABLE_GCODE_VIEWER_DATA_CHECKING m_height_compare.update(m_height, m_extrusion_role); #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING @@ -2252,23 +2465,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 ENABLE_CLAMP_TOOLPATHS_WIDTH - if (m_producers_enabled && m_producer != EProducer::PrusaSlicer) - // 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)); -#else + 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)); -#endif // ENABLE_CLAMP_TOOLPATHS_WIDTH + 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]); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 4fcdd8df3..040f7432a 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; @@ -388,6 +390,45 @@ namespace Slic3r { bool has_first_vertex() const { return m_first_vertex.has_value(); } }; +#if ENABLE_FIX_PREVIEW_OPTIONS_Z + // Helper class used to fix the z for color change, pause print and + // custom gcode markes + class OptionsZCorrector + { + Result& m_result; + std::optional m_move_id; + std::optional m_custom_gcode_per_print_z_id; + + public: + explicit OptionsZCorrector(Result& result) : m_result(result) { + } + + void set() { + m_move_id = m_result.moves.size() - 1; + m_custom_gcode_per_print_z_id = m_result.custom_gcode_per_print_z.size() - 1; + } + + void update(float height) { + if (!m_move_id.has_value() || !m_custom_gcode_per_print_z_id.has_value()) + return; + + const Vec3f position = m_result.moves.back().position; + + MoveVertex& move = m_result.moves.emplace_back(m_result.moves[*m_move_id]); + move.position = position; + move.height = height; + m_result.moves.erase(m_result.moves.begin() + *m_move_id); + m_result.custom_gcode_per_print_z[*m_custom_gcode_per_print_z_id].print_z = position.z(); + reset(); + } + + void reset() { + m_move_id.reset(); + m_custom_gcode_per_print_z_id.reset(); + } + }; +#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z + #if ENABLE_GCODE_VIEWER_DATA_CHECKING struct DataChecker { @@ -492,9 +533,15 @@ namespace Slic3r { CpColor m_cp_color; bool m_use_volumetric_e; SeamsDetector m_seams_detector; +#if ENABLE_FIX_PREVIEW_OPTIONS_Z + OptionsZCorrector m_options_z_corrector; +#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z #if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER size_t m_last_default_color_id; #endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER +#if ENABLE_GCODE_VIEWER_STATISTICS + std::chrono::time_point m_start_time; +#endif // ENABLE_GCODE_VIEWER_STATISTICS enum class EProducer { @@ -511,7 +558,6 @@ namespace Slic3r { static const std::vector> Producers; EProducer m_producer; - bool m_producers_enabled; TimeProcessor m_time_processor; UsedFilaments m_used_filaments; @@ -534,7 +580,6 @@ namespace Slic3r { return m_time_processor.machines[static_cast(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled; } void enable_machine_envelope_processing(bool enabled) { m_time_processor.machine_envelope_processing_enabled = enabled; } - void enable_producers(bool enabled) { m_producers_enabled = enabled; } void reset(); const Result& get_result() const { return m_result; } @@ -542,7 +587,12 @@ namespace Slic3r { // Process the gcode contained in the file with the given filename // throws CanceledException through print->throw_if_canceled() (sent by the caller as callback). - void process_file(const std::string& filename, bool apply_postprocess, std::function cancel_callback = nullptr); + void process_file(const std::string& filename, std::function cancel_callback = nullptr); + + // Streaming interface, for processing G-codes just generated by PrusaSlicer in a pipelined fashion. + void initialize(const std::string& filename); + void process_buffer(const std::string& buffer); + void finalize(); float get_time(PrintEstimatedStatistics::ETimeMode mode) const; std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const; @@ -555,10 +605,10 @@ namespace Slic3r { private: void apply_config(const DynamicPrintConfig& config); void apply_config_simplify3d(const std::string& filename); - void process_gcode_line(const GCodeReader::GCodeLine& line); + void process_gcode_line(const GCodeReader::GCodeLine& line, bool producers_enabled); // Process tags embedded into comments - void process_tags(const std::string_view comment); + void process_tags(const std::string_view comment, bool producers_enabled); bool process_producers_tags(const std::string_view comment); bool process_prusaslicer_tags(const std::string_view comment); bool process_cura_tags(const std::string_view comment); diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp index 54e6802e6..4c4bebee4 100644 --- a/src/libslic3r/GCodeReader.cpp +++ b/src/libslic3r/GCodeReader.cpp @@ -71,9 +71,16 @@ const char* GCodeReader::parse_line_internal(const char *ptr, const char *end, G } 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); @@ -126,7 +133,7 @@ void GCodeReader::update_coordinates(GCodeLine &gline, std::pairhas(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]; @@ -75,7 +77,8 @@ public: typedef std::function callback_t; - GCodeReader() : m_verbose(false), m_extrusion_axis('E') { memset(m_position, 0, sizeof(m_position)); } + GCodeReader() : m_verbose(false), m_extrusion_axis('E') { this->reset(); } + void reset() { memset(m_position, 0, sizeof(m_position)); } void apply_config(const GCodeConfig &config); void apply_config(const DynamicPrintConfig &config); @@ -109,7 +112,8 @@ public: void parse_line(const std::string &line, Callback 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]; } diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index 3aecdfd1f..c97180982 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -309,7 +309,16 @@ public: 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) { diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index a674255f0..dfaf0f3c6 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -37,24 +37,24 @@ //==================== -// 2.4.0.alpha0 techs +// 2.4.0.alpha1 techs //==================== -#define ENABLE_2_4_0_ALPHA0 1 +#define ENABLE_2_4_0_ALPHA1 1 // Enable delayed rendering of transparent volumes -#define ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING (1 && ENABLE_2_4_0_ALPHA0) +#define ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING (1 && ENABLE_2_4_0_ALPHA1) // Enable the fix of importing color print view from gcode files into GCodeViewer -#define ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER (1 && ENABLE_2_4_0_ALPHA0) +#define ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER (1 && ENABLE_2_4_0_ALPHA1) // Enable drawing contours, at cut level, for sinking volumes -#define ENABLE_SINKING_CONTOURS (1 && ENABLE_2_4_0_ALPHA0) +#define ENABLE_SINKING_CONTOURS (1 && ENABLE_2_4_0_ALPHA1) // Enable implementation of retract acceleration in gcode processor -#define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA0) +#define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA1) // Enable the fix for exporting and importing to/from 3mf file of mirrored volumes -#define ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT (1 && ENABLE_2_4_0_ALPHA0) +#define ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT (1 && ENABLE_2_4_0_ALPHA1) // Enable rendering seams (and other options) in preview using models -#define ENABLE_SEAMS_USING_MODELS (1 && ENABLE_2_4_0_ALPHA0) +#define ENABLE_SEAMS_USING_MODELS (1 && ENABLE_2_4_0_ALPHA1) // Enable save and save as commands to be enabled also when the plater is empty and allow to load empty projects -#define ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED (1 && ENABLE_2_4_0_ALPHA0) +#define ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED (1 && ENABLE_2_4_0_ALPHA1) //==================== @@ -62,8 +62,10 @@ //==================== #define ENABLE_2_4_0_ALPHA2 1 -// Enable clamping toolpaths width only for gcodes files produced by 3rd part softwares -#define ENABLE_CLAMP_TOOLPATHS_WIDTH (1 && ENABLE_2_4_0_ALPHA2) +// Enable rendering seams (and other options) in preview using batched models on systems not supporting OpenGL 3.3 +#define ENABLE_SEAMS_USING_BATCHED_MODELS (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_2_4_0_ALPHA2) +// Enable fixing the z position of color change, pause print and custom gcode markers in preview +#define ENABLE_FIX_PREVIEW_OPTIONS_Z (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER && ENABLE_2_4_0_ALPHA2) // Enable coloring of toolpaths in preview by layer time #define ENABLE_PREVIEW_LAYER_TIME (1 && ENABLE_2_4_0_ALPHA2) 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/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/Field.cpp b/src/slic3r/GUI/Field.cpp index 1aaac5469..06cfd0a1c 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -230,18 +230,21 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true } double val; + bool is_na_value = m_opt.nullable && str == na_value(); + const char dec_sep = is_decimal_separator_point() ? '.' : ','; const char dec_sep_alt = dec_sep == '.' ? ',' : '.'; - // Replace the first incorrect separator in decimal number. - if (str.Replace(dec_sep_alt, dec_sep, false) != 0) + // Replace the first incorrect separator in decimal number, + // if this value doesn't "N/A" value in some language + // see https://github.com/prusa3d/PrusaSlicer/issues/6921 + if (!is_na_value && str.Replace(dec_sep_alt, dec_sep, false) != 0) set_value(str, false); - if (str == dec_sep) val = 0.0; else { - if (m_opt.nullable && str == na_value()) + if (is_na_value) val = ConfigOptionFloatsNullable::nil_value(); else if (!str.ToDouble(&val)) { diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 2c5ef72a0..966dd60ae 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -284,45 +284,14 @@ void GCodeViewer::SequentialView::Marker::render() const ImGui::PopStyleVar(); } -void GCodeViewer::SequentialView::GCodeWindow::load_gcode() +void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, 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; @@ -345,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; @@ -379,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 @@ -397,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; } @@ -503,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 @@ -606,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()) @@ -695,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 @@ -815,6 +785,25 @@ void GCodeViewer::render() case EMoveType::Unretract: case EMoveType::Seam: { #if ENABLE_SEAMS_USING_MODELS +#if ENABLE_SEAMS_USING_BATCHED_MODELS + if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) { + buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::InstancedModel; + buffer.shader = "gouraud_light_instanced"; + buffer.model.model.init_from(diamond(16)); + buffer.model.color = option_color(type); + buffer.model.instances.format = InstanceVBuffer::EFormat::InstancedModel; + } + else { + buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::BatchedModel; + buffer.vertices.format = VBuffer::EFormat::PositionNormal3; + buffer.shader = "gouraud_light"; + + buffer.model.data = diamond(16); + buffer.model.color = option_color(type); + buffer.model.instances.format = InstanceVBuffer::EFormat::BatchedModel; + } + break; +#else if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) { buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Model; buffer.shader = "gouraud_light_instanced"; @@ -827,6 +816,7 @@ void GCodeViewer::render() buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110"; } break; +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS #else buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point; buffer.vertices.format = VBuffer::EFormat::Position; @@ -1163,16 +1153,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 @@ -1227,9 +1207,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) }; auto add_indices_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer, unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) { - if (prev.type != curr.type || !buffer.paths.back().matches(curr)) { + if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr)) { // add starting index - indices.push_back(static_cast(indices.size())); + indices.push_back(static_cast(indices.size())); buffer.add_path(curr, ibuffer_id, indices.size() - 1, move_id - 1); buffer.paths.back().sub_paths.front().first.position = prev.position; } @@ -1237,11 +1217,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) Path& last_path = buffer.paths.back(); if (last_path.sub_paths.front().first.i_id != last_path.sub_paths.back().last.i_id) { // add previous index - indices.push_back(static_cast(indices.size())); + indices.push_back(static_cast(indices.size())); } // add current index - indices.push_back(static_cast(indices.size())); + indices.push_back(static_cast(indices.size())); last_path.sub_paths.back().last = { ibuffer_id, indices.size() - 1, move_id, curr.position }; }; @@ -1444,20 +1424,63 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) }; #if ENABLE_SEAMS_USING_MODELS - // format data into the buffers to be rendered as model + // format data into the buffers to be rendered as instanced model auto add_model_instance = [](const GCodeProcessor::MoveVertex& curr, InstanceBuffer& instances, InstanceIdBuffer& instances_ids, size_t move_id) { // append position instances.push_back(curr.position.x()); instances.push_back(curr.position.y()); instances.push_back(curr.position.z()); // append width - instances.push_back(1.5f * curr.width); + instances.push_back(curr.width); // append height - instances.push_back(1.5f * curr.height); + instances.push_back(curr.height); // append id instances_ids.push_back(move_id); }; + +#if ENABLE_SEAMS_USING_BATCHED_MODELS + // format data into the buffers to be rendered as batched model + auto add_vertices_as_model_batch = [](const GCodeProcessor::MoveVertex& curr, const GLModel::InitializationData& data, VertexBuffer& vertices, InstanceBuffer& instances, InstanceIdBuffer& instances_ids, size_t move_id) { + const double width = static_cast(1.5f * curr.width); + const double height = static_cast(1.5f * curr.height); + + const Transform3d trafo = Geometry::assemble_transform((curr.position - 0.5f * curr.height * Vec3f::UnitZ()).cast(), Vec3d::Zero(), { width, width, height }); + const Eigen::Matrix normal_matrix = trafo.matrix().template block<3, 3>(0, 0).inverse().transpose(); + + for (const auto& entity : data.entities) { + // append vertices + for (size_t i = 0; i < entity.positions.size(); ++i) { + // append position + const Vec3d position = trafo * entity.positions[i].cast(); + vertices.push_back(static_cast(position.x())); + vertices.push_back(static_cast(position.y())); + vertices.push_back(static_cast(position.z())); + + // append normal + const Vec3d normal = normal_matrix * entity.normals[i].cast(); + vertices.push_back(static_cast(normal.x())); + vertices.push_back(static_cast(normal.y())); + vertices.push_back(static_cast(normal.z())); + } + } + + // append instance position + instances.push_back(curr.position.x()); + instances.push_back(curr.position.y()); + instances.push_back(curr.position.z()); + // append instance id + instances_ids.push_back(move_id); + }; + + auto add_indices_as_model_batch = [](const GLModel::InitializationData& data, IndexBuffer& indices, IBufferType base_index) { + for (const auto& entity : data.entities) { + for (size_t i = 0; i < entity.indices.size(); ++i) { + indices.push_back(static_cast(entity.indices[i] + base_index)); + } + } + }; +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS #endif // ENABLE_SEAMS_USING_MODELS #if ENABLE_GCODE_VIEWER_STATISTICS @@ -1541,7 +1564,12 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // if adding the vertices for the current segment exceeds the threshold size of the current vertex buffer // add another vertex buffer +#if ENABLE_SEAMS_USING_BATCHED_MODELS + size_t vertices_size_to_add = (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) ? t_buffer.model.data.vertices_size_bytes() : t_buffer.max_vertices_per_segment_size_bytes(); + if (v_multibuffer.back().size() * sizeof(float) > t_buffer.vertices.max_size_bytes() - vertices_size_to_add) { +#else if (v_multibuffer.back().size() * sizeof(float) > t_buffer.vertices.max_size_bytes() - t_buffer.max_vertices_per_segment_size_bytes()) { +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS v_multibuffer.push_back(VertexBuffer()); if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) { Path& last_path = t_buffer.paths.back(); @@ -1558,6 +1586,24 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) case TBuffer::ERenderPrimitiveType::Line: { add_vertices_as_line(prev, curr, v_buffer); break; } case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast(v_multibuffer.size()) - 1, v_buffer, i); break; } #if ENABLE_SEAMS_USING_MODELS +#if ENABLE_SEAMS_USING_BATCHED_MODELS + case TBuffer::ERenderPrimitiveType::InstancedModel: + { + add_model_instance(curr, inst_buffer, inst_id_buffer, i); +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.instances_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + break; + } + case TBuffer::ERenderPrimitiveType::BatchedModel: + { + add_vertices_as_model_batch(curr, t_buffer.model.data, v_buffer, inst_buffer, inst_id_buffer, i); +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.batched_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + break; + } +#else case TBuffer::ERenderPrimitiveType::Model: { add_model_instance(curr, inst_buffer, inst_id_buffer, i); @@ -1567,6 +1613,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) break; } #endif // ENABLE_SEAMS_USING_MODELS +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS } // collect options zs for later use @@ -1749,6 +1796,23 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) for (size_t i = 0; i < m_buffers.size(); ++i) { TBuffer& t_buffer = m_buffers[i]; #if ENABLE_SEAMS_USING_MODELS +#if ENABLE_SEAMS_USING_BATCHED_MODELS + if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::InstancedModel) { + const InstanceBuffer& inst_buffer = instances[i]; + if (!inst_buffer.empty()) { + t_buffer.model.instances.buffer = inst_buffer; + t_buffer.model.instances.s_ids = instances_ids[i]; + } + } + else { + if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) { + const InstanceBuffer& inst_buffer = instances[i]; + if (!inst_buffer.empty()) { + t_buffer.model.instances.buffer = inst_buffer; + t_buffer.model.instances.s_ids = instances_ids[i]; + } + } +#else if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Model) { const InstanceBuffer& inst_buffer = instances[i]; if (!inst_buffer.empty()) { @@ -1757,6 +1821,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) } } else { +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS #endif // ENABLE_SEAMS_USING_MODELS const MultiVertexBuffer& v_multibuffer = vertices[i]; for (const VertexBuffer& v_buffer : v_multibuffer) { @@ -1850,10 +1915,20 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // if adding the indices for the current segment exceeds the threshold size of the current index buffer // create another index buffer +#if ENABLE_SEAMS_USING_BATCHED_MODELS + size_t indiced_size_to_add = (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) ? t_buffer.model.data.indices_size_bytes() : t_buffer.max_indices_per_segment_size_bytes(); + if (i_multibuffer.back().size() * sizeof(IBufferType) >= IBUFFER_THRESHOLD_BYTES - indiced_size_to_add) { +#else if (i_multibuffer.back().size() * sizeof(IBufferType) >= IBUFFER_THRESHOLD_BYTES - t_buffer.max_indices_per_segment_size_bytes()) { +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS i_multibuffer.push_back(IndexBuffer()); vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]); +#if ENABLE_SEAMS_USING_BATCHED_MODELS + if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point && + t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::BatchedModel) { +#else if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) { +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS Path& last_path = t_buffer.paths.back(); last_path.add_sub_path(prev, static_cast(i_multibuffer.size()) - 1, 0, i - 1); } @@ -1861,14 +1936,24 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) // if adding the vertices for the current segment exceeds the threshold size of the current vertex buffer // create another index buffer +#if ENABLE_SEAMS_USING_BATCHED_MODELS + size_t vertices_size_to_add = (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) ? t_buffer.model.data.vertices_size_bytes() : t_buffer.max_vertices_per_segment_size_bytes(); + if (curr_vertex_buffer.second * t_buffer.vertices.vertex_size_bytes() > t_buffer.vertices.max_size_bytes() - vertices_size_to_add) { +#else if (curr_vertex_buffer.second * t_buffer.vertices.vertex_size_bytes() > t_buffer.vertices.max_size_bytes() - t_buffer.max_vertices_per_segment_size_bytes()) { +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS i_multibuffer.push_back(IndexBuffer()); ++curr_vertex_buffer.first; curr_vertex_buffer.second = 0; vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]); +#if ENABLE_SEAMS_USING_BATCHED_MODELS + if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point && + t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::BatchedModel) { +#else if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) { +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS Path& last_path = t_buffer.paths.back(); last_path.add_sub_path(prev, static_cast(i_multibuffer.size()) - 1, 0, i - 1); } @@ -1892,6 +1977,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) add_indices_as_solid(prev, curr, next, t_buffer, curr_vertex_buffer.second, static_cast(i_multibuffer.size()) - 1, i_buffer, i); break; } +#if ENABLE_SEAMS_USING_BATCHED_MODELS + case TBuffer::ERenderPrimitiveType::BatchedModel: { + add_indices_as_model_batch(t_buffer.model.data, i_buffer, curr_vertex_buffer.second); + curr_vertex_buffer.second += t_buffer.model.data.vertices_count(); + break; + } +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS default: { break; } } } @@ -1906,7 +1998,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result) for (size_t i = 0; i < m_buffers.size(); ++i) { TBuffer& t_buffer = m_buffers[i]; #if ENABLE_SEAMS_USING_MODELS +#if ENABLE_SEAMS_USING_BATCHED_MODELS + if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::InstancedModel) { +#else if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Model) { +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS #endif // ENABLE_SEAMS_USING_MODELS const MultiIndexBuffer& i_multibuffer = indices[i]; for (const IndexBuffer& i_buffer : i_multibuffer) { @@ -2200,7 +2296,12 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool continue; #if ENABLE_SEAMS_USING_MODELS +#if ENABLE_SEAMS_USING_BATCHED_MODELS + if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::InstancedModel || + buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) { +#else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Model) { +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS for (size_t id : buffer.model.instances.s_ids) { if (id < m_layers.get_range_at(m_layers_z_range[0]).first || m_layers.get_range_at(m_layers_z_range[1]).last < id) continue; @@ -2265,7 +2366,12 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool bool found = false; for (const TBuffer& buffer : m_buffers) { #if ENABLE_SEAMS_USING_MODELS +#if ENABLE_SEAMS_USING_BATCHED_MODELS + if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::InstancedModel || + buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) { +#else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Model) { +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS for (size_t i = 0; i < buffer.model.instances.s_ids.size(); ++i) { if (buffer.model.instances.s_ids[i] == m_sequential_view.current.last) { size_t offset = i * buffer.model.instances.instance_size_floats(); @@ -2426,10 +2532,19 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool } #if ENABLE_SEAMS_USING_MODELS +#if ENABLE_SEAMS_USING_BATCHED_MODELS + // second pass: for buffers using instanced and batched models, update the instances render ranges +#else // second pass: for buffers using instanced models, update the instances render ranges +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS for (size_t b = 0; b < m_buffers.size(); ++b) { TBuffer& buffer = const_cast(m_buffers[b]); +#if ENABLE_SEAMS_USING_BATCHED_MODELS + if (buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::InstancedModel && + buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::BatchedModel) +#else if (buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Model) +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS continue; buffer.model.instances.render_ranges.reset(); @@ -2445,7 +2560,11 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool if (m_sequential_view.current.first <= buffer.model.instances.s_ids.back() && buffer.model.instances.s_ids.front() <= m_sequential_view.current.last) { for (size_t id : buffer.model.instances.s_ids) { if (has_second_range) { +#if ENABLE_FIX_PREVIEW_OPTIONS_Z + if (id < m_sequential_view.endpoints.first) { +#else if (id <= m_sequential_view.endpoints.first) { +#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z ++buffer.model.instances.render_ranges.ranges.front().offset; if (id <= m_sequential_view.current.first) ++buffer.model.instances.render_ranges.ranges.back().offset; @@ -2715,6 +2834,66 @@ void GCodeViewer::render_toolpaths() } } }; + +#if ENABLE_SEAMS_USING_BATCHED_MODELS +#if ENABLE_GCODE_VIEWER_STATISTICS + auto render_as_batched_model = [this](TBuffer& buffer, GLShaderProgram& shader) { +#else + auto render_as_batched_model = [](TBuffer& buffer, GLShaderProgram& shader) { +#endif // ENABLE_GCODE_VIEWER_STATISTICS + + struct Range + { + unsigned int first; + unsigned int last; + bool intersects(const Range& other) const { return (other.last < first || other.first > last) ? false : true; } + }; + Range buffer_range = { 0, 0 }; + size_t indices_per_instance = buffer.model.data.indices_count(); + + for (size_t j = 0; j < buffer.indices.size(); ++j) { + const IBuffer& i_buffer = buffer.indices[j]; + buffer_range.last = buffer_range.first + i_buffer.count / indices_per_instance; + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo)); + glsafe(::glVertexPointer(buffer.vertices.position_size_floats(), GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_bytes())); + glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); + bool has_normals = buffer.vertices.normal_size_floats() > 0; + if (has_normals) { + glsafe(::glNormalPointer(GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.normal_offset_bytes())); + glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); + } + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo)); + + for (auto& range : buffer.model.instances.render_ranges.ranges) { + Range range_range = { range.offset, range.offset + range.count }; + if (range_range.intersects(buffer_range)) { + shader.set_uniform("uniform_color", range.color); + unsigned int offset = (range_range.first > buffer_range.first) ? range_range.first - buffer_range.first : 0; + size_t offset_bytes = static_cast(offset) * indices_per_instance * sizeof(IBufferType); + Range render_range = { std::max(range_range.first, buffer_range.first), std::min(range_range.last, buffer_range.last) }; + size_t count = static_cast(render_range.last - render_range.first) * indices_per_instance; + if (count > 0) { + glsafe(::glDrawElements(GL_TRIANGLES, (GLsizei)count, GL_UNSIGNED_SHORT, (const void*)offset_bytes)); +#if ENABLE_GCODE_VIEWER_STATISTICS + ++m_statistics.gl_batched_models_calls_count; +#endif // ENABLE_GCODE_VIEWER_STATISTICS + } + } + } + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + if (has_normals) + glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); + + glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + + buffer_range.first = buffer_range.last; + } + }; +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS #endif // ENABLE_SEAMS_USING_MODELS auto line_width = [](double zoom) { @@ -2738,11 +2917,22 @@ void GCodeViewer::render_toolpaths() shader->start_using(); #if ENABLE_SEAMS_USING_MODELS +#if ENABLE_SEAMS_USING_BATCHED_MODELS + if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::InstancedModel) { +#else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Model) { +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS shader->set_uniform("emission_factor", 0.25f); render_as_instanced_model(buffer, *shader); shader->set_uniform("emission_factor", 0.0f); } +#if ENABLE_SEAMS_USING_BATCHED_MODELS + else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) { + shader->set_uniform("emission_factor", 0.25f); + render_as_batched_model(buffer, *shader); + shader->set_uniform("emission_factor", 0.0f); + } +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS else { #endif // ENABLE_SEAMS_USING_MODELS for (size_t j = 0; j < buffer.indices.size(); ++j) { @@ -2895,7 +3085,7 @@ void GCodeViewer::render_legend(float& legend_height) bool imperial_units = wxGetApp().app_config->get("use_inches") == "1"; - auto append_item = [this, icon_size, percent_bar_size, &imgui, imperial_units](EItemType type, const Color& color, const std::string& label, + auto append_item = [icon_size, percent_bar_size, &imgui, imperial_units](EItemType type, const Color& color, const std::string& label, bool visible = true, const std::string& time = "", float percent = 0.0f, float max_percent = 0.0f, const std::array& offsets = { 0.0f, 0.0f, 0.0f, 0.0f }, double used_filament_m = 0.0, double used_filament_g = 0.0, std::function callback = nullptr) { @@ -2913,6 +3103,7 @@ void GCodeViewer::render_legend(float& legend_height) } case EItemType::Circle: { ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size)); +#if !ENABLE_SEAMS_USING_BATCHED_MODELS if (m_buffers[buffer_id(EMoveType::Retract)].shader == "options_120") { draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); @@ -2922,6 +3113,7 @@ void GCodeViewer::render_legend(float& legend_height) draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16); } else +#endif // !ENABLE_SEAMS_USING_BATCHED_MODELS draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16); break; @@ -3597,7 +3789,11 @@ void GCodeViewer::render_legend(float& legend_height) auto add_option = [this, append_item](EMoveType move_type, EOptionsColors color, const std::string& text) { const TBuffer& buffer = m_buffers[buffer_id(move_type)]; if (buffer.visible && buffer.has_data()) +#if ENABLE_SEAMS_USING_BATCHED_MODELS + append_item(EItemType::Circle, Options_Colors[static_cast(color)], text); +#else append_item((buffer.shader == "options_110") ? EItemType::Rect : EItemType::Circle, Options_Colors[static_cast(color)], text); +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS }; // options section @@ -3821,7 +4017,11 @@ void GCodeViewer::render_statistics() add_counter(std::string("Multi GL_TRIANGLES:"), m_statistics.gl_multi_triangles_calls_count); add_counter(std::string("GL_TRIANGLES:"), m_statistics.gl_triangles_calls_count); #if ENABLE_SEAMS_USING_MODELS + ImGui::Separator(); add_counter(std::string("Instanced models:"), m_statistics.gl_instanced_models_calls_count); +#if ENABLE_SEAMS_USING_BATCHED_MODELS + add_counter(std::string("Batched models:"), m_statistics.gl_batched_models_calls_count); +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS #endif // ENABLE_SEAMS_USING_MODELS } @@ -3853,6 +4053,9 @@ void GCodeViewer::render_statistics() add_counter(std::string("Extrude segments count:"), m_statistics.extrude_segments_count); #if ENABLE_SEAMS_USING_MODELS add_counter(std::string("Instances count:"), m_statistics.instances_count); +#if ENABLE_SEAMS_USING_BATCHED_MODELS + add_counter(std::string("Batched count:"), m_statistics.batched_count); +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS #endif // ENABLE_SEAMS_USING_MODELS ImGui::Separator(); add_counter(std::string("VBuffers count:"), m_statistics.vbuffers_count); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 73bdb0ac0..fe88cc539 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -105,9 +105,16 @@ class GCodeViewer }; #if ENABLE_SEAMS_USING_MODELS +#if ENABLE_SEAMS_USING_BATCHED_MODELS + // buffer containing instances data used to render a toolpaths using instanced or batched models + // instance record format: + // instanced models: 5 floats -> position.x|position.y|position.z|width|height (which are sent to the shader as -> vec3 (offset) + vec2 (scales) in GLModel::render_instanced()) + // batched models: 3 floats -> position.x|position.y|position.z +#else // buffer containing instances data used to render a toolpaths using instanced models // instance record format: 5 floats -> position.x|position.y|position.z|width|height // which is sent to the shader as -> vec3 (offset) + vec2 (scales) in GLModel::render_instanced() +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS struct InstanceVBuffer { // ranges used to render only subparts of the intances @@ -130,6 +137,16 @@ class GCodeViewer void reset(); }; +#if ENABLE_SEAMS_USING_BATCHED_MODELS + enum class EFormat : unsigned char + { + InstancedModel, + BatchedModel + }; + + EFormat format; +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS + // cpu-side buffer containing all instances data InstanceBuffer buffer; // indices of the moves for all instances @@ -138,7 +155,18 @@ class GCodeViewer size_t data_size_bytes() const { return s_ids.size() * instance_size_bytes(); } +#if ENABLE_SEAMS_USING_BATCHED_MODELS + size_t instance_size_floats() const { + switch (format) + { + case EFormat::InstancedModel: { return 5; } + case EFormat::BatchedModel: { return 3; } + default: { return 0; } + } + } +#else size_t instance_size_floats() const { return 5; } +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS size_t instance_size_bytes() const { return instance_size_floats() * sizeof(float); } void reset(); @@ -280,7 +308,12 @@ class GCodeViewer Line, #if ENABLE_SEAMS_USING_MODELS Triangle, +#if ENABLE_SEAMS_USING_BATCHED_MODELS + InstancedModel, + BatchedModel +#else Model +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS #else Triangle #endif // ENABLE_SEAMS_USING_MODELS @@ -298,6 +331,9 @@ class GCodeViewer GLModel model; Color color; InstanceVBuffer instances; +#if ENABLE_SEAMS_USING_BATCHED_MODELS + GLModel::InitializationData data; +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS void reset(); }; @@ -362,7 +398,15 @@ class GCodeViewer case ERenderPrimitiveType::Triangle: { return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0; } +#if ENABLE_SEAMS_USING_BATCHED_MODELS + case ERenderPrimitiveType::InstancedModel: { return model.model.is_initialized() && !model.instances.buffer.empty(); } + case ERenderPrimitiveType::BatchedModel: { + return model.data.vertices_count() > 0 && model.data.indices_count() && + !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0; + } +#else case ERenderPrimitiveType::Model: { return model.model.is_initialized() && !model.instances.buffer.empty(); } +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS default: { return false; } } } @@ -528,6 +572,9 @@ class GCodeViewer int64_t gl_triangles_calls_count{ 0 }; #if ENABLE_SEAMS_USING_MODELS int64_t gl_instanced_models_calls_count{ 0 }; +#if ENABLE_SEAMS_USING_BATCHED_MODELS + int64_t gl_batched_models_calls_count{ 0 }; +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS #endif // ENABLE_SEAMS_USING_MODELS // memory int64_t results_size{ 0 }; @@ -549,6 +596,9 @@ class GCodeViewer int64_t extrude_segments_count{ 0 }; #if ENABLE_SEAMS_USING_MODELS int64_t instances_count{ 0 }; +#if ENABLE_SEAMS_USING_BATCHED_MODELS + int64_t batched_count{ 0 }; +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS #endif // ENABLE_SEAMS_USING_MODELS int64_t vbuffers_count{ 0 }; int64_t ibuffers_count{ 0 }; @@ -577,6 +627,9 @@ class GCodeViewer gl_triangles_calls_count = 0; #if ENABLE_SEAMS_USING_MODELS gl_instanced_models_calls_count = 0; +#if ENABLE_SEAMS_USING_BATCHED_MODELS + gl_batched_models_calls_count = 0; +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS #endif // ENABLE_SEAMS_USING_MODELS } @@ -602,6 +655,9 @@ class GCodeViewer extrude_segments_count = 0; #if ENABLE_SEAMS_USING_MODELS instances_count = 0; +#if ENABLE_SEAMS_USING_BATCHED_MODELS + batched_count = 0; +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS #endif // ENABLE_SEAMS_USING_MODELS vbuffers_count = 0; ibuffers_count = 0; @@ -647,18 +703,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(); } @@ -791,8 +846,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/GLModel.cpp b/src/slic3r/GUI/GLModel.cpp index 5cddd7fa3..d22925673 100644 --- a/src/slic3r/GUI/GLModel.cpp +++ b/src/slic3r/GUI/GLModel.cpp @@ -19,6 +19,26 @@ namespace Slic3r { namespace GUI { +#if ENABLE_SEAMS_USING_BATCHED_MODELS +size_t GLModel::InitializationData::vertices_count() const +{ + size_t ret = 0; + for (const Entity& entity : entities) { + ret += entity.positions.size(); + } + return ret; +} + +size_t GLModel::InitializationData::indices_count() const +{ + size_t ret = 0; + for (const Entity& entity : entities) { + ret += entity.indices.size(); + } + return ret; +} +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS + void GLModel::init_from(const InitializationData& data) { if (!m_render_data.empty()) // call reset() if you want to reuse this model diff --git a/src/slic3r/GUI/GLModel.hpp b/src/slic3r/GUI/GLModel.hpp index 8e0039b08..7422466f0 100644 --- a/src/slic3r/GUI/GLModel.hpp +++ b/src/slic3r/GUI/GLModel.hpp @@ -48,6 +48,15 @@ namespace GUI { }; std::vector entities; + +#if ENABLE_SEAMS_USING_BATCHED_MODELS + size_t vertices_count() const; + size_t vertices_size_floats() const { return vertices_count() * 6; } + size_t vertices_size_bytes() const { return vertices_size_floats() * sizeof(float); } + + size_t indices_count() const; + size_t indices_size_bytes() const { return indices_count() * sizeof(unsigned int); } +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS }; private: diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index edbe2bb46..5dd478b57 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -33,11 +33,19 @@ std::pair GLShadersManager::init() bool valid = true; +#if ENABLE_SEAMS_USING_BATCHED_MODELS + // used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells, options in gcode preview +#else // used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS valid &= append_shader("gouraud_light", { "gouraud_light.vs", "gouraud_light.fs" }); // used to render printbed valid &= append_shader("printbed", { "printbed.vs", "printbed.fs" }); // used to render options in gcode preview +#if ENABLE_SEAMS_USING_BATCHED_MODELS + if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) + valid &= append_shader("gouraud_light_instanced", { "gouraud_light_instanced.vs", "gouraud_light_instanced.fs" }); +#else #if ENABLE_SEAMS_USING_MODELS if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) valid &= append_shader("gouraud_light_instanced", { "gouraud_light_instanced.vs", "gouraud_light_instanced.fs" }); @@ -49,6 +57,7 @@ std::pair GLShadersManager::init() #if ENABLE_SEAMS_USING_MODELS } #endif // ENABLE_SEAMS_USING_MODELS +#endif // ENABLE_SEAMS_USING_BATCHED_MODELS // used to render extrusion and travel paths as lines in gcode preview valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" }); // used to render objects in 3d editor diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index c99d9090c..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*/)) 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 af8734184..76612a718 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4871,10 +4871,9 @@ void Plater::load_gcode(const wxString& filename) // process gcode GCodeProcessor processor; - processor.enable_producers(true); try { - processor.process_file(filename.ToUTF8().data(), false); + processor.process_file(filename.ToUTF8().data()); } catch (const std::exception& ex) { @@ -6143,16 +6142,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() {} };