diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index abbd835fe..6e444fe49 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -385,7 +385,11 @@ public: bool is_mm_painted() const; // Checks if object contains just one volume and it's a text bool is_text() const; - + // This object may have a varying layer height by painting or by a table. + // Even if true is returned, the layer height profile may be "flat" with no difference to default layering. + bool has_custom_layering() const + { return ! this->layer_config_ranges.empty() || ! this->layer_height_profile.empty(); } + ModelInstance* add_instance(); ModelInstance* add_instance(const ModelInstance &instance); ModelInstance* add_instance(const Vec3d &offset, const Vec3d &scaling_factor, const Vec3d &rotation, const Vec3d &mirror); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 6314c67f6..b6d8eb2c1 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -491,6 +491,47 @@ std::string Print::validate(std::string* warning) const return L("The Spiral Vase option can only be used when printing single material objects."); } + // Cache of layer height profiles for checking: + // 1) Whether all layers are synchronized if printing with wipe tower and / or unsynchronized supports. + // 2) Whether layer height is constant for Organic supports. + // 3) Whether build volume Z is not violated. + std::vector> layer_height_profiles; + auto layer_height_profile = [this, &layer_height_profiles](const size_t print_object_idx) -> const std::vector& { + const PrintObject &print_object = *m_objects[print_object_idx]; + if (layer_height_profiles.empty()) + layer_height_profiles.assign(m_objects.size(), std::vector()); + std::vector &profile = layer_height_profiles[print_object_idx]; + if (profile.empty()) + PrintObject::update_layer_height_profile(*print_object.model_object(), print_object.slicing_parameters(), profile); + return profile; + }; + + // Checks that the print does not exceed the max print height + for (size_t print_object_idx = 0; print_object_idx < m_objects.size(); ++ print_object_idx) { + const PrintObject &print_object = *m_objects[print_object_idx]; + //FIXME It is quite expensive to generate object layers just to get the print height! + if (auto layers = generate_object_layers(print_object.slicing_parameters(), layer_height_profile(print_object_idx)); + ! layers.empty() && layers.back() > this->config().max_print_height) { + return L("The print is taller than the maximum allowed height. You might want to reduce the size of your model" + " or change current print settings and retry."); + } + } + + // Some of the objects has variable layer height applied by painting or by a table. + bool has_custom_layering = std::find_if(m_objects.begin(), m_objects.end(), + [](const PrintObject *object) { return object->model_object()->has_custom_layering(); }) + != m_objects.end(); + + // Custom layering is not allowed for tree supports as of now. + for (size_t print_object_idx = 0; print_object_idx < m_objects.size(); ++ print_object_idx) + if (const PrintObject &print_object = *m_objects[print_object_idx]; + print_object.has_support_material() && print_object.config().support_material_style.value == smsOrganic && + print_object.model_object()->has_custom_layering()) { + if (const std::vector &layers = layer_height_profile(print_object_idx); ! layers.empty()) + if (! check_object_layers_fixed(print_object.slicing_parameters(), layers)) + return L("Variable layer height is not supported with Organic supports."); + } + if (this->has_wipe_tower() && ! m_objects.empty()) { // Make sure all extruders use same diameter filament and have the same nozzle diameter // EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments @@ -518,19 +559,8 @@ std::string Print::validate(std::string* warning) const return L("The Wipe Tower is currently not supported for multimaterial sequential prints."); if (m_objects.size() > 1) { - bool has_custom_layering = false; - std::vector> layer_height_profiles; - for (const PrintObject *object : m_objects) { - has_custom_layering = ! object->model_object()->layer_config_ranges.empty() || ! object->model_object()->layer_height_profile.empty(); - if (has_custom_layering) { - layer_height_profiles.assign(m_objects.size(), std::vector()); - break; - } - } - const SlicingParameters &slicing_params0 = m_objects.front()->slicing_parameters(); - size_t tallest_object_idx = 0; - if (has_custom_layering) - PrintObject::update_layer_height_profile(*m_objects.front()->model_object(), slicing_params0, layer_height_profiles.front()); + const SlicingParameters &slicing_params0 = m_objects.front()->slicing_parameters(); + size_t tallest_object_idx = 0; for (size_t i = 1; i < m_objects.size(); ++ i) { const PrintObject *object = m_objects[i]; const SlicingParameters &slicing_params = object->slicing_parameters(); @@ -545,8 +575,9 @@ std::string Print::validate(std::string* warning) const if (! equal_layering(slicing_params, slicing_params0)) return L("The Wipe Tower is only supported for multiple objects if they are sliced equally."); if (has_custom_layering) { - PrintObject::update_layer_height_profile(*object->model_object(), slicing_params, layer_height_profiles[i]); - if (*(layer_height_profiles[i].end()-2) > *(layer_height_profiles[tallest_object_idx].end()-2)) + auto &lh = layer_height_profile(i); + auto &lh_tallest = layer_height_profile(tallest_object_idx); + if (*(lh.end()-2) > *(lh_tallest.end()-2)) tallest_object_idx = i; } } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index da330603f..1e45fb574 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1792,6 +1792,8 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c // use the constructor because the assignement is crashing on ASAN OsX layer_height_profile = std::vector(model_object.layer_height_profile.get()); // layer_height_profile = model_object.layer_height_profile; + // The layer height returned is sampled with high density for the UI layer height painting + // and smoothing tool to work. updated = true; } @@ -1806,6 +1808,7 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c if (layer_height_profile.empty()) { //layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes); layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); + // The layer height profile is already compressed. updated = true; } return updated; diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 7d4fac7a2..27c6b8ec3 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -183,32 +183,47 @@ std::vector layer_height_profile_from_ranges( // 2) Convert the trimmed ranges to a height profile, fill in the undefined intervals between z=0 and z=slicing_params.object_print_z_max() // with slicing_params.layer_height std::vector layer_height_profile; - for (std::vector>::const_iterator it_range = ranges_non_overlapping.begin(); it_range != ranges_non_overlapping.end(); ++ it_range) { - coordf_t lo = it_range->first.first; - coordf_t hi = it_range->first.second; - coordf_t height = it_range->second; - coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2]; - if (lo > last_z + EPSILON) { + auto last_z = [&layer_height_profile]() { + return layer_height_profile.empty() ? 0. : *(layer_height_profile.end() - 2); + }; + auto lh_append = [&layer_height_profile, last_z](coordf_t z, coordf_t layer_height) { + if (! layer_height_profile.empty()) { + bool last_z_matches = is_approx(*(layer_height_profile.end() - 2), z); + bool last_h_matches = is_approx(layer_height_profile.back(), layer_height); + if (last_h_matches) { + if (last_z_matches) { + // Drop a duplicate. + return; + } + if (layer_height_profile.size() >= 4 && is_approx(*(layer_height_profile.end() - 3), layer_height)) { + // Third repetition of the same layer_height. Update z of the last entry. + *(layer_height_profile.end() - 2) = z; + return; + } + } + } + layer_height_profile.push_back(z); + layer_height_profile.push_back(layer_height); + }; + + for (const std::pair &non_overlapping_range : ranges_non_overlapping) { + coordf_t lo = non_overlapping_range.first.first; + coordf_t hi = non_overlapping_range.first.second; + coordf_t height = non_overlapping_range.second; + if (coordf_t z = last_z(); lo > z + EPSILON) { // Insert a step of normal layer height. - layer_height_profile.push_back(last_z); - layer_height_profile.push_back(slicing_params.layer_height); - layer_height_profile.push_back(lo); - layer_height_profile.push_back(slicing_params.layer_height); + lh_append(z, slicing_params.layer_height); + lh_append(lo, slicing_params.layer_height); } // Insert a step of the overriden layer height. - layer_height_profile.push_back(lo); - layer_height_profile.push_back(height); - layer_height_profile.push_back(hi); - layer_height_profile.push_back(height); + lh_append(lo, height); + lh_append(hi, height); } - coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2]; - if (last_z < slicing_params.object_print_z_height()) { + if (coordf_t z = last_z(); z < slicing_params.object_print_z_height()) { // Insert a step of normal layer height up to the object top. - layer_height_profile.push_back(last_z); - layer_height_profile.push_back(slicing_params.layer_height); - layer_height_profile.push_back(slicing_params.object_print_z_height()); - layer_height_profile.push_back(slicing_params.layer_height); + lh_append(z, slicing_params.layer_height); + lh_append(slicing_params.object_print_z_height(), slicing_params.layer_height); } return layer_height_profile; @@ -294,7 +309,7 @@ std::vector layer_height_profile_adaptive(const SlicingParameters& slici print_z += height; } - double z_gap = slicing_params.object_print_z_height() - layer_height_profile[layer_height_profile.size() - 2]; + double z_gap = slicing_params.object_print_z_height() - *(layer_height_profile.end() - 2); if (z_gap > 0.0) { layer_height_profile.push_back(slicing_params.object_print_z_height()); @@ -632,6 +647,40 @@ std::vector generate_object_layers( return out; } +// Check whether the layer height profile describes a fixed layer height profile. +bool check_object_layers_fixed( + const SlicingParameters &slicing_params, + const std::vector &layer_height_profile) +{ + assert(layer_height_profile.size() >= 4); + assert(layer_height_profile.size() % 2 == 0); + assert(layer_height_profile[0] == 0); + + if (layer_height_profile.size() != 4 && layer_height_profile.size() != 8) + return false; + + bool fixed_step1 = is_approx(layer_height_profile[1], layer_height_profile[3]); + bool fixed_step2 = layer_height_profile.size() == 4 || + (layer_height_profile[2] == layer_height_profile[4] && is_approx(layer_height_profile[5], layer_height_profile[7])); + + if (! fixed_step1 || ! fixed_step2) + return false; + + if (layer_height_profile[2] < 0.5 * slicing_params.first_object_layer_height + EPSILON || + ! is_approx(layer_height_profile[3], slicing_params.first_object_layer_height)) + return false; + + double z_max = layer_height_profile[layer_height_profile.size() - 2]; + double z_2nd = slicing_params.first_object_layer_height + 0.5 * slicing_params.layer_height; + if (z_2nd > z_max) + return true; + if (z_2nd < *(layer_height_profile.end() - 4) + EPSILON || + ! is_approx(layer_height_profile.back(), slicing_params.layer_height)) + return false; + + return true; +} + int generate_layer_height_texture( const SlicingParameters &slicing_params, const std::vector &layers, diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index 489b2768f..101976f2b 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -129,11 +129,11 @@ inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters typedef std::pair t_layer_height_range; typedef std::map t_layer_config_ranges; -extern std::vector layer_height_profile_from_ranges( +std::vector layer_height_profile_from_ranges( const SlicingParameters &slicing_params, const t_layer_config_ranges &layer_config_ranges); -extern std::vector layer_height_profile_adaptive( +std::vector layer_height_profile_adaptive( const SlicingParameters& slicing_params, const ModelObject& object, float quality_factor); @@ -146,7 +146,7 @@ struct HeightProfileSmoothingParams HeightProfileSmoothingParams(unsigned int radius, bool keep_min) : radius(radius), keep_min(keep_min) {} }; -extern std::vector smooth_height_profile( +std::vector smooth_height_profile( const std::vector& profile, const SlicingParameters& slicing_params, const HeightProfileSmoothingParams& smoothing_params); @@ -157,7 +157,7 @@ enum LayerHeightEditActionType : unsigned int { LAYER_HEIGHT_EDIT_ACTION_SMOOTH = 3 }; -extern void adjust_layer_height_profile( +void adjust_layer_height_profile( const SlicingParameters &slicing_params, std::vector &layer_height_profile, coordf_t z, @@ -167,14 +167,19 @@ extern void adjust_layer_height_profile( // Produce object layers as pairs of low / high layer boundaries, stored into a linear vector. // The object layers are based at z=0, ignoring the raft layers. -extern std::vector generate_object_layers( +std::vector generate_object_layers( + const SlicingParameters &slicing_params, + const std::vector &layer_height_profile); + +// Check whether the layer height profile describes a fixed layer height profile. +bool check_object_layers_fixed( const SlicingParameters &slicing_params, const std::vector &layer_height_profile); // Produce a 1D texture packed into a 2D texture describing in the RGBA format // the planned object layers. // Returns number of cells used by the texture of the 0th LOD level. -extern int generate_layer_height_texture( +int generate_layer_height_texture( const SlicingParameters &slicing_params, const std::vector &layers, void *data, int rows, int cols, bool level_of_detail_2nd_level); diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 678761924..234825508 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -23,7 +23,6 @@ #include "libslic3r/Format/SL1.hpp" #include "libslic3r/Thread.hpp" #include "libslic3r/libslic3r.h" -#include "libslic3r/BuildVolume.hpp" #include #include @@ -145,20 +144,6 @@ std::string BackgroundSlicingProcess::output_filepath_for_project(const boost::f void BackgroundSlicingProcess::process_fff() { assert(m_print == m_fff_print); - - // Checks that the print does not exceed the max print height - const BuildVolume& build_volume = GUI::wxGetApp().mainframe->m_plater->build_volume(); - auto objects = m_fff_print->objects(); - for (auto obj : objects) { - std::vector layer_height_profile; - PrintObject::update_layer_height_profile(*obj->model_object(), obj->slicing_parameters(), layer_height_profile); - auto layers = generate_object_layers(obj->slicing_parameters(), layer_height_profile); - if (!layers.empty() && layers.back() > build_volume.max_print_height()) { - throw Slic3r::SlicingError("The print is taller than the maximum allowed height. You might want to reduce the size of your model" - " or change current print settings and retry."); - } - } - m_print->process(); wxCommandEvent evt(m_event_slicing_completed_id); // Post the Slicing Finished message for the G-code viewer to update.