From 95c6f83b1bfde62f4e60a156b9949861d786b115 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 13 Jan 2023 10:00:00 +0100 Subject: [PATCH 01/28] Overlap point position with rays inside of Raycast manager Fix unstability of text origin position when start use surface --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 5 ----- src/slic3r/Utils/RaycastManager.cpp | 12 ++++++++++-- src/slic3r/Utils/RaycastManager.hpp | 1 + 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 30345c6fa..edb0bfb0c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -2980,11 +2980,6 @@ std::optional priv::calc_surface_offset(const ModelVolume &volume, Raycas // ray in direction of text projection(from volume zero to z-dir) std::optional hit_opt = raycast_manager.unproject(point, direction, &cond); - // start point lay on surface could appear slightly behind surface - std::optional hit_opt_opposit = raycast_manager.unproject(point, -direction, &cond); - if (!hit_opt.has_value() || - (hit_opt_opposit.has_value() && hit_opt->squared_distance > hit_opt_opposit->squared_distance)) - hit_opt = hit_opt_opposit; // Try to find closest point when no hit object in emboss direction if (!hit_opt.has_value()) diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 4667be3e9..4132fd647 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -124,8 +124,16 @@ std::optional RaycastManager::unproject(const Vec3d &point, Transform3d tr_inv = transformation.inverse(); Vec3d mesh_point = tr_inv * point; Vec3d mesh_direction = tr_inv.linear() * direction; - std::vector hits = mesh.query_ray_hits(mesh_point, mesh_direction); - for (const AABBMesh::hit_result &hit : hits) { + + // Need for detect that actual point position is on correct place + Vec3d point_positive = mesh_point - mesh_direction; + Vec3d point_negative = mesh_point + mesh_direction; + + // Throw ray to both directions of ray + std::vector hits = mesh.query_ray_hits(point_positive, mesh_direction); + std::vector hits_neg = mesh.query_ray_hits(point_negative, -mesh_direction); + hits.insert(hits.end(), std::make_move_iterator(hits_neg.begin()), std::make_move_iterator(hits_neg.end())); + for (const AABBMesh::hit_result &hit : hits) { double squared_distance = (mesh_point - hit.position()).squaredNorm(); if (closest.has_value() && closest->squared_distance < squared_distance) diff --git a/src/slic3r/Utils/RaycastManager.hpp b/src/slic3r/Utils/RaycastManager.hpp index a29977150..a185f6307 100644 --- a/src/slic3r/Utils/RaycastManager.hpp +++ b/src/slic3r/Utils/RaycastManager.hpp @@ -106,6 +106,7 @@ public: /// /// Unproject Ray(point direction) on mesh by MeshRaycasters + /// NOTE: It inspect also oposit direction of ray !! /// /// Start point for ray /// Direction of ray From fbd39e376b3bcfb0df3fa9a4cd8a916b75e00585 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 13 Jan 2023 12:31:57 +0100 Subject: [PATCH 02/28] Fix current used face name in warning message --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 38 ++++++++++--------------- src/slic3r/Utils/EmbossStyleManager.cpp | 8 ++++-- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index edb0bfb0c..42d2607b6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -1104,17 +1104,9 @@ bool GLGizmoEmboss::set_volume(ModelVolume *volume) m_style_manager.set_wx_font(wx_font); } } - - if (!is_exact_font) { + + if (!is_exact_font) create_notification_not_valid_font(tc); - - // update changed wxFont path - std::string path = WxFontUtils::store_wxFont(wx_font); - // current used style - EmbossStyle &act_style = m_style_manager.get_style(); - act_style.path = path; - act_style.type = WxFontUtils::get_actual_type(); - } // The change of volume could show or hide part with setter on volume type if (m_volume == nullptr || @@ -3405,31 +3397,31 @@ void GLGizmoEmboss::create_notification_not_valid_font( auto level = NotificationManager::NotificationLevel::WarningNotificationLevel; - const EmbossStyle &es = m_style_manager.get_style(); - const auto &origin_family = tc.style.prop.face_name; - const auto &actual_family = es.prop.face_name; + const EmbossStyle &es = m_style_manager.get_style(); + const auto &face_name_opt = es.prop.face_name; + const auto &face_name_3mf_opt = tc.style.prop.face_name; - const std::string &origin_font_name = origin_family.has_value() ? - *origin_family : + const std::string &face_name_3mf = face_name_3mf_opt.has_value() ? + *face_name_3mf_opt : tc.style.path; - std::string actual_wx_face_name; - if (!actual_family.has_value()) { - auto& wx_font = m_style_manager.get_wx_font(); + std::string face_name_by_wx; + if (!face_name_opt.has_value()) { + const auto& wx_font = m_style_manager.get_wx_font(); if (wx_font.has_value()) { - wxString wx_face_name = wx_font->GetFaceName(); - actual_wx_face_name = std::string((const char *) wx_face_name.ToUTF8()); + wxString wx_face_name = wx_font->GetFaceName(); + face_name_by_wx = std::string((const char *) wx_face_name.ToUTF8()); } } - const std::string &actual_font_name = actual_family.has_value() ? *actual_family : - (!actual_wx_face_name.empty() ? actual_wx_face_name : es.path); + const std::string &face_name = face_name_opt.has_value() ? *face_name_opt : + (!face_name_by_wx.empty() ? face_name_by_wx : es.path); std::string text = GUI::format(_L("Can't load exactly same font(\"%1%\"), " "Aplication select similar one(\"%2%\"). " "You have to specify font for enable edit text."), - origin_font_name, actual_font_name); + face_name_3mf, face_name); auto notification_manager = wxGetApp().plater()->get_notification_manager(); notification_manager->push_notification(type, level, text); } diff --git a/src/slic3r/Utils/EmbossStyleManager.cpp b/src/slic3r/Utils/EmbossStyleManager.cpp index ff7795fdd..4594e171c 100644 --- a/src/slic3r/Utils/EmbossStyleManager.cpp +++ b/src/slic3r/Utils/EmbossStyleManager.cpp @@ -192,9 +192,13 @@ bool StyleManager::load_style(const EmbossStyle &style) { bool StyleManager::load_style(const EmbossStyle &style, const wxFont &font) { + m_style_cache.style = style; // copy + + // wx font property has bigger priority to set + // it must be after copy of the style if (!set_wx_font(font)) return false; - m_style_cache.style = style; // copy - m_style_cache.style_index = std::numeric_limits::max(); + + m_style_cache.style_index = std::numeric_limits::max(); m_style_cache.stored_wx_font = {}; m_style_cache.truncated_name.clear(); return true; From 454363fe2c255af5744645d7f8f2058f890ae8a4 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 13 Jan 2023 13:27:01 +0100 Subject: [PATCH 03/28] Fix of filtering trinagles for reflected projection --- src/libslic3r/CutSurface.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/libslic3r/CutSurface.cpp b/src/libslic3r/CutSurface.cpp index 88e3bba75..c484bba22 100644 --- a/src/libslic3r/CutSurface.cpp +++ b/src/libslic3r/CutSurface.cpp @@ -768,6 +768,15 @@ void priv::set_skip_for_out_of_aoi(std::vector &skip_indicies, point_normals[i] = {p1, normal}; } + + // check that projection is not left handed + // Fix for reflected projection + if (is_out_of(point_normals[2].first, point_normals[0])) { + // projection is reflected so normals are reflected + for (auto &pn : point_normals) + pn.second *= -1; + } + // same meaning as point normal IsOnSides is_on_sides(its.vertices.size(), {false,false,false,false}); From dac1e6015385437dd9ff25f52c273966ca2c2a76 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 13 Jan 2023 14:18:20 +0100 Subject: [PATCH 04/28] Draw origin position of text during dragging --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 42d2607b6..0071e3450 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -706,7 +706,7 @@ static void draw_mouse_offset(const std::optional &offset) } #endif // SHOW_OFFSET_DURING_DRAGGING namespace priv { -static void draw_origin_ball(const GLCanvas3D& canvas) { +static void draw_origin(const GLCanvas3D& canvas) { auto draw_list = ImGui::GetOverlayDrawList(); const Selection &selection = canvas.get_selection(); Transform3d to_world = priv::world_matrix(selection); @@ -715,9 +715,22 @@ static void draw_origin_ball(const GLCanvas3D& canvas) { const Camera &camera = wxGetApp().plater()->get_camera(); Point screen_coor = CameraUtils::project(camera, volume_zero); ImVec2 center(screen_coor.x(), screen_coor.y()); - float radius = 10.f; - ImU32 color = ImGui::GetColorU32(ImGuiWrapper::COL_ORANGE_LIGHT); - draw_list->AddCircleFilled(center, radius, color); + float radius = 16.f; + ImU32 color = ImGui::GetColorU32(ImVec4(1.f, 1.f, 1.f, .75f)); + + int num_segments = 0; + float thickness = 4.f; + draw_list->AddCircle(center, radius, color, num_segments, thickness); + auto dirs = {ImVec2{0, 1}, ImVec2{1, 0}, ImVec2{0, -1}, ImVec2{-1, 0}}; + for (const ImVec2 &dir : dirs) { + ImVec2 start( + center.x + dir.x * 0.5 * radius, + center.y + dir.y * 0.5 * radius); + ImVec2 end( + center.x + dir.x * 1.5 * radius, + center.y + dir.y * 1.5 * radius); + draw_list->AddLine(start, end, color, thickness); + } } } // namespace priv @@ -736,7 +749,9 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) const ImVec2 &min_window_size = get_minimal_window_size(); ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, min_window_size); - priv::draw_origin_ball(m_parent); + // Draw origin position of text during dragging + if (m_temp_transformation.has_value()) + priv::draw_origin(m_parent); #ifdef SHOW_FINE_POSITION draw_fine_position(m_parent.get_selection(), m_parent.get_canvas_size(), min_window_size); From 661463645b3e260fd94648ec2e1ee2ea75db7435 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 12 Jan 2023 18:04:59 +0100 Subject: [PATCH 05/28] Background processing: The milestone state machine was extended with canceled / invalidated states. Print / PrintObject infrastructure was extended with a cleanup() callback, which may check for the new State::Canceled / State::Invalid states of a particular milestone and turn it to State::Fresh while releasing data of that particular milestone which is no more valid. Also fixed a bug in 31fbfa56de70bf8093cea5fe56c73b0e6fa017c3 where the PrintObject shared data invalidation condition was flipped. --- src/libslic3r/GCode.cpp | 2 +- src/libslic3r/Print.hpp | 4 ++ src/libslic3r/PrintApply.cpp | 6 +- src/libslic3r/PrintBase.hpp | 129 ++++++++++++++++++++++++---------- src/libslic3r/PrintObject.cpp | 15 ++++ src/slic3r/GUI/GLCanvas3D.cpp | 10 +-- 6 files changed, 119 insertions(+), 47 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index b9817776f..1e152c35a 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -740,7 +740,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu // Does the file exist? If so, we hope that it is still valid. { PrintStateBase::StateWithTimeStamp state = print->step_state_with_timestamp(psGCodeExport); - if (! state.enabled || (state.state == PrintStateBase::DONE && boost::filesystem::exists(boost::filesystem::path(path)))) + if (! state.enabled || (state.is_done() && boost::filesystem::exists(boost::filesystem::path(path)))) return; } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 31e78d714..7297cdfd5 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -358,11 +358,15 @@ private: // If ! m_slicing_params.valid, recalculate. void update_slicing_parameters(); + // Called on main thread with stopped or paused background processing to let PrintObject release data for its milestones that were invalidated or canceled. + void cleanup(); + static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders); private: void make_perimeters(); void prepare_infill(); + void clear_fills(); void infill(); void ironing(); void generate_support_spots(); diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 5f95edefa..4f586a17c 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -1470,9 +1470,11 @@ void Print::cleanup() for (auto it = all_objects.begin(); it != all_objects.end();) { PrintObjectRegions *shared_regions = (*it)->m_shared_regions; auto it_begin = it; - for (++ it; it != all_objects.end() && shared_regions == (*it)->shared_regions(); ++ it); + for (; it != all_objects.end() && shared_regions == (*it)->shared_regions(); ++ it) + // Let the PrintObject clean up its data with invalidated milestones. + (*it)->cleanup(); auto this_objects = SpanOfConstPtrs(const_cast(&(*it_begin)), it - it_begin); - if (Print::is_shared_print_object_step_valid_unguarded(this_objects, posSupportSpotsSearch)) + if (! Print::is_shared_print_object_step_valid_unguarded(this_objects, posSupportSpotsSearch)) shared_regions->generated_support_points.reset(); } } diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index d218146ac..607a7b5a5 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -23,10 +23,29 @@ public: class PrintStateBase { public: - enum State { - INVALID, - STARTED, - DONE, + enum class State { + // Fresh state, either the object is new or the data of that particular milestone was cleaned up. + // Fresh state may transit to Started. + Fresh, + // Milestone was started and now it is being executed. + // Started state may transit to Canceled with invalid data or Done with valid data. + Started, + // Milestone was being executed, but now it is canceled and not yet cleaned up. + // Canceled state may transit to Fresh state if its invalid data is cleaned up + // or to Started state. + // Canceled and Invalidated states are of similar nature: Canceled step was Started but canceled, + // while Invalidated state was Done but invalidated. + Canceled, + // Milestone was finished successfully, it's data is now valid. + // Done state may transit to Invalidated state if its data is no more valid + // or to a Started state. + Done, + // Milestone was finished successfully (done), but now it is invalidated and it's data is no more valid. + // Invalidated state may transit to Fresh if its invalid data is cleaned up, + // or to state Started. + // Canceled and Invalidated states are of similar nature: Canceled step was Started but canceled, + // while Invalidated state was Done but invalidated. + Invalidated, }; enum class WarningLevel { @@ -39,9 +58,25 @@ public: // A new unique timestamp is being assigned to the step every time the step changes its state. struct StateWithTimeStamp { - State state { INVALID }; + State state { State::Fresh }; TimeStamp timestamp { 0 }; bool enabled { true }; + + bool is_done() const { return state == State::Done; } + // The milestone may have some data available, but it is no more valid and it should be cleaned up to conserve memory. + bool is_dirty() const { return state == State::Canceled || state == State::Invalidated; } + + // If the milestone is Started or Done, invalidate it: + // Turn Started to Canceled, turn Done to Invalidated. + // Update timestamp of this milestone. + bool try_invalidate() { + bool invalidated = this->state == State::Started || this->state == State::Done; + if (invalidated) { + this->state = this->state == State::Started ? State::Canceled : State::Invalidated; + this->timestamp = ++ g_last_timestamp; + } + return invalidated; + } }; struct Warning @@ -93,11 +128,11 @@ public: } bool is_started(StepType step, std::mutex &mtx) const { - return this->state_with_timestamp(step, mtx).state == STARTED; + return this->state_with_timestamp(step, mtx).state == State::Started; } bool is_done(StepType step, std::mutex &mtx) const { - return this->state_with_timestamp(step, mtx).state == DONE; + return this->state_with_timestamp(step, mtx).state == State::Done; } StateWithTimeStamp state_with_timestamp_unguarded(StepType step) const { @@ -105,11 +140,11 @@ public: } bool is_started_unguarded(StepType step) const { - return this->state_with_timestamp_unguarded(step).state == STARTED; + return this->state_with_timestamp_unguarded(step).state == State::Started; } bool is_done_unguarded(StepType step) const { - return this->state_with_timestamp_unguarded(step).state == DONE; + return this->state_with_timestamp_unguarded(step).state == State::Done; } void enable_unguarded(StepType step, bool enable) { @@ -146,12 +181,12 @@ public: // // assert(m_step_active == -1); // for (int i = 0; i < int(COUNT); ++ i) -// assert(m_state[i].state != STARTED); +// assert(m_state[i].state != State::Started); #endif // NDEBUG PrintStateBase::StateWithWarnings &state = m_state[step]; - if (! state.enabled || state.state == DONE) + if (! state.enabled || state.state == State::Done) return false; - state.state = STARTED; + state.state = State::Started; state.timestamp = ++ g_last_timestamp; state.mark_warnings_non_current(); m_step_active = static_cast(step); @@ -161,17 +196,17 @@ public: // Set the step as done. Block on mutex while the Print / PrintObject / PrintRegion objects are being // modified by the UI thread. // Return value: - // Timestamp when this stepentered the DONE state. + // Timestamp when this step entered the Done state. // bool indicates whether the UI has to update the slicing warnings of this step or not. template std::pair set_done(StepType step, std::mutex &mtx, ThrowIfCanceled throw_if_canceled) { std::scoped_lock lock(mtx); // If canceled, throw before changing the step state. throw_if_canceled(); - assert(m_state[step].state == STARTED); + assert(m_state[step].state == State::Started); assert(m_step_active == static_cast(step)); PrintStateBase::StateWithWarnings &state = m_state[step]; - state.state = DONE; + state.state = State::Done; state.timestamp = ++ g_last_timestamp; m_step_active = -1; // Remove all non-current warnings. @@ -190,16 +225,12 @@ public: // processing by calling the cancel callback. template bool invalidate(StepType step, CancelationCallback cancel) { - bool invalidated = m_state[step].state != INVALID; - if (invalidated) { + if (PrintStateBase::StateWithWarnings &state = m_state[step]; state.try_invalidate()) { #if 0 if (mtx.state != mtx.HELD) { printf("Not held!\n"); } #endif - PrintStateBase::StateWithWarnings &state = m_state[step]; - state.state = INVALID; - state.timestamp = ++ g_last_timestamp; // Raise the mutex, so that the following cancel() callback could cancel // the background processing. // Internally the cancel() callback shall unlock the PrintBase::m_status_mutex to let @@ -209,21 +240,17 @@ public: // It is safe to modify it. state.mark_warnings_non_current(); m_step_active = -1; - } - return invalidated; + return true; + } else + return false; } template bool invalidate_multiple(StepTypeIterator step_begin, StepTypeIterator step_end, CancelationCallback cancel) { bool invalidated = false; - for (StepTypeIterator it = step_begin; it != step_end; ++ it) { - StateWithTimeStamp &state = m_state[*it]; - if (state.state != INVALID) { + for (StepTypeIterator it = step_begin; it != step_end; ++ it) + if (m_state[*it].try_invalidate()) invalidated = true; - state.state = INVALID; - state.timestamp = ++ g_last_timestamp; - } - } if (invalidated) { #if 0 if (mtx.state != mtx.HELD) { @@ -251,14 +278,9 @@ public: template bool invalidate_all(CancelationCallback cancel) { bool invalidated = false; - for (size_t i = 0; i < COUNT; ++ i) { - StateWithTimeStamp &state = m_state[i]; - if (state.state != INVALID) { + for (size_t i = 0; i < COUNT; ++ i) + if (m_state[i].try_invalidate()) invalidated = true; - state.state = INVALID; - state.timestamp = ++ g_last_timestamp; - } - } if (invalidated) { cancel(); // Now the worker thread should be stopped, therefore it cannot write into the warnings field. @@ -270,6 +292,26 @@ public: return invalidated; } + // If the milestone is Canceled or Invalidated, return true and turn the state of the milestone to Fresh. + // The caller is responsible for releasing the data of the milestone that is no more valid. + bool query_reset_dirty_unguarded(StepType step) { + if (PrintStateBase::StateWithWarnings &state = m_state[step]; state.is_dirty()) { + state.state = State::Fresh; + return true; + } else + return false; + } + + // To be called after the background thread was stopped by the user pressing the Cancel button, + // which in turn stops the background thread without adjusting state of the milestone being executed. + // This method fixes the state of the canceled milestone by setting it to a Canceled state. + void mark_canceled_unguarded() { + for (size_t i = 0; i < COUNT; ++ i) { + if (State &state = m_state[i].state; state == State::Started) + state = State::Canceled; + } + } + // Update list of warnings of the current milestone with a new warning. // The warning may already exist in the list, marked as current or not current. // If it already exists, mark it as current. @@ -281,7 +323,7 @@ public: std::scoped_lock lock(mtx); assert(m_step_active != -1); StateWithWarnings &state = m_state[m_step_active]; - assert(state.state == STARTED); + assert(state.state == State::Started); std::pair retval(static_cast(m_step_active), true); // Does a warning of the same level and message or message_id exist already? auto it = (message_id == 0) ? @@ -664,15 +706,19 @@ protected: } // Clean up after process() finished, either with success, error or if canceled. - // The adjustments on the Print / PrintObject m_stepmask data due to set_task() are to be reverted here. + // The adjustments on the Print / PrintObject m_stepmask data due to set_task() are to be reverted here: + // Execution of all milestones is enabled in case some of them were suppressed for the last background execution. + // Also if the background processing was canceled, the current milestone that was just abandoned + // in Started state is to be reset to Canceled state. template void finalize_impl(std::vector &print_objects) { // Grab the lock for the Print / PrintObject milestones. std::scoped_lock lock(this->state_mutex()); for (auto *po : print_objects) - po->enable_all_steps_unguarded(true); + po->finalize_impl(); m_state.enable_all_unguarded(true); + m_state.mark_canceled_unguarded(); } private: @@ -722,6 +768,11 @@ protected: bool is_step_enabled_unguarded(PrintObjectStepEnum step) const { return m_state.is_enabled_unguarded(step); } void enable_step_unguarded(PrintObjectStepEnum step, bool enable) { m_state.enable_unguarded(step, enable); } void enable_all_steps_unguarded(bool enable) { m_state.enable_all_unguarded(enable); } + // See the comment at PrintBaseWithState::finalize_impl() + void finalize_impl() { m_state.enable_all_unguarded(true); m_state.mark_canceled_unguarded(); } + // If the milestone is Canceled or Invalidated, return true and turn the state of the milestone to Fresh. + // The caller is responsible for releasing the data of the milestone that is no more valid. + bool query_reset_dirty_step_unguarded(PrintObjectStepEnum step) { return m_state.query_reset_dirty_unguarded(step); } // Add a slicing warning to the active PrintObject step and send a status notification. // This method could be called multiple times between this->set_started() and this->set_done(). diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5423a3ad1..42bb6bf31 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -352,6 +352,12 @@ void PrintObject::prepare_infill() this->set_done(posPrepareInfill); } +void PrintObject::clear_fills() +{ + for (Layer *layer : m_layers) + layer->clear_fills(); +} + void PrintObject::infill() { // prerequisites @@ -818,6 +824,15 @@ bool PrintObject::invalidate_all_steps() return result; } +// Called on main thread with stopped or paused background processing to let PrintObject release data for its milestones that were invalidated or canceled. +void PrintObject::cleanup() +{ + if (this->query_reset_dirty_step_unguarded(posInfill)) + this->clear_fills(); + if (this->query_reset_dirty_step_unguarded(posSupportMaterial)) + this->clear_support_layers(); +} + // This function analyzes slices of a region (SurfaceCollection slices). // Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface. // Initially all slices are of type stInternal. diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 453035024..5331094c1 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1867,11 +1867,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re SLASupportState state; for (size_t istep = 0; istep < sla_steps.size(); ++istep) { state.step[istep] = print_object->step_state_with_timestamp(sla_steps[istep]); - if (state.step[istep].state == PrintStateBase::DONE) { + if (state.step[istep].is_done()) { if (!print_object->has_mesh(sla_steps[istep])) - // Consider the DONE step without a valid mesh as invalid for the purpose + // Consider the Done step without a valid mesh as invalid for the purpose // of mesh visualization. - state.step[istep].state = PrintStateBase::INVALID; + state.step[istep].state = PrintStateBase::State::Fresh; else if (sla_steps[istep] != slaposDrillHoles) for (const ModelInstance* model_instance : print_object->model_object()->instances) // Only the instances, which are currently printable, will have the SLA support structures kept. @@ -2038,7 +2038,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if (! volume.offsets.empty() && state.step[istep].timestamp != volume.offsets.front()) { // The backend either produced a new hollowed mesh, or it invalidated the one that the front end has seen. volume.model.reset(); - if (state.step[istep].state == PrintStateBase::DONE) { + if (state.step[istep].is_done()) { TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles); assert(! mesh.empty()); @@ -2071,7 +2071,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // of various concenrs (model vs. 3D print path). volume.offsets = { state.step[istep].timestamp }; } - else if (state.step[istep].state == PrintStateBase::DONE) { + else if (state.step[istep].is_done()) { // Check whether there is an existing auxiliary volume to be updated, or a new auxiliary volume to be created. ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id); auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower); From 5991850db1f2d09d1b7713f42300e2bfe4a42128 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 16 Jan 2023 10:14:32 +0100 Subject: [PATCH 06/28] WIP Extending the expressivity of ExtrusionRole Co-authored-by: supermerill Based on the unfinished idea of @supermerill, defining ExtrusionRole as a bit mask of ExtrusionRoleModifier. Because the ExtrusionRole was used for both ExtrusionEntity and G-code export / G-code viewer, the original ExtrusionRole had to be split to ExtrusionRole and GCodeExtrusionRole to support bitmask attributes for the former while keeing a low number of ordinary values for the latter. --- src/libslic3r/Brim.cpp | 8 +- src/libslic3r/ExtrusionEntity.cpp | 30 +++- src/libslic3r/ExtrusionEntity.hpp | 167 ++++++++++++++------ src/libslic3r/ExtrusionEntityCollection.cpp | 2 +- src/libslic3r/ExtrusionEntityCollection.hpp | 8 +- src/libslic3r/Fill/Fill.cpp | 15 +- src/libslic3r/GCode.cpp | 60 +++---- src/libslic3r/GCode.hpp | 6 +- src/libslic3r/GCode/GCodeProcessor.cpp | 12 +- src/libslic3r/GCode/GCodeProcessor.hpp | 22 +-- src/libslic3r/GCode/PressureEqualizer.cpp | 8 +- src/libslic3r/GCode/PressureEqualizer.hpp | 4 +- src/libslic3r/GCode/SeamPlacer.cpp | 8 +- src/libslic3r/GCode/ToolOrdering.cpp | 10 +- src/libslic3r/GCode/WipeTower.cpp | 2 +- src/libslic3r/PerimeterGenerator.cpp | 26 +-- src/libslic3r/Print.cpp | 2 +- src/libslic3r/SupportMaterial.cpp | 24 +-- src/libslic3r/SupportSpotsGenerator.cpp | 23 ++- src/libslic3r/enum_bitmask.hpp | 9 +- src/slic3r/GUI/GCodeViewer.cpp | 19 ++- src/slic3r/GUI/GCodeViewer.hpp | 6 +- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- tests/fff_print/test_extrusion_entity.cpp | 46 +++--- tests/fff_print/test_perimeters.cpp | 4 +- 25 files changed, 307 insertions(+), 216 deletions(-) diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp index 061cf1423..8bd9b2fc2 100644 --- a/src/libslic3r/Brim.cpp +++ b/src/libslic3r/Brim.cpp @@ -490,7 +490,7 @@ static void make_inner_brim(const Print &print, loops = union_pt_chained_outside_in(loops); std::reverse(loops.begin(), loops.end()); - extrusion_entities_append_loops(brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), + extrusion_entities_append_loops(brim.entities, std::move(loops), ExtrusionRole::Skirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); } @@ -672,7 +672,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance if (i + 1 == j && first_path.size() > 3 && first_path.front().x() == first_path.back().x() && first_path.front().y() == first_path.back().y()) { auto *loop = new ExtrusionLoop(); brim.entities.emplace_back(loop); - loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); + loop->paths.emplace_back(ExtrusionRole::Skirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); Points &points = loop->paths.front().polyline.points; points.reserve(first_path.size()); for (const ClipperLib_Z::IntPoint &pt : first_path) @@ -683,7 +683,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance ExtrusionEntityCollection this_loop_trimmed; this_loop_trimmed.entities.reserve(j - i); for (; i < j; ++ i) { - this_loop_trimmed.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()))); + this_loop_trimmed.entities.emplace_back(new ExtrusionPath(ExtrusionRole::Skirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()))); const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first; Points &points = dynamic_cast(this_loop_trimmed.entities.back())->polyline.points; points.reserve(path.size()); @@ -699,7 +699,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance } } } else { - extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); + extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), ExtrusionRole::Skirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); } make_inner_brim(print, top_level_objects_with_brim, bottom_layers_expolygons, brim); diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 013d2efea..e180fcfcd 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -322,8 +322,29 @@ double ExtrusionLoop::min_mm3_per_mm() const return min_mm3_per_mm; } +// Convert a rich bitmask based ExtrusionRole to a less expressive ordinal GCodeExtrusionRole. +// GCodeExtrusionRole is to be serialized into G-code and deserialized by G-code viewer, +GCodeExtrusionRole extrusion_role_to_gcode_extrusion_role(ExtrusionRole role) +{ + if (role == ExtrusionRole::None) return erNone; + if (role == ExtrusionRole::Perimeter) return erPerimeter; + if (role == ExtrusionRole::ExternalPerimeter) return erExternalPerimeter; + if (role == ExtrusionRole::OverhangPerimeter) return erOverhangPerimeter; + if (role == ExtrusionRole::InternalInfill) return erInternalInfill; + if (role == ExtrusionRole::SolidInfill) return erSolidInfill; + if (role == ExtrusionRole::TopSolidInfill) return erTopSolidInfill; + if (role == ExtrusionRole::Ironing) return erIroning; + if (role == ExtrusionRole::BridgeInfill) return erBridgeInfill; + if (role == ExtrusionRole::GapFill) return erGapFill; + if (role == ExtrusionRole::Skirt) return erSkirt; + if (role == ExtrusionRole::SupportMaterial) return erSupportMaterial; + if (role == ExtrusionRole::SupportMaterialInterface) return erSupportMaterialInterface; + if (role == ExtrusionRole::WipeTower) return erWipeTower; + assert(false); + return erNone; +} -std::string ExtrusionEntity::role_to_string(ExtrusionRole role) +std::string gcode_extrusion_role_to_string(GCodeExtrusionRole role) { switch (role) { case erNone : return L("Unknown"); @@ -341,13 +362,12 @@ std::string ExtrusionEntity::role_to_string(ExtrusionRole role) case erSupportMaterialInterface : return L("Support material interface"); case erWipeTower : return L("Wipe tower"); case erCustom : return L("Custom"); - case erMixed : return L("Mixed"); default : assert(false); } - return ""; + return {}; } -ExtrusionRole ExtrusionEntity::string_to_role(const std::string_view role) +GCodeExtrusionRole string_to_gcode_extrusion_role(const std::string_view role) { if (role == L("Perimeter")) return erPerimeter; @@ -377,8 +397,6 @@ ExtrusionRole ExtrusionEntity::string_to_role(const std::string_view role) return erWipeTower; else if (role == L("Custom")) return erCustom; - else if (role == L("Mixed")) - return erMixed; else return erNone; } diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 47c33938a..5022f216b 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -4,6 +4,7 @@ #include "libslic3r.h" #include "Polygon.hpp" #include "Polyline.hpp" +#include "enum_bitmask.hpp" #include #include @@ -16,8 +17,113 @@ using ExPolygons = std::vector; class ExtrusionEntityCollection; class Extruder; -// Each ExtrusionRole value identifies a distinct set of { extruder, speed } -enum ExtrusionRole : uint8_t { +enum class ExtrusionRoleModifier : uint16_t { +// 1) Extrusion types + // Perimeter (external, inner, ...) + Perimeter, + // Infill (top / bottom / solid inner / sparse inner / bridging inner ...) + Infill, + // Variable width extrusion + Thin, + // Support material extrusion + Support, + Skirt, + Wipe, +// 2) Extrusion modifiers + External, + Solid, + Ironing, + Bridge, +// 3) Special types + // Indicator that the extrusion role was mixed from multiple differing extrusion roles, + // for example from Support and SupportInterface. + Mixed, + // Stopper, there should be maximum 16 modifiers defined for uint16_t bit mask. + Count +}; +// There should be maximum 16 modifiers defined for uint16_t bit mask. +static_assert(int(ExtrusionRoleModifier::Count) <= 16, "ExtrusionRoleModifier: there must be maximum 16 modifiers defined to fit a 16 bit bitmask"); + +using ExtrusionRoleModifiers = enum_bitmask; +ENABLE_ENUM_BITMASK_OPERATORS(ExtrusionRoleModifier); + +struct ExtrusionRole : public ExtrusionRoleModifiers +{ + constexpr ExtrusionRole(const ExtrusionRoleModifier bit) : ExtrusionRoleModifiers(bit) {} + constexpr ExtrusionRole(const ExtrusionRoleModifiers bits) : ExtrusionRoleModifiers(bits) {} + + static constexpr const ExtrusionRoleModifiers None{}; + // Internal perimeter, not bridging. + static constexpr const ExtrusionRoleModifiers Perimeter{ ExtrusionRoleModifier::Perimeter }; + // External perimeter, not bridging. + static constexpr const ExtrusionRoleModifiers ExternalPerimeter{ ExtrusionRoleModifier::Perimeter | ExtrusionRoleModifier::External }; + // Perimeter, bridging. To be or'ed with ExtrusionRoleModifier::External for external bridging perimeter. + static constexpr const ExtrusionRoleModifiers OverhangPerimeter{ ExtrusionRoleModifier::Perimeter | ExtrusionRoleModifier::Bridge }; + // Sparse internal infill. + static constexpr const ExtrusionRoleModifiers InternalInfill{ ExtrusionRoleModifier::Infill }; + // Solid internal infill. + static constexpr const ExtrusionRoleModifiers SolidInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid }; + // Top solid infill (visible). + //FIXME why there is no bottom solid infill type? + static constexpr const ExtrusionRoleModifiers TopSolidInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::External }; + // Ironing infill at the top surfaces. + static constexpr const ExtrusionRoleModifiers Ironing{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Ironing | ExtrusionRoleModifier::External }; + // Visible bridging infill at the bottom of an object. + static constexpr const ExtrusionRoleModifiers BridgeInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::Bridge | ExtrusionRoleModifier::External }; +// static constexpr const ExtrusionRoleModifiers InternalBridgeInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::Bridge }; + // Gap fill extrusion, currently used for any variable width extrusion: Thin walls outside of the outer extrusion, + // gap fill in between perimeters, gap fill between the inner perimeter and infill. + //FIXME revise GapFill and ThinWall types, split Gap Fill to Gap Fill and ThinWall. + static constexpr const ExtrusionRoleModifiers GapFill{ ExtrusionRoleModifier::Thin }; // | ExtrusionRoleModifier::External }; +// static constexpr const ExtrusionRoleModifiers ThinWall{ ExtrusionRoleModifier::Thin }; + static constexpr const ExtrusionRoleModifiers Skirt{ ExtrusionRoleModifier::Skirt }; + // Support base material, printed with non-soluble plastic. + static constexpr const ExtrusionRoleModifiers SupportMaterial{ ExtrusionRoleModifier::Support }; + // Support interface material, printed with soluble plastic. + static constexpr const ExtrusionRoleModifiers SupportMaterialInterface{ ExtrusionRoleModifier::Support | ExtrusionRoleModifier::External }; + // Wipe tower material. + static constexpr const ExtrusionRoleModifiers WipeTower{ ExtrusionRoleModifier::Wipe }; + // Extrusion role for a collection with multiple extrusion roles. + static constexpr const ExtrusionRoleModifiers Mixed{ ExtrusionRoleModifier::Mixed }; +}; + +// Special flags describing loop +enum ExtrusionLoopRole { + elrDefault, + elrContourInternalPerimeter, + elrSkirt, +}; + +inline bool is_perimeter(ExtrusionRole role) +{ + return role == ExtrusionRole::Perimeter + || role == ExtrusionRole::ExternalPerimeter + || role == ExtrusionRole::OverhangPerimeter; +} + +inline bool is_infill(ExtrusionRole role) +{ + return role == ExtrusionRole::BridgeInfill + || role == ExtrusionRole::InternalInfill + || role == ExtrusionRole::SolidInfill + || role == ExtrusionRole::TopSolidInfill + || role == ExtrusionRole::Ironing; +} + +inline bool is_solid_infill(ExtrusionRole role) +{ + return role == ExtrusionRole::BridgeInfill + || role == ExtrusionRole::SolidInfill + || role == ExtrusionRole::TopSolidInfill + || role == ExtrusionRole::Ironing; +} + +inline bool is_bridge(ExtrusionRole role) { + return role == ExtrusionRole::BridgeInfill + || role == ExtrusionRole::OverhangPerimeter; +} + +enum GCodeExtrusionRole : uint8_t { erNone, erPerimeter, erExternalPerimeter, @@ -32,49 +138,12 @@ enum ExtrusionRole : uint8_t { erSupportMaterial, erSupportMaterialInterface, erWipeTower, + // Custom (user defined) G-code block, for example start / end G-code. erCustom, - // Extrusion role for a collection with multiple extrusion roles. - erMixed, + // Stopper to count number of enums. erCount }; -// Special flags describing loop -enum ExtrusionLoopRole { - elrDefault, - elrContourInternalPerimeter, - elrSkirt, -}; - - -inline bool is_perimeter(ExtrusionRole role) -{ - return role == erPerimeter - || role == erExternalPerimeter - || role == erOverhangPerimeter; -} - -inline bool is_infill(ExtrusionRole role) -{ - return role == erBridgeInfill - || role == erInternalInfill - || role == erSolidInfill - || role == erTopSolidInfill - || role == erIroning; -} - -inline bool is_solid_infill(ExtrusionRole role) -{ - return role == erBridgeInfill - || role == erSolidInfill - || role == erTopSolidInfill - || role == erIroning; -} - -inline bool is_bridge(ExtrusionRole role) { - return role == erBridgeInfill - || role == erOverhangPerimeter; -} - class ExtrusionEntity { public: @@ -108,11 +177,15 @@ public: virtual Polylines as_polylines() const { Polylines dst; this->collect_polylines(dst); return dst; } virtual double length() const = 0; virtual double total_volume() const = 0; - - static std::string role_to_string(ExtrusionRole role); - static ExtrusionRole string_to_role(const std::string_view role); }; +// Convert a rich bitmask based ExtrusionRole to a less expressive ordinal GCodeExtrusionRole. +// GCodeExtrusionRole is to be serialized into G-code and deserialized by G-code viewer, +GCodeExtrusionRole extrusion_role_to_gcode_extrusion_role(ExtrusionRole role); + +std::string gcode_extrusion_role_to_string(GCodeExtrusionRole role); +GCodeExtrusionRole string_to_gcode_extrusion_role(const std::string_view role); + typedef std::vector ExtrusionEntitiesPtr; class ExtrusionPath : public ExtrusionEntity @@ -217,7 +290,7 @@ public: size_t size() const { return this->paths.size(); } bool empty() const { return this->paths.empty(); } double length() const override; - ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); } + ExtrusionRole role() const override { return this->paths.empty() ? ExtrusionRole::None : this->paths.front().role(); } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override; @@ -279,7 +352,7 @@ public: // Test, whether the point is extruded by a bridging flow. // This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead. bool has_overhang_point(const Point &point) const; - ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); } + ExtrusionRole role() const override { return this->paths.empty() ? ExtrusionRole::None : this->paths.front().role(); } ExtrusionLoopRole loop_role() const { return m_loop_role; } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. @@ -304,8 +377,6 @@ public: } double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; } - //static inline std::string role_to_string(ExtrusionLoopRole role); - #ifndef NDEBUG bool validate() const { assert(this->first_point() == this->paths.back().polyline.points.back()); diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp index e6ae1edd4..55167861c 100644 --- a/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/src/libslic3r/ExtrusionEntityCollection.cpp @@ -8,7 +8,7 @@ namespace Slic3r { void filter_by_extrusion_role_in_place(ExtrusionEntitiesPtr &extrusion_entities, ExtrusionRole role) { - if (role != erMixed) { + if (role != ExtrusionRole::Mixed) { auto first = extrusion_entities.begin(); auto last = extrusion_entities.end(); extrusion_entities.erase( diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index cabf4e2f9..0c029a1d5 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -54,10 +54,10 @@ public: bool is_collection() const override { return true; } ExtrusionRole role() const override { - ExtrusionRole out = erNone; + ExtrusionRole out{ ExtrusionRole::None }; for (const ExtrusionEntity *ee : entities) { ExtrusionRole er = ee->role(); - out = (out == erNone || out == er) ? er : erMixed; + out = (out == ExtrusionRole::None || out == er) ? er : ExtrusionRole::Mixed; } return out; } @@ -96,8 +96,8 @@ public: } void replace(size_t i, const ExtrusionEntity &entity); void remove(size_t i); - static ExtrusionEntityCollection chained_path_from(const ExtrusionEntitiesPtr &extrusion_entities, const Point &start_near, ExtrusionRole role = erMixed); - ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const + static ExtrusionEntityCollection chained_path_from(const ExtrusionEntitiesPtr &extrusion_entities, const Point &start_near, ExtrusionRole role = ExtrusionRole::Mixed); + ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = ExtrusionRole::Mixed) const { return this->no_sort ? *this : chained_path_from(this->entities, start_near, role); } void reverse() override; const Point& first_point() const override { return this->entities.front()->first_point(); } diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index c962bbeb0..01c551fb6 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -52,7 +52,7 @@ struct SurfaceFillParams Flow flow; // For the output - ExtrusionRole extrusion_role = ExtrusionRole(0); + ExtrusionRole extrusion_role{ ExtrusionRole::None }; // Various print settings? @@ -81,8 +81,7 @@ struct SurfaceFillParams RETURN_COMPARE_NON_EQUAL(flow.height()); RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter()); RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, bridge); - RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, extrusion_role); - return false; + return this->extrusion_role.lower(rhs.extrusion_role); } bool operator==(const SurfaceFillParams &rhs) const { @@ -145,10 +144,10 @@ std::vector group_fills(const Layer &layer) params.extrusion_role = is_bridge ? - erBridgeInfill : + ExtrusionRole::BridgeInfill : (surface.is_solid() ? - (surface.is_top() ? erTopSolidInfill : erSolidInfill) : - erInternalInfill); + (surface.is_top() ? ExtrusionRole::TopSolidInfill : ExtrusionRole::SolidInfill) : + ExtrusionRole::InternalInfill); params.bridge_angle = float(surface.bridge_angle); params.angle = float(Geometry::deg2rad(region_config.fill_angle.value)); @@ -282,7 +281,7 @@ std::vector group_fills(const Layer &layer) params.extruder = layerm.region().extruder(frSolidInfill); params.pattern = layerm.region().config().top_fill_pattern == ipMonotonic ? ipMonotonic : ipRectilinear; params.density = 100.f; - params.extrusion_role = erInternalInfill; + params.extrusion_role = ExtrusionRole::InternalInfill; params.angle = float(Geometry::deg2rad(layerm.region().config().fill_angle.value)); // calculate the actual flow we'll be using for this infill params.flow = layerm.flow(frSolidInfill); @@ -785,7 +784,7 @@ void Layer::make_ironing() eec->no_sort = true; extrusion_entities_append_paths( eec->entities, std::move(polylines), - erIroning, + ExtrusionRole::Ironing, flow_mm3_per_mm, extrusion_width, float(extrusion_height)); insert_fills_into_islands(*this, ironing_params.region_id, fill_begin, uint32_t(ironing_params.layerm->fills().size())); } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 1e152c35a..6ed0b9024 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -148,7 +148,7 @@ namespace Slic3r { int OozePrevention::_get_temp(GCode& gcodegen) { - return (gcodegen.layer() != NULL && gcodegen.layer()->id() == 0) + return (gcodegen.layer() != nullptr && gcodegen.layer()->id() == 0) ? gcodegen.config().first_layer_temperature.get_at(gcodegen.writer().extruder()->id()) : gcodegen.config().temperature.get_at(gcodegen.writer().extruder()->id()); } @@ -244,7 +244,7 @@ namespace Slic3r { gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); gcode += gcodegen.travel_to( wipe_tower_point_to_object_point(gcodegen, start_pos), - erMixed, + ExtrusionRole::Mixed, "Travel to a Wipe Tower"); gcode += gcodegen.unretract(); } @@ -864,7 +864,7 @@ namespace DoExport { auto min_mm3_per_mm_no_ironing = [](const ExtrusionEntityCollection& eec) -> double { double min = std::numeric_limits::max(); for (const ExtrusionEntity* ee : eec.entities) - if (ee->role() != erIroning) + if (ee->role() != ExtrusionRole::Ironing) min = std::min(min, ee->min_mm3_per_mm()); return min; }; @@ -1271,7 +1271,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false); // adds tag for processor - file.write_format(";%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(), gcode_extrusion_role_to_string(erCustom).c_str()); // Write the custom start G-code file.writeln(start_gcode); @@ -1320,7 +1320,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer m_avoid_crossing_perimeters.use_external_mp_once(); file.write(this->retract()); - file.write(this->travel_to(Point(0, 0), erNone, "move to origin position for next object")); + file.write(this->travel_to(Point(0, 0), ExtrusionRole::None, "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(); @@ -1407,7 +1407,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.write(m_writer.set_fan(0)); // adds tag for processor - file.write_format(";%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(), gcode_extrusion_role_to_string(erCustom).c_str()); // Process filament-specific gcode in extruder order. { @@ -2305,8 +2305,8 @@ void GCode::process_layer_single_object( if (! print_wipe_extrusions && layer_to_print.support_layer != nullptr) if (const SupportLayer &support_layer = *layer_to_print.support_layer; ! support_layer.support_fills.entities.empty()) { ExtrusionRole role = support_layer.support_fills.role(); - bool has_support = role == erMixed || role == erSupportMaterial; - bool has_interface = role == erMixed || role == erSupportMaterialInterface; + bool has_support = role == ExtrusionRole::Mixed || role == ExtrusionRole::SupportMaterial; + bool has_interface = role == ExtrusionRole::Mixed || role == ExtrusionRole::SupportMaterialInterface; // Extruder ID of the support base. -1 if "don't care". unsigned int support_extruder = print_object.config().support_material_extruder.value - 1; // Shall the support be printed with the active extruder, preferably with non-soluble, to avoid tool changes? @@ -2340,8 +2340,8 @@ void GCode::process_layer_single_object( m_layer = layer_to_print.support_layer; m_object_layer_over_raft = false; gcode += this->extrude_support( - // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths. - support_layer.support_fills.chained_path_from(m_last_pos, has_support ? (has_interface ? erMixed : erSupportMaterial) : erSupportMaterialInterface)); + // support_extrusion_role is ExtrusionRole::SupportMaterial, ExtrusionRole::SupportMaterialInterface or ExtrusionRole::Mixed for all extrusion paths. + support_layer.support_fills.chained_path_from(m_last_pos, has_support ? (has_interface ? ExtrusionRole::Mixed : ExtrusionRole::SupportMaterial) : ExtrusionRole::SupportMaterialInterface)); } } @@ -2385,7 +2385,7 @@ void GCode::process_layer_single_object( for (uint32_t fill_id : *it_fill_range) { assert(dynamic_cast(fills.entities[fill_id])); if (auto *eec = static_cast(fills.entities[fill_id]); - (eec->role() == erIroning) == ironing && shall_print_this_extrusion_collection(eec, region)) { + (eec->role() == ExtrusionRole::Ironing) == ironing && shall_print_this_extrusion_collection(eec, region)) { if (eec->can_reverse()) // Flatten the infill collection for better path planning. for (auto *ee : eec->entities) @@ -2631,7 +2631,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, const std::string_view descr } // make a little move inwards before leaving loop - if (paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.perimeters.value > 1 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 3) { + if (paths.back().role() == ExtrusionRole::ExternalPerimeter && m_layer != NULL && m_config.perimeters.value > 1 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 3) { // detect angle between last and first segment // the side depends on the original winding order of the polygon (left for contours, right for holes) //FIXME improve the algorithm in case the loop is tiny. @@ -2739,9 +2739,9 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill const double support_interface_speed = m_config.support_material_interface_speed.get_abs_value(support_speed); for (const ExtrusionEntity *ee : support_fills.entities) { ExtrusionRole role = ee->role(); - assert(role == erSupportMaterial || role == erSupportMaterialInterface); - const auto label = (role == erSupportMaterial) ? support_label : support_interface_label; - const double speed = (role == erSupportMaterial) ? support_speed : support_interface_speed; + assert(role == ExtrusionRole::SupportMaterial || role == ExtrusionRole::SupportMaterialInterface); + const auto label = (role == ExtrusionRole::SupportMaterial) ? support_label : support_interface_label; + const double speed = (role == ExtrusionRole::SupportMaterial) ? support_speed : support_interface_speed; const ExtrusionPath *path = dynamic_cast(ee); if (path) gcode += this->extrude_path(*path, label, speed); @@ -2872,21 +2872,21 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de // set speed if (speed == -1) { - if (path.role() == erPerimeter) { + if (path.role() == ExtrusionRole::Perimeter) { speed = m_config.get_abs_value("perimeter_speed"); - } else if (path.role() == erExternalPerimeter) { + } else if (path.role() == ExtrusionRole::ExternalPerimeter) { speed = m_config.get_abs_value("external_perimeter_speed"); - } else if (path.role() == erOverhangPerimeter || path.role() == erBridgeInfill) { + } else if (path.role() == ExtrusionRole::OverhangPerimeter || path.role() == ExtrusionRole::BridgeInfill) { speed = m_config.get_abs_value("bridge_speed"); - } else if (path.role() == erInternalInfill) { + } else if (path.role() == ExtrusionRole::InternalInfill) { speed = m_config.get_abs_value("infill_speed"); - } else if (path.role() == erSolidInfill) { + } else if (path.role() == ExtrusionRole::SolidInfill) { speed = m_config.get_abs_value("solid_infill_speed"); - } else if (path.role() == erTopSolidInfill) { + } else if (path.role() == ExtrusionRole::TopSolidInfill) { speed = m_config.get_abs_value("top_solid_infill_speed"); - } else if (path.role() == erIroning) { + } else if (path.role() == ExtrusionRole::Ironing) { speed = m_config.get_abs_value("ironing_speed"); - } else if (path.role() == erGapFill) { + } else if (path.role() == ExtrusionRole::GapFill) { speed = m_config.get_abs_value("gap_fill_speed"); } else { throw Slic3r::InvalidArgument("Invalid speed"); @@ -2927,9 +2927,9 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de // extrude arc or line if (m_enable_extrusion_role_markers) { - if (path.role() != m_last_extrusion_role) + if (GCodeExtrusionRole role = extrusion_role_to_gcode_extrusion_role(path.role()); role != m_last_extrusion_role) { - m_last_extrusion_role = path.role(); + m_last_extrusion_role = role; if (m_enable_extrusion_role_markers) { char buf[32]; @@ -2945,10 +2945,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de bool last_was_wipe_tower = (m_last_processor_extrusion_role == erWipeTower); assert(is_decimal_separator_point()); - if (path.role() != m_last_processor_extrusion_role) { - m_last_processor_extrusion_role = path.role(); + if (GCodeExtrusionRole role = extrusion_role_to_gcode_extrusion_role(path.role()); role != m_last_processor_extrusion_role) { + m_last_processor_extrusion_role = role; char buf[64]; - sprintf(buf, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(m_last_processor_extrusion_role).c_str()); + sprintf(buf, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), gcode_extrusion_role_to_string(m_last_processor_extrusion_role).c_str()); gcode += buf; } @@ -2979,7 +2979,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de gcode += ";_BRIDGE_FAN_START\n"; else comment = ";_EXTRUDE_SET_SPEED"; - if (path.role() == erExternalPerimeter) + if (path.role() == ExtrusionRole::ExternalPerimeter) comment += ";_EXTERNAL_PERIMETER"; } @@ -3112,7 +3112,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role) return false; } - if (role == erSupportMaterial) + if (role == ExtrusionRole::SupportMaterial) if (const SupportLayer *support_layer = dynamic_cast(m_layer); support_layer != nullptr && ! support_layer->support_islands_bboxes.empty()) { BoundingBox bbox_travel = get_extents(travel); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 09442cf0f..d57116595 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -326,7 +326,7 @@ private: std::string extrude_support(const ExtrusionEntityCollection &support_fills); std::string travel_to(const Point &point, ExtrusionRole role, std::string comment); - bool needs_retraction(const Polyline &travel, ExtrusionRole role = erNone); + bool needs_retraction(const Polyline &travel, ExtrusionRole role = ExtrusionRole::None); std::string retract(bool toolchange = false); std::string unretract() { return m_writer.unlift() + m_writer.unretract(); } std::string set_extruder(unsigned int extruder_id, double print_z); @@ -363,7 +363,7 @@ private: // The Pressure Equalizer removes the markers from the final G-code. bool m_enable_extrusion_role_markers; // Keeps track of the last extrusion role passed to the processor - ExtrusionRole m_last_processor_extrusion_role; + GCodeExtrusionRole m_last_processor_extrusion_role; // How many times will change_layer() be called? // change_layer() will update the progress bar. unsigned int m_layer_count; @@ -376,7 +376,7 @@ private: bool m_object_layer_over_raft; double m_volumetric_speed; // Support for the extrusion role markers. Which marker is active? - ExtrusionRole m_last_extrusion_role; + GCodeExtrusionRole m_last_extrusion_role; // Support for G-Code Processor float m_last_height{ 0.0f }; float m_last_layer_z{ 0.0f }; diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 0fde8c5ad..3e61e7fec 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -415,7 +415,7 @@ void GCodeProcessor::UsedFilaments::process_role_cache(const GCodeProcessor* pro filament.first = role_cache / s * 0.001; filament.second = role_cache * processor->m_result.filament_densities[processor->m_extruder_id] * 0.001; - ExtrusionRole active_role = processor->m_extrusion_role; + GCodeExtrusionRole active_role = processor->m_extrusion_role; if (filaments_per_role.find(active_role) != filaments_per_role.end()) { filaments_per_role[active_role].first += filament.first; filaments_per_role[active_role].second += filament.second; @@ -1170,14 +1170,14 @@ std::vector> GCodeProcessor::get_moves_time(PrintEst return ret; } -std::vector> GCodeProcessor::get_roles_time(PrintEstimatedStatistics::ETimeMode mode) const +std::vector> GCodeProcessor::get_roles_time(PrintEstimatedStatistics::ETimeMode mode) const { - std::vector> ret; + std::vector> ret; if (mode < PrintEstimatedStatistics::ETimeMode::Count) { for (size_t i = 0; i < m_time_processor.machines[static_cast(mode)].roles_time.size(); ++i) { float time = m_time_processor.machines[static_cast(mode)].roles_time[i]; if (time > 0.0f) - ret.push_back({ static_cast(i), time }); + ret.push_back({ static_cast(i), time }); } } return ret; @@ -1645,7 +1645,7 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers // extrusion role tag if (boost::starts_with(comment, reserved_tag(ETags::Role))) { - set_extrusion_role(ExtrusionEntity::string_to_role(comment.substr(reserved_tag(ETags::Role).length()))); + set_extrusion_role(string_to_gcode_extrusion_role(comment.substr(reserved_tag(ETags::Role).length()))); if (m_extrusion_role == erExternalPerimeter) m_seams_detector.activate(true); return; @@ -3771,7 +3771,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type, bool internal_only) } } -void GCodeProcessor::set_extrusion_role(ExtrusionRole role) +void GCodeProcessor::set_extrusion_role(GCodeExtrusionRole role) { m_used_filaments.process_role_cache(this); m_extrusion_role = role; diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 3baff69aa..a3841343b 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -47,7 +47,7 @@ namespace Slic3r { float travel_time; std::vector>> custom_gcode_times; std::vector> moves_times; - std::vector> roles_times; + std::vector> roles_times; std::vector layers_times; void reset() { @@ -62,7 +62,7 @@ namespace Slic3r { std::vector volumes_per_color_change; std::map volumes_per_extruder; - std::map> used_filaments_per_role; + std::map> used_filaments_per_role; std::map cost_per_extruder; std::array(ETimeMode::Count)> modes; @@ -99,7 +99,7 @@ namespace Slic3r { { unsigned int gcode_id{ 0 }; EMoveType type{ EMoveType::Noop }; - ExtrusionRole extrusion_role{ erNone }; + GCodeExtrusionRole extrusion_role{ erNone }; unsigned char extruder_id{ 0 }; unsigned char cp_color_id{ 0 }; Vec3f position{ Vec3f::Zero() }; // mm @@ -238,7 +238,7 @@ namespace Slic3r { }; EMoveType move_type{ EMoveType::Noop }; - ExtrusionRole role{ erNone }; + GCodeExtrusionRole role{ erNone }; unsigned int g1_line_id{ 0 }; unsigned int layer_id{ 0 }; float distance{ 0.0f }; // mm @@ -310,7 +310,7 @@ namespace Slic3r { std::vector blocks; std::vector g1_times_cache; std::array(EMoveType::Count)> moves_time; - std::array(ExtrusionRole::erCount)> roles_time; + std::array(GCodeExtrusionRole::erCount)> roles_time; std::vector layers_time; void reset(); @@ -360,7 +360,7 @@ namespace Slic3r { std::map volumes_per_extruder; double role_cache; - std::map> filaments_per_role; // ExtrusionRole -> (m, g) + std::map> filaments_per_role; // ExtrusionRole -> (m, g) void reset(); @@ -441,7 +441,7 @@ namespace Slic3r { { float value; float tag_value; - ExtrusionRole role; + GCodeExtrusionRole role; }; std::string type; @@ -454,7 +454,7 @@ namespace Slic3r { : type(type), threshold(threshold) {} - void update(float value, ExtrusionRole role) { + void update(float value, GCodeExtrusionRole role) { if (role != erCustom) { ++count; if (last_tag_value != 0.0f) { @@ -539,7 +539,7 @@ namespace Slic3r { float m_mm3_per_mm; float m_fan_speed; // percentage float m_z_offset; // mm - ExtrusionRole m_extrusion_role; + GCodeExtrusionRole m_extrusion_role; unsigned char m_extruder_id; ExtruderColors m_extruder_colors; ExtruderTemps m_extruder_temps; @@ -620,7 +620,7 @@ namespace Slic3r { std::vector>> get_custom_gcode_times(PrintEstimatedStatistics::ETimeMode mode, bool include_remaining) const; std::vector> get_moves_time(PrintEstimatedStatistics::ETimeMode mode) const; - std::vector> get_roles_time(PrintEstimatedStatistics::ETimeMode mode) const; + std::vector> get_roles_time(PrintEstimatedStatistics::ETimeMode mode) const; std::vector get_layers_time(PrintEstimatedStatistics::ETimeMode mode) const; private: @@ -757,7 +757,7 @@ namespace Slic3r { void store_move_vertex(EMoveType type, bool internal_only = false); - void set_extrusion_role(ExtrusionRole role); + void set_extrusion_role(GCodeExtrusionRole role); float minimum_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const; float minimum_travel_feedrate(PrintEstimatedStatistics::ETimeMode mode, float feedrate) const; diff --git a/src/libslic3r/GCode/PressureEqualizer.cpp b/src/libslic3r/GCode/PressureEqualizer.cpp index 399ab4272..52597517e 100644 --- a/src/libslic3r/GCode/PressureEqualizer.cpp +++ b/src/libslic3r/GCode/PressureEqualizer.cpp @@ -60,7 +60,7 @@ PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_use_ } // Don't regulate the pressure before and after gap-fill and ironing. - for (const ExtrusionRole er : {erGapFill, erIroning}) { + for (const GCodeExtrusionRole er : {erGapFill, erIroning}) { m_max_volumetric_extrusion_rate_slopes[er].negative = 0; m_max_volumetric_extrusion_rate_slopes[er].positive = 0; } @@ -185,7 +185,7 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo if (strncmp(line, EXTRUSION_ROLE_TAG.data(), EXTRUSION_ROLE_TAG.length()) == 0) { line += EXTRUSION_ROLE_TAG.length(); int role = atoi(line); - m_current_extrusion_role = ExtrusionRole(role); + m_current_extrusion_role = GCodeExtrusionRole(role); #ifdef PRESSURE_EQUALIZER_DEBUG ++line_idx; #endif @@ -542,7 +542,7 @@ void PressureEqualizer::adjust_volumetric_rate() for (size_t iRole = 1; iRole < erCount; ++ iRole) { const float &rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].negative; if (rate_slope == 0 || feedrate_per_extrusion_role[iRole] == std::numeric_limits::max()) - continue; // The negative rate is unlimited or the rate for ExtrusionRole iRole is unlimited. + continue; // The negative rate is unlimited or the rate for GCodeExtrusionRole iRole is unlimited. float rate_end = feedrate_per_extrusion_role[iRole]; if (iRole == line.extrusion_role && rate_succ < rate_end) @@ -600,7 +600,7 @@ void PressureEqualizer::adjust_volumetric_rate() for (size_t iRole = 1; iRole < erCount; ++ iRole) { const float &rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].positive; if (rate_slope == 0 || feedrate_per_extrusion_role[iRole] == std::numeric_limits::max()) - continue; // The positive rate is unlimited or the rate for ExtrusionRole iRole is unlimited. + continue; // The positive rate is unlimited or the rate for GCodeExtrusionRole iRole is unlimited. float rate_start = feedrate_per_extrusion_role[iRole]; if (!line.adjustable_flow || line.extrusion_role == erExternalPerimeter || line.extrusion_role == erGapFill || line.extrusion_role == erBridgeInfill || line.extrusion_role == erIroning) { diff --git a/src/libslic3r/GCode/PressureEqualizer.hpp b/src/libslic3r/GCode/PressureEqualizer.hpp index d6b7f2a4f..246a20568 100644 --- a/src/libslic3r/GCode/PressureEqualizer.hpp +++ b/src/libslic3r/GCode/PressureEqualizer.hpp @@ -77,7 +77,7 @@ private: // X,Y,Z,E,F float m_current_pos[5]; size_t m_current_extruder; - ExtrusionRole m_current_extrusion_role; + GCodeExtrusionRole m_current_extrusion_role; bool m_retracted; bool m_use_relative_e_distances; @@ -149,7 +149,7 @@ private: // Index of the active extruder. size_t extruder_id; // Extrusion role of this segment. - ExtrusionRole extrusion_role; + GCodeExtrusionRole extrusion_role; // Current volumetric extrusion rate. float volumetric_extrusion_rate; diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 685855721..244a103ca 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -407,13 +407,13 @@ Polygons extract_perimeter_polygons(const Layer *layer, std::vectorrole(); if (perimeter->is_loop()) { for (const ExtrusionPath &path : static_cast(perimeter)->paths) { - if (path.role() == ExtrusionRole::erExternalPerimeter) { - role = ExtrusionRole::erExternalPerimeter; + if (path.role() == ExtrusionRole::ExternalPerimeter) { + role = ExtrusionRole::ExternalPerimeter; } } } - if (role == ExtrusionRole::erExternalPerimeter) { + if (role == ExtrusionRole::ExternalPerimeter) { Points p; perimeter->collect_points(p); polygons.emplace_back(std::move(p)); @@ -1548,7 +1548,7 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool extern Point seam_point = Point::new_scale(seam_position.x(), seam_position.y()); - if (loop.role() == ExtrusionRole::erPerimeter) { //Hopefully inner perimeter + if (loop.role() == ExtrusionRole::Perimeter) { //Hopefully inner perimeter const SeamCandidate &perimeter_point = layer_perimeters.points[seam_index]; ExtrusionLoop::ClosestPathPoint projected_point = loop.get_closest_path_and_point(seam_point, false); // determine depth of the seam point. diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 5e14035d7..874a134b7 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -197,7 +197,7 @@ void ToolOrdering::initialize_layers(std::vector &zs) if (object.config().wipe_into_objects) return true; - if (!region.config().wipe_into_infill || eec.role() != erInternalInfill) + if (!region.config().wipe_into_infill || eec.role() != ExtrusionRole::InternalInfill) return false; return true; @@ -210,8 +210,8 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto for (auto support_layer : object.support_layers()) { LayerTools &layer_tools = this->tools_for_layer(support_layer->print_z); ExtrusionRole role = support_layer->support_fills.role(); - bool has_support = role == erMixed || role == erSupportMaterial; - bool has_interface = role == erMixed || role == erSupportMaterialInterface; + bool has_support = role == ExtrusionRole::Mixed || role == ExtrusionRole::SupportMaterial; + bool has_interface = role == ExtrusionRole::Mixed || role == ExtrusionRole::SupportMaterialInterface; unsigned int extruder_support = object.config().support_material_extruder.value; unsigned int extruder_interface = object.config().support_material_interface_extruder.value; if (has_support) @@ -266,10 +266,10 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto for (const ExtrusionEntity *ee : layerm->fills()) { // fill represents infill extrusions of a single island. const auto *fill = dynamic_cast(ee); - ExtrusionRole role = fill->entities.empty() ? erNone : fill->entities.front()->role(); + ExtrusionRole role = fill->entities.empty() ? ExtrusionRole::None : fill->entities.front()->role(); if (is_solid_infill(role)) has_solid_infill = true; - else if (role != erNone) + else if (role != ExtrusionRole::None) has_infill = true; if (m_print_config_ptr) { diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index db087a9ba..e83584218 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -37,7 +37,7 @@ public: // adds tag for analyzer: std::ostringstream str; str << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) << m_layer_height << "\n"; // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming - str << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << ExtrusionEntity::role_to_string(erWipeTower) << "\n"; + str << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << gcode_extrusion_role_to_string(erWipeTower) << "\n"; m_gcode += str.str(); change_analyzer_line_width(line_width); } diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 15210b641..a9044367a 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -114,7 +114,7 @@ ExtrusionMultiPath PerimeterGenerator::thick_polyline_to_multi_path(const ThickP } const double w = fmax(line.a_width, line.b_width); - const Flow new_flow = (role == erOverhangPerimeter && flow.bridge()) ? flow : flow.with_width(unscale(w) + flow.height() * float(1. - 0.25 * PI)); + const Flow new_flow = (role == ExtrusionRole::OverhangPerimeter && flow.bridge()) ? flow : flow.with_width(unscale(w) + flow.height() * float(1. - 0.25 * PI)); if (path.polyline.points.empty()) { path.polyline.append(line.a); path.polyline.append(line.b); @@ -292,9 +292,9 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator for (const PerimeterGeneratorLoop &loop : loops) { bool is_external = loop.is_external(); - ExtrusionRole role; + ExtrusionRole role = ExtrusionRole::None; ExtrusionLoopRole loop_role; - role = is_external ? erExternalPerimeter : erPerimeter; + role = is_external ? ExtrusionRole::ExternalPerimeter : ExtrusionRole::Perimeter; if (loop.is_internal_contour()) { // Note that we set loop role to ContourInternalPerimeter // also when loop is both internal and external (i.e. @@ -332,7 +332,7 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator extrusion_paths_append( paths, diff_pl({ polygon }, lower_slices_polygons_clipped), - erOverhangPerimeter, + ExtrusionRole::OverhangPerimeter, params.mm3_per_mm_overhang, params.overhang_flow.width(), params.overhang_flow.height()); @@ -354,7 +354,7 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator // Append thin walls to the nearest-neighbor search (only for first iteration) if (! thin_walls.empty()) { - variable_width_classic(thin_walls, erExternalPerimeter, params.ext_perimeter_flow, coll.entities); + variable_width_classic(thin_walls, ExtrusionRole::ExternalPerimeter, params.ext_perimeter_flow, coll.entities); thin_walls.clear(); } @@ -503,7 +503,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P continue; const bool is_external = extrusion->inset_idx == 0; - ExtrusionRole role = is_external ? erExternalPerimeter : erPerimeter; + ExtrusionRole role = is_external ? ExtrusionRole::ExternalPerimeter : ExtrusionRole::Perimeter; if (pg_extrusion.fuzzify) fuzzy_extrusion_line(*extrusion, scaled(params.config.fuzzy_skin_thickness.value), scaled(params.config.fuzzy_skin_point_dist.value)); @@ -547,7 +547,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P // get overhang paths by checking what parts of this loop fall // outside the grown lower slices (thus where the distance between // the loop centerline and original lower slices is >= half nozzle diameter - extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctDifference), erOverhangPerimeter, + extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctDifference), ExtrusionRole::OverhangPerimeter, params.overhang_flow); // Reapply the nearest point search for starting point. @@ -568,7 +568,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P for (const ExtrusionPath &path : paths) { ++point_occurrence[path.polyline.first_point()].occurrence; ++point_occurrence[path.polyline.last_point()].occurrence; - if (path.role() == erOverhangPerimeter) { + if (path.role() == ExtrusionRole::OverhangPerimeter) { point_occurrence[path.polyline.first_point()].is_overhang = true; point_occurrence[path.polyline.last_point()].is_overhang = true; } @@ -938,7 +938,7 @@ std::tuple, Polygons> generate_extra_perimeters_over Polygons shrinked = offset(prev, -0.4 * overhang_flow.scaled_spacing()); if (!shrinked.empty()) { overhang_region.emplace_back(); - extrusion_paths_append(overhang_region.back(), perimeter, erOverhangPerimeter, overhang_flow.mm3_per_mm(), + extrusion_paths_append(overhang_region.back(), perimeter, ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(), overhang_flow.width(), overhang_flow.height()); } @@ -953,13 +953,13 @@ std::tuple, Polygons> generate_extra_perimeters_over if (!fills.empty()) { fills = intersection_pl(fills, inset_overhang_area); overhang_region.emplace_back(); - extrusion_paths_append(overhang_region.back(), fills, erOverhangPerimeter, overhang_flow.mm3_per_mm(), + extrusion_paths_append(overhang_region.back(), fills, ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(), overhang_flow.width(), overhang_flow.height()); } break; } else { overhang_region.emplace_back(); - extrusion_paths_append(overhang_region.back(), perimeter, erOverhangPerimeter, overhang_flow.mm3_per_mm(), + extrusion_paths_append(overhang_region.back(), perimeter, ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(), overhang_flow.width(), overhang_flow.height()); } @@ -981,7 +981,7 @@ std::tuple, Polygons> generate_extra_perimeters_over } Polylines perimeter = intersection_pl(to_polylines(perimeter_polygon), shrinked_overhang_to_cover); overhang_region.emplace_back(); - extrusion_paths_append(overhang_region.back(), perimeter, erOverhangPerimeter, overhang_flow.mm3_per_mm(), + extrusion_paths_append(overhang_region.back(), perimeter, ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(), overhang_flow.width(), overhang_flow.height()); perimeter_polygon = expand(perimeter_polygon, 0.5 * overhang_flow.scaled_spacing()); @@ -1502,7 +1502,7 @@ void PerimeterGenerator::process_classic( ex.medial_axis(min, max, &polylines); if (! polylines.empty()) { ExtrusionEntityCollection gap_fill; - variable_width_classic(polylines, erGapFill, params.solid_infill_flow, gap_fill.entities); + variable_width_classic(polylines, ExtrusionRole::GapFill, params.solid_infill_flow, gap_fill.entities); /* Make sure we don't infill narrow parts that are already gap-filled (we only consider this surface's gaps to reduce the diff() complexity). Growing actual extrusions ensures that gaps not filled by medial axis diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 79fee4672..e7305234f 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1029,7 +1029,7 @@ void Print::_make_skirt() ExtrusionLoop eloop(elrSkirt); eloop.paths.emplace_back(ExtrusionPath( ExtrusionPath( - erSkirt, + ExtrusionRole::Skirt, (float)mm3_per_mm, // this will be overridden at G-code export time flow.width(), (float)first_layer_height // this will be overridden at G-code export time diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 9965c499f..331fc6c78 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -1178,7 +1178,7 @@ namespace SupportMaterialInternal { static inline bool has_bridging_perimeters(const ExtrusionLoop &loop) { for (const ExtrusionPath &ep : loop.paths) - if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty()) + if (ep.role() == ExtrusionRole::OverhangPerimeter && ! ep.polyline.empty()) return int(ep.size()) >= (ep.is_closed() ? 3 : 2); return false; } @@ -1204,7 +1204,7 @@ namespace SupportMaterialInternal { for (const ExtrusionEntity *ee2 : static_cast(ee)->entities) { assert(! ee2->is_collection()); assert(! ee2->is_loop()); - if (ee2->role() == erBridgeInfill) + if (ee2->role() == ExtrusionRole::BridgeInfill) return true; } } @@ -1225,7 +1225,7 @@ namespace SupportMaterialInternal { { assert(expansion_scaled >= 0.f); for (const ExtrusionPath &ep : loop.paths) - if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty()) { + if (ep.role() == ExtrusionRole::OverhangPerimeter && ! ep.polyline.empty()) { float exp = 0.5f * (float)scale_(ep.width) + expansion_scaled; if (ep.is_closed()) { if (ep.size() >= 3) { @@ -3357,7 +3357,7 @@ static inline void tree_supports_generate_paths( ExPolygons level2 = offset2_ex({ expoly }, -1.5 * flow.scaled_width(), 0.5 * flow.scaled_width()); if (level2.size() == 1) { Polylines polylines; - extrusion_entities_append_paths(dst, draw_perimeters(expoly, clip_length), erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), + extrusion_entities_append_paths(dst, draw_perimeters(expoly, clip_length), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), // Disable reversal of the path, always start with the anchor, always print CCW. false); expoly = level2.front(); @@ -3460,7 +3460,7 @@ static inline void tree_supports_generate_paths( pl.reverse(); polylines.emplace_back(std::move(pl)); } - extrusion_entities_append_paths(dst, polylines, erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), + extrusion_entities_append_paths(dst, polylines, ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), // Disable reversal of the path, always start with the anchor, always print CCW. false); } @@ -3505,7 +3505,7 @@ static inline void fill_expolygons_with_sheath_generate_paths( eec->no_sort = true; } ExtrusionEntitiesPtr &out = no_sort ? eec->entities : dst; - extrusion_entities_append_paths(out, draw_perimeters(expoly, clip_length), erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height()); + extrusion_entities_append_paths(out, draw_perimeters(expoly, clip_length), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height()); // Fill in the rest. fill_expolygons_generate_paths(out, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, density, role, flow); if (no_sort && ! eec->empty()) @@ -3817,7 +3817,7 @@ void LoopInterfaceProcessor::generate(SupportGeneratorLayerExtruded &top_contact extrusion_entities_append_paths( top_contact_layer.extrusions, std::move(loop_lines), - erSupportMaterialInterface, flow.mm3_per_mm(), flow.width(), flow.height()); + ExtrusionRole::SupportMaterialInterface, flow.mm3_per_mm(), flow.width(), flow.height()); } #ifdef SLIC3R_DEBUG @@ -4252,7 +4252,7 @@ void generate_support_toolpaths( // Filler and its parameters filler, float(support_params.support_density), // Extrusion parameters - erSupportMaterial, flow, + ExtrusionRole::SupportMaterial, flow, support_params.with_sheath, false); } } @@ -4284,7 +4284,7 @@ void generate_support_toolpaths( // Filler and its parameters filler, density, // Extrusion parameters - (support_layer_id < slicing_params.base_raft_layers) ? erSupportMaterial : erSupportMaterialInterface, flow, + (support_layer_id < slicing_params.base_raft_layers) ? ExtrusionRole::SupportMaterial : ExtrusionRole::SupportMaterialInterface, flow, // sheath at first layer support_layer_id == 0, support_layer_id == 0); } @@ -4440,7 +4440,7 @@ void generate_support_toolpaths( // Filler and its parameters filler_interface.get(), float(density), // Extrusion parameters - erSupportMaterialInterface, interface_flow); + ExtrusionRole::SupportMaterialInterface, interface_flow); } // Base interface layers under soluble interfaces @@ -4462,7 +4462,7 @@ void generate_support_toolpaths( // Filler and its parameters filler, float(support_params.interface_density), // Extrusion parameters - erSupportMaterial, interface_flow); + ExtrusionRole::SupportMaterial, interface_flow); } // Base support or flange. @@ -4500,7 +4500,7 @@ void generate_support_toolpaths( // Filler and its parameters filler, density, // Extrusion parameters - erSupportMaterial, flow, + ExtrusionRole::SupportMaterial, flow, sheath, no_sort); } diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 68a8a4011..4800a2210 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -58,7 +58,7 @@ public: bool is_external_perimeter() const { assert(origin_entity != nullptr); - return origin_entity->role() == erExternalPerimeter || origin_entity->role() == erOverhangPerimeter; + return origin_entity->role() == ExtrusionRole::ExternalPerimeter || origin_entity->role() == ExtrusionRole::OverhangPerimeter; } Vec2f a; @@ -170,16 +170,15 @@ struct SliceConnection float get_flow_width(const LayerRegion *region, ExtrusionRole role) { - switch (role) { - case ExtrusionRole::erBridgeInfill: return region->flow(FlowRole::frExternalPerimeter).width(); - case ExtrusionRole::erExternalPerimeter: return region->flow(FlowRole::frExternalPerimeter).width(); - case ExtrusionRole::erGapFill: return region->flow(FlowRole::frInfill).width(); - case ExtrusionRole::erPerimeter: return region->flow(FlowRole::frPerimeter).width(); - case ExtrusionRole::erSolidInfill: return region->flow(FlowRole::frSolidInfill).width(); - case ExtrusionRole::erInternalInfill: return region->flow(FlowRole::frInfill).width(); - case ExtrusionRole::erTopSolidInfill: return region->flow(FlowRole::frTopSolidInfill).width(); - default: return region->flow(FlowRole::frPerimeter).width(); - } + if (role == ExtrusionRole::BridgeInfill) return region->flow(FlowRole::frExternalPerimeter).width(); + if (role == ExtrusionRole::ExternalPerimeter) return region->flow(FlowRole::frExternalPerimeter).width(); + if (role == ExtrusionRole::GapFill) return region->flow(FlowRole::frInfill).width(); + if (role == ExtrusionRole::Perimeter) return region->flow(FlowRole::frPerimeter).width(); + if (role == ExtrusionRole::SolidInfill) return region->flow(FlowRole::frSolidInfill).width(); + if (role == ExtrusionRole::InternalInfill) return region->flow(FlowRole::frInfill).width(); + if (role == ExtrusionRole::TopSolidInfill) return region->flow(FlowRole::frTopSolidInfill).width(); + // default + return region->flow(FlowRole::frPerimeter).width(); } std::vector to_short_lines(const ExtrusionEntity *e, float length_limit) @@ -800,7 +799,7 @@ SupportPoints check_stability(const PrintObject *po, const PrintTryCancel& cance const LayerRegion *fill_region = layer->get_region(fill_range.region()); for (const auto &fill_idx : fill_range) { const ExtrusionEntity *entity = fill_region->fills().entities[fill_idx]; - if (entity->role() == erBridgeInfill) { + if (entity->role() == ExtrusionRole::BridgeInfill) { for (const ExtrusionLine &bridge : check_extrusion_entity_stability(entity, fill_region, prev_layer_ext_perim_lines,prev_layer_boundary, params)) { if (bridge.support_point_generated) { diff --git a/src/libslic3r/enum_bitmask.hpp b/src/libslic3r/enum_bitmask.hpp index 4c2076313..72172fad9 100644 --- a/src/libslic3r/enum_bitmask.hpp +++ b/src/libslic3r/enum_bitmask.hpp @@ -40,13 +40,18 @@ public: constexpr bool operator&(option_type t) { return m_bits & mask_value(t); } constexpr bool has(option_type t) { return m_bits & mask_value(t); } + constexpr bool operator==(const enum_bitmask r) const { return m_bits == r.m_bits; } + constexpr bool operator!=(const enum_bitmask r) const { return m_bits != r.m_bits; } + // For sorting by the enum values. + constexpr bool lower(const enum_bitmask r) const { return m_bits < r.m_bits; } + private: underlying_type m_bits = 0; }; // For enabling free functions producing enum_bitmask<> type from bit operations on enums. -template struct is_enum_bitmask_type { static const bool enable = false; }; -#define ENABLE_ENUM_BITMASK_OPERATORS(x) template<> struct is_enum_bitmask_type { static const bool enable = true; }; +template struct is_enum_bitmask_type { static constexpr const bool enable = false; }; +#define ENABLE_ENUM_BITMASK_OPERATORS(x) template<> struct is_enum_bitmask_type { static constexpr const bool enable = true; }; template inline constexpr bool is_enum_bitmask_type_v = is_enum_bitmask_type::enable; // Creates an enum_bitmask from two options, convenient for passing of options to a function: diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 097757ff9..451c43ff2 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -899,7 +899,7 @@ void GCodeViewer::reset() m_shells.volumes.clear(); m_layers.reset(); m_layers_z_range = { 0, 0 }; - m_roles = std::vector(); + m_roles = std::vector(); m_print_statistics.reset(); for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { m_layers_times[i] = std::vector(); @@ -1607,8 +1607,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) curr.extrusion_role != erSupportMaterial && curr.extrusion_role != erSupportMaterialInterface && curr.extrusion_role != erWipeTower && - curr.extrusion_role != erCustom && - curr.extrusion_role != erMixed) { + curr.extrusion_role != erCustom) { const Vec3d curr_pos = curr.position.cast(); const Vec3d prev_pos = prev.position.cast(); m_cog.add_segment(curr_pos, prev_pos, curr.mm3_per_mm * (curr_pos - prev_pos).norm()); @@ -3469,12 +3468,12 @@ void GCodeViewer::render_legend(float& legend_height) return _u8L("from") + " " + std::string(buf1) + " " + _u8L("to") + " " + std::string(buf2) + " " + _u8L("mm"); }; - auto role_time_and_percent = [time_mode](ExtrusionRole role) { - auto it = std::find_if(time_mode.roles_times.begin(), time_mode.roles_times.end(), [role](const std::pair& item) { return role == item.first; }); + auto role_time_and_percent = [time_mode](GCodeExtrusionRole role) { + auto it = std::find_if(time_mode.roles_times.begin(), time_mode.roles_times.end(), [role](const std::pair& item) { return role == item.first; }); return (it != time_mode.roles_times.end()) ? std::make_pair(it->second, it->second / time_mode.time) : std::make_pair(0.0f, 0.0f); }; - auto used_filament_per_role = [this, imperial_units](ExtrusionRole role) { + auto used_filament_per_role = [this, imperial_units](GCodeExtrusionRole role) { auto it = m_print_statistics.used_filaments_per_role.find(role); if (it == m_print_statistics.used_filaments_per_role.end()) return std::make_pair(0.0, 0.0); @@ -3494,10 +3493,10 @@ void GCodeViewer::render_legend(float& legend_height) if (m_view_type == EViewType::FeatureType) { // calculate offsets to align time/percentage data - for (size_t i = 0; i < m_roles.size(); ++i) { - ExtrusionRole role = m_roles[i]; + for (GCodeExtrusionRole role : m_roles) { + assert(role < erCount); if (role < erCount) { - labels.push_back(_u8L(ExtrusionEntity::role_to_string(role))); + labels.push_back(_u8L(gcode_extrusion_role_to_string(role))); auto [time, percent] = role_time_and_percent(role); times.push_back((time > 0.0f) ? short_time(get_time_dhms(time)) : ""); percents.push_back(percent); @@ -3610,7 +3609,7 @@ void GCodeViewer::render_legend(float& legend_height) max_time_percent = std::max(max_time_percent, time_mode.travel_time / time_mode.time); for (size_t i = 0; i < m_roles.size(); ++i) { - ExtrusionRole role = m_roles[i]; + GCodeExtrusionRole role = m_roles[i]; if (role >= erCount) continue; const bool visible = is_visible(role); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index b077de98a..11a0d6476 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -208,7 +208,7 @@ class GCodeViewer }; EMoveType type{ EMoveType::Noop }; - ExtrusionRole role{ erNone }; + GCodeExtrusionRole role{ erNone }; float delta_extruder{ 0.0f }; float height{ 0.0f }; float width{ 0.0f }; @@ -753,7 +753,7 @@ private: std::vector m_tool_colors; Layers m_layers; std::array m_layers_z_range; - std::vector m_roles; + std::vector m_roles; size_t m_extruders_count; std::vector m_extruder_ids; std::vector m_filament_diameters; @@ -849,7 +849,7 @@ private: #if ENABLE_GCODE_VIEWER_STATISTICS void render_statistics(); #endif // ENABLE_GCODE_VIEWER_STATISTICS - bool is_visible(ExtrusionRole role) const { + bool is_visible(GCodeExtrusionRole role) const { return role < erCount && (m_extrusions.role_visibility_flags & (1 << role)) != 0; } bool is_visible(const Path& path) const { return is_visible(path.role); } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5331094c1..92289b9df 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -6567,7 +6567,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c if (support_layer) { for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) _3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy, - select_geometry(idx_layer, (extrusion_entity->role() == erSupportMaterial) ? + select_geometry(idx_layer, (extrusion_entity->role() == ExtrusionRole::SupportMaterial) ? support_layer->object()->config().support_material_extruder : support_layer->object()->config().support_material_interface_extruder, 2)); } diff --git a/tests/fff_print/test_extrusion_entity.cpp b/tests/fff_print/test_extrusion_entity.cpp index 56588d98a..5f7de1f14 100644 --- a/tests/fff_print/test_extrusion_entity.cpp +++ b/tests/fff_print/test_extrusion_entity.cpp @@ -21,7 +21,7 @@ static inline Slic3r::Point random_point(float LO=-50, float HI=50) // build a sample extrusion entity collection with random start and end points. static Slic3r::ExtrusionPath random_path(size_t length = 20, float LO = -50, float HI = 50) { - ExtrusionPath t {erPerimeter, 1.0, 1.0, 1.0}; + ExtrusionPath t { ExtrusionRole::Perimeter, 1.0, 1.0, 1.0 }; for (size_t j = 0; j < length; ++ j) t.polyline.append(random_point(LO, HI)); return t; @@ -37,7 +37,7 @@ static Slic3r::ExtrusionPaths random_paths(size_t count = 10, size_t length = 20 SCENARIO("ExtrusionPath", "[ExtrusionEntity]") { GIVEN("Simple path") { - Slic3r::ExtrusionPath path{ erExternalPerimeter }; + Slic3r::ExtrusionPath path{ ExtrusionRole::ExternalPerimeter }; path.polyline = { { 100, 100 }, { 200, 100 }, { 200, 200 } }; path.mm3_per_mm = 1.; THEN("first point") { @@ -64,7 +64,7 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") Polygon square { { 100, 100 }, { 200, 100 }, { 200, 200 }, { 100, 200 } }; ExtrusionLoop loop; - loop.paths.emplace_back(new_extrusion_path(square.split_at_first_point(), erExternalPerimeter, 1.)); + loop.paths.emplace_back(new_extrusion_path(square.split_at_first_point(), ExtrusionRole::ExternalPerimeter, 1.)); THEN("polygon area") { REQUIRE(loop.polygon().area() == Approx(square.area())); } @@ -81,7 +81,7 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") REQUIRE(loop2->paths.size() == 1); } THEN("cloned role") { - REQUIRE(loop2->paths.front().role() == erExternalPerimeter); + REQUIRE(loop2->paths.front().role() == ExtrusionRole::ExternalPerimeter); } } WHEN("cloned and split") { @@ -107,8 +107,8 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") Polyline polyline1 { { 100, 100 }, { 200, 100 }, { 200, 200 } }; Polyline polyline2 { { 200, 200 }, { 100, 200 }, { 100, 100 } }; ExtrusionLoop loop; - loop.paths.emplace_back(new_extrusion_path(polyline1, erExternalPerimeter, 1.)); - loop.paths.emplace_back(new_extrusion_path(polyline2, erOverhangPerimeter, 1.)); + loop.paths.emplace_back(new_extrusion_path(polyline1, ExtrusionRole::ExternalPerimeter, 1.)); + loop.paths.emplace_back(new_extrusion_path(polyline2, ExtrusionRole::OverhangPerimeter, 1.)); double tot_len = polyline1.length() + polyline2.length(); THEN("length") { @@ -135,9 +135,9 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") REQUIRE(loop2->paths[1].polyline.back() == loop2->paths[2].polyline.front()); } THEN("expected order after splitting") { - REQUIRE(loop2->paths.front().role() == erExternalPerimeter); - REQUIRE(loop2->paths[1].role() == erOverhangPerimeter); - REQUIRE(loop2->paths[2].role() == erExternalPerimeter); + REQUIRE(loop2->paths.front().role() == ExtrusionRole::ExternalPerimeter); + REQUIRE(loop2->paths[1].role() == ExtrusionRole::OverhangPerimeter); + REQUIRE(loop2->paths[2].role() == ExtrusionRole::ExternalPerimeter); } THEN("path has correct number of points") { REQUIRE(loop2->paths.front().polyline.size() == 2); @@ -175,8 +175,8 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") REQUIRE(loop2->paths[1].polyline.back() == loop2->paths.front().polyline.front()); } THEN("expected order after splitting") { - REQUIRE(loop2->paths.front().role() == erOverhangPerimeter); - REQUIRE(loop2->paths[1].role() == erExternalPerimeter); + REQUIRE(loop2->paths.front().role() == ExtrusionRole::OverhangPerimeter); + REQUIRE(loop2->paths[1].role() == ExtrusionRole::ExternalPerimeter); } THEN("path has correct number of points") { REQUIRE(loop2->paths.front().polyline.size() == 3); @@ -207,10 +207,10 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") Polyline polyline3 { { 9829401, 9321068 }, { 4821067, 9321068 }, { 4821067, 4821067 }, { 9829401, 4821067 } }; Polyline polyline4 { { 9829401, 4821067 }, { 59312736,4821067 } }; ExtrusionLoop loop; - loop.paths.emplace_back(new_extrusion_path(polyline1, erExternalPerimeter, 1.)); - loop.paths.emplace_back(new_extrusion_path(polyline2, erOverhangPerimeter, 1.)); - loop.paths.emplace_back(new_extrusion_path(polyline3, erExternalPerimeter, 1.)); - loop.paths.emplace_back(new_extrusion_path(polyline4, erOverhangPerimeter, 1.)); + loop.paths.emplace_back(new_extrusion_path(polyline1, ExtrusionRole::ExternalPerimeter, 1.)); + loop.paths.emplace_back(new_extrusion_path(polyline2, ExtrusionRole::OverhangPerimeter, 1.)); + loop.paths.emplace_back(new_extrusion_path(polyline3, ExtrusionRole::ExternalPerimeter, 1.)); + loop.paths.emplace_back(new_extrusion_path(polyline4, ExtrusionRole::OverhangPerimeter, 1.)); double len = loop.length(); WHEN("splitting at vertex") { Point point(4821067, 9321068); @@ -220,10 +220,10 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") REQUIRE(loop.length() == Approx(len)); } THEN("order is correctly preserved after splitting") { - REQUIRE(loop.paths.front().role() == erExternalPerimeter); - REQUIRE(loop.paths[1].role() == erOverhangPerimeter); - REQUIRE(loop.paths[2].role() == erExternalPerimeter); - REQUIRE(loop.paths[3].role() == erOverhangPerimeter); + REQUIRE(loop.paths.front().role() == ExtrusionRole::ExternalPerimeter); + REQUIRE(loop.paths[1].role() == ExtrusionRole::OverhangPerimeter); + REQUIRE(loop.paths[2].role() == ExtrusionRole::ExternalPerimeter); + REQUIRE(loop.paths[3].role() == ExtrusionRole::OverhangPerimeter); } } } @@ -233,7 +233,7 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") loop.paths.emplace_back(new_extrusion_path( Polyline { { 15896783, 15868739 }, { 24842049, 12117558 }, { 33853238, 15801279 }, { 37591780, 24780128 }, { 37591780, 24844970 }, { 33853231, 33825297 }, { 24842049, 37509013 }, { 15896798, 33757841 }, { 12211841, 24812544 }, { 15896783, 15868739 } }, - erExternalPerimeter, 1.)); + ExtrusionRole::ExternalPerimeter, 1.)); double len = loop.length(); THEN("split_at() preserves total length") { loop.split_at({ 15896783, 15868739 }, false, 0); @@ -245,9 +245,9 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") SCENARIO("ExtrusionEntityCollection: Basics", "[ExtrusionEntity]") { Polyline polyline { { 100, 100 }, { 200, 100 }, { 200, 200 } }; - ExtrusionPath path = new_extrusion_path(polyline, erExternalPerimeter, 1.); + ExtrusionPath path = new_extrusion_path(polyline, ExtrusionRole::ExternalPerimeter, 1.); ExtrusionLoop loop; - loop.paths.emplace_back(new_extrusion_path(Polygon(polyline.points).split_at_first_point(), erInternalInfill, 1.)); + loop.paths.emplace_back(new_extrusion_path(Polygon(polyline.points).split_at_first_point(), ExtrusionRole::InternalInfill, 1.)); ExtrusionEntityCollection collection; collection.append(path); THEN("no_sort is false by default") { @@ -378,7 +378,7 @@ TEST_CASE("ExtrusionEntityCollection: Chained path", "[ExtrusionEntity]") { REQUIRE(chained == test.chained); ExtrusionEntityCollection unchained_extrusions; extrusion_entities_append_paths(unchained_extrusions.entities, test.unchained, - erInternalInfill, 0., 0.4f, 0.3f); + ExtrusionRole::InternalInfill, 0., 0.4f, 0.3f); THEN("Chaining works") { ExtrusionEntityCollection chained_extrusions = unchained_extrusions.chained_path_from(test.initial_point); REQUIRE(chained_extrusions.entities.size() == test.chained.size()); diff --git a/tests/fff_print/test_perimeters.cpp b/tests/fff_print/test_perimeters.cpp index e613fa23d..4239d39dd 100644 --- a/tests/fff_print/test_perimeters.cpp +++ b/tests/fff_print/test_perimeters.cpp @@ -80,13 +80,13 @@ SCENARIO("Perimeter nesting", "[Perimeters]") } THEN("expected number of external loops") { size_t num_external = std::count_if(loops.entities.begin(), loops.entities.end(), - [](const ExtrusionEntity *ee){ return ee->role() == erExternalPerimeter; }); + [](const ExtrusionEntity *ee){ return ee->role() == ExtrusionRole::ExternalPerimeter; }); REQUIRE(num_external == data.external); } THEN("expected external order") { std::vector ext_order; for (auto *ee : loops.entities) - ext_order.emplace_back(ee->role() == erExternalPerimeter); + ext_order.emplace_back(ee->role() == ExtrusionRole::ExternalPerimeter); REQUIRE(ext_order == data.ext_order); } THEN("expected number of internal contour loops") { From d58cb1b5baadc5bf8c3678245dd7b4b0c04afc0d Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 12 Jan 2023 11:46:56 +0100 Subject: [PATCH 07/28] fix support auto-generate - did not work with multiple instances --- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 3827cd331..9b06e5340 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -507,7 +507,7 @@ bool GLGizmoFdmSupports::has_backend_supports() bool done = false; for (const PrintObject *po : m_parent.fff_print()->objects()) { if (po->model_object()->id() == mo->id()) - done = po->is_step_done(posSupportSpotsSearch); + done = done || po->is_step_done(posSupportSpotsSearch); } if (!done && !wxGetApp().plater()->is_background_process_update_scheduled()) { From e50e96bb26eab4f9d56c98706c03166635cf4fff Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 16 Jan 2023 10:47:27 +0100 Subject: [PATCH 08/28] Follow-up to 5991850db1f2d09d1b7713f42300e2bfe4a42128 WIP Extending the expressivity of ExtrusionRole Separated ExtrusionRole / GCodeExtrusionRole into ExtrusionRole.cpp,hpp --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/ExtrusionEntity.cpp | 81 ------------ src/libslic3r/ExtrusionEntity.hpp | 136 +------------------- src/libslic3r/ExtrusionRole.cpp | 89 +++++++++++++ src/libslic3r/ExtrusionRole.hpp | 147 ++++++++++++++++++++++ src/libslic3r/Flow.hpp | 2 +- src/libslic3r/GCode/GCodeProcessor.hpp | 2 +- src/libslic3r/GCode/PressureEqualizer.hpp | 2 +- src/libslic3r/PerimeterGenerator.hpp | 1 + 9 files changed, 243 insertions(+), 219 deletions(-) create mode 100644 src/libslic3r/ExtrusionRole.cpp create mode 100644 src/libslic3r/ExtrusionRole.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 06e32e2a2..6217a11df 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -56,6 +56,8 @@ set(SLIC3R_SOURCES ExtrusionEntity.hpp ExtrusionEntityCollection.cpp ExtrusionEntityCollection.hpp + ExtrusionRole.cpp + ExtrusionRole.hpp ExtrusionSimulator.cpp ExtrusionSimulator.hpp FileParserError.hpp diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index e180fcfcd..2b7bebc37 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -8,8 +8,6 @@ #include #include -#define L(s) (s) - namespace Slic3r { void ExtrusionPath::intersect_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const @@ -322,83 +320,4 @@ double ExtrusionLoop::min_mm3_per_mm() const return min_mm3_per_mm; } -// Convert a rich bitmask based ExtrusionRole to a less expressive ordinal GCodeExtrusionRole. -// GCodeExtrusionRole is to be serialized into G-code and deserialized by G-code viewer, -GCodeExtrusionRole extrusion_role_to_gcode_extrusion_role(ExtrusionRole role) -{ - if (role == ExtrusionRole::None) return erNone; - if (role == ExtrusionRole::Perimeter) return erPerimeter; - if (role == ExtrusionRole::ExternalPerimeter) return erExternalPerimeter; - if (role == ExtrusionRole::OverhangPerimeter) return erOverhangPerimeter; - if (role == ExtrusionRole::InternalInfill) return erInternalInfill; - if (role == ExtrusionRole::SolidInfill) return erSolidInfill; - if (role == ExtrusionRole::TopSolidInfill) return erTopSolidInfill; - if (role == ExtrusionRole::Ironing) return erIroning; - if (role == ExtrusionRole::BridgeInfill) return erBridgeInfill; - if (role == ExtrusionRole::GapFill) return erGapFill; - if (role == ExtrusionRole::Skirt) return erSkirt; - if (role == ExtrusionRole::SupportMaterial) return erSupportMaterial; - if (role == ExtrusionRole::SupportMaterialInterface) return erSupportMaterialInterface; - if (role == ExtrusionRole::WipeTower) return erWipeTower; - assert(false); - return erNone; -} - -std::string gcode_extrusion_role_to_string(GCodeExtrusionRole role) -{ - switch (role) { - case erNone : return L("Unknown"); - case erPerimeter : return L("Perimeter"); - case erExternalPerimeter : return L("External perimeter"); - case erOverhangPerimeter : return L("Overhang perimeter"); - case erInternalInfill : return L("Internal infill"); - case erSolidInfill : return L("Solid infill"); - case erTopSolidInfill : return L("Top solid infill"); - case erIroning : return L("Ironing"); - case erBridgeInfill : return L("Bridge infill"); - case erGapFill : return L("Gap fill"); - case erSkirt : return L("Skirt/Brim"); - case erSupportMaterial : return L("Support material"); - case erSupportMaterialInterface : return L("Support material interface"); - case erWipeTower : return L("Wipe tower"); - case erCustom : return L("Custom"); - default : assert(false); - } - return {}; -} - -GCodeExtrusionRole string_to_gcode_extrusion_role(const std::string_view role) -{ - if (role == L("Perimeter")) - return erPerimeter; - else if (role == L("External perimeter")) - return erExternalPerimeter; - else if (role == L("Overhang perimeter")) - return erOverhangPerimeter; - else if (role == L("Internal infill")) - return erInternalInfill; - else if (role == L("Solid infill")) - return erSolidInfill; - else if (role == L("Top solid infill")) - return erTopSolidInfill; - else if (role == L("Ironing")) - return erIroning; - else if (role == L("Bridge infill")) - return erBridgeInfill; - else if (role == L("Gap fill")) - return erGapFill; - else if (role == L("Skirt") || role == L("Skirt/Brim")) // "Skirt" is for backward compatibility with 2.3.1 and earlier - return erSkirt; - else if (role == L("Support material")) - return erSupportMaterial; - else if (role == L("Support material interface")) - return erSupportMaterialInterface; - else if (role == L("Wipe tower")) - return erWipeTower; - else if (role == L("Custom")) - return erCustom; - else - return erNone; -} - } diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 5022f216b..35c715191 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -2,9 +2,9 @@ #define slic3r_ExtrusionEntity_hpp_ #include "libslic3r.h" +#include "ExtrusionRole.hpp" #include "Polygon.hpp" #include "Polyline.hpp" -#include "enum_bitmask.hpp" #include #include @@ -17,133 +17,6 @@ using ExPolygons = std::vector; class ExtrusionEntityCollection; class Extruder; -enum class ExtrusionRoleModifier : uint16_t { -// 1) Extrusion types - // Perimeter (external, inner, ...) - Perimeter, - // Infill (top / bottom / solid inner / sparse inner / bridging inner ...) - Infill, - // Variable width extrusion - Thin, - // Support material extrusion - Support, - Skirt, - Wipe, -// 2) Extrusion modifiers - External, - Solid, - Ironing, - Bridge, -// 3) Special types - // Indicator that the extrusion role was mixed from multiple differing extrusion roles, - // for example from Support and SupportInterface. - Mixed, - // Stopper, there should be maximum 16 modifiers defined for uint16_t bit mask. - Count -}; -// There should be maximum 16 modifiers defined for uint16_t bit mask. -static_assert(int(ExtrusionRoleModifier::Count) <= 16, "ExtrusionRoleModifier: there must be maximum 16 modifiers defined to fit a 16 bit bitmask"); - -using ExtrusionRoleModifiers = enum_bitmask; -ENABLE_ENUM_BITMASK_OPERATORS(ExtrusionRoleModifier); - -struct ExtrusionRole : public ExtrusionRoleModifiers -{ - constexpr ExtrusionRole(const ExtrusionRoleModifier bit) : ExtrusionRoleModifiers(bit) {} - constexpr ExtrusionRole(const ExtrusionRoleModifiers bits) : ExtrusionRoleModifiers(bits) {} - - static constexpr const ExtrusionRoleModifiers None{}; - // Internal perimeter, not bridging. - static constexpr const ExtrusionRoleModifiers Perimeter{ ExtrusionRoleModifier::Perimeter }; - // External perimeter, not bridging. - static constexpr const ExtrusionRoleModifiers ExternalPerimeter{ ExtrusionRoleModifier::Perimeter | ExtrusionRoleModifier::External }; - // Perimeter, bridging. To be or'ed with ExtrusionRoleModifier::External for external bridging perimeter. - static constexpr const ExtrusionRoleModifiers OverhangPerimeter{ ExtrusionRoleModifier::Perimeter | ExtrusionRoleModifier::Bridge }; - // Sparse internal infill. - static constexpr const ExtrusionRoleModifiers InternalInfill{ ExtrusionRoleModifier::Infill }; - // Solid internal infill. - static constexpr const ExtrusionRoleModifiers SolidInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid }; - // Top solid infill (visible). - //FIXME why there is no bottom solid infill type? - static constexpr const ExtrusionRoleModifiers TopSolidInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::External }; - // Ironing infill at the top surfaces. - static constexpr const ExtrusionRoleModifiers Ironing{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Ironing | ExtrusionRoleModifier::External }; - // Visible bridging infill at the bottom of an object. - static constexpr const ExtrusionRoleModifiers BridgeInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::Bridge | ExtrusionRoleModifier::External }; -// static constexpr const ExtrusionRoleModifiers InternalBridgeInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::Bridge }; - // Gap fill extrusion, currently used for any variable width extrusion: Thin walls outside of the outer extrusion, - // gap fill in between perimeters, gap fill between the inner perimeter and infill. - //FIXME revise GapFill and ThinWall types, split Gap Fill to Gap Fill and ThinWall. - static constexpr const ExtrusionRoleModifiers GapFill{ ExtrusionRoleModifier::Thin }; // | ExtrusionRoleModifier::External }; -// static constexpr const ExtrusionRoleModifiers ThinWall{ ExtrusionRoleModifier::Thin }; - static constexpr const ExtrusionRoleModifiers Skirt{ ExtrusionRoleModifier::Skirt }; - // Support base material, printed with non-soluble plastic. - static constexpr const ExtrusionRoleModifiers SupportMaterial{ ExtrusionRoleModifier::Support }; - // Support interface material, printed with soluble plastic. - static constexpr const ExtrusionRoleModifiers SupportMaterialInterface{ ExtrusionRoleModifier::Support | ExtrusionRoleModifier::External }; - // Wipe tower material. - static constexpr const ExtrusionRoleModifiers WipeTower{ ExtrusionRoleModifier::Wipe }; - // Extrusion role for a collection with multiple extrusion roles. - static constexpr const ExtrusionRoleModifiers Mixed{ ExtrusionRoleModifier::Mixed }; -}; - -// Special flags describing loop -enum ExtrusionLoopRole { - elrDefault, - elrContourInternalPerimeter, - elrSkirt, -}; - -inline bool is_perimeter(ExtrusionRole role) -{ - return role == ExtrusionRole::Perimeter - || role == ExtrusionRole::ExternalPerimeter - || role == ExtrusionRole::OverhangPerimeter; -} - -inline bool is_infill(ExtrusionRole role) -{ - return role == ExtrusionRole::BridgeInfill - || role == ExtrusionRole::InternalInfill - || role == ExtrusionRole::SolidInfill - || role == ExtrusionRole::TopSolidInfill - || role == ExtrusionRole::Ironing; -} - -inline bool is_solid_infill(ExtrusionRole role) -{ - return role == ExtrusionRole::BridgeInfill - || role == ExtrusionRole::SolidInfill - || role == ExtrusionRole::TopSolidInfill - || role == ExtrusionRole::Ironing; -} - -inline bool is_bridge(ExtrusionRole role) { - return role == ExtrusionRole::BridgeInfill - || role == ExtrusionRole::OverhangPerimeter; -} - -enum GCodeExtrusionRole : uint8_t { - erNone, - erPerimeter, - erExternalPerimeter, - erOverhangPerimeter, - erInternalInfill, - erSolidInfill, - erTopSolidInfill, - erIroning, - erBridgeInfill, - erGapFill, - erSkirt, - erSupportMaterial, - erSupportMaterialInterface, - erWipeTower, - // Custom (user defined) G-code block, for example start / end G-code. - erCustom, - // Stopper to count number of enums. - erCount -}; - class ExtrusionEntity { public: @@ -179,13 +52,6 @@ public: virtual double total_volume() const = 0; }; -// Convert a rich bitmask based ExtrusionRole to a less expressive ordinal GCodeExtrusionRole. -// GCodeExtrusionRole is to be serialized into G-code and deserialized by G-code viewer, -GCodeExtrusionRole extrusion_role_to_gcode_extrusion_role(ExtrusionRole role); - -std::string gcode_extrusion_role_to_string(GCodeExtrusionRole role); -GCodeExtrusionRole string_to_gcode_extrusion_role(const std::string_view role); - typedef std::vector ExtrusionEntitiesPtr; class ExtrusionPath : public ExtrusionEntity diff --git a/src/libslic3r/ExtrusionRole.cpp b/src/libslic3r/ExtrusionRole.cpp new file mode 100644 index 000000000..f6f2eb2f5 --- /dev/null +++ b/src/libslic3r/ExtrusionRole.cpp @@ -0,0 +1,89 @@ +#include "ExtrusionRole.hpp" + +#include +#include + +#define L(s) (s) + +namespace Slic3r { + +// Convert a rich bitmask based ExtrusionRole to a less expressive ordinal GCodeExtrusionRole. +// GCodeExtrusionRole is to be serialized into G-code and deserialized by G-code viewer, +GCodeExtrusionRole extrusion_role_to_gcode_extrusion_role(ExtrusionRole role) +{ + if (role == ExtrusionRole::None) return erNone; + if (role == ExtrusionRole::Perimeter) return erPerimeter; + if (role == ExtrusionRole::ExternalPerimeter) return erExternalPerimeter; + if (role == ExtrusionRole::OverhangPerimeter) return erOverhangPerimeter; + if (role == ExtrusionRole::InternalInfill) return erInternalInfill; + if (role == ExtrusionRole::SolidInfill) return erSolidInfill; + if (role == ExtrusionRole::TopSolidInfill) return erTopSolidInfill; + if (role == ExtrusionRole::Ironing) return erIroning; + if (role == ExtrusionRole::BridgeInfill) return erBridgeInfill; + if (role == ExtrusionRole::GapFill) return erGapFill; + if (role == ExtrusionRole::Skirt) return erSkirt; + if (role == ExtrusionRole::SupportMaterial) return erSupportMaterial; + if (role == ExtrusionRole::SupportMaterialInterface) return erSupportMaterialInterface; + if (role == ExtrusionRole::WipeTower) return erWipeTower; + assert(false); + return erNone; +} + +std::string gcode_extrusion_role_to_string(GCodeExtrusionRole role) +{ + switch (role) { + case erNone : return L("Unknown"); + case erPerimeter : return L("Perimeter"); + case erExternalPerimeter : return L("External perimeter"); + case erOverhangPerimeter : return L("Overhang perimeter"); + case erInternalInfill : return L("Internal infill"); + case erSolidInfill : return L("Solid infill"); + case erTopSolidInfill : return L("Top solid infill"); + case erIroning : return L("Ironing"); + case erBridgeInfill : return L("Bridge infill"); + case erGapFill : return L("Gap fill"); + case erSkirt : return L("Skirt/Brim"); + case erSupportMaterial : return L("Support material"); + case erSupportMaterialInterface : return L("Support material interface"); + case erWipeTower : return L("Wipe tower"); + case erCustom : return L("Custom"); + default : assert(false); + } + return {}; +} + +GCodeExtrusionRole string_to_gcode_extrusion_role(const std::string_view role) +{ + if (role == L("Perimeter")) + return erPerimeter; + else if (role == L("External perimeter")) + return erExternalPerimeter; + else if (role == L("Overhang perimeter")) + return erOverhangPerimeter; + else if (role == L("Internal infill")) + return erInternalInfill; + else if (role == L("Solid infill")) + return erSolidInfill; + else if (role == L("Top solid infill")) + return erTopSolidInfill; + else if (role == L("Ironing")) + return erIroning; + else if (role == L("Bridge infill")) + return erBridgeInfill; + else if (role == L("Gap fill")) + return erGapFill; + else if (role == L("Skirt") || role == L("Skirt/Brim")) // "Skirt" is for backward compatibility with 2.3.1 and earlier + return erSkirt; + else if (role == L("Support material")) + return erSupportMaterial; + else if (role == L("Support material interface")) + return erSupportMaterialInterface; + else if (role == L("Wipe tower")) + return erWipeTower; + else if (role == L("Custom")) + return erCustom; + else + return erNone; +} + +} diff --git a/src/libslic3r/ExtrusionRole.hpp b/src/libslic3r/ExtrusionRole.hpp new file mode 100644 index 000000000..eb61f4b79 --- /dev/null +++ b/src/libslic3r/ExtrusionRole.hpp @@ -0,0 +1,147 @@ +#ifndef slic3r_ExtrusionRole_hpp_ +#define slic3r_ExtrusionRole_hpp_ + +#include "enum_bitmask.hpp" + +#include +#include + +namespace Slic3r { + +enum class ExtrusionRoleModifier : uint16_t { +// 1) Extrusion types + // Perimeter (external, inner, ...) + Perimeter, + // Infill (top / bottom / solid inner / sparse inner / bridging inner ...) + Infill, + // Variable width extrusion + Thin, + // Support material extrusion + Support, + Skirt, + Wipe, +// 2) Extrusion modifiers + External, + Solid, + Ironing, + Bridge, +// 3) Special types + // Indicator that the extrusion role was mixed from multiple differing extrusion roles, + // for example from Support and SupportInterface. + Mixed, + // Stopper, there should be maximum 16 modifiers defined for uint16_t bit mask. + Count +}; +// There should be maximum 16 modifiers defined for uint16_t bit mask. +static_assert(int(ExtrusionRoleModifier::Count) <= 16, "ExtrusionRoleModifier: there must be maximum 16 modifiers defined to fit a 16 bit bitmask"); + +using ExtrusionRoleModifiers = enum_bitmask; +ENABLE_ENUM_BITMASK_OPERATORS(ExtrusionRoleModifier); + +struct ExtrusionRole : public ExtrusionRoleModifiers +{ + constexpr ExtrusionRole(const ExtrusionRoleModifier bit) : ExtrusionRoleModifiers(bit) {} + constexpr ExtrusionRole(const ExtrusionRoleModifiers bits) : ExtrusionRoleModifiers(bits) {} + + static constexpr const ExtrusionRoleModifiers None{}; + // Internal perimeter, not bridging. + static constexpr const ExtrusionRoleModifiers Perimeter{ ExtrusionRoleModifier::Perimeter }; + // External perimeter, not bridging. + static constexpr const ExtrusionRoleModifiers ExternalPerimeter{ ExtrusionRoleModifier::Perimeter | ExtrusionRoleModifier::External }; + // Perimeter, bridging. To be or'ed with ExtrusionRoleModifier::External for external bridging perimeter. + static constexpr const ExtrusionRoleModifiers OverhangPerimeter{ ExtrusionRoleModifier::Perimeter | ExtrusionRoleModifier::Bridge }; + // Sparse internal infill. + static constexpr const ExtrusionRoleModifiers InternalInfill{ ExtrusionRoleModifier::Infill }; + // Solid internal infill. + static constexpr const ExtrusionRoleModifiers SolidInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid }; + // Top solid infill (visible). + //FIXME why there is no bottom solid infill type? + static constexpr const ExtrusionRoleModifiers TopSolidInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::External }; + // Ironing infill at the top surfaces. + static constexpr const ExtrusionRoleModifiers Ironing{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Ironing | ExtrusionRoleModifier::External }; + // Visible bridging infill at the bottom of an object. + static constexpr const ExtrusionRoleModifiers BridgeInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::Bridge | ExtrusionRoleModifier::External }; +// static constexpr const ExtrusionRoleModifiers InternalBridgeInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::Bridge }; + // Gap fill extrusion, currently used for any variable width extrusion: Thin walls outside of the outer extrusion, + // gap fill in between perimeters, gap fill between the inner perimeter and infill. + //FIXME revise GapFill and ThinWall types, split Gap Fill to Gap Fill and ThinWall. + static constexpr const ExtrusionRoleModifiers GapFill{ ExtrusionRoleModifier::Thin }; // | ExtrusionRoleModifier::External }; +// static constexpr const ExtrusionRoleModifiers ThinWall{ ExtrusionRoleModifier::Thin }; + static constexpr const ExtrusionRoleModifiers Skirt{ ExtrusionRoleModifier::Skirt }; + // Support base material, printed with non-soluble plastic. + static constexpr const ExtrusionRoleModifiers SupportMaterial{ ExtrusionRoleModifier::Support }; + // Support interface material, printed with soluble plastic. + static constexpr const ExtrusionRoleModifiers SupportMaterialInterface{ ExtrusionRoleModifier::Support | ExtrusionRoleModifier::External }; + // Wipe tower material. + static constexpr const ExtrusionRoleModifiers WipeTower{ ExtrusionRoleModifier::Wipe }; + // Extrusion role for a collection with multiple extrusion roles. + static constexpr const ExtrusionRoleModifiers Mixed{ ExtrusionRoleModifier::Mixed }; +}; + +// Special flags describing loop +enum ExtrusionLoopRole { + elrDefault, + elrContourInternalPerimeter, + elrSkirt, +}; + +inline bool is_perimeter(ExtrusionRole role) +{ + return role == ExtrusionRole::Perimeter + || role == ExtrusionRole::ExternalPerimeter + || role == ExtrusionRole::OverhangPerimeter; +} + +inline bool is_infill(ExtrusionRole role) +{ + return role == ExtrusionRole::BridgeInfill + || role == ExtrusionRole::InternalInfill + || role == ExtrusionRole::SolidInfill + || role == ExtrusionRole::TopSolidInfill + || role == ExtrusionRole::Ironing; +} + +inline bool is_solid_infill(ExtrusionRole role) +{ + return role == ExtrusionRole::BridgeInfill + || role == ExtrusionRole::SolidInfill + || role == ExtrusionRole::TopSolidInfill + || role == ExtrusionRole::Ironing; +} + +inline bool is_bridge(ExtrusionRole role) { + return role == ExtrusionRole::BridgeInfill + || role == ExtrusionRole::OverhangPerimeter; +} + +enum GCodeExtrusionRole : uint8_t { + erNone, + erPerimeter, + erExternalPerimeter, + erOverhangPerimeter, + erInternalInfill, + erSolidInfill, + erTopSolidInfill, + erIroning, + erBridgeInfill, + erGapFill, + erSkirt, + erSupportMaterial, + erSupportMaterialInterface, + erWipeTower, + // Custom (user defined) G-code block, for example start / end G-code. + erCustom, + // Stopper to count number of enums. + erCount +}; + +// Convert a rich bitmask based ExtrusionRole to a less expressive ordinal GCodeExtrusionRole. +// GCodeExtrusionRole is to be serialized into G-code and deserialized by G-code viewer, +GCodeExtrusionRole extrusion_role_to_gcode_extrusion_role(ExtrusionRole role); + +std::string gcode_extrusion_role_to_string(GCodeExtrusionRole role); +GCodeExtrusionRole string_to_gcode_extrusion_role(const std::string_view role); + +} + +#endif // slic3r_ExtrusionRole_hpp_ diff --git a/src/libslic3r/Flow.hpp b/src/libslic3r/Flow.hpp index 04ced3e13..6d999d1c5 100644 --- a/src/libslic3r/Flow.hpp +++ b/src/libslic3r/Flow.hpp @@ -4,7 +4,7 @@ #include "libslic3r.h" #include "Config.hpp" #include "Exception.hpp" -#include "ExtrusionEntity.hpp" +#include "ExtrusionRole.hpp" namespace Slic3r { diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index a3841343b..59c652466 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -3,7 +3,7 @@ #include "libslic3r/GCodeReader.hpp" #include "libslic3r/Point.hpp" -#include "libslic3r/ExtrusionEntity.hpp" +#include "libslic3r/ExtrusionRole.hpp" #include "libslic3r/PrintConfig.hpp" #include "libslic3r/CustomGCode.hpp" diff --git a/src/libslic3r/GCode/PressureEqualizer.hpp b/src/libslic3r/GCode/PressureEqualizer.hpp index 246a20568..350030e05 100644 --- a/src/libslic3r/GCode/PressureEqualizer.hpp +++ b/src/libslic3r/GCode/PressureEqualizer.hpp @@ -3,7 +3,7 @@ #include "../libslic3r.h" #include "../PrintConfig.hpp" -#include "../ExtrusionEntity.hpp" +#include "../ExtrusionRole.hpp" #include diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index 33d735a3e..4908e4f77 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -3,6 +3,7 @@ #include "libslic3r.h" #include +#include "ExtrusionEntityCollection.hpp" #include "Flow.hpp" #include "Polygon.hpp" #include "PrintConfig.hpp" From c64dbacf88c617e3d676bdaa6c8c81317ccaee5d Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 16 Jan 2023 11:14:34 +0100 Subject: [PATCH 09/28] WIP Extending the expressivity of ExtrusionRole Changed GCodeExtrusionRole to enum class. Follow-up to 5991850db1f2d09d1b7713f42300e2bfe4a42128 e50e96bb26eab4f9d56c98706c03166635cf4fff --- src/libslic3r/BuildVolume.cpp | 2 +- src/libslic3r/ExtrusionRole.cpp | 90 ++++++------ src/libslic3r/ExtrusionRole.hpp | 37 ++--- src/libslic3r/GCode.cpp | 10 +- src/libslic3r/GCode.hpp | 4 +- src/libslic3r/GCode/GCodeProcessor.cpp | 170 +++++++++++----------- src/libslic3r/GCode/GCodeProcessor.hpp | 8 +- src/libslic3r/GCode/PressureEqualizer.cpp | 40 ++--- src/libslic3r/GCode/PressureEqualizer.hpp | 2 +- src/libslic3r/GCode/WipeTower.cpp | 2 +- src/slic3r/GUI/GCodeViewer.cpp | 55 ++++--- src/slic3r/GUI/GCodeViewer.hpp | 9 +- 12 files changed, 216 insertions(+), 213 deletions(-) diff --git a/src/libslic3r/BuildVolume.cpp b/src/libslic3r/BuildVolume.cpp index 899055355..f639f9ac7 100644 --- a/src/libslic3r/BuildVolume.cpp +++ b/src/libslic3r/BuildVolume.cpp @@ -320,7 +320,7 @@ BuildVolume::ObjectState BuildVolume::volume_state_bbox(const BoundingBoxf3& vol bool BuildVolume::all_paths_inside(const GCodeProcessorResult& paths, const BoundingBoxf3& paths_bbox, bool ignore_bottom) const { auto move_valid = [](const GCodeProcessorResult::MoveVertex &move) { - return move.type == EMoveType::Extrude && move.extrusion_role != erCustom && move.width != 0.f && move.height != 0.f; + return move.type == EMoveType::Extrude && move.extrusion_role != GCodeExtrusionRole::Custom && move.width != 0.f && move.height != 0.f; }; static constexpr const double epsilon = BedEpsilon; diff --git a/src/libslic3r/ExtrusionRole.cpp b/src/libslic3r/ExtrusionRole.cpp index f6f2eb2f5..4d894b4ac 100644 --- a/src/libslic3r/ExtrusionRole.cpp +++ b/src/libslic3r/ExtrusionRole.cpp @@ -11,42 +11,42 @@ namespace Slic3r { // GCodeExtrusionRole is to be serialized into G-code and deserialized by G-code viewer, GCodeExtrusionRole extrusion_role_to_gcode_extrusion_role(ExtrusionRole role) { - if (role == ExtrusionRole::None) return erNone; - if (role == ExtrusionRole::Perimeter) return erPerimeter; - if (role == ExtrusionRole::ExternalPerimeter) return erExternalPerimeter; - if (role == ExtrusionRole::OverhangPerimeter) return erOverhangPerimeter; - if (role == ExtrusionRole::InternalInfill) return erInternalInfill; - if (role == ExtrusionRole::SolidInfill) return erSolidInfill; - if (role == ExtrusionRole::TopSolidInfill) return erTopSolidInfill; - if (role == ExtrusionRole::Ironing) return erIroning; - if (role == ExtrusionRole::BridgeInfill) return erBridgeInfill; - if (role == ExtrusionRole::GapFill) return erGapFill; - if (role == ExtrusionRole::Skirt) return erSkirt; - if (role == ExtrusionRole::SupportMaterial) return erSupportMaterial; - if (role == ExtrusionRole::SupportMaterialInterface) return erSupportMaterialInterface; - if (role == ExtrusionRole::WipeTower) return erWipeTower; + if (role == ExtrusionRole::None) return GCodeExtrusionRole::None; + if (role == ExtrusionRole::Perimeter) return GCodeExtrusionRole::Perimeter; + if (role == ExtrusionRole::ExternalPerimeter) return GCodeExtrusionRole::ExternalPerimeter; + if (role == ExtrusionRole::OverhangPerimeter) return GCodeExtrusionRole::OverhangPerimeter; + if (role == ExtrusionRole::InternalInfill) return GCodeExtrusionRole::InternalInfill; + if (role == ExtrusionRole::SolidInfill) return GCodeExtrusionRole::SolidInfill; + if (role == ExtrusionRole::TopSolidInfill) return GCodeExtrusionRole::TopSolidInfill; + if (role == ExtrusionRole::Ironing) return GCodeExtrusionRole::Ironing; + if (role == ExtrusionRole::BridgeInfill) return GCodeExtrusionRole::BridgeInfill; + if (role == ExtrusionRole::GapFill) return GCodeExtrusionRole::GapFill; + if (role == ExtrusionRole::Skirt) return GCodeExtrusionRole::Skirt; + if (role == ExtrusionRole::SupportMaterial) return GCodeExtrusionRole::SupportMaterial; + if (role == ExtrusionRole::SupportMaterialInterface) return GCodeExtrusionRole::SupportMaterialInterface; + if (role == ExtrusionRole::WipeTower) return GCodeExtrusionRole::WipeTower; assert(false); - return erNone; + return GCodeExtrusionRole::None; } std::string gcode_extrusion_role_to_string(GCodeExtrusionRole role) { switch (role) { - case erNone : return L("Unknown"); - case erPerimeter : return L("Perimeter"); - case erExternalPerimeter : return L("External perimeter"); - case erOverhangPerimeter : return L("Overhang perimeter"); - case erInternalInfill : return L("Internal infill"); - case erSolidInfill : return L("Solid infill"); - case erTopSolidInfill : return L("Top solid infill"); - case erIroning : return L("Ironing"); - case erBridgeInfill : return L("Bridge infill"); - case erGapFill : return L("Gap fill"); - case erSkirt : return L("Skirt/Brim"); - case erSupportMaterial : return L("Support material"); - case erSupportMaterialInterface : return L("Support material interface"); - case erWipeTower : return L("Wipe tower"); - case erCustom : return L("Custom"); + case GCodeExtrusionRole::None : return L("Unknown"); + case GCodeExtrusionRole::Perimeter : return L("Perimeter"); + case GCodeExtrusionRole::ExternalPerimeter : return L("External perimeter"); + case GCodeExtrusionRole::OverhangPerimeter : return L("Overhang perimeter"); + case GCodeExtrusionRole::InternalInfill : return L("Internal infill"); + case GCodeExtrusionRole::SolidInfill : return L("Solid infill"); + case GCodeExtrusionRole::TopSolidInfill : return L("Top solid infill"); + case GCodeExtrusionRole::Ironing : return L("Ironing"); + case GCodeExtrusionRole::BridgeInfill : return L("Bridge infill"); + case GCodeExtrusionRole::GapFill : return L("Gap fill"); + case GCodeExtrusionRole::Skirt : return L("Skirt/Brim"); + case GCodeExtrusionRole::SupportMaterial : return L("Support material"); + case GCodeExtrusionRole::SupportMaterialInterface : return L("Support material interface"); + case GCodeExtrusionRole::WipeTower : return L("Wipe tower"); + case GCodeExtrusionRole::Custom : return L("Custom"); default : assert(false); } return {}; @@ -55,35 +55,35 @@ std::string gcode_extrusion_role_to_string(GCodeExtrusionRole role) GCodeExtrusionRole string_to_gcode_extrusion_role(const std::string_view role) { if (role == L("Perimeter")) - return erPerimeter; + return GCodeExtrusionRole::Perimeter; else if (role == L("External perimeter")) - return erExternalPerimeter; + return GCodeExtrusionRole::ExternalPerimeter; else if (role == L("Overhang perimeter")) - return erOverhangPerimeter; + return GCodeExtrusionRole::OverhangPerimeter; else if (role == L("Internal infill")) - return erInternalInfill; + return GCodeExtrusionRole::InternalInfill; else if (role == L("Solid infill")) - return erSolidInfill; + return GCodeExtrusionRole::SolidInfill; else if (role == L("Top solid infill")) - return erTopSolidInfill; + return GCodeExtrusionRole::TopSolidInfill; else if (role == L("Ironing")) - return erIroning; + return GCodeExtrusionRole::Ironing; else if (role == L("Bridge infill")) - return erBridgeInfill; + return GCodeExtrusionRole::BridgeInfill; else if (role == L("Gap fill")) - return erGapFill; + return GCodeExtrusionRole::GapFill; else if (role == L("Skirt") || role == L("Skirt/Brim")) // "Skirt" is for backward compatibility with 2.3.1 and earlier - return erSkirt; + return GCodeExtrusionRole::Skirt; else if (role == L("Support material")) - return erSupportMaterial; + return GCodeExtrusionRole::SupportMaterial; else if (role == L("Support material interface")) - return erSupportMaterialInterface; + return GCodeExtrusionRole::SupportMaterialInterface; else if (role == L("Wipe tower")) - return erWipeTower; + return GCodeExtrusionRole::WipeTower; else if (role == L("Custom")) - return erCustom; + return GCodeExtrusionRole::Custom; else - return erNone; + return GCodeExtrusionRole::None; } } diff --git a/src/libslic3r/ExtrusionRole.hpp b/src/libslic3r/ExtrusionRole.hpp index eb61f4b79..b99eb5034 100644 --- a/src/libslic3r/ExtrusionRole.hpp +++ b/src/libslic3r/ExtrusionRole.hpp @@ -114,25 +114,28 @@ inline bool is_bridge(ExtrusionRole role) { || role == ExtrusionRole::OverhangPerimeter; } -enum GCodeExtrusionRole : uint8_t { - erNone, - erPerimeter, - erExternalPerimeter, - erOverhangPerimeter, - erInternalInfill, - erSolidInfill, - erTopSolidInfill, - erIroning, - erBridgeInfill, - erGapFill, - erSkirt, - erSupportMaterial, - erSupportMaterialInterface, - erWipeTower, +// Be careful when editing this list as many parts of the code depend +// on the values of these ordinars, for example +// GCodeViewer::Extrusion_Role_Colors +enum class GCodeExtrusionRole : uint8_t { + None, + Perimeter, + ExternalPerimeter, + OverhangPerimeter, + InternalInfill, + SolidInfill, + TopSolidInfill, + Ironing, + BridgeInfill, + GapFill, + Skirt, + SupportMaterial, + SupportMaterialInterface, + WipeTower, // Custom (user defined) G-code block, for example start / end G-code. - erCustom, + Custom, // Stopper to count number of enums. - erCount + Count }; // Convert a rich bitmask based ExtrusionRole to a less expressive ordinal GCodeExtrusionRole. diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6ed0b9024..0f84c1a53 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1271,7 +1271,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false); // adds tag for processor - file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), gcode_extrusion_role_to_string(erCustom).c_str()); + file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), gcode_extrusion_role_to_string(GCodeExtrusionRole::Custom).c_str()); // Write the custom start G-code file.writeln(start_gcode); @@ -1407,7 +1407,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.write(m_writer.set_fan(0)); // adds tag for processor - file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), gcode_extrusion_role_to_string(erCustom).c_str()); + file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), gcode_extrusion_role_to_string(GCodeExtrusionRole::Custom).c_str()); // Process filament-specific gcode in extruder order. { @@ -2182,7 +2182,7 @@ LayerResult GCode::process_layer( // let analyzer tag generator aware of a role type change if (layer_tools.has_wipe_tower && m_wipe_tower) - m_last_processor_extrusion_role = erWipeTower; + m_last_processor_extrusion_role = GCodeExtrusionRole::WipeTower; if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) { const std::pair loops = loops_it->second; @@ -2941,8 +2941,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de // adds processor tags and updates processor tracking data // PrusaMultiMaterial::Writer may generate GCodeProcessor::Height_Tag lines without updating m_last_height - // so, if the last role was erWipeTower we force export of GCodeProcessor::Height_Tag lines - bool last_was_wipe_tower = (m_last_processor_extrusion_role == erWipeTower); + // so, if the last role was GCodeExtrusionRole::WipeTower we force export of GCodeProcessor::Height_Tag lines + bool last_was_wipe_tower = (m_last_processor_extrusion_role == GCodeExtrusionRole::WipeTower); assert(is_decimal_separator_point()); if (GCodeExtrusionRole role = extrusion_role_to_gcode_extrusion_role(path.role()); role != m_last_processor_extrusion_role) { diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index d57116595..0741d7e37 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -137,14 +137,14 @@ public: m_enable_loop_clipping(true), m_enable_cooling_markers(false), m_enable_extrusion_role_markers(false), - m_last_processor_extrusion_role(erNone), + m_last_processor_extrusion_role(GCodeExtrusionRole::None), m_layer_count(0), m_layer_index(-1), m_layer(nullptr), m_object_layer_over_raft(false), m_volumetric_speed(0), m_last_pos_defined(false), - m_last_extrusion_role(erNone), + m_last_extrusion_role(GCodeExtrusionRole::None), m_last_width(0.0f), #if ENABLE_GCODE_VIEWER_DATA_CHECKING m_last_mm3_per_mm(0.0), diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 3e61e7fec..7af46d5c0 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -942,7 +942,7 @@ void GCodeProcessor::reset() m_fan_speed = 0.0f; m_z_offset = 0.0f; - m_extrusion_role = erNone; + m_extrusion_role = GCodeExtrusionRole::None; m_extruder_id = 0; m_extruder_colors.resize(MIN_EXTRUDERS_COUNT); for (size_t i = 0; i < MIN_EXTRUDERS_COUNT; ++i) { @@ -1646,7 +1646,7 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers // extrusion role tag if (boost::starts_with(comment, reserved_tag(ETags::Role))) { set_extrusion_role(string_to_gcode_extrusion_role(comment.substr(reserved_tag(ETags::Role).length()))); - if (m_extrusion_role == erExternalPerimeter) + if (m_extrusion_role == GCodeExtrusionRole::ExternalPerimeter) m_seams_detector.activate(true); return; } @@ -1827,27 +1827,27 @@ bool GCodeProcessor::process_cura_tags(const std::string_view comment) if (pos != comment.npos) { const std::string_view type = comment.substr(pos + tag.length()); if (type == "SKIRT") - set_extrusion_role(erSkirt); + set_extrusion_role(GCodeExtrusionRole::Skirt); else if (type == "WALL-OUTER") - set_extrusion_role(erExternalPerimeter); + set_extrusion_role(GCodeExtrusionRole::ExternalPerimeter); else if (type == "WALL-INNER") - set_extrusion_role(erPerimeter); + set_extrusion_role(GCodeExtrusionRole::Perimeter); else if (type == "SKIN") - set_extrusion_role(erSolidInfill); + set_extrusion_role(GCodeExtrusionRole::SolidInfill); else if (type == "FILL") - set_extrusion_role(erInternalInfill); + set_extrusion_role(GCodeExtrusionRole::InternalInfill); else if (type == "SUPPORT") - set_extrusion_role(erSupportMaterial); + set_extrusion_role(GCodeExtrusionRole::SupportMaterial); else if (type == "SUPPORT-INTERFACE") - set_extrusion_role(erSupportMaterialInterface); + set_extrusion_role(GCodeExtrusionRole::SupportMaterialInterface); else if (type == "PRIME-TOWER") - set_extrusion_role(erWipeTower); + set_extrusion_role(GCodeExtrusionRole::WipeTower); else { - set_extrusion_role(erNone); + set_extrusion_role(GCodeExtrusionRole::None); BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown extrusion role: " << type; } - if (m_extrusion_role == erExternalPerimeter) + if (m_extrusion_role == GCodeExtrusionRole::ExternalPerimeter) m_seams_detector.activate(true); return true; @@ -1906,14 +1906,14 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string_view comment) // ; skirt pos = cmt.find(" skirt"); if (pos == 0) { - set_extrusion_role(erSkirt); + set_extrusion_role(GCodeExtrusionRole::Skirt); return true; } // ; outer perimeter pos = cmt.find(" outer perimeter"); if (pos == 0) { - set_extrusion_role(erExternalPerimeter); + set_extrusion_role(GCodeExtrusionRole::ExternalPerimeter); m_seams_detector.activate(true); return true; } @@ -1921,77 +1921,77 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string_view comment) // ; inner perimeter pos = cmt.find(" inner perimeter"); if (pos == 0) { - set_extrusion_role(erPerimeter); + set_extrusion_role(GCodeExtrusionRole::Perimeter); return true; } // ; gap fill pos = cmt.find(" gap fill"); if (pos == 0) { - set_extrusion_role(erGapFill); + set_extrusion_role(GCodeExtrusionRole::GapFill); return true; } // ; infill pos = cmt.find(" infill"); if (pos == 0) { - set_extrusion_role(erInternalInfill); + set_extrusion_role(GCodeExtrusionRole::InternalInfill); return true; } // ; solid layer pos = cmt.find(" solid layer"); if (pos == 0) { - set_extrusion_role(erSolidInfill); + set_extrusion_role(GCodeExtrusionRole::SolidInfill); return true; } // ; bridge pos = cmt.find(" bridge"); if (pos == 0) { - set_extrusion_role(erBridgeInfill); + set_extrusion_role(GCodeExtrusionRole::BridgeInfill); return true; } // ; support pos = cmt.find(" support"); if (pos == 0) { - set_extrusion_role(erSupportMaterial); + set_extrusion_role(GCodeExtrusionRole::SupportMaterial); return true; } // ; dense support pos = cmt.find(" dense support"); if (pos == 0) { - set_extrusion_role(erSupportMaterialInterface); + set_extrusion_role(GCodeExtrusionRole::SupportMaterialInterface); return true; } // ; prime pillar pos = cmt.find(" prime pillar"); if (pos == 0) { - set_extrusion_role(erWipeTower); + set_extrusion_role(GCodeExtrusionRole::WipeTower); return true; } // ; ooze shield pos = cmt.find(" ooze shield"); if (pos == 0) { - set_extrusion_role(erNone); // Missing mapping + set_extrusion_role(GCodeExtrusionRole::None); // Missing mapping return true; } // ; raft pos = cmt.find(" raft"); if (pos == 0) { - set_extrusion_role(erSupportMaterial); + set_extrusion_role(GCodeExtrusionRole::SupportMaterial); return true; } // ; internal single extrusion pos = cmt.find(" internal single extrusion"); if (pos == 0) { - set_extrusion_role(erNone); // Missing mapping + set_extrusion_role(GCodeExtrusionRole::None); // Missing mapping return true; } @@ -2043,33 +2043,33 @@ bool GCodeProcessor::process_craftware_tags(const std::string_view comment) if (pos != comment.npos) { const std::string_view type = comment.substr(pos + tag.length()); if (type == "Skirt") - set_extrusion_role(erSkirt); + set_extrusion_role(GCodeExtrusionRole::Skirt); else if (type == "Perimeter") - set_extrusion_role(erExternalPerimeter); + set_extrusion_role(GCodeExtrusionRole::ExternalPerimeter); else if (type == "HShell") - set_extrusion_role(erNone); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + set_extrusion_role(GCodeExtrusionRole::None); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< else if (type == "InnerHair") - set_extrusion_role(erNone); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + set_extrusion_role(GCodeExtrusionRole::None); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< else if (type == "Loop") - set_extrusion_role(erNone); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + set_extrusion_role(GCodeExtrusionRole::None); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< else if (type == "Infill") - set_extrusion_role(erInternalInfill); + set_extrusion_role(GCodeExtrusionRole::InternalInfill); else if (type == "Raft") - set_extrusion_role(erSkirt); + set_extrusion_role(GCodeExtrusionRole::Skirt); else if (type == "Support") - set_extrusion_role(erSupportMaterial); + set_extrusion_role(GCodeExtrusionRole::SupportMaterial); else if (type == "SupportTouch") - set_extrusion_role(erSupportMaterial); + set_extrusion_role(GCodeExtrusionRole::SupportMaterial); else if (type == "SoftSupport") - set_extrusion_role(erSupportMaterialInterface); + set_extrusion_role(GCodeExtrusionRole::SupportMaterialInterface); else if (type == "Pillar") - set_extrusion_role(erWipeTower); + set_extrusion_role(GCodeExtrusionRole::WipeTower); else { - set_extrusion_role(erNone); + set_extrusion_role(GCodeExtrusionRole::None); BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown extrusion role: " << type; } - if (m_extrusion_role == erExternalPerimeter) + if (m_extrusion_role == GCodeExtrusionRole::ExternalPerimeter) m_seams_detector.activate(true); return true; @@ -2093,25 +2093,25 @@ bool GCodeProcessor::process_ideamaker_tags(const std::string_view comment) if (pos != comment.npos) { const std::string_view type = comment.substr(pos + tag.length()); if (type == "RAFT") - set_extrusion_role(erSkirt); + set_extrusion_role(GCodeExtrusionRole::Skirt); else if (type == "WALL-OUTER") - set_extrusion_role(erExternalPerimeter); + set_extrusion_role(GCodeExtrusionRole::ExternalPerimeter); else if (type == "WALL-INNER") - set_extrusion_role(erPerimeter); + set_extrusion_role(GCodeExtrusionRole::Perimeter); else if (type == "SOLID-FILL") - set_extrusion_role(erSolidInfill); + set_extrusion_role(GCodeExtrusionRole::SolidInfill); else if (type == "FILL") - set_extrusion_role(erInternalInfill); + set_extrusion_role(GCodeExtrusionRole::InternalInfill); else if (type == "BRIDGE") - set_extrusion_role(erBridgeInfill); + set_extrusion_role(GCodeExtrusionRole::BridgeInfill); else if (type == "SUPPORT") - set_extrusion_role(erSupportMaterial); + set_extrusion_role(GCodeExtrusionRole::SupportMaterial); else { - set_extrusion_role(erNone); + set_extrusion_role(GCodeExtrusionRole::None); BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown extrusion role: " << type; } - if (m_extrusion_role == erExternalPerimeter) + if (m_extrusion_role == GCodeExtrusionRole::ExternalPerimeter) m_seams_detector.activate(true); return true; @@ -2153,35 +2153,35 @@ bool GCodeProcessor::process_kissslicer_tags(const std::string_view comment) // ; 'Raft Path' size_t pos = comment.find(" 'Raft Path'"); if (pos == 0) { - set_extrusion_role(erSkirt); + set_extrusion_role(GCodeExtrusionRole::Skirt); return true; } // ; 'Support Interface Path' pos = comment.find(" 'Support Interface Path'"); if (pos == 0) { - set_extrusion_role(erSupportMaterialInterface); + set_extrusion_role(GCodeExtrusionRole::SupportMaterialInterface); return true; } // ; 'Travel/Ironing Path' pos = comment.find(" 'Travel/Ironing Path'"); if (pos == 0) { - set_extrusion_role(erIroning); + set_extrusion_role(GCodeExtrusionRole::Ironing); return true; } // ; 'Support (may Stack) Path' pos = comment.find(" 'Support (may Stack) Path'"); if (pos == 0) { - set_extrusion_role(erSupportMaterial); + set_extrusion_role(GCodeExtrusionRole::SupportMaterial); return true; } // ; 'Perimeter Path' pos = comment.find(" 'Perimeter Path'"); if (pos == 0) { - set_extrusion_role(erExternalPerimeter); + set_extrusion_role(GCodeExtrusionRole::ExternalPerimeter); m_seams_detector.activate(true); return true; } @@ -2189,56 +2189,56 @@ bool GCodeProcessor::process_kissslicer_tags(const std::string_view comment) // ; 'Pillar Path' pos = comment.find(" 'Pillar Path'"); if (pos == 0) { - set_extrusion_role(erNone); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + set_extrusion_role(GCodeExtrusionRole::None); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< return true; } // ; 'Destring/Wipe/Jump Path' pos = comment.find(" 'Destring/Wipe/Jump Path'"); if (pos == 0) { - set_extrusion_role(erNone); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + set_extrusion_role(GCodeExtrusionRole::None); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< return true; } // ; 'Prime Pillar Path' pos = comment.find(" 'Prime Pillar Path'"); if (pos == 0) { - set_extrusion_role(erNone); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + set_extrusion_role(GCodeExtrusionRole::None); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< return true; } // ; 'Loop Path' pos = comment.find(" 'Loop Path'"); if (pos == 0) { - set_extrusion_role(erNone); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + set_extrusion_role(GCodeExtrusionRole::None); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< return true; } // ; 'Crown Path' pos = comment.find(" 'Crown Path'"); if (pos == 0) { - set_extrusion_role(erNone); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + set_extrusion_role(GCodeExtrusionRole::None); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< return true; } // ; 'Solid Path' pos = comment.find(" 'Solid Path'"); if (pos == 0) { - set_extrusion_role(erNone); + set_extrusion_role(GCodeExtrusionRole::None); return true; } // ; 'Stacked Sparse Infill Path' pos = comment.find(" 'Stacked Sparse Infill Path'"); if (pos == 0) { - set_extrusion_role(erInternalInfill); + set_extrusion_role(GCodeExtrusionRole::InternalInfill); return true; } // ; 'Sparse Infill Path' pos = comment.find(" 'Sparse Infill Path'"); if (pos == 0) { - set_extrusion_role(erSolidInfill); + set_extrusion_role(GCodeExtrusionRole::SolidInfill); return true; } @@ -2263,45 +2263,45 @@ bool GCodeProcessor::process_bambustudio_tags(const std::string_view comment) if (pos != comment.npos) { const std::string_view type = comment.substr(pos + tag.length()); if (type == "Custom") - set_extrusion_role(erCustom); + set_extrusion_role(GCodeExtrusionRole::Custom); else if (type == "Inner wall") - set_extrusion_role(erPerimeter); + set_extrusion_role(GCodeExtrusionRole::Perimeter); else if (type == "Outer wall") - set_extrusion_role(erExternalPerimeter); + set_extrusion_role(GCodeExtrusionRole::ExternalPerimeter); else if (type == "Overhang wall") - set_extrusion_role(erOverhangPerimeter); + set_extrusion_role(GCodeExtrusionRole::OverhangPerimeter); else if (type == "Gap infill") - set_extrusion_role(erGapFill); + set_extrusion_role(GCodeExtrusionRole::GapFill); else if (type == "Bridge") - set_extrusion_role(erBridgeInfill); + set_extrusion_role(GCodeExtrusionRole::BridgeInfill); else if (type == "Sparse infill") - set_extrusion_role(erInternalInfill); + set_extrusion_role(GCodeExtrusionRole::InternalInfill); else if (type == "Internal solid infill") - set_extrusion_role(erSolidInfill); + set_extrusion_role(GCodeExtrusionRole::SolidInfill); else if (type == "Top surface") - set_extrusion_role(erTopSolidInfill); + set_extrusion_role(GCodeExtrusionRole::TopSolidInfill); else if (type == "Bottom surface") - set_extrusion_role(erNone); + set_extrusion_role(GCodeExtrusionRole::None); else if (type == "Ironing") - set_extrusion_role(erIroning); + set_extrusion_role(GCodeExtrusionRole::Ironing); else if (type == "Skirt") - set_extrusion_role(erSkirt); + set_extrusion_role(GCodeExtrusionRole::Skirt); else if (type == "Brim") - set_extrusion_role(erSkirt); + set_extrusion_role(GCodeExtrusionRole::Skirt); else if (type == "Support") - set_extrusion_role(erSupportMaterial); + set_extrusion_role(GCodeExtrusionRole::SupportMaterial); else if (type == "Support interface") - set_extrusion_role(erSupportMaterialInterface); + set_extrusion_role(GCodeExtrusionRole::SupportMaterialInterface); else if (type == "Support transition") - set_extrusion_role(erNone); + set_extrusion_role(GCodeExtrusionRole::None); else if (type == "Prime tower") - set_extrusion_role(erWipeTower); + set_extrusion_role(GCodeExtrusionRole::WipeTower); else { - set_extrusion_role(erNone); + set_extrusion_role(GCodeExtrusionRole::None); BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown extrusion role: " << type; } - if (m_extrusion_role == erExternalPerimeter) + if (m_extrusion_role == GCodeExtrusionRole::ExternalPerimeter) m_seams_detector.activate(true); return true; @@ -2405,7 +2405,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) if (m_height == 0.0f) m_height = DEFAULT_TOOLPATH_HEIGHT; - if (m_end_position[Z] == 0.0f || (m_extrusion_role == erCustom && m_layer_id == 0)) + if (m_end_position[Z] == 0.0f || (m_extrusion_role == GCodeExtrusionRole::Custom && m_layer_id == 0)) m_end_position[Z] = m_height; if (line.comment() != INTERNAL_G2G3_TAG) @@ -2418,10 +2418,10 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) if (m_forced_width > 0.0f) m_width = m_forced_width; - else if (m_extrusion_role == erExternalPerimeter) + else if (m_extrusion_role == GCodeExtrusionRole::ExternalPerimeter) // cross section: rectangle m_width = delta_pos[E] * static_cast(M_PI * sqr(1.05f * filament_radius)) / (delta_xyz * m_height); - else if (m_extrusion_role == erBridgeInfill || m_extrusion_role == erNone) + else if (m_extrusion_role == GCodeExtrusionRole::BridgeInfill || m_extrusion_role == GCodeExtrusionRole::None) // cross section: circle m_width = static_cast(m_result.filament_diameters[m_extruder_id]) * std::sqrt(delta_pos[E] / delta_xyz); else @@ -2605,10 +2605,10 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) if (m_seams_detector.is_active()) { // check for seam starting vertex - if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter && !m_seams_detector.has_first_vertex()) + if (type == EMoveType::Extrude && m_extrusion_role == GCodeExtrusionRole::ExternalPerimeter && !m_seams_detector.has_first_vertex()) m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id]); // check for seam ending vertex and store the resulting move - else if ((type != EMoveType::Extrude || (m_extrusion_role != erExternalPerimeter && m_extrusion_role != erOverhangPerimeter)) && m_seams_detector.has_first_vertex()) { + else if ((type != EMoveType::Extrude || (m_extrusion_role != GCodeExtrusionRole::ExternalPerimeter && m_extrusion_role != GCodeExtrusionRole::OverhangPerimeter)) && m_seams_detector.has_first_vertex()) { auto set_end_position = [this](const Vec3f& pos) { m_end_position[X] = pos.x(); m_end_position[Y] = pos.y(); m_end_position[Z] = pos.z(); }; @@ -2627,7 +2627,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) m_seams_detector.activate(false); } } - else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) { + else if (type == EMoveType::Extrude && m_extrusion_role == GCodeExtrusionRole::ExternalPerimeter) { m_seams_detector.activate(true); m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id]); } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 59c652466..59159ee0a 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -99,7 +99,7 @@ namespace Slic3r { { unsigned int gcode_id{ 0 }; EMoveType type{ EMoveType::Noop }; - GCodeExtrusionRole extrusion_role{ erNone }; + GCodeExtrusionRole extrusion_role{ GCodeExtrusionRole::None }; unsigned char extruder_id{ 0 }; unsigned char cp_color_id{ 0 }; Vec3f position{ Vec3f::Zero() }; // mm @@ -238,7 +238,7 @@ namespace Slic3r { }; EMoveType move_type{ EMoveType::Noop }; - GCodeExtrusionRole role{ erNone }; + GCodeExtrusionRole role{ GCodeExtrusionRole::None }; unsigned int g1_line_id{ 0 }; unsigned int layer_id{ 0 }; float distance{ 0.0f }; // mm @@ -310,7 +310,7 @@ namespace Slic3r { std::vector blocks; std::vector g1_times_cache; std::array(EMoveType::Count)> moves_time; - std::array(GCodeExtrusionRole::erCount)> roles_time; + std::array(GCodeExtrusionRole::Count)> roles_time; std::vector layers_time; void reset(); @@ -455,7 +455,7 @@ namespace Slic3r { {} void update(float value, GCodeExtrusionRole role) { - if (role != erCustom) { + if (role != GCodeExtrusionRole::Custom) { ++count; if (last_tag_value != 0.0f) { if (std::abs(value - last_tag_value) / last_tag_value > threshold) diff --git a/src/libslic3r/GCode/PressureEqualizer.cpp b/src/libslic3r/GCode/PressureEqualizer.cpp index 52597517e..138200e59 100644 --- a/src/libslic3r/GCode/PressureEqualizer.cpp +++ b/src/libslic3r/GCode/PressureEqualizer.cpp @@ -37,7 +37,7 @@ PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_use_ m_current_extruder = 0; // Zero the position of the XYZE axes + the current feed memset(m_current_pos, 0, sizeof(float) * 5); - m_current_extrusion_role = erNone; + m_current_extrusion_role = GCodeExtrusionRole::None; // Expect the first command to fill the nozzle (deretract). m_retracted = true; @@ -60,9 +60,9 @@ PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_use_ } // Don't regulate the pressure before and after gap-fill and ironing. - for (const GCodeExtrusionRole er : {erGapFill, erIroning}) { - m_max_volumetric_extrusion_rate_slopes[er].negative = 0; - m_max_volumetric_extrusion_rate_slopes[er].positive = 0; + for (const GCodeExtrusionRole er : {GCodeExtrusionRole::GapFill, GCodeExtrusionRole::Ironing}) { + m_max_volumetric_extrusion_rate_slopes[size_t(er)].negative = 0; + m_max_volumetric_extrusion_rate_slopes[size_t(er)].positive = 0; } opened_extrude_set_speed_block = false; @@ -519,9 +519,9 @@ void PressureEqualizer::adjust_volumetric_rate() // Nothing to do, the last move is not extruding. return; - std::array feedrate_per_extrusion_role{}; + std::array feedrate_per_extrusion_role{}; feedrate_per_extrusion_role.fill(std::numeric_limits::max()); - feedrate_per_extrusion_role[m_gcode_lines[line_idx].extrusion_role] = m_gcode_lines[line_idx].volumetric_extrusion_rate_start; + feedrate_per_extrusion_role[int(m_gcode_lines[line_idx].extrusion_role)] = m_gcode_lines[line_idx].volumetric_extrusion_rate_start; while (line_idx != fist_line_idx) { size_t idx_prev = line_idx - 1; @@ -529,7 +529,7 @@ void PressureEqualizer::adjust_volumetric_rate() if (!m_gcode_lines[idx_prev].extruding()) break; // Don't decelerate before ironing and gap-fill. - if (m_gcode_lines[line_idx].extrusion_role == erIroning || m_gcode_lines[line_idx].extrusion_role == erGapFill) { + if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing || m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::GapFill) { line_idx = idx_prev; continue; } @@ -539,23 +539,23 @@ void PressureEqualizer::adjust_volumetric_rate() line_idx = idx_prev; GCodeLine &line = m_gcode_lines[line_idx]; - for (size_t iRole = 1; iRole < erCount; ++ iRole) { + for (size_t iRole = 1; iRole < size_t(GCodeExtrusionRole::Count); ++ iRole) { const float &rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].negative; if (rate_slope == 0 || feedrate_per_extrusion_role[iRole] == std::numeric_limits::max()) continue; // The negative rate is unlimited or the rate for GCodeExtrusionRole iRole is unlimited. float rate_end = feedrate_per_extrusion_role[iRole]; - if (iRole == line.extrusion_role && rate_succ < rate_end) + if (iRole == size_t(line.extrusion_role) && rate_succ < rate_end) // Limit by the succeeding volumetric flow rate. rate_end = rate_succ; - if (!line.adjustable_flow || line.extrusion_role == erExternalPerimeter || line.extrusion_role == erGapFill || line.extrusion_role == erBridgeInfill || line.extrusion_role == erIroning) { + if (!line.adjustable_flow || line.extrusion_role == GCodeExtrusionRole::ExternalPerimeter || line.extrusion_role == GCodeExtrusionRole::GapFill || line.extrusion_role == GCodeExtrusionRole::BridgeInfill || line.extrusion_role == GCodeExtrusionRole::Ironing) { rate_end = line.volumetric_extrusion_rate_end; } else if (line.volumetric_extrusion_rate_end > rate_end) { line.volumetric_extrusion_rate_end = rate_end; line.max_volumetric_extrusion_rate_slope_negative = rate_slope; line.modified = true; - } else if (iRole == line.extrusion_role) { + } else if (iRole == size_t(line.extrusion_role)) { rate_end = line.volumetric_extrusion_rate_end; } else { // Use the original, 'floating' extrusion rate as a starting point for the limiter. @@ -573,13 +573,13 @@ void PressureEqualizer::adjust_volumetric_rate() } // feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_start : rate_start; // Don't store feed rate for ironing and gap-fill. - if (line.extrusion_role != erIroning && line.extrusion_role != erGapFill) + if (line.extrusion_role != GCodeExtrusionRole::Ironing && line.extrusion_role != GCodeExtrusionRole::GapFill) feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_start; } } feedrate_per_extrusion_role.fill(std::numeric_limits::max()); - feedrate_per_extrusion_role[m_gcode_lines[line_idx].extrusion_role] = m_gcode_lines[line_idx].volumetric_extrusion_rate_end; + feedrate_per_extrusion_role[size_t(m_gcode_lines[line_idx].extrusion_role)] = m_gcode_lines[line_idx].volumetric_extrusion_rate_end; assert(m_gcode_lines[line_idx].extruding()); while (line_idx != last_line_idx) { @@ -588,7 +588,7 @@ void PressureEqualizer::adjust_volumetric_rate() if (!m_gcode_lines[idx_next].extruding()) break; // Don't accelerate after ironing and gap-fill. - if (m_gcode_lines[line_idx].extrusion_role == erIroning || m_gcode_lines[line_idx].extrusion_role == erGapFill) { + if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing || m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::GapFill) { line_idx = idx_next; continue; } @@ -597,21 +597,21 @@ void PressureEqualizer::adjust_volumetric_rate() line_idx = idx_next; GCodeLine &line = m_gcode_lines[line_idx]; - for (size_t iRole = 1; iRole < erCount; ++ iRole) { + for (size_t iRole = 1; iRole < size_t(GCodeExtrusionRole::Count); ++ iRole) { const float &rate_slope = m_max_volumetric_extrusion_rate_slopes[iRole].positive; if (rate_slope == 0 || feedrate_per_extrusion_role[iRole] == std::numeric_limits::max()) continue; // The positive rate is unlimited or the rate for GCodeExtrusionRole iRole is unlimited. float rate_start = feedrate_per_extrusion_role[iRole]; - if (!line.adjustable_flow || line.extrusion_role == erExternalPerimeter || line.extrusion_role == erGapFill || line.extrusion_role == erBridgeInfill || line.extrusion_role == erIroning) { + if (!line.adjustable_flow || line.extrusion_role == GCodeExtrusionRole::ExternalPerimeter || line.extrusion_role == GCodeExtrusionRole::GapFill || line.extrusion_role == GCodeExtrusionRole::BridgeInfill || line.extrusion_role == GCodeExtrusionRole::Ironing) { rate_start = line.volumetric_extrusion_rate_start; - } else if (iRole == line.extrusion_role && rate_prec < rate_start) + } else if (iRole == size_t(line.extrusion_role) && rate_prec < rate_start) rate_start = rate_prec; if (line.volumetric_extrusion_rate_start > rate_start) { line.volumetric_extrusion_rate_start = rate_start; line.max_volumetric_extrusion_rate_slope_positive = rate_slope; line.modified = true; - } else if (iRole == line.extrusion_role) { + } else if (iRole == size_t(line.extrusion_role)) { rate_start = line.volumetric_extrusion_rate_start; } else { // Use the original, 'floating' extrusion rate as a starting point for the limiter. @@ -629,7 +629,7 @@ void PressureEqualizer::adjust_volumetric_rate() } // feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_end : rate_end; // Don't store feed rate for ironing and gap-fill. - if (line.extrusion_role != erIroning && line.extrusion_role != erGapFill) + if (line.extrusion_role != GCodeExtrusionRole::Ironing && line.extrusion_role != GCodeExtrusionRole::GapFill) feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_end; } } @@ -713,7 +713,7 @@ void PressureEqualizer::push_line_to_output(const size_t line_idx, const float n GCodeG1Formatter feedrate_formatter; feedrate_formatter.emit_f(new_feedrate); feedrate_formatter.emit_string(std::string(EXTRUDE_SET_SPEED_TAG.data(), EXTRUDE_SET_SPEED_TAG.length())); - if (line.extrusion_role == erExternalPerimeter) + if (line.extrusion_role == GCodeExtrusionRole::ExternalPerimeter) feedrate_formatter.emit_string(std::string(EXTERNAL_PERIMETER_TAG.data(), EXTERNAL_PERIMETER_TAG.length())); push_to_output(feedrate_formatter); diff --git a/src/libslic3r/GCode/PressureEqualizer.hpp b/src/libslic3r/GCode/PressureEqualizer.hpp index 350030e05..a22208904 100644 --- a/src/libslic3r/GCode/PressureEqualizer.hpp +++ b/src/libslic3r/GCode/PressureEqualizer.hpp @@ -65,7 +65,7 @@ private: float positive; float negative; }; - ExtrusionRateSlope m_max_volumetric_extrusion_rate_slopes[erCount]; + ExtrusionRateSlope m_max_volumetric_extrusion_rate_slopes[size_t(GCodeExtrusionRole::Count)]; float m_max_volumetric_extrusion_rate_slope_positive; float m_max_volumetric_extrusion_rate_slope_negative; diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index e83584218..f24311a13 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -37,7 +37,7 @@ public: // adds tag for analyzer: std::ostringstream str; str << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) << m_layer_height << "\n"; // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming - str << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << gcode_extrusion_role_to_string(erWipeTower) << "\n"; + str << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << gcode_extrusion_role_to_string(GCodeExtrusionRole::WipeTower) << "\n"; m_gcode += str.str(); change_analyzer_line_width(line_width); } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 451c43ff2..fdfb6bfe8 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -560,23 +560,22 @@ void GCodeViewer::SequentialView::render(float legend_height) gcode_window.render(legend_height, bottom, static_cast(gcode_ids[current.last])); } -const std::vector GCodeViewer::Extrusion_Role_Colors{ { - { 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 - { 0.69f, 0.19f, 0.16f, 1.0f }, // erInternalInfill - { 0.59f, 0.33f, 0.80f, 1.0f }, // erSolidInfill - { 0.94f, 0.25f, 0.25f, 1.0f }, // erTopSolidInfill - { 1.00f, 0.55f, 0.41f, 1.0f }, // erIroning - { 0.30f, 0.50f, 0.73f, 1.0f }, // erBridgeInfill - { 1.00f, 1.00f, 1.00f, 1.0f }, // erGapFill - { 0.00f, 0.53f, 0.43f, 1.0f }, // erSkirt - { 0.00f, 1.00f, 0.00f, 1.0f }, // erSupportMaterial - { 0.00f, 0.50f, 0.00f, 1.0f }, // erSupportMaterialInterface - { 0.70f, 0.89f, 0.67f, 1.0f }, // erWipeTower - { 0.37f, 0.82f, 0.58f, 1.0f }, // erCustom - { 0.00f, 0.00f, 0.00f, 1.0f } // erMixed +const std::array(GCodeExtrusionRole::Count)> GCodeViewer::Extrusion_Role_Colors{ { + { 0.90f, 0.70f, 0.70f, 1.0f }, // GCodeExtrusionRole::None + { 1.00f, 0.90f, 0.30f, 1.0f }, // GCodeExtrusionRole::Perimeter + { 1.00f, 0.49f, 0.22f, 1.0f }, // GCodeExtrusionRole::ExternalPerimeter + { 0.12f, 0.12f, 1.00f, 1.0f }, // GCodeExtrusionRole::OverhangPerimeter + { 0.69f, 0.19f, 0.16f, 1.0f }, // GCodeExtrusionRole::InternalInfill + { 0.59f, 0.33f, 0.80f, 1.0f }, // GCodeExtrusionRole::SolidInfill + { 0.94f, 0.25f, 0.25f, 1.0f }, // GCodeExtrusionRole::TopSolidInfill + { 1.00f, 0.55f, 0.41f, 1.0f }, // GCodeExtrusionRole::Ironing + { 0.30f, 0.50f, 0.73f, 1.0f }, // GCodeExtrusionRole::BridgeInfill + { 1.00f, 1.00f, 1.00f, 1.0f }, // GCodeExtrusionRole::GapFill + { 0.00f, 0.53f, 0.43f, 1.0f }, // GCodeExtrusionRole::Skirt + { 0.00f, 1.00f, 0.00f, 1.0f }, // GCodeExtrusionRole::SupportMaterial + { 0.00f, 0.50f, 0.00f, 1.0f }, // GCodeExtrusionRole::SupportMaterialInterface + { 0.70f, 0.89f, 0.67f, 1.0f }, // GCodeExtrusionRole::WipeTower + { 0.37f, 0.82f, 0.58f, 1.0f }, // GCodeExtrusionRole::Custom }}; const std::vector GCodeViewer::Options_Colors{ { @@ -840,7 +839,7 @@ void GCodeViewer::refresh(const GCodeProcessorResult& gcode_result, const std::v m_extrusions.ranges.width.update_from(round_to_bin(curr.width)); m_extrusions.ranges.fan_speed.update_from(curr.fan_speed); m_extrusions.ranges.temperature.update_from(curr.temperature); - if (curr.extrusion_role != erCustom || is_visible(erCustom)) + if (curr.extrusion_role != GCodeExtrusionRole::Custom || is_visible(GCodeExtrusionRole::Custom)) m_extrusions.ranges.volumetric_rate.update_from(round_to_bin(curr.volumetric_rate())); [[fallthrough]]; } @@ -1556,7 +1555,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) // for the gcode viewer we need to take in account all moves to correctly size the printbed m_paths_bounding_box.merge(move.position.cast()); else { - if (move.type == EMoveType::Extrude && move.extrusion_role != erCustom && move.width != 0.0f && move.height != 0.0f) + if (move.type == EMoveType::Extrude && move.extrusion_role != GCodeExtrusionRole::Custom && move.width != 0.0f && move.height != 0.0f) m_paths_bounding_box.merge(move.position.cast()); } } @@ -1603,11 +1602,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result) const GCodeProcessorResult::MoveVertex& prev = gcode_result.moves[i - 1]; if (curr.type == EMoveType::Extrude && - curr.extrusion_role != erSkirt && - curr.extrusion_role != erSupportMaterial && - curr.extrusion_role != erSupportMaterialInterface && - curr.extrusion_role != erWipeTower && - curr.extrusion_role != erCustom) { + curr.extrusion_role != GCodeExtrusionRole::Skirt && + curr.extrusion_role != GCodeExtrusionRole::SupportMaterial && + curr.extrusion_role != GCodeExtrusionRole::SupportMaterialInterface && + curr.extrusion_role != GCodeExtrusionRole::WipeTower && + curr.extrusion_role != GCodeExtrusionRole::Custom) { const Vec3d curr_pos = curr.position.cast(); const Vec3d prev_pos = prev.position.cast(); m_cog.add_segment(curr_pos, prev_pos, curr.mm3_per_mm * (curr_pos - prev_pos).norm()); @@ -3494,8 +3493,8 @@ void GCodeViewer::render_legend(float& legend_height) if (m_view_type == EViewType::FeatureType) { // calculate offsets to align time/percentage data for (GCodeExtrusionRole role : m_roles) { - assert(role < erCount); - if (role < erCount) { + assert(role < GCodeExtrusionRole::Count); + if (role < GCodeExtrusionRole::Count) { labels.push_back(_u8L(gcode_extrusion_role_to_string(role))); auto [time, percent] = role_time_and_percent(role); times.push_back((time > 0.0f) ? short_time(get_time_dhms(time)) : ""); @@ -3610,12 +3609,12 @@ void GCodeViewer::render_legend(float& legend_height) for (size_t i = 0; i < m_roles.size(); ++i) { GCodeExtrusionRole role = m_roles[i]; - if (role >= erCount) + if (role >= GCodeExtrusionRole::Count) continue; const bool visible = is_visible(role); append_item(EItemType::Rect, Extrusion_Role_Colors[static_cast(role)], labels[i], visible, times[i], percents[i], max_time_percent, offsets, used_filaments_m[i], used_filaments_g[i], [this, role, visible]() { - m_extrusions.role_visibility_flags = visible ? m_extrusions.role_visibility_flags & ~(1 << role) : m_extrusions.role_visibility_flags | (1 << role); + m_extrusions.role_visibility_flags = visible ? m_extrusions.role_visibility_flags & ~(1 << int(role)) : m_extrusions.role_visibility_flags | (1 << int(role)); // update buffers' render paths refresh_render_paths(false, false); wxGetApp().plater()->update_preview_moves_slider(); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 11a0d6476..3eb61cfc4 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -2,6 +2,7 @@ #define slic3r_GCodeViewer_hpp_ #include "3DScene.hpp" +#include "libslic3r/ExtrusionRole.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp" #include "GLModel.hpp" @@ -30,7 +31,7 @@ class GCodeViewer using InstanceIdBuffer = std::vector; using InstancesOffsets = std::vector; - static const std::vector Extrusion_Role_Colors; + static const std::array(GCodeExtrusionRole::Count)> Extrusion_Role_Colors; static const std::vector Options_Colors; static const std::vector Travel_Colors; static const std::vector Range_Colors; @@ -208,7 +209,7 @@ class GCodeViewer }; EMoveType type{ EMoveType::Noop }; - GCodeExtrusionRole role{ erNone }; + GCodeExtrusionRole role{ GCodeExtrusionRole::None }; float delta_extruder{ 0.0f }; float height{ 0.0f }; float width{ 0.0f }; @@ -482,7 +483,7 @@ class GCodeViewer void reset_role_visibility_flags() { role_visibility_flags = 0; - for (unsigned int i = 0; i < erCount; ++i) { + for (uint32_t i = 0; i < uint32_t(GCodeExtrusionRole::Count); ++i) { role_visibility_flags |= 1 << i; } } @@ -850,7 +851,7 @@ private: void render_statistics(); #endif // ENABLE_GCODE_VIEWER_STATISTICS bool is_visible(GCodeExtrusionRole role) const { - return role < erCount && (m_extrusions.role_visibility_flags & (1 << role)) != 0; + return role < GCodeExtrusionRole::Count && (m_extrusions.role_visibility_flags & (1 << int(role))) != 0; } bool is_visible(const Path& path) const { return is_visible(path.role); } void log_memory_used(const std::string& label, int64_t additional = 0) const; From d1f610d957e41cbea3ce09010bab7f72148a15c3 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 16 Jan 2023 12:39:08 +0100 Subject: [PATCH 10/28] Extending the expressivity of ExtrusionRole Moved is_xx() test functions to ExtrusionRole enum class, changed the tests to use the new ExtrusionRole bitmask. Follow-up to 5991850db1f2d09d1b7713f42300e2bfe4a42128 e50e96bb26eab4f9d56c98706c03166635cf4fff c64dbacf88c617e3d676bdaa6c8c81317ccaee5d --- src/libslic3r/ExtrusionEntity.cpp | 6 ++-- src/libslic3r/ExtrusionRole.hpp | 38 ++++++------------------- src/libslic3r/GCode.cpp | 23 ++++++++------- src/libslic3r/GCode/ToolOrdering.cpp | 10 +++---- src/libslic3r/SupportSpotsGenerator.cpp | 2 ++ src/libslic3r/enum_bitmask.hpp | 8 +++--- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- 7 files changed, 35 insertions(+), 54 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 2b7bebc37..8a1a88b4b 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -50,7 +50,7 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale { // Instantiating the Flow class to get the line spacing. // Don't know the nozzle diameter, setting to zero. It shall not matter it shall be optimized out by the compiler. - bool bridge = is_bridge(this->role()); + bool bridge = this->role().is_bridge(); assert(! bridge || this->width == this->height); auto flow = bridge ? Flow::bridging_flow(this->width, 0.f) : Flow(this->width, this->height, 0.f); polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon)); @@ -208,7 +208,7 @@ ExtrusionLoop::ClosestPathPoint ExtrusionLoop::get_closest_path_and_point(const out.segment_idx = foot_pt_.first; min2 = d2; } - if (prefer_non_overhang && !is_bridge(path.role()) && d2 < min2_non_overhang) { + if (prefer_non_overhang && ! path.role().is_bridge() && d2 < min2_non_overhang) { best_non_overhang.foot_pt = foot_pt_.second; best_non_overhang.path_idx = &path - &this->paths.front(); best_non_overhang.segment_idx = foot_pt_.first; @@ -294,7 +294,7 @@ bool ExtrusionLoop::has_overhang_point(const Point &point) const if (pos != -1) { // point belongs to this path // we consider it overhang only if it's not an endpoint - return (is_bridge(path.role()) && pos > 0 && pos != (int)(path.polyline.points.size())-1); + return (path.role().is_bridge() && pos > 0 && pos != int(path.polyline.points.size())-1); } } return false; diff --git a/src/libslic3r/ExtrusionRole.hpp b/src/libslic3r/ExtrusionRole.hpp index b99eb5034..a6426e0c6 100644 --- a/src/libslic3r/ExtrusionRole.hpp +++ b/src/libslic3r/ExtrusionRole.hpp @@ -58,7 +58,7 @@ struct ExtrusionRole : public ExtrusionRoleModifiers //FIXME why there is no bottom solid infill type? static constexpr const ExtrusionRoleModifiers TopSolidInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::External }; // Ironing infill at the top surfaces. - static constexpr const ExtrusionRoleModifiers Ironing{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Ironing | ExtrusionRoleModifier::External }; + static constexpr const ExtrusionRoleModifiers Ironing{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::Ironing | ExtrusionRoleModifier::External }; // Visible bridging infill at the bottom of an object. static constexpr const ExtrusionRoleModifiers BridgeInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::Bridge | ExtrusionRoleModifier::External }; // static constexpr const ExtrusionRoleModifiers InternalBridgeInfill{ ExtrusionRoleModifier::Infill | ExtrusionRoleModifier::Solid | ExtrusionRoleModifier::Bridge }; @@ -76,6 +76,13 @@ struct ExtrusionRole : public ExtrusionRoleModifiers static constexpr const ExtrusionRoleModifiers WipeTower{ ExtrusionRoleModifier::Wipe }; // Extrusion role for a collection with multiple extrusion roles. static constexpr const ExtrusionRoleModifiers Mixed{ ExtrusionRoleModifier::Mixed }; + + bool is_perimeter() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Perimeter); } + bool is_external_perimeter() const { return this->is_perimeter() && this->is_external(); } + bool is_infill() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Infill); } + bool is_solid_infill() const { return this->is_infill() && this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Solid); } + bool is_external() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::External); } + bool is_bridge() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Bridge); } }; // Special flags describing loop @@ -85,35 +92,6 @@ enum ExtrusionLoopRole { elrSkirt, }; -inline bool is_perimeter(ExtrusionRole role) -{ - return role == ExtrusionRole::Perimeter - || role == ExtrusionRole::ExternalPerimeter - || role == ExtrusionRole::OverhangPerimeter; -} - -inline bool is_infill(ExtrusionRole role) -{ - return role == ExtrusionRole::BridgeInfill - || role == ExtrusionRole::InternalInfill - || role == ExtrusionRole::SolidInfill - || role == ExtrusionRole::TopSolidInfill - || role == ExtrusionRole::Ironing; -} - -inline bool is_solid_infill(ExtrusionRole role) -{ - return role == ExtrusionRole::BridgeInfill - || role == ExtrusionRole::SolidInfill - || role == ExtrusionRole::TopSolidInfill - || role == ExtrusionRole::Ironing; -} - -inline bool is_bridge(ExtrusionRole role) { - return role == ExtrusionRole::BridgeInfill - || role == ExtrusionRole::OverhangPerimeter; -} - // Be careful when editing this list as many parts of the code depend // on the values of these ordinars, for example // GCodeViewer::Extrusion_Role_Colors diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 0f84c1a53..e26e0b55e 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2601,7 +2601,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, const std::string_view descr if (paths.empty()) return ""; // apply the small perimeter speed - if (is_perimeter(paths.front().role()) && loop.length() <= SMALL_PERIMETER_LENGTH && speed == -1) + if (paths.front().role().is_perimeter() && loop.length() <= SMALL_PERIMETER_LENGTH && speed == -1) speed = m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed); // extrude along the path @@ -2618,7 +2618,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, const std::string_view descr m_wipe.path = paths.front().polyline; for (auto it = std::next(paths.begin()); it != paths.end(); ++it) { - if (is_bridge(it->role())) + if (it->role().is_bridge()) break; // Don't perform a wipe on bridges. assert(it->polyline.points.size() >= 2); @@ -2686,7 +2686,7 @@ std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, const std::s m_wipe.path.reverse(); for (auto it = std::next(multipath.paths.rbegin()); it != multipath.paths.rend(); ++it) { - if (is_bridge(it->role())) + if (it->role().is_bridge()) break; // Do not perform a wipe on bridges. assert(it->polyline.points.size() >= 2); @@ -2831,7 +2831,7 @@ void GCode::GCodeOutputStream::write_format(const char* format, ...) std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view description, double speed) { std::string gcode; - const std::string_view description_bridge = is_bridge(path.role()) ? " (bridge)"sv : ""sv; + const std::string_view description_bridge = path.role().is_bridge() ? " (bridge)"sv : ""sv; // go to first point of extrusion path if (!m_last_pos_defined || m_last_pos != path.first_point()) { @@ -2852,11 +2852,11 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de acceleration = m_config.first_layer_acceleration.value; } else if (this->object_layer_over_raft() && m_config.first_layer_acceleration_over_raft.value > 0) { acceleration = m_config.first_layer_acceleration_over_raft.value; - } else if (m_config.bridge_acceleration.value > 0 && is_bridge(path.role())) { + } else if (m_config.bridge_acceleration.value > 0 && path.role().is_bridge()) { acceleration = m_config.bridge_acceleration.value; - } else if (m_config.infill_acceleration.value > 0 && is_infill(path.role())) { + } else if (m_config.infill_acceleration.value > 0 && path.role().is_infill()) { acceleration = m_config.infill_acceleration.value; - } else if (m_config.perimeter_acceleration.value > 0 && is_perimeter(path.role())) { + } else if (m_config.perimeter_acceleration.value > 0 && path.role().is_perimeter()) { acceleration = m_config.perimeter_acceleration.value; } else { acceleration = m_config.default_acceleration.value; @@ -2876,7 +2876,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de speed = m_config.get_abs_value("perimeter_speed"); } else if (path.role() == ExtrusionRole::ExternalPerimeter) { speed = m_config.get_abs_value("external_perimeter_speed"); - } else if (path.role() == ExtrusionRole::OverhangPerimeter || path.role() == ExtrusionRole::BridgeInfill) { + } else if (path.role().is_bridge()) { + assert(path.role() == ExtrusionRole::OverhangPerimeter || path.role() == ExtrusionRole::BridgeInfill); speed = m_config.get_abs_value("bridge_speed"); } else if (path.role() == ExtrusionRole::InternalInfill) { speed = m_config.get_abs_value("infill_speed"); @@ -2915,7 +2916,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de bool variable_speed = false; std::vector new_points{}; - if (this->m_config.enable_dynamic_overhang_speeds && !this->on_first_layer() && is_perimeter(path.role())) { + if (this->m_config.enable_dynamic_overhang_speeds && !this->on_first_layer() && path.role().is_perimeter()) { new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, m_config.overhang_overlap_levels, m_config.dynamic_overhang_speeds, m_config.get_abs_value("external_perimeter_speed"), speed); @@ -2975,7 +2976,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de std::string comment; if (m_enable_cooling_markers) { - if (is_bridge(path.role())) + if (path.role().is_bridge()) gcode += ";_BRIDGE_FAN_START\n"; else comment = ";_EXTRUDE_SET_SPEED"; @@ -3026,7 +3027,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de } if (m_enable_cooling_markers) - gcode += is_bridge(path.role()) ? ";_BRIDGE_FAN_END\n" : ";_EXTRUDE_END\n"; + gcode += path.role().is_bridge() ? ";_BRIDGE_FAN_END\n" : ";_EXTRUDE_END\n"; this->set_last_pos(path.last_point()); return gcode; diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 874a134b7..6a8a7a812 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -63,11 +63,11 @@ unsigned int LayerTools::extruder(const ExtrusionEntityCollection &extrusions, c assert(region.config().infill_extruder.value > 0); assert(region.config().solid_infill_extruder.value > 0); // 1 based extruder ID. - unsigned int extruder = ((this->extruder_override == 0) ? - (is_infill(extrusions.role()) ? - (is_solid_infill(extrusions.entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) : + unsigned int extruder = this->extruder_override == 0 ? + (extrusions.role().is_infill() ? + (extrusions.entities.front()->role().is_solid_infill() ? region.config().solid_infill_extruder : region.config().infill_extruder) : region.config().perimeter_extruder.value) : - this->extruder_override); + this->extruder_override; return (extruder == 0) ? 0 : extruder - 1; } @@ -267,7 +267,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto // fill represents infill extrusions of a single island. const auto *fill = dynamic_cast(ee); ExtrusionRole role = fill->entities.empty() ? ExtrusionRole::None : fill->entities.front()->role(); - if (is_solid_infill(role)) + if (role.is_solid_infill()) has_solid_infill = true; else if (role != ExtrusionRole::None) has_infill = true; diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 4800a2210..43637d25f 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -58,6 +58,8 @@ public: bool is_external_perimeter() const { assert(origin_entity != nullptr); + //FIXME origin_entity->role() == ExtrusionRole::OverhangPerimeter is not quite right. + // rather use origin_entity->role().is_external_perimeter() return origin_entity->role() == ExtrusionRole::ExternalPerimeter || origin_entity->role() == ExtrusionRole::OverhangPerimeter; } diff --git a/src/libslic3r/enum_bitmask.hpp b/src/libslic3r/enum_bitmask.hpp index 72172fad9..274952cb0 100644 --- a/src/libslic3r/enum_bitmask.hpp +++ b/src/libslic3r/enum_bitmask.hpp @@ -31,14 +31,14 @@ public: constexpr enum_bitmask(option_type o) : m_bits(mask_value(o)) {} // Set the bit corresponding to the given option. - constexpr enum_bitmask operator|(option_type t) { return enum_bitmask(m_bits | mask_value(t)); } + constexpr enum_bitmask operator|(option_type t) const { return enum_bitmask(m_bits | mask_value(t)); } // Combine with another enum_bitmask of the same type. - constexpr enum_bitmask operator|(enum_bitmask t) { return enum_bitmask(m_bits | t.m_bits); } + constexpr enum_bitmask operator|(enum_bitmask t) const { return enum_bitmask(m_bits | t.m_bits); } // Get the value of the bit corresponding to the given option. - constexpr bool operator&(option_type t) { return m_bits & mask_value(t); } - constexpr bool has(option_type t) { return m_bits & mask_value(t); } + constexpr bool operator&(option_type t) const { return m_bits & mask_value(t); } + constexpr bool has(option_type t) const { return m_bits & mask_value(t); } constexpr bool operator==(const enum_bitmask r) const { return m_bits == r.m_bits; } constexpr bool operator!=(const enum_bitmask r) const { return m_bits != r.m_bits; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 92289b9df..d8a005b34 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -6556,7 +6556,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c const auto *fill = dynamic_cast(ee); if (! fill->entities.empty()) _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy, - select_geometry(idx_layer, is_solid_infill(fill->entities.front()->role()) ? + select_geometry(idx_layer, fill->entities.front()->role().is_solid_infill() ? layerm->region().config().solid_infill_extruder : layerm->region().config().infill_extruder, 1)); } From 07b8bd0f7e76e6cb1f36c7b6baec3d0b2c562183 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 16 Jan 2023 12:54:53 +0100 Subject: [PATCH 11/28] Fixed missing include. --- src/libslic3r/ExtrusionRole.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/ExtrusionRole.cpp b/src/libslic3r/ExtrusionRole.cpp index 4d894b4ac..f43db4835 100644 --- a/src/libslic3r/ExtrusionRole.cpp +++ b/src/libslic3r/ExtrusionRole.cpp @@ -2,6 +2,7 @@ #include #include +#include #define L(s) (s) From 50bb164263cae9094e5ea6d4ab9865409b3c6eff Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 16 Jan 2023 13:44:34 +0100 Subject: [PATCH 12/28] Fix of slice button state after text editing --- src/slic3r/GUI/Jobs/EmbossJob.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index b04c519e0..f5f238216 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -533,6 +533,9 @@ void UpdateJob::update_volume(ModelVolume *volume, // redraw scene bool refresh_immediately = false; canvas->reload_scene(refresh_immediately); + + // Change buttons "Export G-code" into "Slice now" + canvas->post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } void priv::update_volume(TriangleMesh &&mesh, const DataUpdate &data, Transform3d* tr) From ca0c83d914bb8cd654357f3f146ed582555c3b61 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 16 Jan 2023 14:39:48 +0100 Subject: [PATCH 13/28] The new ExtrusionRole implementation newly supports both "Bridging" and "External" attributes at the same time. PerimeterGenerator was updated to make use of it and set "External" attribute for overhang perimeters. --- src/libslic3r/ExtrusionRole.cpp | 7 ++++--- src/libslic3r/GCode.cpp | 4 ++-- src/libslic3r/PerimeterGenerator.cpp | 23 ++++++++++++----------- src/libslic3r/SupportSpotsGenerator.cpp | 4 +--- src/libslic3r/enum_bitmask.hpp | 6 ++++++ 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/ExtrusionRole.cpp b/src/libslic3r/ExtrusionRole.cpp index f43db4835..1e91df204 100644 --- a/src/libslic3r/ExtrusionRole.cpp +++ b/src/libslic3r/ExtrusionRole.cpp @@ -13,9 +13,10 @@ namespace Slic3r { GCodeExtrusionRole extrusion_role_to_gcode_extrusion_role(ExtrusionRole role) { if (role == ExtrusionRole::None) return GCodeExtrusionRole::None; - if (role == ExtrusionRole::Perimeter) return GCodeExtrusionRole::Perimeter; - if (role == ExtrusionRole::ExternalPerimeter) return GCodeExtrusionRole::ExternalPerimeter; - if (role == ExtrusionRole::OverhangPerimeter) return GCodeExtrusionRole::OverhangPerimeter; + if (role.is_perimeter()) { + return role.is_bridge() ? GCodeExtrusionRole::OverhangPerimeter : + role.is_external() ? GCodeExtrusionRole::ExternalPerimeter : GCodeExtrusionRole::Perimeter; + } if (role == ExtrusionRole::InternalInfill) return GCodeExtrusionRole::InternalInfill; if (role == ExtrusionRole::SolidInfill) return GCodeExtrusionRole::SolidInfill; if (role == ExtrusionRole::TopSolidInfill) return GCodeExtrusionRole::TopSolidInfill; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index e26e0b55e..ad0d3c43f 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2631,7 +2631,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, const std::string_view descr } // make a little move inwards before leaving loop - if (paths.back().role() == ExtrusionRole::ExternalPerimeter && m_layer != NULL && m_config.perimeters.value > 1 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 3) { + if (paths.back().role().is_external_perimeter() && m_layer != NULL && m_config.perimeters.value > 1 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 3) { // detect angle between last and first segment // the side depends on the original winding order of the polygon (left for contours, right for holes) //FIXME improve the algorithm in case the loop is tiny. @@ -2877,7 +2877,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de } else if (path.role() == ExtrusionRole::ExternalPerimeter) { speed = m_config.get_abs_value("external_perimeter_speed"); } else if (path.role().is_bridge()) { - assert(path.role() == ExtrusionRole::OverhangPerimeter || path.role() == ExtrusionRole::BridgeInfill); + assert(path.role().is_perimeter() || path.role() == ExtrusionRole::BridgeInfill); speed = m_config.get_abs_value("bridge_speed"); } else if (path.role() == ExtrusionRole::InternalInfill) { speed = m_config.get_abs_value("infill_speed"); diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index a9044367a..1f3066147 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -114,7 +114,7 @@ ExtrusionMultiPath PerimeterGenerator::thick_polyline_to_multi_path(const ThickP } const double w = fmax(line.a_width, line.b_width); - const Flow new_flow = (role == ExtrusionRole::OverhangPerimeter && flow.bridge()) ? flow : flow.with_width(unscale(w) + flow.height() * float(1. - 0.25 * PI)); + const Flow new_flow = (role.is_bridge() && flow.bridge()) ? flow : flow.with_width(unscale(w) + flow.height() * float(1. - 0.25 * PI)); if (path.polyline.points.empty()) { path.polyline.append(line.a); path.polyline.append(line.b); @@ -292,9 +292,9 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator for (const PerimeterGeneratorLoop &loop : loops) { bool is_external = loop.is_external(); - ExtrusionRole role = ExtrusionRole::None; ExtrusionLoopRole loop_role; - role = is_external ? ExtrusionRole::ExternalPerimeter : ExtrusionRole::Perimeter; + ExtrusionRole role_normal = is_external ? ExtrusionRole::ExternalPerimeter : ExtrusionRole::Perimeter; + ExtrusionRole role_overhang = role_normal | ExtrusionRoleModifier::Bridge; if (loop.is_internal_contour()) { // Note that we set loop role to ContourInternalPerimeter // also when loop is both internal and external (i.e. @@ -321,7 +321,7 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator extrusion_paths_append( paths, intersection_pl({ polygon }, lower_slices_polygons_clipped), - role, + role_normal, is_external ? params.ext_mm3_per_mm : params.mm3_per_mm, is_external ? params.ext_perimeter_flow.width() : params.perimeter_flow.width(), float(params.layer_height)); @@ -332,7 +332,7 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator extrusion_paths_append( paths, diff_pl({ polygon }, lower_slices_polygons_clipped), - ExtrusionRole::OverhangPerimeter, + role_overhang, params.mm3_per_mm_overhang, params.overhang_flow.width(), params.overhang_flow.height()); @@ -341,7 +341,7 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator // We allow polyline reversal because Clipper may have randomly reversed polylines during clipping. chain_and_reorder_extrusion_paths(paths, &paths.front().first_point()); } else { - ExtrusionPath path(role); + ExtrusionPath path(role_normal); path.polyline = polygon.split_at_first_point(); path.mm3_per_mm = is_external ? params.ext_mm3_per_mm : params.mm3_per_mm; path.width = is_external ? params.ext_perimeter_flow.width() : params.perimeter_flow.width(); @@ -503,7 +503,8 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P continue; const bool is_external = extrusion->inset_idx == 0; - ExtrusionRole role = is_external ? ExtrusionRole::ExternalPerimeter : ExtrusionRole::Perimeter; + ExtrusionRole role_normal = is_external ? ExtrusionRole::ExternalPerimeter : ExtrusionRole::Perimeter; + ExtrusionRole role_overhang = role_normal | ExtrusionRoleModifier::Bridge; if (pg_extrusion.fuzzify) fuzzy_extrusion_line(*extrusion, scaled(params.config.fuzzy_skin_thickness.value), scaled(params.config.fuzzy_skin_point_dist.value)); @@ -541,13 +542,13 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P } // get non-overhang paths by intersecting this loop with the grown lower slices - extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role, + extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role_normal, is_external ? params.ext_perimeter_flow : params.perimeter_flow); // get overhang paths by checking what parts of this loop fall // outside the grown lower slices (thus where the distance between // the loop centerline and original lower slices is >= half nozzle diameter - extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctDifference), ExtrusionRole::OverhangPerimeter, + extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctDifference), role_overhang, params.overhang_flow); // Reapply the nearest point search for starting point. @@ -568,7 +569,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P for (const ExtrusionPath &path : paths) { ++point_occurrence[path.polyline.first_point()].occurrence; ++point_occurrence[path.polyline.last_point()].occurrence; - if (path.role() == ExtrusionRole::OverhangPerimeter) { + if (path.role().is_bridge()) { point_occurrence[path.polyline.first_point()].is_overhang = true; point_occurrence[path.polyline.last_point()].is_overhang = true; } @@ -588,7 +589,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P chain_and_reorder_extrusion_paths(paths, &start_point); } } else { - extrusion_paths_append(paths, *extrusion, role, is_external ? params.ext_perimeter_flow : params.perimeter_flow); + extrusion_paths_append(paths, *extrusion, role_normal, is_external ? params.ext_perimeter_flow : params.perimeter_flow); } // Append paths to collection. diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 43637d25f..eaf7dd57d 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -58,9 +58,7 @@ public: bool is_external_perimeter() const { assert(origin_entity != nullptr); - //FIXME origin_entity->role() == ExtrusionRole::OverhangPerimeter is not quite right. - // rather use origin_entity->role().is_external_perimeter() - return origin_entity->role() == ExtrusionRole::ExternalPerimeter || origin_entity->role() == ExtrusionRole::OverhangPerimeter; + return origin_entity->role().is_external_perimeter(); } Vec2f a; diff --git a/src/libslic3r/enum_bitmask.hpp b/src/libslic3r/enum_bitmask.hpp index 274952cb0..d5d247371 100644 --- a/src/libslic3r/enum_bitmask.hpp +++ b/src/libslic3r/enum_bitmask.hpp @@ -36,6 +36,12 @@ public: // Combine with another enum_bitmask of the same type. constexpr enum_bitmask operator|(enum_bitmask t) const { return enum_bitmask(m_bits | t.m_bits); } + // Set the bit corresponding to the given option. + constexpr void operator|=(option_type t) { m_bits = enum_bitmask(m_bits | mask_value(t)); } + + // Combine with another enum_bitmask of the same type. + constexpr void operator|=(enum_bitmask t) { m_bits = enum_bitmask(m_bits | t.m_bits); } + // Get the value of the bit corresponding to the given option. constexpr bool operator&(option_type t) const { return m_bits & mask_value(t); } constexpr bool has(option_type t) const { return m_bits & mask_value(t); } From e2a51a4da9d9ae892c11fc38dbf914724ba24686 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 16 Jan 2023 14:57:33 +0100 Subject: [PATCH 14/28] Fixed info for SplashScreen --- src/slic3r/GUI/GUI_App.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 42ba4295f..5a2562aab 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -276,7 +276,7 @@ private: _L("Developed by Prusa Research.") + "\n\n" + title + " " + _L("is licensed under the") + " " + _L("GNU Affero General Public License, version 3") + ".\n\n" + _L("Contributions by Vojtech Bubnik, Enrico Turri, Oleksandra Iushchenko, Tamas Meszaros, Lukas Matena, Vojtech Kral, David Kocik and numerous others.") + "\n\n" + - _L("Artwork model by Leslie Ing"); + _L("Artwork model by Creative Tools"); title_font = version_font = credits_font = init_font; } From 9dce38b9a77f179db9437d6e5fad07c31821897c Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 16 Jan 2023 15:22:22 +0100 Subject: [PATCH 15/28] missing include --- src/slic3r/GUI/Jobs/ArrangeJob.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 7214ccdab..7873af758 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -13,6 +13,8 @@ #include "libnest2d/common.hpp" +#include + namespace Slic3r { namespace GUI { // Cache the wti info From 63a3fcf5714ded2c3a41670dc3b359f673ceaf0b Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 16 Jan 2023 14:09:17 +0100 Subject: [PATCH 16/28] Changes in windows eject. Second request if first fail with different dev inst. Small refactor. --- src/slic3r/GUI/RemovableDriveManager.cpp | 50 +++++++++++------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index 2189f2ad9..b6881f1ae 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -78,34 +78,13 @@ namespace { DEVINST get_dev_inst_by_device_number(long device_number, UINT drive_type, WCHAR* dos_device_name) { bool is_floppy = (wcsstr(dos_device_name, L"\\Floppy") != NULL); // TODO: could be tested better? - GUID* guid; - switch (drive_type) { - case DRIVE_REMOVABLE: - if (is_floppy) { - // we are interested only in SD cards or USB sticks - BOOST_LOG_TRIVIAL(debug) << "get_dev_inst_by_device_number failed: Drive is floppy disk."; - return 0; - //guid = (GUID*)&GUID_DEVINTERFACE_FLOPPY; - } else { - guid = (GUID*)&GUID_DEVINTERFACE_DISK; - } - break; - case DRIVE_FIXED: - // we are interested only in SD cards or USB sticks - BOOST_LOG_TRIVIAL(debug) << "get_dev_inst_by_device_number failed: Drive is harddisk."; - return 0; - //guid = (GUID*)&GUID_DEVINTERFACE_DISK; - //break; - case DRIVE_CDROM: - BOOST_LOG_TRIVIAL(debug) << "get_dev_inst_by_device_number failed: Drive is cd-rom."; - // we are interested only in SD cards or USB sticks - return 0; - //guid = (GUID*)&GUID_DEVINTERFACE_CDROM; - //break; - default: + + if (drive_type != DRIVE_REMOVABLE || is_floppy) { + BOOST_LOG_TRIVIAL(debug) << "get_dev_inst_by_device_number failed: Drive is not removable."; return 0; } + GUID* guid = (GUID*)& GUID_DEVINTERFACE_DISK; // Get device interface info set handle for all devices attached to system HDEVINFO h_dev_info = SetupDiGetClassDevs(guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); @@ -228,7 +207,11 @@ int eject_inner(const std::string& path) // get drives's parent, e.g. the USB bridge, the SATA port, an IDE channel with two drives! DEVINST dev_inst_parent = 0; res = CM_Get_Parent(&dev_inst_parent, dev_inst, 0); - + if (res != CR_SUCCESS) { + BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1% has failed: Failed to get drive parent. Code: %2%", path, res); + return 1; + } + #if 0 // loop with several tries and sleep (this is running on main UI thread) for (int i = 0; i < 3; ++i) { @@ -247,13 +230,22 @@ int eject_inner(const std::string& path) } #endif // 0 - // perform eject + // Perform eject over parent dev_inst. This works for usb drives and some SD cards. res = CM_Request_Device_EjectW(dev_inst_parent, &veto_type, veto_name, MAX_PATH, 0); + //res = CM_Query_And_Remove_SubTreeW(dev_inst_parent, &veto_type, veto_name, MAX_PATH, CM_REMOVE_UI_OK); + if (res == CR_SUCCESS && veto_type == PNP_VetoTypeUnknown) { + return 0; + } + BOOST_LOG_TRIVIAL(warning) << GUI::format("Ejecting of %1% has failed: Request to eject device has failed. Another request will follow. Veto type: %2%", path, veto_type); + + // But on some PC, SD cards ejects only with its own dev_inst. + res = CM_Request_Device_EjectW(dev_inst, &veto_type, veto_name, MAX_PATH, 0); + //res = CM_Query_And_Remove_SubTreeW(dev_inst_parent, &veto_type, veto_name, MAX_PATH, CM_REMOVE_UI_OK); if (res == CR_SUCCESS && veto_type == PNP_VetoTypeUnknown) { return 0; } - BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1% has failed: Request to eject device has failed.", path); + BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1% has failed: Request to eject device has failed. Veto type: %2%", path, veto_type); return 1; } @@ -272,6 +264,7 @@ void RemovableDriveManager::eject_drive() BOOST_LOG_TRIVIAL(info) << "Ejecting started"; std::scoped_lock lock(m_drives_mutex); auto it_drive_data = this->find_last_save_path_drive_data(); +#if 1 if (it_drive_data != m_current_drives.end()) { if (!eject_inner(m_last_save_path)) { // success @@ -291,6 +284,7 @@ void RemovableDriveManager::eject_drive() if (m_callback_evt_handler) wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair({"",""}, false))); } +#endif #if 0 // Implementation used until 2.5.x version // Some usb drives does not eject properly (still visible in file explorer). Some even does not write all content and eject. From 21c8cefef28a52c854a0613c95e77b5e7de0f3cd Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 16 Jan 2023 13:30:09 +0100 Subject: [PATCH 17/28] downloader - binary write on resume --- src/slic3r/GUI/DownloaderFileGet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/DownloaderFileGet.cpp b/src/slic3r/GUI/DownloaderFileGet.cpp index 10142c7c1..deac45ad5 100644 --- a/src/slic3r/GUI/DownloaderFileGet.cpp +++ b/src/slic3r/GUI/DownloaderFileGet.cpp @@ -129,7 +129,7 @@ void FileGet::priv::get_perform() if (m_written == 0) file = fopen(temp_path_wstring.c_str(), "wb"); else - file = fopen(temp_path_wstring.c_str(), "a"); + file = fopen(temp_path_wstring.c_str(), "ab"); assert(file != NULL); From e959fd0e58200b737952a36a65603c719f09225c Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 16 Jan 2023 16:17:20 +0100 Subject: [PATCH 18/28] Error dialog on download failure. --- src/slic3r/GUI/Downloader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/Downloader.cpp b/src/slic3r/GUI/Downloader.cpp index 157233076..ff66b85c5 100644 --- a/src/slic3r/GUI/Downloader.cpp +++ b/src/slic3r/GUI/Downloader.cpp @@ -177,6 +177,7 @@ void Downloader::on_error(wxCommandEvent& event) BOOST_LOG_TRIVIAL(error) << "Download error: " << event.GetString(); NotificationManager* ntf_mngr = wxGetApp().notification_manager(); ntf_mngr->set_download_URL_error(id, boost::nowide::narrow(event.GetString())); + show_error(nullptr, format_wxstr(L"%1%\n%2%", _L("The download has failed:"), event.GetString())); } void Downloader::on_complete(wxCommandEvent& event) { From 52ea2edf842e81e0f81fcd8303b69d288d903ae3 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 16 Jan 2023 17:01:28 +0100 Subject: [PATCH 19/28] Fixed Layer::sort_perimeters_into_islands() for fuzzy skin: 1) An extrusion sample is taken for sorting extrusions into island so that a sample deep inside its island is taken with high probability. 2) With fuzzy skin active, the inexact search is done with bounding boxes inflated with the fuzzying distance. --- src/libslic3r/Layer.cpp | 86 ++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 31 deletions(-) diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 37a24b54c..44fc18f91 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -568,19 +568,33 @@ void Layer::sort_perimeters_into_islands( const std::pair &extrusions = perimeter_and_gapfill_ranges[islice]; Point sample; bool sample_set = false; - if (! extrusions.first.empty()) { - sample = this_layer_region.perimeters().entities[*extrusions.first.begin()]->first_point(); - sample_set = true; - } else if (! extrusions.second.empty()) { - sample = this_layer_region.thin_fills().entities[*extrusions.second.begin()]->first_point(); - sample_set = true; - } else { - for (uint32_t iexpoly : fill_expolygons_ranges[islice]) - if (const ExPolygon &expoly = fill_expolygons[iexpoly]; ! expoly.empty()) { - sample = expoly.contour.points.front(); + // Take a sample deep inside its island if available. Infills are usually quite far from the island boundary. + for (uint32_t iexpoly : fill_expolygons_ranges[islice]) + if (const ExPolygon &expoly = fill_expolygons[iexpoly]; ! expoly.empty()) { + sample = expoly.contour.points.front(); + sample_set = true; + break; + } + if (! sample_set) { + // If there is no infill, take a sample of some inner perimeter. + for (uint32_t iperimeter : extrusions.first) + if (const ExtrusionEntity &ee = *this_layer_region.perimeters().entities[iperimeter]; ! ee.role().is_external()) { + sample = ee.first_point(); sample_set = true; break; } + if (! sample_set) { + if (! extrusions.second.empty()) { + // If there is no inner perimeter, take a sample of some gap fill extrusion. + sample = this_layer_region.thin_fills().entities[*extrusions.second.begin()]->first_point(); + sample_set = true; + } + if (! sample_set && ! extrusions.first.empty()) { + // As a last resort, take a sample of some external perimeter. + sample = this_layer_region.perimeters().entities[*extrusions.first.begin()]->first_point(); + sample_set = true; + } + } } // There may be a valid empty island. // assert(sample_set); @@ -729,27 +743,37 @@ void Layer::sort_perimeters_into_islands( perimeter_slices_queue.pop_back(); break; } - // If anything fails to be sorted in using exact fit, try to find a closest island. - auto point_inside_surface_dist2 = - [&lslices = this->lslices, &lslices_ex = this->lslices_ex, bbox_eps = scaled(this->object()->print()->config().gcode_resolution.value) + SCALED_EPSILON] - (const size_t lslice_idx, const Point &point) { - const BoundingBox &bbox = lslices_ex[lslice_idx].bbox; - return - point.x() < bbox.min.x() - bbox_eps || point.x() > bbox.max.x() + bbox_eps || - point.y() < bbox.min.y() - bbox_eps || point.y() > bbox.max.y() + bbox_eps ? - std::numeric_limits::max() : - (lslices[lslice_idx].point_projection(point) - point).cast().squaredNorm(); - }; - for (auto it_source_slice = perimeter_slices_queue.begin(); it_source_slice != perimeter_slices_queue.end(); ++ it_source_slice) { - double d2min = std::numeric_limits::max(); - int lslice_idx_min = -1; - for (int lslice_idx = int(lslices_ex.size()) - 1; lslice_idx >= 0; -- lslice_idx) - if (double d2 = point_inside_surface_dist2(lslice_idx, it_source_slice->second); d2 < d2min) { - d2min = d2; - lslice_idx_min = lslice_idx; - } - assert(lslice_idx_min != -1); - insert_into_island(lslice_idx_min, it_source_slice->first); + if (! perimeter_slices_queue.empty()) { + // If the slice sample was not fitted into any slice using exact fit, try to find a closest island as a last resort. + // This should be a rare event especially if the sample point was taken from infill or inner perimeter, + // however we may land here for external perimeter only islands with fuzzy skin applied. + // Check whether fuzzy skin was enabled and adjust the bounding box accordingly. + const PrintConfig &print_config = this->object()->print()->config(); + const PrintRegionConfig ®ion_config = this_layer_region.region().config(); + const auto bbox_eps = scaled( + EPSILON + print_config.gcode_resolution.value + + (region_config.fuzzy_skin.value == FuzzySkinType::None ? 0. : region_config.fuzzy_skin_thickness.value)); + auto point_inside_surface_dist2 = + [&lslices = this->lslices, &lslices_ex = this->lslices_ex, bbox_eps] + (const size_t lslice_idx, const Point &point) { + const BoundingBox &bbox = lslices_ex[lslice_idx].bbox; + return + point.x() < bbox.min.x() - bbox_eps || point.x() > bbox.max.x() + bbox_eps || + point.y() < bbox.min.y() - bbox_eps || point.y() > bbox.max.y() + bbox_eps ? + std::numeric_limits::max() : + (lslices[lslice_idx].point_projection(point) - point).cast().squaredNorm(); + }; + for (auto it_source_slice = perimeter_slices_queue.begin(); it_source_slice != perimeter_slices_queue.end(); ++ it_source_slice) { + double d2min = std::numeric_limits::max(); + int lslice_idx_min = -1; + for (int lslice_idx = int(lslices_ex.size()) - 1; lslice_idx >= 0; -- lslice_idx) + if (double d2 = point_inside_surface_dist2(lslice_idx, it_source_slice->second); d2 < d2min) { + d2min = d2; + lslice_idx_min = lslice_idx; + } + assert(lslice_idx_min != -1); + insert_into_island(lslice_idx_min, it_source_slice->first); + } } } From 3438b5184ed85cd78ba8f036646137092f13fd8e Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 16 Jan 2023 17:18:04 +0100 Subject: [PATCH 20/28] missing include --- src/slic3r/GUI/Downloader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/Downloader.cpp b/src/slic3r/GUI/Downloader.cpp index ff66b85c5..4ec4e04eb 100644 --- a/src/slic3r/GUI/Downloader.cpp +++ b/src/slic3r/GUI/Downloader.cpp @@ -1,6 +1,7 @@ #include "Downloader.hpp" #include "GUI_App.hpp" #include "NotificationManager.hpp" +#include "format.hpp" #include #include From 02d5ad9d8ca4f7ce6179d3a86da39f98cdd50874 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Mon, 16 Jan 2023 17:41:22 +0100 Subject: [PATCH 21/28] Downloader URL check --- src/slic3r/GUI/Downloader.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/Downloader.cpp b/src/slic3r/GUI/Downloader.cpp index 4ec4e04eb..ad46162cb 100644 --- a/src/slic3r/GUI/Downloader.cpp +++ b/src/slic3r/GUI/Downloader.cpp @@ -146,14 +146,15 @@ void Downloader::start_download(const std::string& full_url) #else std::string escaped_url = FileGet::escape_url(full_url.substr(24)); #endif - // TODO: enable after testing - /* - if (!boost::starts_with(escaped_url, "https://media.printables.com/")) { - BOOST_LOG_TRIVIAL(error) << "Download won't start. Download URL doesn't point to https://media.printables.com : " << escaped_url; - // TODO: show error? + + if (!boost::starts_with(escaped_url, "https://files.printables.com") && !boost::starts_with(escaped_url, "https://dev-files.printables.com")) { + std::string msg = format(_L("Download won't start. Download URL doesn't point to https://files.printables.com : %1%"), escaped_url); + BOOST_LOG_TRIVIAL(error) << msg; + NotificationManager* ntf_mngr = wxGetApp().notification_manager(); + ntf_mngr->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::RegularNotificationLevel, msg); return; } - */ + std::string text(escaped_url); m_downloads.emplace_back(std::make_unique(id, std::move(escaped_url), this, m_dest_folder)); NotificationManager* ntf_mngr = wxGetApp().notification_manager(); From 6047e82310f9e7d4ea053268f7e5cb18b45d9fc2 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 17 Jan 2023 10:31:06 +0100 Subject: [PATCH 22/28] Deps: Updated wxWidgets --- deps/wxWidgets/wxWidgets.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index 95f54c1a2..93d716c12 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -13,8 +13,8 @@ if (UNIX AND NOT APPLE) # wxWidgets will not use char as the underlying type for endif() prusaslicer_add_cmake_project(wxWidgets - URL https://github.com/prusa3d/wxWidgets/archive/34b524f8d5134a40a90d93a16360d533af2676ae.zip - URL_HASH SHA256=e76ca0dd998905c4dbb86f41f264e6e0468504dc2398f7e7e3bba8dc37de2f45 + URL https://github.com/prusa3d/wxWidgets/archive/9c12f4f269e545bacc7a6418bc82bf10db5842a6.zip + URL_HASH SHA256=901ae1148855507968d5ecd09b5a6ce2e24400f77ba67467165c55053ac213fd DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} dep_TIFF dep_JPEG dep_NanoSVG CMAKE_ARGS -DwxBUILD_PRECOMP=ON From bcc193d8dc131fcb7fb6bd76edc2494852e2a0f3 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Tue, 17 Jan 2023 10:38:56 +0100 Subject: [PATCH 23/28] Added thumbnails for new printer models. --- .../profiles/Elegoo/NEPTUNE3MAX_thumbnail.png | Bin 0 -> 40643 bytes .../profiles/Elegoo/NEPTUNE3PLUS_thumbnail.png | Bin 0 -> 35858 bytes .../profiles/Elegoo/NEPTUNE3PRO_thumbnail.png | Bin 0 -> 36880 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/profiles/Elegoo/NEPTUNE3MAX_thumbnail.png create mode 100644 resources/profiles/Elegoo/NEPTUNE3PLUS_thumbnail.png create mode 100644 resources/profiles/Elegoo/NEPTUNE3PRO_thumbnail.png diff --git a/resources/profiles/Elegoo/NEPTUNE3MAX_thumbnail.png b/resources/profiles/Elegoo/NEPTUNE3MAX_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..5b429ac32ab0f0ac6cb208629ff387d6b1f1162d GIT binary patch literal 40643 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 Ifvsi#2Scu7=l}o! literal 0 HcmV?d00001 diff --git a/resources/profiles/Elegoo/NEPTUNE3PLUS_thumbnail.png b/resources/profiles/Elegoo/NEPTUNE3PLUS_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..78293eebbdb64d1524d85a87d67df4702bad9721 GIT binary patch literal 35858 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%0D#Vw!hAHKbvK&|yDy%UL^iK+^&& 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

FRgB^PT_e&2M^R#^unB) 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%0DDx7UqQts5;AUZ=MxpC@3sFcGUCO?3+KsrduM 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 Date: Tue, 17 Jan 2023 10:40:01 +0100 Subject: [PATCH 24/28] Added new printer models --- resources/profiles/Elegoo.idx | 2 + resources/profiles/Elegoo.ini | 90 +++++++++++++++++++++++++---------- 2 files changed, 66 insertions(+), 26 deletions(-) diff --git a/resources/profiles/Elegoo.idx b/resources/profiles/Elegoo.idx index 6fcdedd3f..8e95dcec9 100644 --- a/resources/profiles/Elegoo.idx +++ b/resources/profiles/Elegoo.idx @@ -1,3 +1,5 @@ +min_slic3r_version = 2.6.0-alpha1 +1.0.2 Added new printer models. min_slic3r_version = 2.5.0-alpha3 1.0.1 Decreased bed size to 220x220. 1.0.0 Initial version diff --git a/resources/profiles/Elegoo.ini b/resources/profiles/Elegoo.ini index d9053e5a2..2a734002e 100644 --- a/resources/profiles/Elegoo.ini +++ b/resources/profiles/Elegoo.ini @@ -6,7 +6,7 @@ name = Elegoo # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.0.1 +config_version = 1.0.2 config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Elegoo/ # The printer models will be shown by the Configuration Wizard in this order, @@ -58,6 +58,33 @@ bed_model = bed_texture = default_materials = Generic PLA @ELEGOO; Generic PETG @ELEGOO; Generic ABS @ELEGOO +[printer_model:NEPTUNE3MAX] +name = Elegoo Neptune-3 Max +variants = 0.4 +technology = FFF +family = NEPTUNE +bed_model = +bed_texture = +default_materials = Generic PLA @ELEGOO; Generic PETG @ELEGOO; Generic ABS @ELEGOO + +[printer_model:NEPTUNE3PLUS] +name = Elegoo Neptune-3 Plus +variants = 0.4 +technology = FFF +family = NEPTUNE +bed_model = +bed_texture = +default_materials = Generic PLA @ELEGOO; Generic PETG @ELEGOO; Generic ABS @ELEGOO + +[printer_model:NEPTUNE3PRO] +name = Elegoo Neptune-3 Pro +variants = 0.4 +technology = FFF +family = NEPTUNE +bed_model = +bed_texture = +default_materials = Generic PLA @ELEGOO; Generic PETG @ELEGOO; Generic ABS @ELEGOO + [printer_model:NEPTUNEX] name = Elegoo Neptune-X variants = 0.4 @@ -428,26 +455,9 @@ retract_length = 5 retract_speed = 60 deretract_speed = 40 retract_before_wipe = 70% -start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S120 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nM140 S[first_layer_bed_temperature] ; set final bed temp\nG4 S10 ; allow partial nozzle warmup\nG28 ; home all axis\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S[first_layer_temperature] ; set final nozzle temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp to stabilize\nM109 S[first_layer_temperature] ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000\nG92 E0\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0 +start_gcode = M413 S0 ; disable Power Loss Recovery\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S120 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nM140 S[first_layer_bed_temperature] ; set final bed temp\nG4 S10 ; allow partial nozzle warmup\nG28 ; home all axis\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S[first_layer_temperature] ; set final nozzle temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp to stabilize\nM109 S[first_layer_temperature] ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000\nG92 E0\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0 end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F600 ; Move print head up{endif}\nG1 X5 Y{print_bed_max[1]*0.8} F{travel_speed*60} ; present print\n{if max_layer_z < max_print_height-10}G1 Z{z_offset+min(max_layer_z+70, max_print_height-10)} F600 ; Move print head further up{endif}\n{if max_layer_z < max_print_height*0.6}G1 Z{max_print_height*0.6} F600 ; Move print head further up{endif}\nM140 S0 ; turn off heatbed\nM104 S0 ; turn off temperature\nM107 ; turn off fan\nM84 X Y E ; disable motors - -# Intended for printers with a smaller bed -# [printer:*fastabl*] -# start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S120 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nM140 S[first_layer_bed_temperature] ; set final bed temp\nG4 S10 ; allow partial nozzle warmup\nG28 ; home all axis\nG29 ; auto bed levelling\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S[first_layer_temperature] ; set final nozzle temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp to stabilize\nM109 S[first_layer_temperature] ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000\nG92 E0\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0 - -# Intended for printers with a larger bed -# [printer:*slowabl*] -# start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S120 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nM140 S[first_layer_bed_temperature] ; set final bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp to stabilize\nG28 ; home all axis\nG29 ; auto bed levelling\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S[first_layer_temperature] ; set final nozzle temp\nM109 S[first_layer_temperature] ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000\nG92 E0\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0 - -# Intended for printers with vendor official firmware verified to support M25 -# [printer:*pauseprint*] -# pause_print_gcode = M25 ; pause print - -# Intended for printers where the Z-axis lowers the print bed during printing -# [printer:*invertedz*] -# end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F600{endif} ; Move print bed down\nG1 X50 Y50 F{travel_speed*60} ; present print\n{if max_layer_z < max_print_height-10}G1 Z{z_offset+max_print_height-10} F600{endif} ; Move print bed down further down\nM140 S0 ; turn off heatbed\nM104 S0 ; turn off temperature\nM107 ; turn off fan\nM84 X Y E ; disable motors - # Intended for printers with dual extruders and a single hotend/nozzle [printer:*dualextruder*] single_extruder_multi_material = 1 @@ -473,7 +483,7 @@ retract_restart_extra = 0,0 retract_restart_extra_toolchange = 0,0 retract_speed = 60,60 wipe = 1,1 -start_gcode = T[initial_tool] ; set active extruder\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\nM104 S150 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nG4 S10 ; allow partial nozzle warmup\nG28 ; home all axis\n;G29 ; auto bed levelling - remove ; at beginning of line to enable\n;M420 S1 ; enable mesh - remove ; at beginning of line to enable\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG1 Z0.28 F240 ; move down to prime nozzle\nG92 E0 ; reset extruder\nG1 E90 ; load filament\nG92 E0 ; reset extruder\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000 ; move over for second prime line\nG92 E0 ; reset extruder\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0 ; reset extruder +start_gcode = T[initial_tool] ; set active extruder\nM413 S0 ; disable Power Loss Recovery\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\nM104 S150 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nG4 S10 ; allow partial nozzle warmup\nG28 ; home all axis\n;G29 ; auto bed levelling - remove ; at beginning of line to enable\n;M420 S1 ; enable mesh - remove ; at beginning of line to enable\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG1 Z0.28 F240 ; move down to prime nozzle\nG92 E0 ; reset extruder\nG1 E90 ; load filament\nG92 E0 ; reset extruder\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000 ; move over for second prime line\nG92 E0 ; reset extruder\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0 ; reset extruder end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F600 ; Move print head up{endif}\nG1 X5 Y{print_bed_max[1]*0.8} F{travel_speed*60} ; present print\nG1 E-80 F2000 ; unload filament\n{if max_layer_z < max_print_height-10}G1 Z{z_offset+min(max_layer_z+70, max_print_height-10)} F600 ; Move print head further up{endif}\n{if max_layer_z < max_print_height*0.6}G1 Z{max_print_height*0.6} F600 ; Move print head further up{endif}\nM140 S0 ; turn off heatbed\nM104 S0 ; turn off temperature\nM107 ; turn off fan\nM84 X Y E ; disable motors # Copy of Creality CR-X config for the Neptune 2D (dual extruder, single hotend) @@ -482,29 +492,57 @@ end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, inherits = Elegoo Neptune-2; *dualextruder* retract_length = 6,6 printer_model = NEPTUNE2D -printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE2D\nPRINTER_HAS_BOWDEN +printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE2D [printer:Elegoo Neptune-2S] inherits = Elegoo Neptune-2 printer_model = NEPTUNE2S -printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE2D\nPRINTER_HAS_BOWDEN +printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE2S [printer:Elegoo Neptune-X] inherits = Elegoo Neptune-2 max_print_height = 300 printer_model = NEPTUNEX -printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE2D\nPRINTER_HAS_BOWDEN +printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNEX [printer:Elegoo Neptune-3] inherits = Elegoo Neptune-2 max_print_height = 280 -start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S120 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nM140 S[first_layer_bed_temperature] ; set final bed temp\nG4 S10 ; allow partial nozzle warmup\nG28 ; home all axis\nG29 ; run abl mesh\nM420 S1 ; load mesh\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S[first_layer_temperature] ; set final nozzle temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp to stabilize\nM109 S[first_layer_temperature] ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000\nG92 E0\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0 +start_gcode = M413 S0 ; disable Power Loss Recovery\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S120 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nM140 S[first_layer_bed_temperature] ; set final bed temp\nG4 S10 ; allow partial nozzle warmup\nG28 ; home all axis\n;G29 ; run abl mesh\nM420 S1 ; load mesh\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S[first_layer_temperature] ; set final nozzle temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp to stabilize\nM109 S[first_layer_temperature] ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000\nG92 E0\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0 printer_model = NEPTUNE3 -printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE2D\nPRINTER_HAS_BOWDEN +printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE3 [printer:Elegoo Neptune-1] inherits = Elegoo Neptune-2 bed_shape = 0x0,210x0,210x210,0x210 max_print_height = 200 printer_model = NEPTUNE1 -printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE2D\nPRINTER_HAS_BOWDEN +printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE1 + +[printer:Elegoo Neptune-3 Max] +inherits = Elegoo Neptune-3 +retract_length = 2.5 +retract_speed = 25 +bed_shape = 0x0,420x0,420x420,0x420 +max_print_height = 500 +printer_model = NEPTUNE3MAX +printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE3MAX + +[printer:Elegoo Neptune-3 Plus] +inherits = Elegoo Neptune-3 +retract_length = 2.5 +retract_speed = 25 +bed_shape = 0x0,320x0,320x320,0x320 +max_print_height = 400 +printer_model = NEPTUNE3PLUS +printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE3PLUS + +[printer:Elegoo Neptune-3 Pro] +inherits = Elegoo Neptune-3 +bed_shape = 0x0,225x0,225x225,0x225 +max_print_height = 280 +retract_length = 2.5 +retract_speed = 25 +printer_model = NEPTUNE3PRO +printer_notes = Do not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ELEGOO\nPRINTER_MODEL_NEPTUNE3PRO + From fc3cdbc99dea85c15ea3b3f23416f88f3f5c20c3 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Tue, 17 Jan 2023 12:17:41 +0100 Subject: [PATCH 25/28] sync with PrusaSlicer-settings --- resources/profiles/Creality.idx | 4 + resources/profiles/Creality.ini | 554 +++++++++++++++++++++++++++++--- 2 files changed, 513 insertions(+), 45 deletions(-) diff --git a/resources/profiles/Creality.idx b/resources/profiles/Creality.idx index 80d951b0b..cd44bb1f1 100644 --- a/resources/profiles/Creality.idx +++ b/resources/profiles/Creality.idx @@ -1,4 +1,8 @@ +min_slic3r_version = 2.6.0-alpha0 +0.2.6 Add Ender-5 Pro, Ender-5 S1, Sermoon-V1, Sermoon-V1 Pro. Unlock HIGHSPEED/SUPERSPEED presets for Ender-5 S1/Ender-6/Ender-7. min_slic3r_version = 2.5.0-alpha0 +0.2.4 Add SPEED presets. More conservative extruder clearance. +0.2.3 Improve start_gcode's. Object labeling. MatterHackers filament profiles. 0.2.2 General improvements. 0.2.1 Added Ender 3 Neo and Ender 3 S1 Plus. Various updates. 0.2.0 Added alternative nozzle support diff --git a/resources/profiles/Creality.ini b/resources/profiles/Creality.ini index 4fab957e2..b617e2e6d 100644 --- a/resources/profiles/Creality.ini +++ b/resources/profiles/Creality.ini @@ -5,7 +5,7 @@ name = Creality # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 0.2.2 +config_version = 0.2.6 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Creality/ # changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -131,14 +131,14 @@ bed_model = ender3_bed.stl bed_texture = ender3.svg default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY -#[printer_model:ENDER5PRO] -#name = Creality Ender-5 Pro -#variants = 0.4; 0.3; 0.5; 0.6 -#technology = FFF -#family = ENDER -#bed_model = ender3_bed.stl -#bed_texture = ender3.svg -#default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY +[printer_model:ENDER5PRO] +name = Creality Ender-5 Pro +variants = 0.4; 0.3; 0.5; 0.6 +technology = FFF +family = ENDER +bed_model = ender3_bed.stl +bed_texture = ender3.svg +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY [printer_model:ENDER5PLUS] name = Creality Ender-5 Plus @@ -149,6 +149,15 @@ bed_model = ender5plus_bed.stl bed_texture = ender5plus.svg default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY +[printer_model:ENDER5S1] +name = Creality Ender-5 S1 +variants = 0.4; 0.3; 0.5; 0.6 +technology = FFF +family = ENDER +bed_model = ender3_bed.stl +bed_texture = ender3.svg +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY + [printer_model:ENDER6] name = Creality Ender-6 variants = 0.4; 0.3; 0.5; 0.6 @@ -394,6 +403,28 @@ default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @ +[printer_model:SERMOONV1] +name = Creality Sermoon-V1 +variants = 0.4; 0.3; 0.5; 0.6 +technology = FFF +family = SERMOON +bed_model = sermoonv1_bed.stl +bed_texture = sermoonv1.svg +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY + + + +[printer_model:SERMOONV1PRO] +name = Creality Sermoon-V1 Pro +variants = 0.4; 0.3; 0.5; 0.6 +technology = FFF +family = SERMOON +bed_model = sermoonv1_bed.stl +bed_texture = sermoonv1.svg +default_materials = Generic PLA @CREALITY; Generic PETG @CREALITY; Generic ABS @CREALITY; Creality PLA @CREALITY; Prusament PLA @CREALITY; Prusament PETG @CREALITY; AzureFilm PLA @CREALITY; Devil Design PLA @CREALITY; Devil Design PLA Matt @CREALITY; Devil Design PLA Galaxy @CREALITY; Extrudr PLA NX2 @CREALITY; Real Filament PLA @CREALITY; Velleman PLA @CREALITY; 3DJAKE ecoPLA @CREALITY; 3DJAKE ecoPLA Matt @CREALITY; 3DJAKE ecoPLA Tough @CREALITY; 123-3D Jupiter PLA @CREALITY; Verbatim PLA @CREALITY + + + # All presets starting with asterisk, for example *common*, are intermediate and they will # not make it into the user interface. @@ -416,8 +447,8 @@ external_fill_pattern = rectilinear external_perimeters_first = 0 external_perimeter_speed = 25 extra_perimeters = 0 -extruder_clearance_height = 34 -extruder_clearance_radius = 47 +extruder_clearance_height = 25 +extruder_clearance_radius = 55 fill_angle = 45 fill_density = 15% fill_pattern = grid @@ -425,6 +456,7 @@ first_layer_height = 0.2 first_layer_speed = 20 gap_fill_speed = 30 gcode_comments = 0 +gcode_label_objects = 1 infill_every_layers = 1 infill_extruder = 1 infill_first = 0 @@ -489,7 +521,62 @@ wipe_tower_x = 170 wipe_tower_y = 140 xy_size_compensation = 0 +[print:*speed*] +perimeter_speed = 60 +small_perimeter_speed = 30 +external_perimeter_speed = 30 +infill_speed = 60 +solid_infill_speed = 60 +top_solid_infill_speed = 30 +support_material_speed = 40 +support_material_interface_speed = 100% +bridge_speed = 25 +ironing_speed = 15 +travel_speed = 150 +travel_speed_z = 0 +first_layer_speed = 20 +first_layer_speed_over_raft = 30 +[print:*highspeed*] +perimeter_speed = 120 +small_perimeter_speed = 60 +external_perimeter_speed = 60 +infill_speed = 120 +solid_infill_speed = 120 +top_solid_infill_speed = 60 +support_material_speed = 80 +support_material_interface_speed = 100% +bridge_speed = 50 +ironing_speed = 30 +travel_speed = 150 +travel_speed_z = 0 +first_layer_speed = 40 +first_layer_speed_over_raft = 60 + +[print:*superspeed*] +perimeter_speed = 180 +small_perimeter_speed = 90 +external_perimeter_speed = 90 +infill_speed = 180 +solid_infill_speed = 180 +top_solid_infill_speed = 90 +support_material_speed = 120 +support_material_interface_speed = 100% +bridge_speed = 75 +ironing_speed = 45 +travel_speed = 250 +travel_speed_z = 0 +first_layer_speed = 60 +first_layer_speed_over_raft = 90 + + + +[print:*0.06mm*] +inherits = *common* +layer_height = 0.06 +bottom_solid_layers = 11 +top_solid_layers = 13 +bridge_flow_ratio = 0.70 [print:*0.08mm*] inherits = *common* @@ -609,6 +696,16 @@ support_material_extrusion_width = 0.54 +[print:0.06 mm ULTRADETAIL (0.3 mm nozzle) @CREALITY] +inherits = *0.06mm*; *0.3nozzle* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.3 and printer_notes=~/.*PRINTER_HAS_ULTRADETAIL.*/ + +[print:0.06 mm ULTRADETAIL (0.4 mm nozzle) @CREALITY] +inherits = *0.06mm*; *0.4nozzle* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.4 and printer_notes=~/.*PRINTER_HAS_ULTRADETAIL.*/ + + + [print:0.08 mm SUPERDETAIL (0.3 mm nozzle) @CREALITY] inherits = *0.08mm*; *0.3nozzle* compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.3 @@ -685,6 +782,60 @@ compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle +[print:0.16 mm OPTIMAL SPEED (0.3 mm nozzle) @CREALITY] +inherits = *0.16mm*; *0.3nozzle*; *speed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.3 + +[print:0.16 mm OPTIMAL SPEED (0.4 mm nozzle) @CREALITY] +inherits = *0.16mm*; *0.4nozzle*; *speed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.4 + +[print:0.16 mm OPTIMAL SPEED (0.5 mm nozzle) @CREALITY] +inherits = *0.16mm*; *0.5nozzle*; *speed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.5 + +[print:0.16 mm OPTIMAL SPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.16mm*; *0.6nozzle*; *speed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 + + + +[print:0.16 mm OPTIMAL HIGHSPEED (0.3 mm nozzle) @CREALITY] +inherits = *0.16mm*; *0.3nozzle*; *highspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.3 and printer_notes=~/.*PRINTER_HAS_HIGHSPEED.*/ + +[print:0.16 mm OPTIMAL HIGHSPEED (0.4 mm nozzle) @CREALITY] +inherits = *0.16mm*; *0.4nozzle*; *highspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.4 and printer_notes=~/.*PRINTER_HAS_HIGHSPEED.*/ + +[print:0.16 mm OPTIMAL HIGHSPEED (0.5 mm nozzle) @CREALITY] +inherits = *0.16mm*; *0.5nozzle*; *highspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.5 and printer_notes=~/.*PRINTER_HAS_HIGHSPEED.*/ + +[print:0.16 mm OPTIMAL HIGHSPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.16mm*; *0.6nozzle*; *highspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 and printer_notes=~/.*PRINTER_HAS_HIGHSPEED.*/ + + + +[print:0.16 mm OPTIMAL SUPERSPEED (0.3 mm nozzle) @CREALITY] +inherits = *0.16mm*; *0.3nozzle*; *superspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.3 and printer_notes=~/.*PRINTER_HAS_SUPERSPEED.*/ + +[print:0.16 mm OPTIMAL SUPERSPEED (0.4 mm nozzle) @CREALITY] +inherits = *0.16mm*; *0.4nozzle*; *superspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.4 and printer_notes=~/.*PRINTER_HAS_SUPERSPEED.*/ + +[print:0.16 mm OPTIMAL SUPERSPEED (0.5 mm nozzle) @CREALITY] +inherits = *0.16mm*; *0.5nozzle*; *superspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.5 and printer_notes=~/.*PRINTER_HAS_SUPERSPEED.*/ + +[print:0.16 mm OPTIMAL SUPERSPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.16mm*; *0.6nozzle*; *superspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 and printer_notes=~/.*PRINTER_HAS_SUPERSPEED.*/ + + + [print:0.20 mm NORMAL (0.3 mm nozzle) @CREALITY] inherits = *0.20mm*; *0.3nozzle* compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.3 @@ -704,6 +855,60 @@ compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle +[print:0.20 mm NORMAL SPEED (0.3 mm nozzle) @CREALITY] +inherits = *0.20mm*; *0.3nozzle*; *speed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.3 + +[print:0.20 mm NORMAL SPEED (0.4 mm nozzle) @CREALITY] +inherits = *0.20mm*; *0.4nozzle*; *speed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.4 + +[print:0.20 mm NORMAL SPEED (0.5 mm nozzle) @CREALITY] +inherits = *0.20mm*; *0.5nozzle*; *speed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.5 + +[print:0.20 mm NORMAL SPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.20mm*; *0.6nozzle*; *speed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 + + + +[print:0.20 mm NORMAL HIGHSPEED (0.3 mm nozzle) @CREALITY] +inherits = *0.20mm*; *0.3nozzle*; *highspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.3 and printer_notes=~/.*PRINTER_HAS_HIGHSPEED.*/ + +[print:0.20 mm NORMAL HIGHSPEED (0.4 mm nozzle) @CREALITY] +inherits = *0.20mm*; *0.4nozzle*; *highspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.4 and printer_notes=~/.*PRINTER_HAS_HIGHSPEED.*/ + +[print:0.20 mm NORMAL HIGHSPEED (0.5 mm nozzle) @CREALITY] +inherits = *0.20mm*; *0.5nozzle*; *highspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.5 and printer_notes=~/.*PRINTER_HAS_HIGHSPEED.*/ + +[print:0.20 mm NORMAL HIGHSPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.20mm*; *0.6nozzle*; *highspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 and printer_notes=~/.*PRINTER_HAS_HIGHSPEED.*/ + + + +[print:0.20 mm NORMAL SUPERSPEED (0.3 mm nozzle) @CREALITY] +inherits = *0.20mm*; *0.3nozzle*; *superspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.3 and printer_notes=~/.*PRINTER_HAS_SUPERSPEED.*/ + +[print:0.20 mm NORMAL SUPERSPEED (0.4 mm nozzle) @CREALITY] +inherits = *0.20mm*; *0.4nozzle*; *superspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.4 and printer_notes=~/.*PRINTER_HAS_SUPERSPEED.*/ + +[print:0.20 mm NORMAL SUPERSPEED (0.5 mm nozzle) @CREALITY] +inherits = *0.20mm*; *0.5nozzle*; *superspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.5 and printer_notes=~/.*PRINTER_HAS_SUPERSPEED.*/ + +[print:0.20 mm NORMAL SUPERSPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.20mm*; *0.6nozzle*; *superspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 and printer_notes=~/.*PRINTER_HAS_SUPERSPEED.*/ + + + [print:0.24 mm DRAFT (0.4 mm nozzle) @CREALITY] inherits = *0.24mm*; *0.4nozzle* renamed_from = "0.24mm DRAFT @CREALITY"; "0.24mm DRAFT @ENDER3" @@ -719,6 +924,48 @@ compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle +[print:0.24 mm DRAFT SPEED (0.4 mm nozzle) @CREALITY] +inherits = *0.24mm*; *0.4nozzle*; *speed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.4 + +[print:0.24 mm DRAFT SPEED (0.5 mm nozzle) @CREALITY] +inherits = *0.24mm*; *0.5nozzle*; *speed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.5 + +[print:0.24 mm DRAFT SPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.24mm*; *0.6nozzle*; *speed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 + + + +[print:0.24 mm DRAFT HIGHSPEED (0.4 mm nozzle) @CREALITY] +inherits = *0.24mm*; *0.4nozzle*; *highspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.4 and printer_notes=~/.*PRINTER_HAS_HIGHSPEED.*/ + +[print:0.24 mm DRAFT HIGHSPEED (0.5 mm nozzle) @CREALITY] +inherits = *0.24mm*; *0.5nozzle*; *highspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.5 and printer_notes=~/.*PRINTER_HAS_HIGHSPEED.*/ + +[print:0.24 mm DRAFT HIGHSPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.24mm*; *0.6nozzle*; *highspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 and printer_notes=~/.*PRINTER_HAS_HIGHSPEED.*/ + + + +[print:0.24 mm DRAFT SUPERSPEED (0.4 mm nozzle) @CREALITY] +inherits = *0.24mm*; *0.4nozzle*; *superspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.4 and printer_notes=~/.*PRINTER_HAS_SUPERSPEED.*/ + +[print:0.24 mm DRAFT SUPERSPEED (0.5 mm nozzle) @CREALITY] +inherits = *0.24mm*; *0.5nozzle*; *superspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.5 and printer_notes=~/.*PRINTER_HAS_SUPERSPEED.*/ + +[print:0.24 mm DRAFT SUPERSPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.24mm*; *0.6nozzle*; *superspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 and printer_notes=~/.*PRINTER_HAS_SUPERSPEED.*/ + + + [print:0.28 mm SUPERDRAFT (0.4 mm nozzle) @CREALITY] inherits = *0.28mm*; *0.4nozzle* renamed_from = "0.28mm SUPERDRAFT @CREALITY" @@ -734,6 +981,48 @@ compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle +[print:0.28 mm SUPERDRAFT SPEED (0.4 mm nozzle) @CREALITY] +inherits = *0.28mm*; *0.4nozzle*; *speed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.4 + +[print:0.28 mm SUPERDRAFT SPEED (0.5 mm nozzle) @CREALITY] +inherits = *0.28mm*; *0.5nozzle*; *speed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.5 + +[print:0.28 mm SUPERDRAFT SPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.28mm*; *0.6nozzle*; *speed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 + + + +[print:0.28 mm SUPERDRAFT HIGHSPEED (0.4 mm nozzle) @CREALITY] +inherits = *0.28mm*; *0.4nozzle*; *highspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.4 and printer_notes=~/.*PRINTER_HAS_HIGHSPEED.*/ + +[print:0.28 mm SUPERDRAFT HIGHSPEED (0.5 mm nozzle) @CREALITY] +inherits = *0.28mm*; *0.5nozzle*; *highspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.5 and printer_notes=~/.*PRINTER_HAS_HIGHSPEED.*/ + +[print:0.28 mm SUPERDRAFT HIGHSPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.28mm*; *0.6nozzle*; *highspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 and printer_notes=~/.*PRINTER_HAS_HIGHSPEED.*/ + + + +[print:0.28 mm SUPERDRAFT SUPERSPEED (0.4 mm nozzle) @CREALITY] +inherits = *0.28mm*; *0.4nozzle*; *superspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.4 and printer_notes=~/.*PRINTER_HAS_SUPERSPEED.*/ + +[print:0.28 mm SUPERDRAFT SUPERSPEED (0.5 mm nozzle) @CREALITY] +inherits = *0.28mm*; *0.5nozzle*; *superspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.5 and printer_notes=~/.*PRINTER_HAS_SUPERSPEED.*/ + +[print:0.28 mm SUPERDRAFT SUPERSPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.28mm*; *0.6nozzle*; *superspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 and printer_notes=~/.*PRINTER_HAS_SUPERSPEED.*/ + + + [print:0.36 mm CHUNKY (0.5 mm nozzle) @CREALITY] inherits = *0.36mm*; *0.5nozzle* compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.5 @@ -744,12 +1033,60 @@ compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle +[print:0.36 mm CHUNKY SPEED (0.5 mm nozzle) @CREALITY] +inherits = *0.36mm*; *0.5nozzle*; *speed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.5 + +[print:0.36 mm CHUNKY SPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.36mm*; *0.6nozzle*; *speed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 + + + +[print:0.36 mm CHUNKY HIGHSPEED (0.5 mm nozzle) @CREALITY] +inherits = *0.36mm*; *0.5nozzle*; *highspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.5 and printer_notes=~/.*PRINTER_HAS_HIGHSPEED.*/ + +[print:0.36 mm CHUNKY HIGHSPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.36mm*; *0.6nozzle*; *highspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 and printer_notes=~/.*PRINTER_HAS_HIGHSPEED.*/ + + + +[print:0.36 mm CHUNKY SUPERSPEED (0.5 mm nozzle) @CREALITY] +inherits = *0.36mm*; *0.5nozzle*; *superspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.5 and printer_notes=~/.*PRINTER_HAS_SUPERSPEED.*/ + +[print:0.36 mm CHUNKY SUPERSPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.36mm*; *0.6nozzle*; *superspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 and printer_notes=~/.*PRINTER_HAS_SUPERSPEED.*/ + + + [print:0.44 mm SUPERCHUNKY (0.6 mm nozzle) @CREALITY] inherits = *0.44mm*; *0.6nozzle* compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 +[print:0.44 mm SUPERCHUNKY SPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.44mm*; *0.6nozzle*; *speed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 + + + +[print:0.44 mm SUPERCHUNKY HIGHSPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.44mm*; *0.6nozzle*; *highspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 and printer_notes=~/.*PRINTER_HAS_HIGHSPEED.*/ + + + +[print:0.44 mm SUPERCHUNKY SUPERSPEED (0.6 mm nozzle) @CREALITY] +inherits = *0.44mm*; *0.6nozzle*; *superspeed* +compatible_printers_condition = printer_model=~/(ENDER|CR|SERMOON).*/ and nozzle_diameter[0]==0.6 and printer_notes=~/.*PRINTER_HAS_SUPERSPEED.*/ + + + # When submitting new filaments please print the following temperature tower at 0.1mm layer height: # https://www.thingiverse.com/thing:2615842 # Pay particular attention to bridging, overhangs and retractions. @@ -830,6 +1167,26 @@ bridge_fan_speed = 30 top_fan_speed = 0 temperature = 245 +[filament:*TPU*] +inherits = *common* +bed_temperature = 50 +cooling = 0 +disable_fan_first_layers = 3 +fan_below_layer_time = 20 +filament_colour = #DDDDDD +filament_max_volumetric_speed = 11 +filament_type = TPU +filament_density = 1.2 +filament_cost = 30 +first_layer_bed_temperature = 55 +first_layer_temperature = 245 +fan_always_on = 0 +max_fan_speed = 0 +min_fan_speed = 0 +bridge_fan_speed = 30 +top_fan_speed = 0 +temperature = 245 + [filament:Generic PLA @CREALITY] inherits = *PLA* renamed_from = "Generic PLA @ENDER3" @@ -1109,6 +1466,50 @@ filament_cost = 16.99 filament_density = 1.24 filament_colour = #2862C4 +[filament:MatterHackers MH Build Series PLA @CREALITY] +inherits = *PLA* +filament_vendor = MatterHackers +temperature = 205 +bed_temperature = 60 +first_layer_temperature = 210 +first_layer_bed_temperature = 60 +filament_cost = 20.87 +filament_density = 1.25 +filament_colour = #3598DB + +[filament:MatterHackers MH Build Series PETG @CREALITY] +inherits = *PET* +filament_vendor = MatterHackers +temperature = 245 +bed_temperature = 60 +first_layer_temperature = 250 +first_layer_bed_temperature = 65 +filament_cost = 21.98 +filament_density = 1.27 +filament_colour = #3598DB + +[filament:MatterHackers MH Build Series ABS @CREALITY] +inherits = *ABS* +filament_vendor = MatterHackers +temperature = 230 +bed_temperature = 90 +first_layer_temperature = 240 +first_layer_bed_temperature = 100 +filament_cost = 20.87 +filament_density = 1.07 +filament_colour = #3598DB + +[filament:MatterHackers MH Build Series TPU @CREALITY] +inherits = *TPU* +filament_vendor = MatterHackers +temperature = 240 +bed_temperature = 50 +first_layer_temperature = 250 +first_layer_bed_temperature = 60 +filament_cost = 28.99 +filament_density = 1.12 +filament_colour = #3598DB + # Common printer preset @@ -1165,7 +1566,7 @@ wipe = 1 z_offset = 0 printer_model = default_filament_profile = "Generic PLA @CREALITY" -start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\nM104 S150 ; set temporary nozzle temp to prevent oozing during homing\nG4 S10 ; allow partial nozzle warmup\nG28 ; home all axis\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000\nG92 E0\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0 +start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S150 ; set temporary nozzle temp to prevent oozing during homing\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\nG4 S30 ; allow partial nozzle warmup\nG28 ; home all axis\nG1 Z50 F240\nG1 X2.0 Y10 F3000\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 X2.0 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 Y140 F5000\nG92 E0\nG1 X2.3 Y10 E10 F1200 ; prime the nozzle\nG92 E0 end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F600 ; Move print head up{endif}\nG1 X5 Y{print_bed_max[1]*0.85} F{travel_speed*60} ; present print\n{if max_layer_z < max_print_height-10}G1 Z{z_offset+min(max_layer_z+70, max_print_height-10)} F600 ; Move print head further up{endif}\n{if max_layer_z < max_print_height*0.6}G1 Z{max_print_height*0.6} F600 ; Move print head further up{endif}\nM140 S0 ; turn off heatbed\nM104 S0 ; turn off temperature\nM107 ; turn off fan\nM84 X Y E ; disable motors # Intended for printers that have exclusively shipped with a 32bit mainboard @@ -1174,19 +1575,19 @@ gcode_flavor = marlin2 # Intended for printers equipped with a strain gauge mechanism, like the CR-6 series [printer:*straingauge*] -start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\nM109 S{first_layer_temperature[0]-50} ; set temporary nozzle temp to prevent oozing during homing\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\nG28 ; home all axis\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000\nG92 E0\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0 +start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM109 S{first_layer_temperature[0]-50} ; set temporary nozzle temp to prevent oozing during homing\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\nG28 ; home all axis\nG1 Z50 F240\nG1 X2.0 Y10 F3000\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 X2.0 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 Y140 F5000\nG92 E0\nG1 X2.3 Y10 E10 F1200 ; prime the nozzle\nG92 E0 # Intended for printers with a smaller bed, like the Ender-3 series [printer:*fastabl*] -start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\nM104 S150 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nG4 S10 ; allow partial nozzle warmup\nG28 ; home all axis\nG29 ; auto bed levelling\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000\nG92 E0\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0 +start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S150 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\nG4 S30 ; allow partial nozzle warmup\nG28 ; home all axis\nG29 ; auto bed levelling\nG1 Z50 F240\nG1 X2.0 Y10 F3000\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 X2.0 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 Y140 F5000\nG92 E0\nG1 X2.3 Y10 E10 F1200 ; prime the nozzle\nG92 E0 # Intended for printers with a larger bed, like the CR-10 series [printer:*slowabl*] -start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\nM104 S150 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\nG28 ; home all axis\nG29 ; auto bed levelling\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000\nG92 E0\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0 +start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S150 ; set temporary nozzle temp to prevent oozing during homing and auto bed leveling\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\nG28 ; home all axis\nG29 ; auto bed levelling\nG1 Z50 F240\nG1 X2.0 Y10 F3000\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 X2.0 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 Y140 F5000\nG92 E0\nG1 X2.3 Y10 E10 F1200 ; prime the nozzle\nG92 E0 # intended for printers that have RESTORE_LEVELING_AFTER_G28 enabled in firmware [printer:*storedabl*] -start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\nM104 S150 ; set temporary nozzle temp to prevent oozing during homing\nG4 S10 ; allow partial nozzle warmup\nG28 ; home all axis and restore leveling\nG1 Z50 F240\nG1 X2 Y10 F3000\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 F5000\nG92 E0\nG1 Y10 E10 F1200 ; prime the nozzle\nG92 E0 +start_gcode = G90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S150 ; set temporary nozzle temp to prevent oozing during homing\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\nG4 S30 ; allow partial nozzle warmup\nG28 ; home all axis and restore leveling\nG1 Z50 F240\nG1 X2.0 Y10 F3000\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG1 Z0.28 F240\nG92 E0\nG1 X2.0 Y140 E10 F1500 ; prime the nozzle\nG1 X2.3 Y140 F5000\nG92 E0\nG1 X2.3 Y10 E10 F1200 ; prime the nozzle\nG92 E0 # Intended for printers with vendor official firmware verified to support M25 [printer:*pauseprint*] @@ -1238,7 +1639,7 @@ retract_before_wipe = 0% [printer:*0.3nozzle*] nozzle_diameter = 0.3 printer_variant = 0.3 -min_layer_height = 0.08 +min_layer_height = 0.06 max_layer_height = 0.24 retract_lift_above = 0.2 default_print_profile = "0.12 mm DETAIL (0.3 mm nozzle) @CREALITY" @@ -1246,7 +1647,7 @@ default_print_profile = "0.12 mm DETAIL (0.3 mm nozzle) @CREALITY" [printer:*0.4nozzle*] nozzle_diameter = 0.4 printer_variant = 0.4 -min_layer_height = 0.08 +min_layer_height = 0.06 max_layer_height = 0.32 retract_lift_above = 0.2 default_print_profile = "0.16 mm OPTIMAL (0.4 mm nozzle) @CREALITY" @@ -1405,7 +1806,7 @@ inherits = *common*; *storedabl*; *spriteextruder*; *pauseprint* bed_shape = 5x0,215x0,215x220,5x220 max_print_height = 270 printer_model = ENDER3S1 -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3S1 +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3S1\nPRINTER_HAS_ULTRADETAIL [printer:Creality Ender-3 S1 (0.3 mm nozzle)] inherits = *ENDER3S1*; *0.3nozzle* @@ -1427,7 +1828,7 @@ inherits = *common*; *storedabl*; *spriteextruder*; *pauseprint* bed_shape = 5x0,215x0,215x220,5x220 max_print_height = 270 printer_model = ENDER3S1PRO -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3S1PRO +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3S1PRO\nPRINTER_HAS_ULTRADETAIL [printer:Creality Ender-3 S1 Pro (0.3 mm nozzle)] inherits = *ENDER3S1PRO*; *0.3nozzle* @@ -1449,7 +1850,7 @@ inherits = *common*; *storedabl*; *spriteextruder*; *pauseprint* bed_shape = 5x5,295x5,295x295,5x295 max_print_height = 300 printer_model = ENDER3S1PLUS -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3S1PLUS +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER3S1PLUS\nPRINTER_HAS_ULTRADETAIL [printer:Creality Ender-3 S1 Plus (0.3 mm nozzle)] inherits = *ENDER3S1PLUS*; *0.3nozzle* @@ -1554,26 +1955,26 @@ inherits = *ENDER5*; *0.6nozzle* -#[printer:*ENDER5PRO*] -#inherits = *common*; *bowdencapricorn*; *descendingz* -#bed_shape = 5x2.5,225x2.5,225x222.5,5x222.5 -#max_print_height = 300 -#printer_model = ENDER5PRO -#printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER5PRO\nPRINTER_HAS_BOWDEN -#machine_max_acceleration_e = 1000 -#machine_max_feedrate_z = 5 -# -#[printer:Creality Ender-5 Pro (0.3 mm nozzle)] -#inherits = *ENDER5PRO*; *0.3nozzle* -# -#[printer:Creality Ender-5 Pro (0.4 mm nozzle)] -#inherits = *ENDER5PRO*; *0.4nozzle* -# -#[printer:Creality Ender-5 Pro (0.5 mm nozzle)] -#inherits = *ENDER5PRO*; *0.5nozzle* -# -#[printer:Creality Ender-5 Pro (0.6 mm nozzle)] -#inherits = *ENDER5PRO*; *0.6nozzle* +[printer:*ENDER5PRO*] +inherits = *common*; *bowdencapricorn*; *descendingz* +bed_shape = 5x2.5,225x2.5,225x222.5,5x222.5 +max_print_height = 300 +printer_model = ENDER5PRO +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER5PRO\nPRINTER_HAS_BOWDEN +machine_max_acceleration_e = 1000 +machine_max_feedrate_z = 5 + +[printer:Creality Ender-5 Pro (0.3 mm nozzle)] +inherits = *ENDER5PRO*; *0.3nozzle* + +[printer:Creality Ender-5 Pro (0.4 mm nozzle)] +inherits = *ENDER5PRO*; *0.4nozzle* + +[printer:Creality Ender-5 Pro (0.5 mm nozzle)] +inherits = *ENDER5PRO*; *0.5nozzle* + +[printer:Creality Ender-5 Pro (0.6 mm nozzle)] +inherits = *ENDER5PRO*; *0.6nozzle* @@ -1603,12 +2004,33 @@ inherits = *ENDER5PLUS*; *0.6nozzle* +[printer:*ENDER5S1*] +inherits = *common*; *descendingz*; *spriteextruder* +bed_shape = 5x0,215x0,215x220,5x220 +max_print_height = 280 +printer_model = ENDER5S1 +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER5S1\nPRINTER_HAS_ULTRADETAIL\nPRINTER_HAS_HIGHSPEED\nPRINTER_HAS_SUPERSPEED + +[printer:Creality Ender-5 S1 (0.3 mm nozzle)] +inherits = *ENDER5S1*; *0.3nozzle* + +[printer:Creality Ender-5 S1 (0.4 mm nozzle)] +inherits = *ENDER5S1*; *0.4nozzle* + +[printer:Creality Ender-5 S1 (0.5 mm nozzle)] +inherits = *ENDER5S1*; *0.5nozzle* + +[printer:Creality Ender-5 S1 (0.6 mm nozzle)] +inherits = *ENDER5S1*; *0.6nozzle* + + + [printer:*ENDER6*] inherits = *common*; *bowden*; *descendingz* bed_shape = 5x5,255x5,255x255,5x255 max_print_height = 400 printer_model = ENDER6 -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER6\nPRINTER_HAS_BOWDEN +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER6\nPRINTER_HAS_BOWDEN\nPRINTER_HAS_HIGHSPEED [printer:Creality Ender-6 (0.3 mm nozzle)] inherits = *ENDER6*; *0.3nozzle* @@ -1630,7 +2052,7 @@ inherits = *common*; *bowden*; *descendingz* bed_shape = 5x5,245x5,245x245,5x245 max_print_height = 300 printer_model = ENDER7 -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER7\nPRINTER_HAS_BOWDEN +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_ENDER7\nPRINTER_HAS_BOWDEN\nPRINTER_HAS_HIGHSPEED\nPRINTER_HAS_SUPERSPEED [printer:Creality Ender-7 (0.3 mm nozzle)] inherits = *ENDER7*; *0.3nozzle* @@ -1805,7 +2227,7 @@ inherits = *common*; *slowabl*; *spriteextruder* bed_shape = 5x5,295x5,295x295,5x295 max_print_height = 400 printer_model = CR10SMARTPRO -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR10SMARTPRO\nPRINTER_HAS_BOWDEN +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_CR10SMARTPRO\nPRINTER_HAS_ULTRADETAIL [printer:Creality CR-10 SMART Pro (0.3 mm nozzle)] inherits = *CR10SMARTPRO*; *0.3nozzle* @@ -2130,7 +2552,7 @@ inherits = *common*; *directdriveextruder*; *descendingz* bed_shape = 5x5,275x5,275x255,5x255 max_print_height = 310 printer_model = SERMOOND1 -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_SERMOOND1\nPRINTER_HAS_BOWDEN +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_SERMOOND1 [printer:Creality Sermoon-D1 (0.3 mm nozzle)] inherits = *SERMOOND1*; *0.3nozzle* @@ -2144,3 +2566,45 @@ inherits = *SERMOOND1*; *0.5nozzle* [printer:Creality Sermoon-D1 (0.6 mm nozzle)] inherits = *SERMOOND1*; *0.6nozzle* + + + +[printer:*SERMOONV1*] +inherits = *common*; *spriteextruder*; *descendingz* +bed_shape = 5x5,170x5,170x170,5x170 +max_print_height = 165 +printer_model = SERMOONV1 +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_SERMOONV1\nPRINTER_HAS_ULTRADETAIL + +[printer:Creality Sermoon-V1 (0.3 mm nozzle)] +inherits = *SERMOONV1*; *0.3nozzle* + +[printer:Creality Sermoon-V1 (0.4 mm nozzle)] +inherits = *SERMOONV1*; *0.4nozzle* + +[printer:Creality Sermoon-V1 (0.5 mm nozzle)] +inherits = *SERMOONV1*; *0.5nozzle* + +[printer:Creality Sermoon-V1 (0.6 mm nozzle)] +inherits = *SERMOONV1*; *0.6nozzle* + + + +[printer:*SERMOONV1PRO*] +inherits = *common*; *spriteextruder*; *descendingz* +bed_shape = 5x5,170x5,170x170,5x170 +max_print_height = 165 +printer_model = SERMOONV1PRO +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_CREALITY\nPRINTER_MODEL_SERMOONV1PRO\nPRINTER_HAS_ULTRADETAIL + +[printer:Creality Sermoon-V1 Pro (0.3 mm nozzle)] +inherits = *SERMOONV1PRO*; *0.3nozzle* + +[printer:Creality Sermoon-V1 Pro (0.4 mm nozzle)] +inherits = *SERMOONV1PRO*; *0.4nozzle* + +[printer:Creality Sermoon-V1 Pro (0.5 mm nozzle)] +inherits = *SERMOONV1PRO*; *0.5nozzle* + +[printer:Creality Sermoon-V1 Pro (0.6 mm nozzle)] +inherits = *SERMOONV1PRO*; *0.6nozzle* From 086dec2196782d36ab5525da54ee9d42a6a05f3b Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Tue, 17 Jan 2023 12:20:23 +0100 Subject: [PATCH 26/28] Sync with PrusaSlicer-settings --- resources/profiles/PrusaResearch.idx | 1 + resources/profiles/PrusaResearch.ini | 203 +++++++++++++++++++++++++-- 2 files changed, 193 insertions(+), 11 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index 7a5b18a1a..68dff19c1 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,4 +1,5 @@ min_slic3r_version = 2.5.0-alpha0 +1.5.5 Added new Prusament Resin material profiles. Enabled g-code thumbnails for MK2.5 family printers. 1.5.4 Added material profiles for Prusament Resin BioBased60. 1.5.3 Added filament profiles for ColorFabb VarioShore TPU, FormFutura PP, NinjaTek NinjaFlex/Cheetah TPU and for multiple Eolas Prints filaments. Updated bridging settings in 50um and 70um profiles. 1.5.2 Added SLA material profiles. diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index f24d3a71a..5922b216a 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.5.4 +config_version = 1.5.5 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -4065,7 +4065,6 @@ inherits = *FLEX* filament_vendor = Eolas Prints filament_cost = 34.99 filament_density = 1.21 -filament_spool_weight = 1000 filament_colour = #4D9398 filament_max_volumetric_speed = 1.2 temperature = 235 @@ -5755,6 +5754,14 @@ material_type = Tough material_vendor = BlueCast material_colour = #007EFD +[sla_material:BlueCast X-One @0.025] +inherits = *common 0.025* +exposure_time = 25 +initial_exposure_time = 35 +material_type = Casting +material_vendor = BlueCast +material_colour = #C0C0C0 + [sla_material:DruckWege Type D High Temp @0.025] inherits = *common 0.025* exposure_time = 6 @@ -5957,6 +5964,14 @@ material_type = Tough material_vendor = Prusa Polymers material_colour = #FCB30E +[sla_material:Prusament Resin Tough Classic Red @0.025] +inherits = *common 0.025* +exposure_time = 3 +initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #EC0000 + [sla_material:Prusament Resin BioBased60 Herbal Green @0.025] inherits = *common 0.025* exposure_time = 7 @@ -5981,6 +5996,22 @@ material_type = Tough material_vendor = Prusa Polymers material_colour = #ECDE05 +[sla_material:Prusament Resin Flex80 Transparent Clear @0.025] +inherits = *common 0.025* +exposure_time = 10 +initial_exposure_time = 30 +material_type = Flexible +material_vendor = Prusa Polymers +material_colour = #F3F6F4 + +[sla_material:Prusament Resin Flex80 Black @0.025] +inherits = *common 0.025* +exposure_time = 8 +initial_exposure_time = 30 +material_type = Flexible +material_vendor = Prusa Polymers +material_colour = #595959 + ## Prusa 0.025 [sla_material:Prusa Orange Tough @0.025] @@ -6432,6 +6463,14 @@ material_type = Tough material_vendor = BlueCast material_colour = #007EFD +[sla_material:BlueCast X-One @0.05] +inherits = *common 0.05* +exposure_time = 27 +initial_exposure_time = 35 +material_type = Casting +material_vendor = BlueCast +material_colour = #C0C0C0 + [sla_material:DruckWege Type D High Temp @0.05] inherits = *common 0.05* exposure_time = 10 @@ -6938,6 +6977,14 @@ material_type = Tough material_vendor = Prusa Polymers material_colour = #FCB30E +[sla_material:Prusament Resin Tough Classic Red @0.05] +inherits = *common 0.05* +exposure_time = 4 +initial_exposure_time = 35 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #EC0000 + [sla_material:Prusament Resin BioBased60 Herbal Green @0.05] inherits = *common 0.05* exposure_time = 8 @@ -6962,6 +7009,22 @@ material_type = Tough material_vendor = Prusa Polymers material_colour = #ECDE05 +[sla_material:Prusament Resin Flex80 Transparent Clear @0.05] +inherits = *common 0.05* +exposure_time = 15 +initial_exposure_time = 30 +material_type = Flexible +material_vendor = Prusa Polymers +material_colour = #F3F6F4 + +[sla_material:Prusament Resin Flex80 Black @0.05] +inherits = *common 0.05* +exposure_time = 10 +initial_exposure_time = 30 +material_type = Flexible +material_vendor = Prusa Polymers +material_colour = #595959 + ## Prusa 0.05 [sla_material:Prusa Beige Tough @0.05] @@ -7252,6 +7315,14 @@ material_type = Tough material_vendor = BlueCast material_colour = #FFEEE6 +[sla_material:BlueCast X-One @0.1] +inherits = *common 0.1* +exposure_time = 30 +initial_exposure_time = 45 +material_type = Casting +material_vendor = BlueCast +material_colour = #C0C0C0 + [sla_material:Ameralabs TGM-7 LED @0.1] inherits = *common 0.1* exposure_time = 10 @@ -7286,6 +7357,14 @@ material_type = Tough material_vendor = Prusa Polymers material_colour = #808080 +[sla_material:Prusament Resin Tough Classic Red @0.1] +inherits = *common 0.1* +exposure_time = 6 +initial_exposure_time = 45 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #EC0000 + [sla_material:Prusament Resin Tough Sandstone Model @0.1] inherits = *common 0.1* exposure_time = 13 @@ -7374,6 +7453,22 @@ material_type = Tough material_vendor = Prusa Polymers material_colour = #ECDE05 +[sla_material:Prusament Resin Flex80 Transparent Clear @0.1] +inherits = *common 0.1* +exposure_time = 20 +initial_exposure_time = 30 +material_type = Flexible +material_vendor = Prusa Polymers +material_colour = #F3F6F4 + +[sla_material:Prusament Resin Flex80 Black @0.1] +inherits = *common 0.1* +exposure_time = 13 +initial_exposure_time = 30 +material_type = Flexible +material_vendor = Prusa Polymers +material_colour = #595959 + ## Prusa 0.1 [sla_material:Prusa Orange Tough @0.1] @@ -7590,6 +7685,14 @@ material_type = Tough material_vendor = Prusa Polymers material_colour = #FCB30E +[sla_material:Prusament Resin Tough Classic Red @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 1.8 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #EC0000 + [sla_material:Prusament Resin BioBased60 Herbal Green @0.025 SL1S] inherits = *0.025_sl1s* exposure_time = 3.5 @@ -7615,6 +7718,24 @@ material_vendor = Prusa Polymers material_colour = #ECDE05 material_print_speed = slow +[sla_material:Prusament Resin Flex80 Transparent Clear @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 4 +initial_exposure_time = 25 +material_type = Flexible +material_vendor = Prusa Polymers +material_colour = #F3F6F4 +material_print_speed = slow + +[sla_material:Prusament Resin Flex80 Black @0.025 SL1S] +inherits = *0.025_sl1s* +exposure_time = 2.6 +initial_exposure_time = 25 +material_type = Flexible +material_vendor = Prusa Polymers +material_colour = #595959 +material_print_speed = slow + ## Made for Prusa 0.025 [sla_material:Prusa Orange Tough @0.025 SL1S] @@ -8042,6 +8163,14 @@ material_type = Tough material_vendor = Prusa Polymers material_colour = #FCB30E +[sla_material:Prusament Resin Tough Classic Red @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 2 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #EC0000 + [sla_material:Prusament Resin BioBased60 Herbal Green @0.05 SL1S] inherits = *0.05_sl1s* exposure_time = 4 @@ -8067,6 +8196,24 @@ material_vendor = Prusa Polymers material_colour = #ECDE05 material_print_speed = slow +[sla_material:Prusament Resin Flex80 Transparent Clear @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 5 +initial_exposure_time = 25 +material_type = Flexible +material_vendor = Prusa Polymers +material_colour = #F3F6F4 +material_print_speed = slow + +[sla_material:Prusament Resin Flex80 Black @0.05 SL1S] +inherits = *0.05_sl1s* +exposure_time = 3 +initial_exposure_time = 25 +material_type = Flexible +material_vendor = Prusa Polymers +material_colour = #595959 +material_print_speed = slow + ## Made for Prusa 0.05 [sla_material:Prusa Orange Tough @0.05 SL1S] @@ -8798,6 +8945,14 @@ material_type = Tough material_vendor = Prusa Polymers material_colour = #FCB30E +[sla_material:Prusament Resin Tough Classic Red @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 2.6 +initial_exposure_time = 25 +material_type = Tough +material_vendor = Prusa Polymers +material_colour = #EC0000 + [sla_material:Prusament Resin BioBased60 Herbal Green @0.1 SL1S] inherits = *0.1_sl1s* exposure_time = 5 @@ -8823,6 +8978,24 @@ material_vendor = Prusa Polymers material_colour = #ECDE05 material_print_speed = slow +[sla_material:Prusament Resin Flex80 Transparent Clear @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 6 +initial_exposure_time = 25 +material_type = Flexible +material_vendor = Prusa Polymers +material_colour = #F3F6F4 +material_print_speed = slow + +[sla_material:Prusament Resin Flex80 Black @0.1 SL1S] +inherits = *0.1_sl1s* +exposure_time = 3.5 +initial_exposure_time = 25 +material_type = Flexible +material_vendor = Prusa Polymers +material_colour = #595959 +material_print_speed = slow + ## Made for Prusa 0.1 [sla_material:Prusa Orange Tough @0.1 SL1S] @@ -9312,6 +9485,8 @@ printer_model = MK2.5 remaining_times = 1 machine_max_jerk_e = 4.5 start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n{if filament_settings_id[initial_tool]=~/.*Prusament PA11.*/}\nG1 Z0.3 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E9 F1000 ; intro line\n{else}\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\n{endif}\nG92 E0 +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y200 F3600 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM900 K0 ; reset LA\nM84 ; disable motors\n; max_layer_z = [max_layer_z] +thumbnails = 160x120 [printer:Original Prusa i3 MK2.5 0.25 nozzle] inherits = Original Prusa i3 MK2S 0.25 nozzle @@ -9319,6 +9494,8 @@ printer_model = MK2.5 remaining_times = 1 machine_max_jerk_e = 4.5 start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\nG92 E0 +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y200 F3600 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM900 K0 ; reset LA\nM84 ; disable motors\n; max_layer_z = [max_layer_z] +thumbnails = 160x120 [printer:Original Prusa i3 MK2.5 0.6 nozzle] inherits = Original Prusa i3 MK2S 0.6 nozzle @@ -9327,6 +9504,8 @@ remaining_times = 1 machine_max_jerk_e = 4.5 deretract_speed = 25 start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n{if filament_settings_id[initial_tool]=~/.*Prusament PA11.*/}\nG1 Z0.3 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E9 F1000 ; intro line\n{else}\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\n{endif}\nG92 E0 +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y200 F3600 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM900 K0 ; reset LA\nM84 ; disable motors\n; max_layer_z = [max_layer_z] +thumbnails = 160x120 [printer:Original Prusa i3 MK2.5 0.8 nozzle] inherits = Original Prusa i3 MK2S 0.6 nozzle @@ -9342,9 +9521,11 @@ retract_lift = 0.25 remaining_times = 1 machine_max_jerk_e = 4.5 start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n{if filament_settings_id[initial_tool]=~/.*Prusament PA11.*/}\nG1 Z0.3 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E9 F1000 ; intro line\n{else}\nG1 Z0.2 F720\nG1 Y-3 F1000 ; go outside print area\nG92 E0\nG1 X60 E9 F1000 ; intro line\nG1 X100 E12.5 F1000 ; intro line\n{endif}\nG92 E0 +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y200 F3600 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM900 K0 ; reset LA\nM84 ; disable motors\n; max_layer_z = [max_layer_z] default_print_profile = 0.40mm QUALITY @0.8 nozzle default_filament_profile = Prusament PLA @0.8 nozzle color_change_gcode = M600\nG1 E0.6 F1500 ; prime after color change +thumbnails = 160x120 [printer:Original Prusa i3 MK2.5 MMU2 Single] inherits = *25mm2* @@ -9379,7 +9560,7 @@ single_extruder_multi_material = 1 nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#3EC0FF;#FF4F4F;#FBEB7D start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E32 F1073\nG1 X5 E32 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\nG92 E0\n -end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y210 F7200 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM900 K0 ; reset LA\nM84 ; disable motors\n +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y210 F7200 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM900 K0 ; reset LA\nM84 ; disable motors\n; max_layer_z = [max_layer_z] [printer:Original Prusa i3 MK2.5S] inherits = Original Prusa i3 MK2.5 @@ -9406,7 +9587,7 @@ default_print_profile = 0.15mm OPTIMAL @MK2.5 default_filament_profile = Prusament PLA printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2.5\n start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n -end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y210 F7200 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM104 S0 ; turn off temperature\nM900 K0 ; reset LA\nM84 ; disable motors +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y210 F7200 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM104 S0 ; turn off temperature\nM900 K0 ; reset LA\nM84 ; disable motors\n; max_layer_z = [max_layer_z] [printer:Original Prusa i3 MK2.5S MMU2S Single 0.8 nozzle] inherits = Original Prusa i3 MK2.5S MMU2S Single @@ -9457,7 +9638,7 @@ single_extruder_multi_material = 1 nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#3EC0FF;#FF4F4F;#FBEB7D start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E29 F1073\nG1 X5 E29 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\nG92 E0\n -end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y210 F7200 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM900 K0 ; reset LA\nM84 ; disable motors\n +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y210 F7200 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM900 K0 ; reset LA\nM84 ; disable motors\n; max_layer_z = [max_layer_z] [printer:Original Prusa i3 MK2.5S MMU2S 0.6 nozzle] inherits = Original Prusa i3 MK2.5S MMU2S @@ -9527,7 +9708,7 @@ color_change_gcode = M600\nG1 E0.3 F1500 ; prime after color change [printer:Original Prusa i3 MK3] inherits = *common* -end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y200 F3600 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM84 ; disable motors +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y200 F3600 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM84 ; disable motors\n; max_layer_z = [max_layer_z] machine_max_acceleration_e = 5000,5000 machine_max_acceleration_extruding = 1250,1250 machine_max_acceleration_retracting = 1250,1250 @@ -9659,7 +9840,7 @@ inherits = *mm2* single_extruder_multi_material = 0 default_filament_profile = Prusament PLA start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} -end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y210 F7200 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y210 F7200 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors\n; max_layer_z = [max_layer_z] [printer:Original Prusa i3 MK3 MMU2 Single 0.6 nozzle] inherits = Original Prusa i3 MK3 MMU2 Single @@ -9707,7 +9888,7 @@ machine_max_acceleration_e = 8000,8000 nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#3EC0FF;#FF4F4F;#FBEB7D start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E32 F1073\nG1 X5 E32 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} -end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y210 F7200 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors\n +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y210 F7200 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors\n; max_layer_z = [max_layer_z] [printer:Original Prusa i3 MK3S & MK3S+ MMU2S Single] inherits = *mm2s* @@ -9715,7 +9896,7 @@ renamed_from = "Original Prusa i3 MK3S MMU2S Single" single_extruder_multi_material = 0 default_filament_profile = Prusament PLA start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nTc\n; purge line\nG1 X55 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} -end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y210 F7200 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y210 F7200 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM702 C\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors\n; max_layer_z = [max_layer_z] [printer:Original Prusa i3 MK3S & MK3S+ MMU2S Single 0.6 nozzle] inherits = Original Prusa i3 MK3S & MK3S+ MMU2S Single @@ -9766,7 +9947,7 @@ machine_max_acceleration_e = 8000,8000 nozzle_diameter = 0.4,0.4,0.4,0.4,0.4 extruder_colour = #FF8000;#DB5182;#3EC0FF;#FF4F4F;#FBEB7D start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM115 U3.11.0 ; tell printer latest fw version\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3 F1000\nG1 Z0.4 F1000\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55 E29 F1073\nG1 X5 E29 F1800\nG1 X55 E8 F2000\nG1 Z0.3 F1000\nG92 E0\nG1 X240 E25 F2200\nG1 Y-2 F1000\nG1 X55 E25 F1400\nG1 Z0.2 F1000\nG1 X5 E4 F1000\nG92 E0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG92 E0\n\n; Don't change E values below. Excessive value can damage the printer.\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE).*/}M907 E430 ; set extruder motor current{endif}\n{if print_settings_id=~/.*(SPEED @MK3|DRAFT @MK3).*/}M907 E538 ; set extruder motor current{endif} -end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y210 F7200 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors\n +end_gcode = {if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nG1 X0 Y210 F7200 ; park\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+49, max_print_height)} F720 ; Move print head further up{endif}\n{if has_wipe_tower}\nG1 E-15 F3000\n{else}\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15 F5800\nG1 E-20 F5500\nG1 E10 F3000\nG1 E-10 F3100\nG1 E10 F3150\nG1 E-10 F3250\nG1 E10 F3300\n{endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM221 S100 ; reset flow\nM900 K0 ; reset LA\n{if print_settings_id=~/.*(DETAIL @MK3|QUALITY @MK3|SOLUBLE|@0.25 nozzle MK3).*/}M907 E538 ; reset extruder motor current{endif}\nM104 S0 ; turn off temperature\nM84 ; disable motors\n; max_layer_z = [max_layer_z] ## 0.6mm nozzle MMU2/S printer profiles @@ -9883,7 +10064,7 @@ retract_layer_change = 1 silent_mode = 0 remaining_times = 1 start_gcode = M862.3 P \"[printer_model]\" ; printer model check\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM204 T1250 ; set travel acceleration\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM204 T[machine_max_acceleration_travel] ; restore travel acceleration\nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0\nG1 Y-2 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110 E8 F900\nG1 X40 E10 F700\nG92 E0\n\nM221 S95 ; set flow -end_gcode = G1 E-1 F2100 ; retract\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F720 ; Move print head up{endif}\nG1 X178 Y178 F4200 ; park print head\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)} F720 ; Move print head further up{endif}\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM221 S100 ; reset flow\nM900 K0 ; reset LA\nM84 ; disable motors +end_gcode = G1 E-1 F2100 ; retract\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F720 ; Move print head up{endif}\nG1 X178 Y178 F4200 ; park print head\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+30, max_print_height)} F720 ; Move print head further up{endif}\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM221 S100 ; reset flow\nM900 K0 ; reset LA\nM84 ; disable motors\n; max_layer_z = [max_layer_z] printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MINI\n extruder_colour = color_change_gcode = M600 From 9db305c51c5aacd6853ac2c87e2306167e6aa96a Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Tue, 17 Jan 2023 14:12:16 +0100 Subject: [PATCH 27/28] Initial BIQU bundle Based on https://github.com/slic3r/slic3r-profiles/pull/32 --- resources/profiles/BIQU.idx | 2 + resources/profiles/BIQU.ini | 437 +++++++++++++++++++ resources/profiles/BIQU/BIQUBX_thumbnail.png | Bin 0 -> 26530 bytes resources/profiles/BIQU/BX_Bed.stl | Bin 0 -> 273784 bytes resources/profiles/BIQU/BX_Texture.png | Bin 0 -> 74266 bytes 5 files changed, 439 insertions(+) create mode 100644 resources/profiles/BIQU.idx create mode 100644 resources/profiles/BIQU.ini create mode 100644 resources/profiles/BIQU/BIQUBX_thumbnail.png create mode 100644 resources/profiles/BIQU/BX_Bed.stl create mode 100644 resources/profiles/BIQU/BX_Texture.png diff --git a/resources/profiles/BIQU.idx b/resources/profiles/BIQU.idx new file mode 100644 index 000000000..03c0035af --- /dev/null +++ b/resources/profiles/BIQU.idx @@ -0,0 +1,2 @@ +min_slic3r_version = 2.6.0-alpha1 +0.1.0 Initial version diff --git a/resources/profiles/BIQU.ini b/resources/profiles/BIQU.ini new file mode 100644 index 000000000..b21d5a276 --- /dev/null +++ b/resources/profiles/BIQU.ini @@ -0,0 +1,437 @@ +# Print profiles for BIQU printers. +# Based on PR https://github.com/slic3r/slic3r-profiles/pull/32 by @bkonosky + + +[vendor] +name = BIQU +config_version = 0.1.0 +config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/BIQU/ + +# The printer models will be shown by the Configuration Wizard in this order, +# also the first model installed & the first nozzle installed will be activated after install. +# Printer model name will be shown by the installation wizard. + +[printer_model:BIQUBX] +name = BIQU BX +variants = 0.4 +technology = FFF +bed_model = BX_Bed.stl +bed_texture = BX_Texture.png +default_materials = Generic PLA @BIQU; Generic PETG @BIQU; Generic ABS @BIQU + +# All presets starting with asterisk, for example *common*, are intermediate and they will +# not make it into the user interface. + +# Common print preset +[print:*common*] +avoid_crossing_perimeters = 0 +bottom_fill_pattern = monotonic +bridge_angle = 0 +bridge_flow_ratio = 0.95 +bridge_speed = 25 +brim_width = 0 +clip_multipart_objects = 1 +complete_objects = 0 +bridge_acceleration = 250 +perimeter_acceleration = 500 +infill_acceleration = 500 +first_layer_acceleration = 500 +default_acceleration = 500 +dont_support_bridges = 1 +ensure_vertical_shell_thickness = 1 +external_perimeters_first = 0 +external_perimeter_speed = 25 +extra_perimeters = 0 +extruder_clearance_height = 34 +extruder_clearance_radius = 47 +fill_angle = 45 +fill_density = 15% +fill_pattern = gyroid +first_layer_height = 0.2 +elefant_foot_compensation = 0.1 +first_layer_speed = 20 +gap_fill_speed = 30 +gcode_comments = 0 +infill_every_layers = 1 +infill_extruder = 1 +infill_first = 0 +infill_only_where_needed = 0 +infill_overlap = 25% +infill_speed = 50 +interface_shells = 0 +max_print_speed = 100 +max_volumetric_extrusion_rate_slope_negative = 0 +max_volumetric_extrusion_rate_slope_positive = 0 +max_volumetric_speed = 0 +min_skirt_length = 4 +notes = +only_retract_when_crossing_perimeters = 0 +ooze_prevention = 0 +output_filename_format = {input_filename_base}_{print_time}_{digits(layer_height,1,2)}mm_{temperature[0]}C_{filament_type[0]}_{printer_model}.gcode +perimeters = 2 +perimeter_extruder = 1 +perimeter_speed = 40 +raft_layers = 0 +seam_position = nearest +single_extruder_multi_material_priming = 0 +skirts = 1 +skirt_distance = 3 +skirt_height = 2 +small_perimeter_speed = 25 +solid_infill_below_area = 0 +solid_infill_every_layers = 0 +solid_infill_extruder = 1 +solid_infill_speed = 40 +spiral_vase = 0 +standby_temperature_delta = -5 +support_material = 0 +support_material_extruder = 0 +support_material_interface_extruder = 0 +support_material_angle = 0 +support_material_buildplate_only = 0 +support_material_enforce_layers = 0 +support_material_contact_distance = 0.15 +support_material_bottom_contact_distance = 0.15 +support_material_interface_contact_loops = 0 +support_material_interface_layers = 2 +support_material_interface_pattern = rectilinear +support_material_interface_spacing = 0.2 +support_material_interface_speed = 100% +support_material_pattern = rectilinear +support_material_solid_first_layer = 1 +support_material_spacing = 2 +support_material_speed = 40 +support_material_synchronize_layers = 0 +support_material_threshold = 40 +support_material_with_sheath = 0 +support_material_xy_spacing = 60% +thin_perimeters = 1 +thin_walls = 1 +top_fill_pattern = monotonic +top_solid_infill_speed = 30 +travel_speed = 150 +wipe_tower = 1 +wipe_tower_bridging = 10 +wipe_tower_rotation_angle = 0 +wipe_tower_width = 60 +wipe_tower_x = 160 +wipe_tower_y = 160 +xy_size_compensation = 0 +extrusion_width = 0.45 +external_perimeter_extrusion_width = 0.43 +first_layer_extrusion_width = 0.55 +support_material_extrusion_width = 0.38 +top_infill_extrusion_width = 0.43 + +[print:*0.08mm*] +inherits = *common* +layer_height = 0.08 +perimeters = 3 +bottom_solid_layers = 9 +top_solid_layers = 11 +bridge_flow_ratio = 0.7 + +[print:*0.10mm*] +inherits = *common* +layer_height = 0.10 +perimeters = 3 +bottom_solid_layers = 7 +top_solid_layers = 9 +bridge_flow_ratio = 0.7 + +[print:*0.12mm*] +inherits = *common* +layer_height = 0.12 +perimeters = 3 +bottom_solid_layers = 6 +top_solid_layers = 7 +bridge_flow_ratio = 0.7 + +[print:*0.16mm*] +inherits = *common* +layer_height = 0.16 +bottom_solid_layers = 5 +top_solid_layers = 7 +bridge_flow_ratio = 0.85 + +[print:*0.20mm*] +inherits = *common* +layer_height = 0.20 +bottom_solid_layers = 4 +top_solid_layers = 5 + +[print:*0.24mm*] +inherits = *common* +layer_height = 0.24 +bottom_solid_layers = 3 +top_solid_layers = 4 + +[print:*0.28mm*] +inherits = *common* +layer_height = 0.28 +bottom_solid_layers = 3 +top_solid_layers = 4 + +[print:*0.32mm*] +inherits = *common* +layer_height = 0.32 +bottom_solid_layers = 3 +top_solid_layers = 4 + +[print:0.08mm HIGHDETAIL @BIQU] +inherits = *0.08mm* +compatible_printers_condition = printer_model=~/(BIQUBX).*/ + +[print:0.10mm HIGHDETAIL @BIQU] +inherits = *0.10mm* +compatible_printers_condition = printer_model=~/(BIQUBX).*/ + +[print:0.12mm DETAIL @BIQU] +inherits = *0.12mm* +compatible_printers_condition = printer_model=~/(BIQUBX).*/ + +[print:0.16mm OPTIMAL @BIQU] +inherits = *0.16mm* +compatible_printers_condition = printer_model=~/(BIQUBX).*/ + +[print:0.20mm NORMAL @BIQU] +inherits = *0.20mm* +compatible_printers_condition = printer_model=~/(BIQUBX).*/ + +[print:0.24mm DRAFT @BIQU] +inherits = *0.24mm* +compatible_printers_condition = printer_model=~/(BIQUBX).*/ + +[print:0.28mm SUPERDRAFT @BIQU] +inherits = *0.28mm* +compatible_printers_condition = printer_model=~/(BIQUBX).*/ + +[filament:*common*] +cooling = 0 +compatible_printers = +extrusion_multiplier = 1 +filament_cost = 0 +filament_density = 0 +filament_diameter = 1.75 +filament_notes = "" +filament_settings_id = "" +filament_soluble = 0 +min_print_speed = 15 +slowdown_below_layer_time = 20 +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_BIQU.*/ + +[filament:*PLA*] +inherits = *common* +bed_temperature = 60 +fan_below_layer_time = 100 +filament_colour = #DDDDDD +filament_max_volumetric_speed = 15 +filament_type = PLA +filament_density = 1.24 +filament_cost = 20 +first_layer_bed_temperature = 60 +first_layer_temperature = 210 +fan_always_on = 1 +cooling = 1 +max_fan_speed = 100 +min_fan_speed = 100 +bridge_fan_speed = 100 +disable_fan_first_layers = 1 +temperature = 205 + +[filament:*PET*] +inherits = *common* +bed_temperature = 70 +cooling = 1 +disable_fan_first_layers = 3 +fan_below_layer_time = 20 +filament_colour = #DDDDDD +filament_max_volumetric_speed = 8 +filament_type = PETG +filament_density = 1.27 +filament_cost = 30 +first_layer_bed_temperature = 70 +first_layer_temperature = 240 +fan_always_on = 1 +max_fan_speed = 50 +min_fan_speed = 20 +bridge_fan_speed = 100 +temperature = 240 + +[filament:*ABS*] +inherits = *common* +bed_temperature = 100 +cooling = 0 +disable_fan_first_layers = 3 +fan_below_layer_time = 20 +filament_colour = #DDDDDD +filament_max_volumetric_speed = 11 +filament_type = ABS +filament_density = 1.04 +filament_cost = 20 +first_layer_bed_temperature = 100 +first_layer_temperature = 245 +fan_always_on = 0 +max_fan_speed = 0 +min_fan_speed = 0 +bridge_fan_speed = 30 +top_fan_speed = 0 +temperature = 245 + +[filament:Generic PLA @BIQU] +inherits = *PLA* +filament_vendor = Generic + +[filament:Generic PETG @BIQU] +inherits = *PET* +filament_vendor = Generic + +[filament:Generic ABS @BIQU] +inherits = *ABS* +first_layer_bed_temperature = 90 +bed_temperature = 90 +filament_vendor = Generic + + + +# Common printer preset +[printer:*common*] +printer_technology = FFF +before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;{layer_z}\n\n +between_objects_gcode = +pause_print_gcode = +deretract_speed = 40 +extruder_colour = #FCE94F +extruder_offset = 0x0 +gcode_flavor = marlin2 +silent_mode = 0 +remaining_times = 0 +machine_max_acceleration_e = 10000 +machine_max_acceleration_extruding = 1000 +machine_max_acceleration_retracting = 1000 +machine_max_acceleration_x = 1000 +machine_max_acceleration_y = 1000 +machine_max_acceleration_z = 100 +machine_max_feedrate_e = 65 +machine_max_feedrate_x = 200 +machine_max_feedrate_y = 200 +machine_max_feedrate_z = 10 +machine_max_jerk_e = 5 +machine_max_jerk_x = 10 +machine_max_jerk_y = 10 +machine_max_jerk_z = 2 +machine_min_extruding_rate = 0 +machine_min_travel_rate = 0 +layer_gcode = ;AFTER_LAYER_CHANGE\n;{layer_z} +max_layer_height = 0.28 +min_layer_height = 0.08 +max_print_height = 250 +nozzle_diameter = 0.4 +printer_notes = +printer_settings_id = +retract_before_travel = 2 +retract_before_wipe = 70% +retract_layer_change = 1 +retract_length = 5 +retract_length_toolchange = 1 +retract_lift = 0 +retract_lift_above = 0 +retract_lift_below = 0 +retract_restart_extra = 0 +retract_restart_extra_toolchange = 0 +retract_speed = 60 +single_extruder_multi_material = 0 +thumbnails = 16x16,220x124 +thumbnails_color = #018aff +thumbnails_custom_color = 0 +thumbnails_end_file = 0 +thumbnails_format = PNG +thumbnails_with_bed = 0 +toolchange_gcode = +use_firmware_retraction = 0 +use_relative_e_distances = 0 +use_volumetric_e = 0 +variable_layer_height = 1 +wipe = 1 +z_offset = 0 +z_step = 0.04 +printer_model = +default_print_profile = 0.16mm OPTIMAL @BIQU +default_filament_profile = Generic PLA @BIQU +start_gcode = M117 Initial homing sequence. ; Home so that the probe is positioned to heat\nG28\n\nM117 Probe heating position\nG0 X65 Y5 Z1 ; Move the probe to the heating position.\n\nM117 Getting the heaters up to temp!\nM104 S140 ; Set Extruder temperature, no wait\nM140 S[first_layer_bed_temperature] ; Set Heat Bed temperature\nM190 S[first_layer_bed_temperature] ; Wait for Heat Bed temperature\n\nM117 Waiting for probe to warm!\nG4 S90 ; Wait another 90s for the probe to absorb heat.\n\nM117 Post warming re-home\nG28 ; Home all axes again after warming\n\nM117 Z-Align\nG34\n\nM117 ABL Probing\nG29\n\nM900 K0 L0 T0 ; Edit the K and L values if you have calibrated a k factor for your filament\nM900 T0 S0\n\nG1 Z2.0 F3000 ; Move Z Axis up little to prevent scratching of Heat Bed\nG1 X4.1 Y10 Z0.3 F5000.0 ; Move to start position\n\nM117 Getting the extruder up to temp\nM140 S[first_layer_bed_temperature] ; Set Heat Bed temperature\nM104 S{first_layer_temperature[initial_tool]} ; Set Extruder temperature\nM109 S{first_layer_temperature[initial_tool]} ; Wait for Extruder temperature\nM190 S[first_layer_bed_temperature] ; Wait for Heat Bed temperature\n\nG92 E0 ; Reset Extruder\nM117 Purging\nG1 X4.1 Y200.0 Z0.3 F1500.0 E15 ; Draw the first line\nG1 X4.4 Y200.0 Z0.3 F5000.0 ; Move to side a little\nG1 X4.4 Y20 Z0.3 F1500.0 E30 ; Draw the second line\nG92 E0 ; Reset Extruder\nM117 Printing...\nG1 X8 Y20 Z0.3 F5000.0 ; Move over to prevent blob squish +end_gcode = G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract a bit more and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z by 10mm\nG90 ;Return to absolute positioning\n\nG1 X0 Y{print_bed_max[1]*0.8} ;TaDaaaa\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n + +[printer:*spriteextruder*] +retract_length = 0.6 +retract_speed = 40 +deretract_speed = 40 +retract_before_travel = 1 +retract_before_wipe = 0% + +# Intended for printers with dual extruders and a single hotend/nozzle, like the CR-X series +[printer:*dualextruder*] +single_extruder_multi_material = 1 +cooling_tube_length = 5 +cooling_tube_retraction = 91.5 +extra_loading_move = -2 +parking_pos_retraction = 92 +deretract_speed = 40,40 +extruder_colour = #FCE94F;#729FCF +extruder_offset = 0x0,0x0 +max_layer_height = 0.28,0.28 +min_layer_height = 0.08,0.08 +nozzle_diameter = 0.4,0.4 +retract_before_travel = 2,2 +retract_before_wipe = 70%,70% +retract_layer_change = 1,1 +retract_length = 5,5 +retract_length_toolchange = 1,1 +retract_lift = 0,0 +retract_lift_above = 0,0 +retract_lift_below = 0,0 +retract_restart_extra = 0,0 +retract_restart_extra_toolchange = 0,0 +retract_speed = 60,60 +wipe = 1,1 + +[printer:*SmallBowden*] +inherits = *common* +machine_max_acceleration_e = 5000 +machine_max_acceleration_extruding = 500 +machine_max_acceleration_retracting = 1000 +machine_max_acceleration_x = 500 +machine_max_acceleration_y = 500 +machine_max_acceleration_z = 100 +machine_max_feedrate_e = 60 +machine_max_feedrate_x = 500 +machine_max_feedrate_y = 500 +machine_max_feedrate_z = 10 +machine_max_jerk_e = 5 +machine_max_jerk_x = 8 +machine_max_jerk_y = 8 +machine_max_jerk_z = 0.4 +machine_min_extruding_rate = 0 +machine_min_travel_rate = 0 +retract_before_travel = 2 +retract_length = 5 +retract_speed = 60 +deretract_speed = 40 +retract_before_wipe = 70% +default_filament_profile = Generic PLA @BIQU + +[printer:*0.4nozzle*] +nozzle_diameter = 0.4 +max_layer_height = 0.32 +min_layer_height = 0.04 +printer_variant = 0.4 +default_print_profile = 0.20mm NORMAL @BIQU + +[printer:*BIQU BX*] +inherits = *common*; *spriteextruder* +bed_shape = 0x0,250x0,250x250,0x250 +max_print_height = 250 +printer_model = BIQUBX +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_BIQU\nPRINTER_MODEL_BIQUBX + +[printer:BIQU BX] +inherits = *BIQU BX*; *0.4nozzle* \ No newline at end of file diff --git a/resources/profiles/BIQU/BIQUBX_thumbnail.png b/resources/profiles/BIQU/BIQUBX_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..ee41f3c48e20cdc5971719ee68f3137668282769 GIT binary patch literal 26530 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 literal 0 HcmV?d00001 diff --git a/resources/profiles/BIQU/BX_Bed.stl b/resources/profiles/BIQU/BX_Bed.stl new file mode 100644 index 0000000000000000000000000000000000000000..0499b79bd407dc76d00f1fbb5b9c35991968aa01 GIT binary patch literal 273784 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

iQ*pUeMPstJf1men-}sF~_{&);%Ve&kT| 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 act^u2y6KaiHD`Xe-`Z~8|0L(D??+O% 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 =Xg6r#&AiCW0S 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 literal 0 HcmV?d00001 From abb8e3d9b144c79bc14b3529d14ab94107fc70e7 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 17 Jan 2023 13:08:44 +0100 Subject: [PATCH 28/28] Fixed update of UI when background slicing is stopped by Platter update function. The update used to be performed by sending EVT_PROCESS_COMPLETED wxCommandEvent to Plater, however the type of the event was refactored (enhanced) to SlicingProcessCompletedEvent long time ago, thus the message sent in this particular case was not being delivered. --- src/slic3r/GUI/Plater.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ea168ba7f..79ce8e4b1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -124,9 +124,14 @@ static const std::pair THUMBNAIL_SIZE_3MF = { 256, 2 namespace Slic3r { namespace GUI { +// Trigger Plater::schedule_background_process(). wxDEFINE_EVENT(EVT_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); +// BackgroundSlicingProcess updates UI with slicing progress: Status bar / progress bar has to be updated, possibly scene has to be refreshed, +// see PrintBase::SlicingStatus for the content of the message. wxDEFINE_EVENT(EVT_SLICING_UPDATE, SlicingStatusEvent); +// FDM slicing finished, but G-code was not exported yet. Initial G-code preview shall be displayed by the UI. wxDEFINE_EVENT(EVT_SLICING_COMPLETED, wxCommandEvent); +// BackgroundSlicingProcess finished either with success or error. wxDEFINE_EVENT(EVT_PROCESS_COMPLETED, SlicingProcessCompletedEvent); wxDEFINE_EVENT(EVT_EXPORT_BEGAN, wxCommandEvent); @@ -3350,10 +3355,8 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool if (invalidated != Print::APPLY_STATUS_UNCHANGED && was_running && ! this->background_process.running() && (return_state & UPDATE_BACKGROUND_PROCESS_RESTART) == 0) { // The background processing was killed and it will not be restarted. - wxCommandEvent evt(EVT_PROCESS_COMPLETED); - evt.SetInt(-1); // Post the "canceled" callback message, so that it will be processed after any possible pending status bar update messages. - wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone()); + wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new SlicingProcessCompletedEvent(EVT_PROCESS_COMPLETED, 0, SlicingProcessCompletedEvent::Cancelled, std::exception_ptr{})); } if ((return_state & UPDATE_BACKGROUND_PROCESS_INVALID) != 0)