From 21cf82aee0f0f6547099f925df6ee26b99d2fa7f Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 22 Sep 2023 10:38:44 +0800 Subject: [PATCH] Organic supports: Added check for variable layer height, with which Organic supports are not compatible. Fixes prusa3d/PrusaSlicer#9528 and similar. Check the object max Z against build volume Z in Print::validate(). Cherry-picked from prusa3d/PrusaSlicer@5b94971 --- src/libslic3r/Model.hpp | 16 +++++++ src/libslic3r/Print.cpp | 85 +++++++++++++++++++++++++++++------ src/libslic3r/PrintObject.cpp | 12 +++++ src/libslic3r/Slicing.cpp | 40 ++++++++++++++++- src/libslic3r/Slicing.hpp | 21 ++++++--- 5 files changed, 153 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 36e4186f297..f088b6b24bb 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -1,3 +1,15 @@ +///|/ Copyright (c) Prusa Research 2016 - 2023 Tomáš Mészáros @tamasmeszaros, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, David Kocík @kocikdav, Vojtěch Král @vojtechkral +///|/ Copyright (c) 2019 John Drake @foxox +///|/ Copyright (c) 2019 Sijmen Schoon +///|/ Copyright (c) 2017 Eyal Soha @eyal0 +///|/ Copyright (c) Slic3r 2014 - 2015 Alessandro Ranellucci @alranel +///|/ +///|/ ported from lib/Slic3r/Model.pm: +///|/ Copyright (c) Prusa Research 2016 - 2022 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966 +///|/ Copyright (c) Slic3r 2012 - 2016 Alessandro Ranellucci @alranel +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #ifndef slic3r_Model_hpp_ #define slic3r_Model_hpp_ @@ -398,6 +410,10 @@ class ModelObject final : public ObjectBase bool is_seam_painted() const; // Checks if any of object volume is painted using the multi-material painting gizmo. bool is_mm_painted() 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); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 4656e318a19..31d8147c253 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1,3 +1,25 @@ +///|/ Copyright (c) Prusa Research 2016 - 2023 Lukáš Matěna @lukasmatena, Tomáš Mészáros @tamasmeszaros, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Pavel Mikuš @Godrak, Oleksandra Iushchenko @YuSanka, Lukáš Hejl @hejllukas, Filip Sykala @Jony01, Roman Beránek @zavorka, David Kocík @kocikdav +///|/ Copyright (c) BambuStudio 2023 manch1n @manch1n +///|/ Copyright (c) SuperSlicer 2023 Remi Durand @supermerill +///|/ Copyright (c) 2021 Martin Budden +///|/ Copyright (c) 2020 Paul Arden @ardenpm +///|/ Copyright (c) 2019 Thomas Moore +///|/ Copyright (c) 2019 Bryan Smith +///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel +///|/ Copyright (c) 2014 Petr Ledvina @ledvinap +///|/ +///|/ ported from lib/Slic3r/Print.pm: +///|/ Copyright (c) Prusa Research 2016 - 2018 Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros +///|/ Copyright (c) Slic3r 2011 - 2016 Alessandro Ranellucci @alranel +///|/ Copyright (c) 2012 - 2013 Mark Hindess +///|/ Copyright (c) 2013 Devin Grady +///|/ Copyright (c) 2012 - 2013 Mike Sheldrake @mesheldrake +///|/ Copyright (c) 2012 Henrik Brix Andersen @henrikbrixandersen +///|/ Copyright (c) 2012 Michael Moon +///|/ Copyright (c) 2011 Richard Goodwin +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "Exception.hpp" #include "Print.hpp" #include "BoundingBox.hpp" @@ -1034,6 +1056,51 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons* return {L("The spiral vase mode does not work when an object contains more than one materials."), nullptr, "spiral_mode"}; } + // 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().printable_height + EPSILON) { + return + // Test whether the last slicing plane is below or above the print volume. + { 0.5 * (layers[layers.size() - 2] + layers.back()) > this->config().printable_height + EPSILON ? + format(_u8L("The object %1% exceeds the maximum build volume height."), print_object.model_object()->name) : + format(_u8L("While the object %1% itself fits the build volume, its last layer exceeds the maximum build volume height."), print_object.model_object()->name) + + " " + _u8L("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_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 {_u8L("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 @@ -1081,19 +1148,8 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons* #endif 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()); + 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(); @@ -1111,8 +1167,9 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons* if (!equal_layering(slicing_params, slicing_params0)) return { L("The prime tower requires that all objects are sliced with the same layer heights."), object }; 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 e3b089ca6e5..6f7df3c5ede 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1,3 +1,12 @@ +///|/ Copyright (c) Prusa Research 2016 - 2023 Lukáš Hejl @hejllukas, Pavel Mikuš @Godrak, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, David Kocík @kocikdav, Roman Beránek @zavorka +///|/ Copyright (c) 2021 Justin Schuh @jschuh +///|/ Copyright (c) 2021 Ilya @xorza +///|/ Copyright (c) 2016 Joseph Lenox @lordofhyphens +///|/ Copyright (c) Slic3r 2014 - 2016 Alessandro Ranellucci @alranel +///|/ Copyright (c) 2015 Maksim Derbasov @ntfshard +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include "Exception.hpp" #include "Print.hpp" #include "BoundingBox.hpp" @@ -2086,6 +2095,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; } @@ -2100,6 +2111,7 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c if (layer_height_profile.empty() || layer_height_profile[1] != slicing_parameters.first_object_layer_height) { //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; } diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index e84d00630ae..acfb17e856b 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2016 - 2023 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, David Kocík @kocikdav, Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ #include #include "libslic3r.h" @@ -321,7 +325,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()); @@ -685,6 +689,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 db0be3b863e..464a05ba443 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -1,3 +1,7 @@ +///|/ Copyright (c) Prusa Research 2016 - 2023 Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, David Kocík @kocikdav, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Vojtěch Král @vojtechkral +///|/ +///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher +///|/ // Based on implementation by @platsch #ifndef slic3r_Slicing_hpp_ @@ -133,11 +137,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); @@ -150,7 +154,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); @@ -161,7 +165,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, @@ -171,14 +175,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);