diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 9e6d1434fb3..6c4419c9f3c 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -143,6 +143,8 @@ set(SLIC3R_SOURCES Format/SL1_SVG.cpp Format/AnycubicSLA.hpp Format/AnycubicSLA.cpp + Format/GOO.hpp + Format/GOO.cpp Format/STEP.hpp Format/STEP.cpp Format/SVG.hpp diff --git a/src/libslic3r/Format/GOO.cpp b/src/libslic3r/Format/GOO.cpp new file mode 100644 index 00000000000..67220d62a6a --- /dev/null +++ b/src/libslic3r/Format/GOO.cpp @@ -0,0 +1,434 @@ +///|/ Copyright (c) 2024 Felix Reißmann @felix-rm +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ + +#include "GOO.hpp" + +#include "Time.hpp" +#include "libslic3r/SLAPrint.hpp" + +#include +#include + +namespace Slic3r { + +namespace { + +// Templated host to network endianess conversion +template static T hton(T value) { + auto *bytes = reinterpret_cast(&value); + for (size_t i = 0; i < sizeof(T) / 2; ++i) + std::swap(bytes[i], bytes[sizeof(T) - 1 - i]); + return value; +} + +template static T constrain(T value, T min, T max) { + if (value < min) + return min; + else if (value > max) + return max; + else + return value; +} + +static float constrained_map(float value, float min, float max, float out_min, float out_max) { + value = constrain(value, min, max); + float t = (value - min) / (max - min); + return out_min * (1 - t) + out_max * t; +} + +static void write_thumbnail( + size_t width, size_t height, const ThumbnailsList &thumbnails, uint16_t *buffer +) { + if (thumbnails.empty()) + return; + + auto thumbnail = std::find_if(thumbnails.begin(), thumbnails.end(), [&](const ThumbnailData &t) { + return t.width == width && t.height == height; + }); + + // If no exact match is found choose the largest (last) and let the scaling do the rest + if (thumbnail == thumbnails.end()) + thumbnail = thumbnails.begin() + (thumbnails.size() - 1); + + auto point_to_index = [](size_t x, size_t y, size_t width, size_t pixel_size) { + return y * pixel_size * width + x * pixel_size; + }; + + // Scale thumbnail to fit (without cropping) + size_t thumb_x_max = thumbnail->width - 1; + size_t thumb_y_max = thumbnail->height - 1; + size_t thumb_scaling = std::max(thumb_x_max, thumb_y_max); + + for (size_t y = 0; y < height; y++) { + for (size_t x = 0; x < width; x++) { + double x_norm = double(x) / (width - 1) - .5; + double y_norm = double(y) / (height - 1) - .5; + + int from_x = round(thumb_x_max / 2.0 + x_norm * thumb_scaling); + int from_y = round(thumb_y_max / 2.0 + y_norm * thumb_scaling); + + if (from_x < 0 || from_x > thumb_x_max || from_y < 0 || from_y > thumb_y_max) + continue; + + auto to_idx = point_to_index(x, height - 1 - y, width, 1); + auto from_idx = point_to_index(from_x, from_y, thumbnail->width, 4); + + // Reformat pixel color into uint16_t and write to buffer + uint16_t r = thumbnail->pixels[from_idx] >> 3; + uint16_t g = thumbnail->pixels[from_idx + 1] >> 2; + uint16_t b = thumbnail->pixels[from_idx + 2] >> 3; + uint16_t pixel = (r << 11) | (g << 5) | b; + buffer[to_idx] = pixel << 8 | pixel >> 8; + } + } +} + +template +static const ConfigOption &read_config_value(const DynamicPrintConfig &config, std::string key) { + static ConfigOptionType fallback{}; + + if (config.has(key)) + return *config.option(key); + + return fallback; +} + +static void write_config_value( + char *buffer, size_t size, const DynamicPrintConfig &config, std::string key +) { + std::string value{}; + + if (config.has(key)) { + auto opt = config.option(key); + value = opt->serialize(); + } + + strncpy(buffer, value.c_str(), size); +} + +static void write_timestamp(char *buffer, size_t size) { + auto timestamp = Utils::utc_timestamp(); + strncpy(buffer, timestamp.c_str(), size); +} + +#if defined(__GNUC__) || defined(__clang__) +#define PACKED_PRE +#define PACKED_IN [[gnu::packed]] +#define PACKED_POST +#elif defined(_MSC_VER) +#define PACKED_PRE __pragma(pack(push, 1)) +#define PACKED_IN +#define PACKED_POST __pragma(pack(pop)) +#elif +#error "Unknown compiler for structure packing" +#endif + +// NOTE: All members need to be converted to big endian +PACKED_PRE; +struct PACKED_IN header_info +{ + char version[4] = {'v', '3', '.', '0'}; + uint8_t magic_tag[8] = {0x07, 0x00, 0x00, 0x00, 0x44, 0x4c, 0x50, 0x00}; + char software_info[32] = SLIC3R_APP_NAME; + char software_version[24] = SLIC3R_VERSION; + char file_time[24]{}; + char printer_name[32]{}; + char printer_type[32]{}; + char resin_profile_name[32]{}; + uint16_t aa_level; + uint16_t grey_level; + uint16_t blur_level; + uint16_t small_preview[116 * 116]{}; + uint8_t delimiter_1[2] = {0xd, 0xa}; + uint16_t big_preview[290 * 290]{}; + uint8_t delimiter_2[2] = {0xd, 0xa}; + uint32_t total_layers; + uint16_t x_resolution; + uint16_t y_resolution; + uint8_t x_mirror; + uint8_t y_mirror; + float x_platform_size_mm; + float y_platform_size_mm; + float z_platform_size_mm; + float layer_thickness_mm; + float common_exposure_time_s; + bool exposure_delivery_time_static; + float turn_off_time_s; + float bottom_before_lift_time_s; + float bottom_after_lift_time_s; + float bottom_after_retract_time_s; + float before_lift_time_s; + float after_lift_time_s; + float after_retract_time_s; + float bottom_exposure_time_s; + uint32_t bottom_layers; + float bottom_lift_distance_mm; + float bottom_lift_speed_mm_min; + float lift_distance_mm; + float lift_speed_mm_min; + float bottom_retract_distance_mm; + float bottom_retract_speed_mm_min; + float retract_distance_mm; + float retract_speed_mm_min; + float bottom_second_lift_distance_mm; + float bottom_second_lift_speed_mm_min; + float second_lift_distance_mm; + float second_lift_speed_mm_min; + float bottom_second_retract_distance_mm; + float bottom_second_retract_speed_mm_min; + float second_retract_distance_mm; + float second_retract_speed_mm_min; + uint16_t bottom_light_pwm; + uint16_t light_pwm; + bool advance_mode_layer_definition; + uint32_t printing_time_s; + float total_volume_mm3; + float total_weight_g; + float total_price; + uint8_t price_unit[8]; + uint32_t layer_content_offset; + bool grayscale_level = 0; + uint16_t transition_layers; +}; +PACKED_POST; +static_assert(sizeof(header_info) == 195477, "struct is not packed"); + +// NOTE: All members need to be converted to big endian +PACKED_PRE; +struct PACKED_IN layer_definition +{ + uint8_t reserved{}; + bool pause_at_layer; + float pause_lift_distance_mm; + float position_mm; + float exposure_time_s; + float off_time_s; + float before_lift_time_s; + float after_lift_time_s; + float after_retract_time_s; + float lift_distance_mm; + float lift_speed_mm_min; + float second_lift_distance_mm; + float second_lift_speed_mm_min; + float retract_distance_mm; + float retract_speed_mm_min; + float second_retract_distance_mm; + float second_retract_speed_mm_min; + uint16_t light_pwm; + uint8_t delimiter[2] = {0xd, 0xa}; +}; +PACKED_POST; +static_assert(sizeof(layer_definition) == 66, "struct is not packed"); + +#undef PACKED_PRE +#undef PACKED_IN +#undef PACKED_POST + +} // namespace + +std::unique_ptr GOOWriter::create_raster() const { + auto orientation = m_cfg.display_orientation.getInt() == sla::RasterBase::roPortrait ? + sla::RasterBase::roPortrait : + sla::RasterBase::roLandscape; + + sla::Resolution resolution{ + static_cast(m_cfg.display_pixels_x.getInt()), + static_cast(m_cfg.display_pixels_y.getInt()) + }; + + sla::PixelDim pixel_dimensions{ + m_cfg.display_width.getFloat() / resolution.width_px, + m_cfg.display_height.getFloat() / resolution.height_px + }; + + if (orientation == sla::RasterBase::roPortrait) { + std::swap(resolution.width_px, resolution.height_px); + std::swap(pixel_dimensions.w_mm, pixel_dimensions.h_mm); + } + + std::array mirror = {}; + mirror[X] = m_cfg.display_mirror_x.getBool(); + mirror[Y] = m_cfg.display_mirror_y.getBool(); + + sla::RasterBase::Trafo tr{orientation, mirror}; + + double gamma = m_cfg.gamma_correction.getFloat(); + + return sla::create_raster_grayscale_aa(resolution, pixel_dimensions, gamma, tr); +} + +sla::RasterEncoder GOOWriter::get_encoder() const { return sla::GOORLERasterEncoder{}; } + +void GOOWriter::export_print( + const std::string filename, + const SLAPrint &print, + const ThumbnailsList &thumbnails, + const std::string &project_name +) { + static constexpr std::array end_string = {0x00, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x00, 0x44, 0x4c, 0x50, 0x00}; + + auto &config = print.full_print_config(); + auto &stats = print.print_statistics(); + auto &obj_stats = print.default_object_config(); + auto &mat = print.material_config(); + + auto option_as_float = [](auto const &option) -> float { return option.getFloat(); }; + auto option_as_uint16 = [](auto const &option) -> uint16_t { return option.getInt(); }; + + auto orientation = static_cast(m_cfg.display_orientation.getInt()); + + sla::Resolution resolution{ + static_cast(m_cfg.display_pixels_x.getInt()), + static_cast(m_cfg.display_pixels_y.getInt()) + }; + + if (orientation == sla::RasterBase::roPortrait) + std::swap(resolution.width_px, resolution.height_px); + + // NOTE: We populate exposure, lift, retract and wait parameters here but only the + // ones in the layer_definition will be used for printing. + // (advance_mode_layer_definition = true) + // This allows more control over exposure and layer hight... + + // clang-format off + header_info h_info{}; + h_info.aa_level = hton(m_cfg.gamma_correction.getFloat() * 100); + h_info.grey_level = 0, // NOTE: Unused + h_info.blur_level = 0, // NOTE: Unused + h_info.total_layers = hton(stats.slow_layers_count + stats.fast_layers_count); + h_info.x_resolution = hton(resolution.width_px); + h_info.y_resolution = hton(resolution.height_px); + h_info.x_mirror = m_cfg.display_mirror_x.getBool(); + h_info.y_mirror = m_cfg.display_mirror_y.getBool(); + h_info.x_platform_size_mm = hton(option_as_float(m_cfg.display_width)); + h_info.y_platform_size_mm = hton(option_as_float(m_cfg.display_height)); + h_info.z_platform_size_mm = hton(option_as_float(m_cfg.max_print_height)); + h_info.layer_thickness_mm = hton(option_as_float(obj_stats.layer_height)); + h_info.common_exposure_time_s = hton(option_as_float(mat.exposure_time)); + h_info.exposure_delivery_time_static = true; + h_info.turn_off_time_s = 0, // NOTE: Unused because of exposure_delivery_time_static==tru; + h_info.bottom_before_lift_time_s = hton(option_as_float(mat.sla_initial_wait_before_lift)); + h_info.bottom_after_lift_time_s = hton(option_as_float(mat.sla_initial_wait_after_lift)); + h_info.bottom_after_retract_time_s = hton(option_as_float(mat.sla_initial_wait_after_retract)); + h_info.before_lift_time_s = hton(option_as_float(mat.sla_wait_before_lift)); + h_info.after_lift_time_s = hton(option_as_float(mat.sla_wait_after_lift)); + h_info.after_retract_time_s = hton(option_as_float(mat.sla_wait_after_retract)); + h_info.bottom_exposure_time_s = hton(option_as_float(mat.initial_exposure_time)); + h_info.bottom_layers = hton(obj_stats.faded_layers + 1), // NOTE: Faded layers + initial layer have increased exposur; + h_info.bottom_lift_distance_mm = hton(option_as_float(mat.sla_initial_primary_lift_distance)); + h_info.bottom_lift_speed_mm_min = hton(option_as_float(mat.sla_initial_primary_lift_speed)); + h_info.lift_distance_mm = hton(option_as_float(mat.sla_primary_lift_distance)); + h_info.lift_speed_mm_min = hton(option_as_float(mat.sla_primary_lift_speed)); + h_info.bottom_retract_distance_mm = hton(option_as_float(mat.sla_initial_primary_retract_distance)); + h_info.bottom_retract_speed_mm_min = hton(option_as_float(mat.sla_initial_primary_retract_speed)); + h_info.retract_distance_mm = hton(option_as_float(mat.sla_primary_retract_distance)); + h_info.retract_speed_mm_min = hton(option_as_float(mat.sla_primary_retract_speed)); + h_info.bottom_second_lift_distance_mm = hton(option_as_float(mat.sla_initial_secondary_lift_distance)); + h_info.bottom_second_lift_speed_mm_min = hton(option_as_float(mat.sla_initial_secondary_lift_speed)); + h_info.second_lift_distance_mm = hton(option_as_float(mat.sla_secondary_lift_distance)); + h_info.second_lift_speed_mm_min = hton(option_as_float(mat.sla_secondary_lift_speed)); + h_info.bottom_second_retract_distance_mm = hton(option_as_float(mat.sla_initial_secondary_retract_distance)); + h_info.bottom_second_retract_speed_mm_min = hton(option_as_float(mat.sla_initial_secondary_retract_speed)); + h_info.second_retract_distance_mm = hton(option_as_float(mat.sla_secondary_retract_distance)); + h_info.second_retract_speed_mm_min = hton(option_as_float(mat.sla_secondary_retract_speed)); + h_info.bottom_light_pwm = hton(option_as_uint16(mat.initial_exposure_pwm)); + h_info.light_pwm = hton(option_as_uint16(mat.exposure_pwm)); + h_info.advance_mode_layer_definition = false; + h_info.printing_time_s = hton(stats.estimated_print_time); + h_info.total_volume_mm3 = hton(stats.total_weight / mat.material_density.getFloat()); + h_info.total_weight_g = hton(stats.total_weight); + h_info.total_price = hton(mat.bottle_cost.getFloat() / + (mat.bottle_volume.getFloat() * mat.material_density.getFloat()) * stats.total_weight); + h_info.price_unit[0] = '\0'; //NOTE: No currency unit + h_info.layer_content_offset = hton(sizeof(header_info)); + h_info.grayscale_level = true; + h_info.transition_layers = hton(option_as_uint16(obj_stats.faded_layers)); + // clang-format on + + write_timestamp(h_info.file_time, sizeof(h_info.file_time)); + write_thumbnail(116, 116, thumbnails, h_info.small_preview); + write_thumbnail(290, 290, thumbnails, h_info.big_preview); + write_config_value(h_info.printer_name, sizeof(h_info.printer_name), config, "printer_model"); + write_config_value(h_info.printer_type, sizeof(h_info.printer_type), config, "printer_variant"); + write_config_value( + h_info.resin_profile_name, sizeof(h_info.resin_profile_name), config, + "sla_material_settings_id" + ); + + std::ofstream output{filename, std::ios_base::binary}; + output.write(reinterpret_cast(&h_info), sizeof(header_info)); + + // Cache value for use in the loop + auto layer_height = obj_stats.layer_height.getFloat(); + + size_t current_layer = 1; + for (const sla::EncodedRaster &raster : m_layers) { + bool bottom = current_layer == 1; + + // clang-format off + layer_definition l_def{}; + l_def.pause_at_layer = false; + l_def.pause_lift_distance_mm = 0; + l_def.position_mm = hton( + mat.initial_layer_height.getFloat() + layer_height * (current_layer - 1) + ); + l_def.exposure_time_s = hton(constrained_map( + current_layer, 1, obj_stats.faded_layers + 1, // + mat.initial_exposure_time, mat.exposure_time + )); + l_def.off_time_s = 0; + l_def.before_lift_time_s = hton(option_as_float( // + bottom ? mat.sla_initial_wait_before_lift : mat.sla_wait_before_lift + )); + l_def.after_lift_time_s = hton(option_as_float( // + bottom ? mat.sla_initial_wait_after_lift : mat.sla_wait_after_lift + )); + l_def.after_retract_time_s = hton(option_as_float( // + bottom ? mat.sla_initial_wait_after_retract : mat.sla_wait_after_retract + )); + l_def.lift_distance_mm = hton(option_as_float( // + bottom ? mat.sla_initial_primary_lift_distance : mat.sla_primary_lift_distance + )); + l_def.lift_speed_mm_min = hton(option_as_float( // + bottom ? mat.sla_initial_primary_lift_speed : mat.sla_primary_lift_speed + )); + l_def.second_lift_distance_mm = hton(option_as_float( // + bottom ? mat.sla_initial_secondary_lift_distance : mat.sla_secondary_lift_distance + )); + l_def.second_lift_speed_mm_min = hton(option_as_float( // + bottom ? mat.sla_initial_secondary_lift_speed : mat.sla_secondary_lift_speed + )); + l_def.retract_distance_mm = hton(option_as_float( // + bottom ? mat.sla_initial_primary_retract_distance : mat.sla_primary_retract_distance + )); + l_def.retract_speed_mm_min = hton(option_as_float( // + bottom ? mat.sla_initial_primary_retract_speed : mat.sla_primary_retract_speed + )); + l_def.second_retract_distance_mm = hton(option_as_float( // + bottom ? mat.sla_initial_secondary_retract_distance : mat.sla_secondary_retract_distance + )); + l_def.second_retract_speed_mm_min = hton(option_as_float( // + bottom ? mat.sla_initial_secondary_retract_speed : mat.sla_secondary_retract_speed + )); + l_def.light_pwm = hton(option_as_uint16(bottom ? mat.initial_exposure_pwm : mat.exposure_pwm)); + // clang-format on + + output.write(reinterpret_cast(&l_def), sizeof(layer_definition)); + output.write(reinterpret_cast(raster.data()), raster.size()); + current_layer++; + } + + output.write(reinterpret_cast(end_string.data()), end_string.size()); +} + +ConfigSubstitutions GOOReader::read( + std::vector &slices, DynamicPrintConfig &profile_out +) { + BOOST_LOG_TRIVIAL(error) << "GOOReader::read not yet implemented"; + return {}; +} + +} // namespace Slic3r diff --git a/src/libslic3r/Format/GOO.hpp b/src/libslic3r/Format/GOO.hpp new file mode 100644 index 00000000000..088d53cc8e0 --- /dev/null +++ b/src/libslic3r/Format/GOO.hpp @@ -0,0 +1,72 @@ +///|/ Copyright (c) 2024 Felix Reißmann @felix-rm +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ + +#pragma once + +#include "SLAArchiveWriter.hpp" +#include "SLAArchiveReader.hpp" + +#include "libslic3r/PrintConfig.hpp" + +namespace Slic3r { + +class GOOWriter : public SLAArchiveWriter +{ + SLAPrinterConfig m_cfg{}; + +protected: + SLAPrinterConfig &cfg() { return m_cfg; } + const SLAPrinterConfig &cfg() const { return m_cfg; } + + std::unique_ptr create_raster() const override; + sla::RasterEncoder get_encoder() const override; + +public: + GOOWriter() = default; + explicit GOOWriter(const SLAPrinterConfig &cfg) : m_cfg(cfg) {} + explicit GOOWriter(SLAPrinterConfig &&cfg) : m_cfg(std::move(cfg)) {} + + void export_print( + const std::string filename, + const SLAPrint &print, + const ThumbnailsList &thumbnails, + const std::string &project_name = "" + ) override; +}; + +class GOOReader : public SLAArchiveReader +{ + using progress_callback_t = std::function; + + SLAImportQuality m_quality = SLAImportQuality::Balanced; + progress_callback_t m_progress_callback; + std::string m_filename; + +protected: + std::string &filename() { return m_filename; } + const std::string &filename() const { return m_filename; } + + progress_callback_t &progress_callback() { return m_progress_callback; } + const progress_callback_t &progress_callback() const { return m_progress_callback; } + +public: + GOOReader() = default; + GOOReader( + const std::string &filename, // + SLAImportQuality quality, + progress_callback_t progress_callback + ) + : m_quality(quality), m_progress_callback(progress_callback), m_filename(filename) {} + + // If the profile is missing from the archive (older PS versions did not have + // it), profile_out's initial value will be used as fallback. profile_out will be empty on + // function return if the archive did not contain any profile. + ConfigSubstitutions read(std::vector &slices, DynamicPrintConfig &profile_out) + override; + + ConfigSubstitutions read(DynamicPrintConfig &profile) override { return {}; } +}; + +} // namespace Slic3r \ No newline at end of file diff --git a/src/libslic3r/Format/SLAArchiveFormatRegistry.cpp b/src/libslic3r/Format/SLAArchiveFormatRegistry.cpp index 01d539fbc83..8d14901baf0 100644 --- a/src/libslic3r/Format/SLAArchiveFormatRegistry.cpp +++ b/src/libslic3r/Format/SLAArchiveFormatRegistry.cpp @@ -1,4 +1,5 @@ ///|/ Copyright (c) Prusa Research 2023 Tomáš Mészáros @tamasmeszaros, Pavel Mikuš @Godrak +///|/ Copyright (c) 2024 Felix Reißmann @felix-rm ///|/ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher ///|/ @@ -10,6 +11,7 @@ #include "SL1_SVG.hpp" #include "AnycubicSLA.hpp" #include "I18N.hpp" +#include "GOO.hpp" #include "SLAArchiveFormatRegistry.hpp" @@ -75,6 +77,22 @@ class Registry { anycubic_sla_format_versioned("pmx2", "Photon Mono X2", ANYCUBIC_SLA_VERSION_515), anycubic_sla_format_versioned("pm3r", "Photon M3 Premium", ANYCUBIC_SLA_VERSION_515), */ + { + "GOO", // id + L("Goo"), // description + "goo", // main extension + {}, // extension aliases + + // Writer factory + [] (const auto &cfg) { + return std::make_unique(cfg); + }, + + // Reader factory + [] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) { + return std::make_unique(fname, quality, progr); + } + }, }; } diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 8be5a3f45f6..17d108862fa 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1,4 +1,5 @@ ///|/ Copyright (c) Prusa Research 2017 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Tomáš Mészáros @tamasmeszaros, Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv, Pavel Mikuš @Godrak, David Kocík @kocikdav, Enrico Turri @enricoturri1966, Vojtěch Král @vojtechkral +///|/ Copyright (c) 2024 Felix Reißmann @felix-rm ///|/ Copyright (c) 2021 Martin Budden ///|/ Copyright (c) 2021 Ilya @xorza ///|/ Copyright (c) 2019 John Drake @foxox @@ -597,6 +598,30 @@ static std::vector s_Preset_sla_material_options { "material_density", "exposure_time", "initial_exposure_time", + "exposure_pwm", + "initial_exposure_pwm", + "sla_initial_primary_lift_distance", + "sla_initial_secondary_lift_distance", + "sla_initial_primary_lift_speed", + "sla_initial_secondary_lift_speed", + "sla_initial_primary_retract_distance", + "sla_initial_secondary_retract_distance", + "sla_initial_primary_retract_speed", + "sla_initial_secondary_retract_speed", + "sla_initial_wait_before_lift", + "sla_initial_wait_after_lift", + "sla_initial_wait_after_retract", + "sla_primary_lift_distance", + "sla_secondary_lift_distance", + "sla_primary_lift_speed", + "sla_secondary_lift_speed", + "sla_primary_retract_distance", + "sla_secondary_retract_distance", + "sla_primary_retract_speed", + "sla_secondary_retract_speed", + "sla_wait_before_lift", + "sla_wait_after_lift", + "sla_wait_after_retract", "material_correction", "material_correction_x", "material_correction_y", @@ -642,6 +667,7 @@ static std::vector s_Preset_sla_printer_options { "elefant_foot_compensation", "elefant_foot_min_width", "gamma_correction", + "time_estimate_correction", "min_exposure_time", "max_exposure_time", "min_initial_exposure_time", "max_initial_exposure_time", "sla_archive_format", "sla_output_precision", //FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset. diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 4404aea7beb..9b987d23f93 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1,4 +1,5 @@ ///|/ Copyright (c) Prusa Research 2016 - 2023 Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Tomáš Mészáros @tamasmeszaros, Oleksandra Iushchenko @YuSanka, Pavel Mikuš @Godrak, David Kocík @kocikdav, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Vojtěch Král @vojtechkral +///|/ Copyright (c) 2024 Felix Reißmann @felix-rm ///|/ Copyright (c) 2023 Pedro Lamas @PedroLamas ///|/ Copyright (c) 2023 Mimoja @Mimoja ///|/ Copyright (c) 2020 - 2021 Sergey Kovalev @RandoMan70 @@ -3787,32 +3788,32 @@ void PrintConfigDef::init_sla_params() def->mode = comExpert; def->set_default_value(new ConfigOptionEnum(sladoPortrait)); - def = this->add("fast_tilt_time", coFloat); + def = this->add_nullable("fast_tilt_time", coFloat); def->label = L("Fast"); def->full_label = L("Fast tilt"); def->tooltip = L("Time of the fast tilt"); def->sidetext = L("s"); def->min = 0; def->mode = comExpert; - def->set_default_value(new ConfigOptionFloat(5.)); + def->set_default_value(new ConfigOptionFloatNullable(5.)); - def = this->add("slow_tilt_time", coFloat); + def = this->add_nullable("slow_tilt_time", coFloat); def->label = L("Slow"); def->full_label = L("Slow tilt"); def->tooltip = L("Time of the slow tilt"); def->sidetext = L("s"); def->min = 0; def->mode = comExpert; - def->set_default_value(new ConfigOptionFloat(8.)); + def->set_default_value(new ConfigOptionFloatNullable(8.)); - def = this->add("high_viscosity_tilt_time", coFloat); + def = this->add_nullable("high_viscosity_tilt_time", coFloat); def->label = L("High viscosity"); def->full_label = L("Tilt for high viscosity resin"); def->tooltip = L("Time of the super slow tilt"); def->sidetext = L("s"); def->min = 0; def->mode = comExpert; - def->set_default_value(new ConfigOptionFloat(10.)); + def->set_default_value(new ConfigOptionFloatNullable(10.)); def = this->add("area_fill", coFloat); def->label = L("Area fill"); @@ -3883,6 +3884,15 @@ void PrintConfigDef::init_sla_params() def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(1.0)); + def = this->add("time_estimate_correction", coFloat); + def->label = L("Time estimate correction"); + def->full_label = L("Time estimate correction"); + def->tooltip = L("This time will be added for every layer when calculating the printing time estimate. " + "It may correct for any additional delays in the printing process."); + def->sidetext = L("s"); + def->min = 0; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.0)); // SLA Material settings. @@ -3998,6 +4008,176 @@ void PrintConfigDef::init_sla_params() def->min = 0; def->set_default_value(new ConfigOptionFloat(15)); + def = this->add("exposure_pwm", coInt); + def->label = L("Exposure PWM"); + def->tooltip = L("Exposure PWM"); + def->sidetext = L("s"); + def->min = 0; + def->max = 255; + def->set_default_value(new ConfigOptionInt(255)); + + def = this->add("initial_exposure_pwm", coInt); + def->label = L("Initial exposure PWM"); + def->tooltip = L("Initial exposure PWM"); + def->sidetext = L("s"); + def->min = 0; + def->max = 255; + def->set_default_value(new ConfigOptionInt(255)); + + def = this->add("sla_initial_primary_lift_distance", coFloat); + def->label = L("Primary"); + def->tooltip = L("Initial primary lift distance"); + def->sidetext = L("mm"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(3)); + + def = this->add("sla_initial_secondary_lift_distance", coFloat); + def->label = L("Secondary"); + def->tooltip = L("Initial secondary lift distance"); + def->sidetext = L("mm"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(4)); + + def = this->add("sla_initial_primary_lift_speed", coFloat); + def->label = L("Primary"); + def->tooltip = L("Initial primary lift speed"); + def->sidetext = L("mm/min"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(70)); + + def = this->add("sla_initial_secondary_lift_speed", coFloat); + def->label = L("Secondary"); + def->tooltip = L("Initial secondary lift speed"); + def->sidetext = L("mm/min"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(200)); + + def = this->add("sla_initial_primary_retract_distance", coFloat); + def->label = L("Primary"); + def->tooltip = L("Initial primary retract distance"); + def->sidetext = L("mm"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(5.5)); + + def = this->add("sla_initial_secondary_retract_distance", coFloat); + def->label = L("Secondary"); + def->tooltip = L("Initial secondary retract distance"); + def->sidetext = L("mm"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(1.5)); + + def = this->add("sla_initial_primary_retract_speed", coFloat); + def->label = L("Primary"); + def->tooltip = L("Initial primary retract speed"); + def->sidetext = L("mm/min"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(200)); + + def = this->add("sla_initial_secondary_retract_speed", coFloat); + def->label = L("Secondary"); + def->tooltip = L("Initial secondary retract speed"); + def->sidetext = L("mm/min"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(70)); + + def = this->add("sla_initial_wait_before_lift", coFloat); + def->label = L("Initial wait before lift"); + def->tooltip = L("Initial wait before lift"); + def->sidetext = L("s"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("sla_initial_wait_after_lift", coFloat); + def->label = L("Initial wait after lift"); + def->tooltip = L("Initial wait after lift"); + def->sidetext = L("s"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(1)); + + def = this->add("sla_initial_wait_after_retract", coFloat); + def->label = L("Initial wait after retract"); + def->tooltip = L("Initial wait after retract"); + def->sidetext = L("s"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(2)); + + def = this->add("sla_primary_lift_distance", coFloat); + def->label = L("Primary"); + def->tooltip = L("Primary lift distance"); + def->sidetext = L("mm"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(3)); + + def = this->add("sla_secondary_lift_distance", coFloat); + def->label = L("Secondary"); + def->tooltip = L("Secondary lift distance"); + def->sidetext = L("mm"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(4)); + + def = this->add("sla_primary_lift_speed", coFloat); + def->label = L("Primary"); + def->tooltip = L("Primary lift speed"); + def->sidetext = L("mm/min"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(70)); + + def = this->add("sla_secondary_lift_speed", coFloat); + def->label = L("Secondary"); + def->tooltip = L("Secondary lift speed"); + def->sidetext = L("mm/min"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(200)); + + def = this->add("sla_primary_retract_distance", coFloat); + def->label = L("Primary"); + def->tooltip = L("Primary retract distance"); + def->sidetext = L("mm"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(5.5)); + + def = this->add("sla_secondary_retract_distance", coFloat); + def->label = L("Secondary"); + def->tooltip = L("Secondary retract distance"); + def->sidetext = L("mm"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(1.5)); + + def = this->add("sla_primary_retract_speed", coFloat); + def->label = L("Primary"); + def->tooltip = L("Primary retract speed"); + def->sidetext = L("mm/min"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(200)); + + def = this->add("sla_secondary_retract_speed", coFloat); + def->label = L("Secondary"); + def->tooltip = L("Secondary retract speed"); + def->sidetext = L("mm/min"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(70)); + + def = this->add("sla_wait_before_lift", coFloat); + def->label = L("Wait before lift"); + def->tooltip = L("Wait before lift"); + def->sidetext = L("s"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("sla_wait_after_lift", coFloat); + def->label = L("Wait after lift"); + def->tooltip = L("Wait after lift"); + def->sidetext = L("s"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("sla_wait_after_retract", coFloat); + def->label = L("Wait after retract"); + def->tooltip = L("Wait after retract"); + def->sidetext = L("s"); + def->min = 0; + def->set_default_value(new ConfigOptionFloat(1)); + def = this->add("material_correction", coFloats); def->full_label = L("Correction for expansion"); def->tooltip = L("Correction for expansion"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index f1dc6b96a43..26b38a717d1 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1,4 +1,5 @@ ///|/ Copyright (c) Prusa Research 2016 - 2023 Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Tomáš Mészáros @tamasmeszaros, Pavel Mikuš @Godrak, David Kocík @kocikdav, Oleksandra Iushchenko @YuSanka, Vojtěch Král @vojtechkral, Enrico Turri @enricoturri1966 +///|/ Copyright (c) 2024 Felix Reißmann @felix-rm ///|/ Copyright (c) 2023 Pedro Lamas @PedroLamas ///|/ Copyright (c) 2020 Sergey Kovalev @RandoMan70 ///|/ Copyright (c) 2021 Martin Budden @@ -1112,6 +1113,30 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, material_density)) ((ConfigOptionFloat, exposure_time)) ((ConfigOptionFloat, initial_exposure_time)) + ((ConfigOptionInt, exposure_pwm)) + ((ConfigOptionInt, initial_exposure_pwm)) + ((ConfigOptionFloat, sla_initial_primary_lift_distance)) + ((ConfigOptionFloat, sla_initial_primary_lift_speed)) + ((ConfigOptionFloat, sla_initial_primary_retract_distance)) + ((ConfigOptionFloat, sla_initial_primary_retract_speed)) + ((ConfigOptionFloat, sla_initial_secondary_lift_distance)) + ((ConfigOptionFloat, sla_initial_secondary_lift_speed)) + ((ConfigOptionFloat, sla_initial_secondary_retract_distance)) + ((ConfigOptionFloat, sla_initial_secondary_retract_speed)) + ((ConfigOptionFloat, sla_initial_wait_before_lift)) + ((ConfigOptionFloat, sla_initial_wait_after_lift)) + ((ConfigOptionFloat, sla_initial_wait_after_retract)) + ((ConfigOptionFloat, sla_primary_lift_distance)) + ((ConfigOptionFloat, sla_primary_lift_speed)) + ((ConfigOptionFloat, sla_primary_retract_distance)) + ((ConfigOptionFloat, sla_primary_retract_speed)) + ((ConfigOptionFloat, sla_secondary_lift_distance)) + ((ConfigOptionFloat, sla_secondary_lift_speed)) + ((ConfigOptionFloat, sla_secondary_retract_distance)) + ((ConfigOptionFloat, sla_secondary_retract_speed)) + ((ConfigOptionFloat, sla_wait_before_lift)) + ((ConfigOptionFloat, sla_wait_after_lift)) + ((ConfigOptionFloat, sla_wait_after_retract)) ((ConfigOptionFloats, material_correction)) ((ConfigOptionFloat, material_correction_x)) ((ConfigOptionFloat, material_correction_y)) @@ -1154,9 +1179,10 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, elefant_foot_compensation)) ((ConfigOptionFloat, elefant_foot_min_width)) ((ConfigOptionFloat, gamma_correction)) - ((ConfigOptionFloat, fast_tilt_time)) - ((ConfigOptionFloat, slow_tilt_time)) - ((ConfigOptionFloat, high_viscosity_tilt_time)) + ((ConfigOptionFloat, time_estimate_correction)) + ((ConfigOptionFloatNullable, fast_tilt_time)) + ((ConfigOptionFloatNullable, slow_tilt_time)) + ((ConfigOptionFloatNullable, high_viscosity_tilt_time)) ((ConfigOptionFloat, area_fill)) ((ConfigOptionFloat, min_exposure_time)) ((ConfigOptionFloat, max_exposure_time)) diff --git a/src/libslic3r/SLA/RasterBase.cpp b/src/libslic3r/SLA/RasterBase.cpp index 9f599e04869..809814fc1eb 100644 --- a/src/libslic3r/SLA/RasterBase.cpp +++ b/src/libslic3r/SLA/RasterBase.cpp @@ -1,4 +1,5 @@ ///|/ Copyright (c) Prusa Research 2020 - 2022 Tomáš Mészáros @tamasmeszaros +///|/ Copyright (c) 2024 Felix Reißmann @felix-rm ///|/ Copyright (c) 2022 ole00 @ole00 ///|/ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher @@ -6,6 +7,9 @@ #ifndef SLARASTER_CPP #define SLARASTER_CPP +#include +#include +#include #include #include @@ -67,6 +71,171 @@ EncodedRaster PPMRasterEncoder::operator()(const void *ptr, size_t w, size_t h, return EncodedRaster(std::move(buf), "ppm"); } +namespace { + +class RunLengthIterate +{ + class iterator + { + public: + explicit iterator(boost::span buffer) : m_buffer(buffer) { + calculate_run_length(); + } + + std::pair operator*() const { + if (m_buffer.empty()) + return {0, 0}; + else + return {m_buffer[0], run_length}; + } + + void operator++() { + m_buffer = m_buffer.subspan(run_length); + calculate_run_length(); + } + + // NOTE: Comparison ignores address of span + // works for comparison against end() because size will be 0 in both compared elements once + // the end is reached + bool operator==(iterator other) { return m_buffer.size() == other.m_buffer.size(); } + bool operator!=(iterator other) { return m_buffer.size() != other.m_buffer.size(); } + + private: + void calculate_run_length() { + run_length = 1; + while (run_length + 1 < m_buffer.size()) { + if (m_buffer[run_length - 1] == m_buffer[run_length]) + run_length++; + else + break; + } + } + + boost::span m_buffer; + size_t run_length{}; + }; + + boost::span m_buffer; + +public: + explicit RunLengthIterate(boost::span buffer) : m_buffer(buffer) {} + + auto begin() const { return iterator{m_buffer}; } + auto end() const { return iterator{boost::span{}}; } +}; + +} + +EncodedRaster GOORLERasterEncoder::operator()( + const void *ptr, size_t w, size_t h, size_t num_components +) { + static constexpr uint8_t magic = 0x55; + static constexpr uint8_t delimiter[] = {0xd, 0xa}; + static constexpr uint8_t header_size = 5; + + enum class type_tag : uint8_t { + VALUE_00 = 0b00, + VALUE_GRAYSCALE = 0b01, + VALUE_DIFF = 0b10, + VALUE_FF = 0b11 + }; + + enum class length_tag : uint8_t { + RUN_LENGTH_4BIT = 0b00, + RUN_LENGTH_12BIT = 0b01, + RUN_LENGTH_20BIT = 0b10, + RUN_LENGTH_28BIT = 0b11, + }; + + struct chunk_header + { + // NOTE: bit field layout is reversed + uint8_t length : 4; + uint8_t length_tag : 2; + uint8_t type_tag : 2; + }; + static_assert(sizeof(chunk_header) == 1, "struct is not packed"); + + if (ptr == nullptr) { + throw std::runtime_error("GOO Encoder received nullptr as image data"); + } + + // NOTE: GOO can only represent grayscale + if (num_components != 1) { + throw std::runtime_error("GOO Encoder received non grayscale image data"); + } + + // NOTE: Layer definition will be written beforehand by the export process + + // Write image data header + std::vector output_buffer(header_size); + // 0-3 reserved for 4 byte of encoded image size + output_buffer[4] = magic; + + boost::span + input_buffer{static_cast(ptr), w * h * num_components}; + + // Write RLE encoded image data + for (auto [value, run_length] : RunLengthIterate(input_buffer)) { + // NOTE: type_tag::VALUE_DIFF not currently supported + type_tag t_tag = [](uint8_t value) { + if (value == 0) + return type_tag::VALUE_00; + else if (value == 255) + return type_tag::VALUE_FF; + else + return type_tag::VALUE_GRAYSCALE; + }(value); + + length_tag l_tag = [](size_t run_length) { + if (run_length >> 4 == 0) + return length_tag::RUN_LENGTH_4BIT; + else if (run_length >> 12 == 0) + return length_tag::RUN_LENGTH_12BIT; + else if (run_length >> 20 == 0) + return length_tag::RUN_LENGTH_20BIT; + else + return length_tag::RUN_LENGTH_28BIT; + }(run_length); + + chunk_header header{}; + header.type_tag = (uint8_t) t_tag; + header.length_tag = (uint8_t) l_tag; + header.length = (uint8_t) (run_length & 0x0f); + + output_buffer.push_back(boost::core::bit_cast(header)); + + if (t_tag == type_tag::VALUE_GRAYSCALE) + output_buffer.push_back(value); + + if (l_tag == length_tag::RUN_LENGTH_28BIT) + output_buffer.push_back((run_length >> 20 & 0xff)); + if (l_tag >= length_tag::RUN_LENGTH_20BIT) + output_buffer.push_back((run_length >> 12 & 0xff)); + if (l_tag >= length_tag::RUN_LENGTH_12BIT) + output_buffer.push_back((run_length >> 4 & 0xff)); + } + + // Write encoded image size (without length, with magic and checksum) into reserved space at the + // start of the buffer + uint32_t encoded_length = output_buffer.size() - header_size + 2; + output_buffer[0] = encoded_length >> 24; + output_buffer[1] = encoded_length >> 16 & 0xff; + output_buffer[2] = encoded_length >> 8 & 0xff; + output_buffer[3] = encoded_length & 0xff; + + // Write checksum + uint8_t checksum = + ~std::reduce(output_buffer.begin() + 5, output_buffer.end(), 0, std::plus{}); + output_buffer.push_back(checksum); + + // Write delimiter + output_buffer.push_back(delimiter[0]); + output_buffer.push_back(delimiter[1]); + + return EncodedRaster(std::move(output_buffer), ""); +} + std::unique_ptr create_raster_grayscale_aa( const Resolution &res, const PixelDim &pxdim, diff --git a/src/libslic3r/SLA/RasterBase.hpp b/src/libslic3r/SLA/RasterBase.hpp index 5f2826aec47..bdd52a5f09f 100644 --- a/src/libslic3r/SLA/RasterBase.hpp +++ b/src/libslic3r/SLA/RasterBase.hpp @@ -1,4 +1,5 @@ ///|/ Copyright (c) Prusa Research 2020 - 2022 Tomáš Mészáros @tamasmeszaros, Vojtěch Bubník @bubnikv +///|/ Copyright (c) 2024 Felix Reißmann @felix-rm ///|/ Copyright (c) 2022 ole00 @ole00 ///|/ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher @@ -109,6 +110,11 @@ struct PPMRasterEncoder { EncodedRaster operator()(const void *ptr, size_t w, size_t h, size_t num_components); }; +// Run-Length-Encoding RasterEncoder for GOO file format +struct GOORLERasterEncoder { + EncodedRaster operator()(const void *ptr, size_t w, size_t h, size_t num_components); +}; + std::ostream& operator<<(std::ostream &stream, const EncodedRaster &bytes); // If gamma is zero, thresholding will be performed which disables AA. diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 56da543fc85..0f6ca9c11f2 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -911,6 +911,23 @@ void SLAPrint::Steps::initialize_printer_input() } } +namespace { +template static T constrain(T value, T min, T max) { + if (value < min) + return min; + else if (value > max) + return max; + else + return value; +} + +static float constrained_map(float value, float min, float max, float out_min, float out_max) { + value = constrain(value, min, max); + float t = (value - min) / (max - min); + return out_min * (1 - t) + out_max * t; +} +} + // Merging the slices from all the print objects into one slice grid and // calculating print statistics from the merge result. void SLAPrint::Steps::merge_slices_and_eval_stats() { @@ -928,10 +945,13 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { const double fast_tilt = printer_config.fast_tilt_time.getFloat();// 5.0; const double slow_tilt = printer_config.slow_tilt_time.getFloat();// 8.0; const double hv_tilt = printer_config.high_viscosity_tilt_time.getFloat();// 10.0; + const bool is_printer_with_tilt = !(std::isnan(fast_tilt) || std::isnan(slow_tilt) || std::isnan(hv_tilt)) ; const double init_exp_time = material_config.initial_exposure_time.getFloat(); const double exp_time = material_config.exposure_time.getFloat(); + const double time_estimate_correction = printer_config.time_estimate_correction.getFloat(); + const int fade_layers_cnt = m_print->m_default_object_config.faded_layers.getInt();// 10 // [3;20] const auto width = scaled(printer_config.display_width.getFloat()); @@ -948,7 +968,6 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { size_t slow_layers = 0; size_t fast_layers = 0; - const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1); double fade_layer_time = init_exp_time; execution::SpinningMutex mutex; @@ -957,7 +976,7 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { // Going to parallel: auto printlayerfn = [this, // functions and read only vars - area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, hv_tilt, material_config, delta_fade_time, + area_fill, display_area, exp_time, init_exp_time, fade_layers_cnt, fast_tilt, slow_tilt, hv_tilt, is_printer_with_tilt, time_estimate_correction, material_config, // write vars &mutex, &models_volume, &supports_volume, &estim_time, &slow_layers, @@ -1037,53 +1056,98 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { layer.transformed_slices(union_ex(trslices)); - // Calculation of the slow and fast layers to the future controlling those values on FW + { + Lock lck(mutex); + if(is_printer_with_tilt){ + // Calculation of the slow and fast layers to the future controlling those values on FW - const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill; - const double tilt_time = material_config.material_print_speed == slamsSlow ? slow_tilt : - material_config.material_print_speed == slamsHighViscosity ? hv_tilt : - is_fast_layer ? fast_tilt : slow_tilt; + const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1); + const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill; + const double tilt_time = material_config.material_print_speed == slamsSlow ? slow_tilt : + material_config.material_print_speed == slamsHighViscosity ? hv_tilt : + is_fast_layer ? fast_tilt : slow_tilt; - { Lock lck(mutex); - if (is_fast_layer) - fast_layers++; - else - slow_layers++; + + if (is_fast_layer) + fast_layers++; + else + slow_layers++; - // Calculation of the printing time + // Calculation of the printing time - double layer_times = 0.0; - if (sliced_layer_cnt < 3) - layer_times += init_exp_time; - else if (fade_layer_time > exp_time) { - fade_layer_time -= delta_fade_time; - layer_times += fade_layer_time; + double layer_times = 0.0; + if (sliced_layer_cnt < 3) + layer_times += init_exp_time; + else if (fade_layer_time > exp_time) { + fade_layer_time -= delta_fade_time; + layer_times += fade_layer_time; + } + else + layer_times += exp_time; + layer_times += tilt_time; + + //// Per layer times (magical constants cuclulated from FW) + + static double exposure_safe_delay_before{ 3.0 }; + static double exposure_high_viscosity_delay_before{ 3.5 }; + static double exposure_slow_move_delay_before{ 1.0 }; + + if (material_config.material_print_speed == slamsSlow) + layer_times += exposure_safe_delay_before; + else if (material_config.material_print_speed == slamsHighViscosity) + layer_times += exposure_high_viscosity_delay_before; + else if (!is_fast_layer) + layer_times += exposure_slow_move_delay_before; + + // Increase layer time for "magic constants" from FW + layer_times += ( + l_height * 5 // tower move + + 120 / 1000 // Magical constant to compensate remaining computation delay in exposure thread + ); + + layer_times += time_estimate_correction; + + layers_times.push_back(layer_times); + estim_time += layer_times; + } else { + bool first_layer = sliced_layer_cnt == 0; + + double layer_exposure_time = constrained_map(sliced_layer_cnt, + 0, fade_layers_cnt, init_exp_time, exp_time); + + // NOTE: Following times are in minutes and are therefore multiplied by 60 + double primary_lift_time = + (first_layer ? material_config.sla_initial_primary_lift_distance : material_config.sla_primary_lift_distance) / + (first_layer ? material_config.sla_initial_primary_lift_speed : material_config.sla_primary_lift_speed) * 60; + double secondary_lift_time = + (first_layer ? material_config.sla_initial_secondary_lift_distance : material_config.sla_secondary_lift_distance) / + (first_layer ? material_config.sla_initial_secondary_lift_speed : material_config.sla_secondary_lift_speed) * 60; + double primary_retract_time = + (first_layer ? material_config.sla_initial_primary_retract_distance : material_config.sla_primary_retract_distance) / + (first_layer ? material_config.sla_initial_primary_retract_speed : material_config.sla_primary_retract_speed) * 60; + double secondary_retract_time = + (first_layer ? material_config.sla_initial_secondary_retract_distance : material_config.sla_secondary_retract_distance) / + (first_layer ? material_config.sla_initial_secondary_retract_speed : material_config.sla_secondary_retract_speed) * 60; + + double wait_times = + (first_layer ? material_config.sla_initial_wait_before_lift : material_config.sla_wait_before_lift) + + (first_layer ? material_config.sla_initial_wait_after_lift : material_config.sla_wait_after_lift) + + (first_layer ? material_config.sla_initial_wait_after_retract : material_config.sla_wait_after_retract); + + double total_layer_time = layer_exposure_time + + primary_lift_time + secondary_lift_time + + primary_retract_time + secondary_retract_time + + wait_times + time_estimate_correction; + + // NOTE: All faded layers are considered slow layers + if (sliced_layer_cnt <= fade_layers_cnt) + slow_layers++; + else + fast_layers++; + + layers_times.push_back(total_layer_time); + estim_time += total_layer_time; } - else - layer_times += exp_time; - layer_times += tilt_time; - - //// Per layer times (magical constants cuclulated from FW) - - static double exposure_safe_delay_before{ 3.0 }; - static double exposure_high_viscosity_delay_before{ 3.5 }; - static double exposure_slow_move_delay_before{ 1.0 }; - - if (material_config.material_print_speed == slamsSlow) - layer_times += exposure_safe_delay_before; - else if (material_config.material_print_speed == slamsHighViscosity) - layer_times += exposure_high_viscosity_delay_before; - else if (!is_fast_layer) - layer_times += exposure_slow_move_delay_before; - - // Increase layer time for "magic constants" from FW - layer_times += ( - l_height * 5 // tower move - + 120 / 1000 // Magical constant to compensate remaining computation delay in exposure thread - ); - - layers_times.push_back(layer_times); - estim_time += layer_times; } }; diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index c214f954cbc..d84e6a94f9c 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -499,7 +499,7 @@ static const FileWildcards file_wildcards_by_type[FT_SIZE] = { /* FT_TEX */ { "Texture"sv, { ".png"sv, ".svg"sv } }, - /* FT_SL1 (deprecated, overriden by sla_wildcards) */ { "Masked SLA files"sv, { ".sl1"sv, ".sl1s"sv, ".pwmx"sv } }, + /* FT_SL1 (deprecated, overriden by sla_wildcards) */ { "Masked SLA files"sv, { ".sl1"sv, ".sl1s"sv, ".pwmx"sv, ".goo"sv } }, /* FT_ZIP */ { "Zip files"sv, { ".zip"sv } }, }; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index c0a232e85da..6c32cd3d833 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1,4 +1,5 @@ ///|/ Copyright (c) Prusa Research 2017 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv, Pavel Mikuš @Godrak, Tomáš Mészáros @tamasmeszaros, David Kocík @kocikdav, Enrico Turri @enricoturri1966, Vojtěch Král @vojtechkral +///|/ Copyright (c) 2024 Felix Reißmann @felix-rm ///|/ Copyright (c) 2021 Martin Budden ///|/ Copyright (c) 2021 Ilya @xorza ///|/ Copyright (c) 2019 John Drake @foxox @@ -54,6 +55,8 @@ #include #include +#include + #include "wxExtensions.hpp" #include "PresetComboBoxes.hpp" #include @@ -2947,6 +2950,27 @@ void TabPrinter::build_sla() line.append_option(optgroup->get_option("fast_tilt_time")); line.append_option(optgroup->get_option("slow_tilt_time")); line.append_option(optgroup->get_option("high_viscosity_tilt_time")); + line.near_label_widget = [this, optgroup_wk = ConfigOptionsGroupWkp(optgroup)](wxWindow* parent) { + wxWindow* check_box = CheckBox::GetNewWin(parent); + wxGetApp().UpdateDarkUI(check_box); + + check_box->Bind(wxEVT_CHECKBOX, [this, optgroup_wk](wxCommandEvent& evt) { + const bool is_checked = evt.IsChecked(); + if (auto optgroup_sh = optgroup_wk.lock(); optgroup_sh) { + for (const std::string& opt_key : {"fast_tilt_time", "slow_tilt_time", "high_viscosity_tilt_time"}) + if (Field* field = optgroup_sh->get_fieldc(opt_key, 0); field != nullptr) { + field->toggle(is_checked); + if (is_checked) + field->set_last_meaningful_value(); + else + field->set_na_value(); + } + } + + toggle_options(); + }); + return check_box; + }; optgroup->append_line(line); optgroup->append_single_option_line("area_fill"); @@ -2962,6 +2986,7 @@ void TabPrinter::build_sla() optgroup->append_single_option_line("elefant_foot_compensation"); optgroup->append_single_option_line("elefant_foot_min_width"); optgroup->append_single_option_line("gamma_correction"); + optgroup->append_single_option_line("time_estimate_correction"); optgroup = page->new_optgroup(L("Exposure")); optgroup->append_single_option_line("min_exposure_time"); @@ -5340,6 +5365,44 @@ void TabSLAMaterial::build() optgroup = page->new_optgroup(L("Exposure")); optgroup->append_single_option_line("exposure_time"); optgroup->append_single_option_line("initial_exposure_time"); + optgroup->append_single_option_line("exposure_pwm"); + optgroup->append_single_option_line("initial_exposure_pwm"); + + auto add_line_with_padded_options = [](ConfigOptionsGroupShp& optgroup, auto const& label, auto const& first_option_name, auto const&... option_names){ + auto line = Line{label, ""}; + + auto add_option_to_line = [&](bool first, auto const& option_name){ + auto option = optgroup->get_option(option_name); + + // NOTE: Add some padding between option columns + // Using line.full_width and option.opt.width does not seem to work here + if(!first) + option.opt.label = " " + option.opt.label; + line.append_option(option); + }; + + add_option_to_line(true, first_option_name); + (add_option_to_line(false, option_names), ...); + optgroup->append_line(line); + }; + + optgroup = page->new_optgroup(L("Initial Lift and Retract")); + add_line_with_padded_options(optgroup, L("Initial lift distance"), "sla_initial_primary_lift_distance", "sla_initial_secondary_lift_distance"); + add_line_with_padded_options(optgroup, L("Initial lift speed"), "sla_initial_primary_lift_speed", "sla_initial_secondary_lift_speed"); + add_line_with_padded_options(optgroup, L("Initial retract distance"), "sla_initial_primary_retract_distance", "sla_initial_secondary_retract_distance"); + add_line_with_padded_options(optgroup, L("Initial retract speed"), "sla_initial_primary_retract_speed", "sla_initial_secondary_retract_speed"); + optgroup->append_single_option_line("sla_initial_wait_before_lift"); + optgroup->append_single_option_line("sla_initial_wait_after_lift"); + optgroup->append_single_option_line("sla_initial_wait_after_retract"); + + optgroup = page->new_optgroup(L("Lift and Retract")); + add_line_with_padded_options(optgroup, L("Lift distance"), "sla_primary_lift_distance", "sla_secondary_lift_distance"); + add_line_with_padded_options(optgroup, L("Lift speed"), "sla_primary_lift_speed", "sla_secondary_lift_speed"); + add_line_with_padded_options(optgroup, L("Retract distance"), "sla_primary_retract_distance", "sla_secondary_retract_distance"); + add_line_with_padded_options(optgroup, L("Retract speed"), "sla_primary_retract_speed", "sla_secondary_retract_speed"); + optgroup->append_single_option_line("sla_wait_before_lift"); + optgroup->append_single_option_line("sla_wait_after_lift"); + optgroup->append_single_option_line("sla_wait_after_retract"); optgroup = page->new_optgroup(L("Corrections")); auto line = Line{ m_config->def()->get("material_correction")->full_label, "" };